Building the same solution using msbuild and deven

2019-06-18 22:52发布

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.

2条回答
Luminary・发光体
2楼-- · 2019-06-18 23:06

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.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-06-18 23:20

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.

查看更多
登录 后发表回答