如何在csproj文件中忽略从命令行传递的属性值?

4
我们的TFS构建控制器将同一解决方案中的所有项目构建到同一个共享二进制目录中,因为TFS构建工作流会将OutDir参数传递给负责构建解决方案的msbuild命令。
我有一个项目想要抑制这种行为,并让它构建到标准的相对bin\Debugbin\Release目录中。
但我找不到如何做到这一点。实际上,观察下面这个简单的msbuild脚本:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <OutDir>$(MSBuildThisFileDirectory)bin\$(Configuration)</OutDir>
  </PropertyGroup>
  <Target Name="Build">
      <Message Text="$(OutDir)" Importance="High"/>
  </Target>
</Project>

现在,我正在运行它:

PS C:\> msbuild .\1.csproj /p:OutDir=XoXo /nologo
Build started 11/13/2015 9:50:57 PM.
Project "C:\1.csproj" on node 1 (default targets).
Build:
  XoXo
Done Building Project "C:\1.csproj" (default targets).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.03
PS C:\>

注意,它展示的是XoXo,忽略了我的内部重写尝试。

那么,这可行吗?

1个回答

5
这是一个经典的RTFM情况,但仍然很有趣。请参阅MSBuild Properties文档,特别是有关全局属性部分以及如何使属性不被覆盖:

通过使用/property(或/p)开关,在命令行上设置属性。这些全局属性值将覆盖在项目文件中设置的属性值。这包括环境属性,但不包括保留属性,这些属性无法更改。

可以使用MSBuild任务的Properties属性为多项目构建中的子项目设置或修改全局属性。

如果在project标记中使用TreatAsLocalProperty属性指定属性,则该全局属性值不会覆盖在项目文件中设置的属性值。

它还链接到项目元素文档,基本上重复了相同的信息,并表示属性中的多个属性应由分号分隔。
简而言之,适用于您的情况的代码:
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         TreatAsLocalProperty="OutDir">

请注意,这将完全禁用从项目外更改OutDir。一个更可配置的替代方案可能是拥有一个小的存根项目,您使TFS构建该项目而不是主项目。在该项目中,您可以决定是否将OutDir传递给实际项目或覆盖它,例如通过导入一个可能定义或未定义的文件来获取值,或者基于环境变量等。这是基本的想法:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Try import if this file exists, it should supply the value for CustomOutDir-->
  <Import Project="$(MSBuildThisFileDirectory)customoutdir.props" Condition="Exists('$(MSBuildThisFileDirectory)customoutdir.props')"/>
  <PropertyGroup>
    <!-- Default value for CustomOutDir if not set elsewhere -->
    <CustomOutDir Condition="'$(CustomOutDir)' == ''">$(MSBuildThisFileDirectory)bin\$(Configuration)</CustomOutDir>
    <!-- ApplyCustomOutDir specifies whether or not to apply CustomOutDir -->
    <ActualOutDir Condition="'$(ApplyCustomOutDir)' == 'True'">$(CustomOutDir)</ActualOutDir>
    <ActualOutDir Condition="'$(ApplyCustomOutDir)' != 'True'">$(OutDir)</ActualOutDir>
  </PropertyGroup>
  <Target Name="Build">
    <MSBuild Projects="$(MasterProject)" Properties="OutDir=$(ActualOutDir)"/>
  </Target>
</Project>

应该通过传递所需的属性来调用,例如:

msbuild stub.targets /p:MasterProject=/path/to/main.vcxproj;ApplyCustomOutDir=True

我从未使用过TFS,因此传递属性的方式可能不同。


1
RTFM的批评是应该的,虽然我不确定我的阅读理解能力是否足够强大,能够突破这份特定的文档 :-). 我正在采用存根项目方法来进行Gated Check-In构建,但CI构建在我之前就已经存在了,所以它就是这样工作的。非常感谢,收获颇丰。 - mark
你对文档的观点是正确的:它并不十分清晰,例如如果文档一开始就说明“全局属性是……”会更好。 - stijn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接