为什么覆盖msbuild中的Build目标适用于C ++项目但C#项目失败?

时间:2018-06-19 08:02:01

标签: visual-studio msbuild

我在我的文件OverrideBuild.targets中重写了这样的Build目标:

<Target Name="OriginalBuild" DependsOnTargets="$(BuildDependsOn)">
  <Message Text="Finished running target OriginalBuild" Importance="High" />
</Target>

<Target Name="Build" >
  <CheckArtifacts ProjectGuid = "$(ProjectGuid)" SolutionPath = "$(SolutionPath)" >
    <Output PropertyName = "ArtifactsHaveChanged" TaskParameter = "Result" />
  </CheckArtifacts>
  <Message Text="ArtifactsHaveChanged = $(ArtifactsHaveChanged)" Importance="high" />

  <!-- if the artifacts.props file has not just been updated then we can run the original build target -->
  <Message    Condition="'$(ArtifactsHaveChanged)' == 'false'" Text="Running target OriginalBuild" Importance="High" />
  <CallTarget Condition="'$(ArtifactsHaveChanged)' == 'false'" Targets="OriginalBuild" />

  <!-- Otherwise we need to run a new msbuild to avoid using an out-of-date cached version of the artifacts.props file.
     To force the msbuild process not to use the cached values from this process we must pass at least one property.
  -->
  <Message Condition="'$(ArtifactsHaveChanged)' == 'true'" Text="Running target OriginalBuild in nested msbuild" Importance="High" />
  <MSBuild Condition="'$(ArtifactsHaveChanged)' == 'true'" Targets="OriginalBuild"
           Projects="$(MSBuildProjectFullPath)" Properties="InNestedMsbuild=true" />
  <!-- Visual Studio doesn't pick up on the modified artifacts.props file unless we force it to reload the solution -->
  <Touch Condition="'$(ArtifactsHaveChanged)' == 'true' and '$(BuildingInsideVisualStudio)' == 'true'" Files = "$(SolutionPath)" />

  <Message Text="Finished running build target override" Importance="High" />
</Target>

我的每个.vcxproj或.csproj文件都包含此文件:

  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\..\OverrideBuild.targets" />
</Project>

这就像我想要的C ++项目一样,但是C#项目失败了。通过msbuild构建C#项目时,它会失败,因为C#编译器的命令行缺少本地程序集的引用参数。例如,一个C#文件,在文件顶部有一行这样的行:

using My.Utils.Common;

失败,并显示以下错误消息:

error CS0234: The type or namespace name 'Common' does not exist in the namespace 'My.Utils' (are you missing an assembly reference?)

查看使用的编译器命令,它缺少这一行:

/reference:C:\Code\scratch\Build\My.Utils.Common\Bin\Release\My.Utils.Common.dll

当我注释掉我对构建目标的覆盖时,存在缺少的行。奇怪的是,即使我的Build覆盖到位,它也可以在Visual Studio中构建。它仅在从命令行使用msbuild进行构建时才会失败,并且仅适用于C#项目。

我认为我覆盖Build目标的方式是完全透明的,但显然不是。任何人都可以了解出了什么问题吗?

2 个答案:

答案 0 :(得分:1)

似乎,当项目A依赖于具有项目引用的项目B时,B的Build目标的输出将用于推断构建A时应作为引用传递给编译器的内容。 ResolveAssemblyReferences逻辑。

因此,要使替换的Build目标正常工作,您需要使其输出与标准Build的输出匹配。

这是您可以实现的方法:

<Target
  Name="Build"
  Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
  DependsOnTargets="GetTargetPathWithTargetPlatformMoniker"
  Returns="@(TargetPathWithTargetPlatformMoniker)" >
</Target>

SDK中的标准Returns="@(TargetPathWithTargetPlatformMoniker)"的{​​{1}}是Returns。但是项目数组Build最初是空的,因此您需要先运行目标@(TargetPathWithTargetPlatformMoniker)来填充它。

这些是构建系统的实现细节,因此它们可能因SDK版本而异,但是您始终可以检查GetTargetPathWithTargetPlatformMoniker或等效语言中的逻辑。

请注意,这不能直接用于C ++项目,它们的默认目标C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.target有点不同。您可能需要根据项目类型而有所不同以同时支持两者。目标上的条件不会阻止它覆盖现有的条件,只会停止执行它,因此,如果您需要不同的目标覆盖,则需要将替代方案放入文件中并有条件地将其导入。我不知道有什么更方便的方法,但这至少行得通。

答案 1 :(得分:0)

  

为什么在msbuild中覆盖Build目标对于C ++项目有效,但对于C#项目却失败?

测试完样本后,我发现错误并非来自overriden the Build target,它应与您引用的项目类型有关。

因为我尝试在HelloWorld项目文件中注释导入行:

<Import Project="..\..\OverrideBuild.targets" />

然后,MSBuild命令行仍然会抛出该错误。

此外,我发现您引用的项目HelloWorldHelper是一个控制台应用程序项目,其输出类型为 Class library

要解决此问题,我创建了一个新的 Class库,而不是Console应用程序,然后从MSBuild命令行对其进行构建,效果很好。

因此,请尝试将您引用的项目转换为Class library

希望这会有所帮助。