Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building the same solution using msbuild and devenv interchangeably

It so happens quite often that I start by building some solution on the command line using msbuild, run it for awhile, but then discover that I need to change and debug it. So, I open it in Visual Studio and end up building it inside the Visual Studio.

What happens is that VS rebuilts quite a few parts of the solution, even when I change nothing!

Digging into it reveals the following:

Building inside Visual Studio generates empty cs files and injects them into the Compile item group. Naturally, they are newer than the already built binaries and so devenv.exe rebuilds an awful lot of projects. Thanks to TemporaryGeneratedFile_[guid] in /obj/debug breaking build

This is a real bummer. I disabled this behavior by renaming the Microsoft.WorkflowBuildExtensions.targets file - I do not do workflow.

I suppose I could hack into CoreCompileDependsOn and somehow neutralize the GenerateCompiledExpressionsTempFile target from Microsoft.WorkflowBuildExtensions.targets, but this has to be done in 190 projects! This is a serious change.

devenv.exe seems to care about some files always being copied to the output directory, even if msbuild does not think it is a problem.

Indeed, here is a line from the devenv.exe build log:

Project 'HRCore.Tests' is not up to date. Project item 'C:\abc\UI\HRCore.Tests\HRImport\Import_missing_state_county.xml' has 'Copy to Output Directory' attribute set to 'Copy always'.

So what? msbuild does not care about it, but devenv does. This files is not a dependency of the HRCore.Tests and msbuild got it right.

In the meantime I changed it from Always to PreserveNewest.

Anyway, I am interested to know how can I eliminate these differences.

For example, is it a good idea to set BuildingInsideVisualStudio to true even when building with msbuild?

Any ideas?

P.S.

We build both .NET and Silverlight. Console applications, dlls and web applications.

like image 691
mark Avatar asked Mar 28 '14 02:03

mark


People also ask

What is the difference between MSBuild and Devenv?

Devenv uses MSBuild to build projects, but MSBuild does not build Visual C++ Projects. If MSBuild comes across a solution that includes Visual C++ projects, it will call VCBuild. If the projects are using C++, then MSBuild is not called.

What is the difference between MSBuild and Visual Studio build?

Visual Studio determines the build order and calls into MSBuild separately (as needed), all completely under Visual Studio's control. Another difference arises when MSBuild is invoked with a solution file, MSBuild parses the solution file, creates a standard XML input file, evaluates it, and executes it as a project.

How do you create a solution using MSBuild?

At the command line, type MSBuild.exe <SolutionName>. sln , where <SolutionName> corresponds to the file name of the solution that contains the target that you want to execute. Specify the target after the -target: switch in the format <ProjectName>:<TargetName>.

Can I use MSBuild without Visual Studio?

To install MSBuild on a system that doesn't have Visual Studio, go to Build Tools for Visual Studio 2019, or install the . NET SDK. If you have Visual Studio, then you already have MSBuild installed. With Visual Studio 2022, it's installed under the Visual Studio installation folder.


2 Answers

That .targets file indeed causes different behavior between Visual Studio and command line MsBuild due to the explicit check for $(BuildingInsideVisualStudio):

  <PropertyGroup>
    <PrepareResourcesDependsOn>
      ValidationExtension;
      ExpressionBuildExtension;
      $(PrepareResourcesDependsOn)
    </PrepareResourcesDependsOn>
  </PropertyGroup>

  <PropertyGroup>
    <!-- Explicit check for Visual Studio here -->
    <CoreCompileDependsOn Condition="'$(BuildingInsideVisualStudio)' == 'true'">
        GenerateCompiledExpressionsTempFile;
        $(CoreCompileDependsOn)
    </CoreCompileDependsOn>   
  </PropertyGroup>

That explicit check makes the temp file generation only occur in Visual Studio in my configuration. These files are made so that you can step into them and debug them when running inside Visual Studio.

The behavior can be turned off by overriding <GenerateCompiledExpressionsTempFilePathForEditing /> to empty in your project file of clearing it's value explicitly on the commandline.

/p:GenerateCompiledExpressionsTempFilePathForEditing=''

This is the condition that guards this behavior:

 <Target Name ="GenerateCompiledExpressionsTempFile" 
      Condition = "'$(GenerateCompiledExpressionsTempFilePathForEditing)' != ''">  

Looking at the MsBuild path, you should be able to turn off the generation of compiled expressions completely by passing /p:DisableWorkflowCompiledExpression=true on the commandline on.

You can also override CoreCompileDependsOn and remove the GenerateCompiledExpressionsTempFile; item from it.

It's really a pitty when there is "special behavior" when buildign inside the editor. In the end, I'd always err towards making Visual Studio behave as MsBuild and not the other way around. Many other targets will assume special services or the availability of the Host Compiler when BuildingInsideVisualStudio is true, it seems a very bad idea to make MsBuild think it's inside Visual Studio while it actually isn't.

like image 198
jessehouwing Avatar answered Oct 23 '22 14:10

jessehouwing


Another useful solution on this page suggested suggested several alternatives, of which clearly the best is to remove the GenerateCompiledExpressionsTempFile target from CoreCompileDependsOn property. Unfortunately, that property is, well..., an MSBuild property, as opposed to an MSBuild item, so removing a specific part of it isn't totally straightforward.[note 1.]

This is perhaps why @jessehouwing went on to suggest other ways of preventing the Microsoft.Workflow­BuildExtensions target from running--and therefore adding those three annoying .cs files to every compile, namely, by tweaking some of the ambient MSBuild values the target examines when deciding what to do.

So now to my small contribution... Why manipulate the factors the unwanted target considers, trying to trick it not to run, when you can simply blank-out the whole Target itself?

Just add the following line to the bottom of your .csproj project file:

    ...etc...
    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    <Target Name="GenerateCompiledExpressionsTempFile" />          <!-- add this line -->
</Project>



Notes:
1. Removing a delimited item from a property is certainly possible in MSBuild, using inline $([System.String]::Replace(...)), for example.
like image 22
Glenn Slayden Avatar answered Oct 23 '22 14:10

Glenn Slayden