如何将PreProcessorDefinitions设置为msbuild任务的属性?

13
以下内容来自常规的VS2010 C++项目。
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>

如果我编辑PreprocessorDefinitions,我可以设置一个被预处理器使用的定义。我可以通过#ifdef等在我的代码中看到这一点。
然而,如果我使用以下内容:
  <Target Name="NormalBuild" Condition=" '$(_InvalidConfigurationWarning)' != 'true' " DependsOnTargets="_DetermineManagedStateFromCL;CustomBeforeBuild;$(BuildDependsOn)" Returns="@(ManagedTargetPath)">
    <ItemGroup>
        <ManagedTargetPath Include="$(TargetPath)" Condition="'$(ManagedAssembly)' == 'true'" />
    </ItemGroup>
      <Message Text="PreprocessorDefinitions: $(PreprocessorDefinitions)" Importance="High" />
  </Target>

  <Target Name="TestBuild" Returns="@(ManagedTargetPath)">
    <MSBuild Projects="demo.vcxproj" Targets="NormalBuild" Properties="PreprocessorDefinitions=THISGETSSETBUTDOESNOTHING"/>
  </Target>

我可以通过消息看到PreprocessorDefinitions包含我通过Properties="PreprocessorDefinitions=THISGETSSETBUTDOESNOTHING"设置的值,但是我无法使用#ifdef等来控制我的构建。 如果我使用常规设置并尝试使用<Message Text="PreprocessorDefinitions: $(PreprocessorDefinitions)"输出PreprocessorDefinitions ,则该字段实际上为空,并且不包含预期的<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>,尽管我可以使用其中任何一个键来使用#ifdef等来控制我的构建。
  1. 为什么会这样?
  2. 我应该如何通过任务的Properties元素传递PreprocessorDefinitions给VS2010 C++项目?
3个回答

38
这可以在不修改原始项目的情况下完成:通常在普通C++项目中导入的最后一件事是在 Microsoft.Cpp.Targets 中执行的第一项操作,它会检查是否存在名为 ForceImportBeforeCppTargets 的属性,如果存在,则导入它。
因此,假设您想要将ADDITIONAL添加到预处理器定义中,您可以创建一个名为'override.props'的文件,其内容如下(要完全自动化,请使用WritelinesToFile任务来创建文件):
<?xml version="1.0" encoding="utf-8"?> 
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>%(PreprocessorDefinitions);ADDITIONAL</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>

</Project>

并且呼叫

<MSBuild Projects="demo.vcxproj" 
         Properties="ForceImportBeforeCppTargets=override.props"/>

或者从命令行执行

msbuild demo.vcxproj /p:ForceImportBeforeCppTargets=override.props

注意,如评论中的richb所述,上述方法仅在msbuild的查找规则可以找到override.props时才有效。为确保它始终被发现,请指定完整路径。

更新

几年后,在msbuild 15及以上版本中,有新的构建自定义方式。原理与上述相同,但更简单:msbuild将自动获取名为Directory.Build.props的文件,该文件位于项目文件目录甚至其上面所有目录中,无需其他命令行选项(因此,也可以在VS内使用而无需任何问题)。而且项目文件也变得更加简洁:

<Project>
  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>%(PreprocessorDefinitions);ADDITIONAL</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>
</Project>

8
谢谢提供这些信息,非常有帮助。只需要做出一些更正。首先,命令行参数是 /p: 而不是 /t:。其次,您需要指定 override.props 的完整路径,否则 msbuild 会在没有任何警告的情况下忽略它。 - richb
2
我只想强调@richb的完整路径提及,我发现这对使其工作至关重要。通过输出<ResourceCompile>部分,它可以与资源编译器定义一起使用。 - mj2008
1
是的,实际上你回答中的最后一句话有误导性——即使文件位于vcxproj所在的同一目录或者msbuild被调用的目录中,它也不起作用。我花了相当长的时间才弄清楚这一点。 - Predelnik
1
抱歉,我会将其更改为仅使用“完整路径”,这总是有效的,而且是更好的做法。 - stijn
是的,由于找不到文件,静默失败加载.props文件非常令人恼火。您可以在.props文件中使用特意格式错误的xml来检查文件路径是否正确。如果.props文件被正确找到,它将在构建时打印一个错误。 - cowlinator

8

如果你需要访问CLCompilePreprocessorDefinitions,就必须修改demo.vcxproj,因为它不是PropertyGroup,所以无法通过MSBuild命令行传递。

你可以通过GUI界面修改预处理器定义:项目属性 -> 配置属性 -> C/C++ -> 预处理器,或直接编辑XML文件:

  <ClCompile>
    ....
    <PreprocessorDefinitions>$(MyMacro);%(PreprocessorDefinitions)</PreprocessorDefinitions>
  </ClCompile>

在你的MSBuild项目中:
  <Target Name="TestBuild" Returns="@(ManagedTargetPath)">
    <MSBuild Projects="demo.vcxproj" Targets="NormalBuild" Properties="MyMacro=THISGETSSETBUTDOESNOTHING"/>
  </Target>

这相当于以以下方式运行MSBuild.exe:
  MSBuild demo.vcxproj /p:MyMacro=THISGETSSETBUTDOESNOTHING

我的2017版本中没有任何一个能够工作,而且我也没有ClCompile部分。 - Pysis
我不经常在MSVC/VS世界中工作,更多使用Eclipse。我已经添加了一个新文件夹和一个空的cpp文件,但是像Build Path一样,我认为它还没有被识别,或者C++“facet”不在“aggregate”项目文件中。一旦我意识到错误在于各个子项目vcxproj文件中定义自己的预处理器语句,导致出现了大量的xkeycheck错误,我就看到每个项目都需要进行编辑。谢谢! - Pysis

2
根据@Kevin的回答,您需要在PropertySheet中定义一个用户定义的宏。然后创建一个引用用户定义的宏的预处理器。现在,您可以在代码中使用新的预处理器值。最后,在构建过程中,您可以使用/p标志更改用户定义的宏的值。 在这里,我定义了一个用户定义的值,如mymacro和一个预处理器值,如VAL。 现在,您只需使用/p:mymacro="\"some thing new\""编译项目即可。
#include <iostream>


int main() {
    std::cout << VAL << std::endl;

    getchar();
}

yourproject.vcxproj:

<ClCompile>
  ...
  <PreprocessorDefinitions>VAL=$(mymacro);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>

最初的回答:
运行以下命令来使用MSBuild构建您的项目:msbuild yourproject.vcxproj /p:mymacro="\"some thing new\""。

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