【问题标题】:How can one modify an ItemDefinitionGroup from an MSBuild target?如何从 MSBuild 目标修改 ItemDefinitionGroup?
【发布时间】:2013-08-19 00:14:02
【问题描述】:

我编写了一个 msbuild 脚本来编译 Google Protocol Buffers 文件:

<ItemGroup>
  <ProtocolBuffer Include="Whitelist.proto" />
  <ProtocolBuffer Include="Whitelist2.proto" />
</ItemGroup>
<ItemDefinitionGroup>
  <ProtocolBuffer>
    <ProtoPath>$(ProjectDir)</ProtoPath>
  </ProtocolBuffer>
</ItemDefinitionGroup>
<PropertyGroup>
  <ProtoC>$([System.IO.Path]::GetFullPath($(ProjectDir)..\ThirdParty\protobuf-2.4.1\protoc.exe))</ProtoC>
  <ProtoOutPath>$(IntDir)CompiledProtocolBuffers</ProtoOutPath>
</PropertyGroup>
<Target Name="CompileProtocolBuffers"
        BeforeTargets="ClCompile"
        Inputs="@(ProtocolBuffer)"
        Outputs="@(ProtocolBuffer->'$(ProtoOutPath)\%(FileName).pb.cc');@(ProtocolBuffer->'$(ProtoOutPath)\%(FileName).pb.h')">
  <MakeDir Directories="$(ProtoOutPath)" />
  <Exec
    Command="&quot;$(ProtoC)&quot; --proto_path=&quot;$([System.IO.Path]::GetDirectoryName(%(ProtocolBuffer.ProtoPath)))&quot; --cpp_out=&quot;$(ProtoOutPath)&quot; &quot;%(ProtocolBuffer.FullPath)&quot; --error_format=msvs"
        />
  <ItemGroup>
    <ClInclude Include="$(ProtoOutPath)\%(ProtocolBuffer.FileName).pb.h" />
    <ClCompile Include="$(ProtoOutPath)\%(ProtocolBuffer.FileName).pb.cc">
      <AdditionalIncludeDirectories>$(MSBuildThisDirectory)..\ThirdParty\protobuf-2.4.1\src</AdditionalIncludeDirectories>
      <PrecompiledHeader></PrecompiledHeader>
      <DisableSpecificWarnings>4244;4276;4018;4355;4800;4251;4996;4146;4305</DisableSpecificWarnings>
      <PreprocessorDefinitions>GOOGLE_PROTOBUF_NO_RTTI</PreprocessorDefinitions>
      <WarningLevel>Level3</WarningLevel>
    </ClCompile>
  </ItemGroup>
</Target>

这完美地编译了协议缓冲区文件,并将它们添加到编译器的输入中(耶!)。但是,我想要包含 .pb.h 文件的其他源文件需要知道这些文件是在哪里生成的——该生成位置需要放在包含路径中。

因此,当且仅当用户在其脚本中的某处包含 &lt;ProtocolBuffer 项目时,我想添加生成位置(在本例中为 $(ProtoOutPath) 到 ClCompile 的 &lt;AdditionalIncludeDirectories&gt;

这可能吗,还是我需要让想要使用这些生成的位的 .cpp 文件跳过箍?

【问题讨论】:

    标签: c++ msbuild


    【解决方案1】:

    阅读您的问题并认为“不可能那么难”。伙计,我错了吗。首先我想只是给它一个条件,但是由于评估顺序,当然不能在顶级条件中使用 ItemGroups。然后我认为也不可能将 ItemDefinitionGroup 放在目标中(因为那里可以使用条件)并在那里修改它。然后我意识到这可能就是你问这个问题的原因后,我在键盘上敲了几次头

    也许有一个更简单的解决方案,但最后我想:如果没有任何效果,我最喜欢的 msbuild 玩具又名 CodeTaskFactory 必须能够修复它。确实如此(我希望,没有完全测试结果),但这一点也不简单。在这里,请确保在 C++ 构建开始之前在某处调用测试目标。

    <!--Uncomment the below to define some ProtocolBuffers-->  
    <!--<ItemGroup>
      <ProtocolBuffer Include="Whitelist.proto" />
      <ProtocolBuffer Include="Whitelist2.proto" />
    </ItemGroup>-->
    
    <!--Suppose these are your default include files defined in your C++ project-->
    <ItemDefinitionGroup Label="DefaultIncludes">
      <ClCompile>
        <AdditionalIncludeDirectories>/path/to/x;/path/to/y</AdditionalIncludeDirectories>
      </ClCompile>
    </ItemDefinitionGroup>
    
    <!--Include at least one item so we can play with it-->
    <ItemGroup>
      <ClCompile Include="iamaninclude"/>
    </ItemGroup>
    
    <!--Use code to append to AdditionalIncludeDirectories-->
    <UsingTask TaskName="AppendMetadata" TaskFactory="CodeTaskFactory" 
               AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
      <ParameterGroup>
        <Append ParameterType="System.String" Required="true"/>
        <ItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true"/>
        <OutputItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
      </ParameterGroup>
        <Task>
            <Code>
            <![CDATA[
                const string dirz = "AdditionalIncludeDirectories";
                foreach( var item in ItemList )
                {
                  var cur = item.GetMetadata( dirz );
                  item.SetMetadata( dirz, cur + ";" + Append );
                }
                OutputItemList = ItemList;
            ]]>
        </Code>
      </Task>
    </UsingTask>
    
    <!--Main target-->  
    <Target Name="Test">
      <!--stage 1: copy the itemgroup, then clear it:
      if an Output TaskParameter is an Itemgroup, apparently the content
      gets appended to the group instead of replacing it.
      Found no documentation about this whatsoever though???-->
      <ItemGroup Condition="@(ProtocolBuffer) != ''">
        <ClCompileCopy Include="@(ClCompile)"/>
        <ClCompile Remove="@(ClCompile)"/>
      </ItemGroup>
    
      <!--stage 2: append 'ProtoBufIncludeDir' to AdditionalIncludeDirectories,
      and append the result to the origiginal again-->
      <AppendMetadata ItemList="@(ClCompileCopy)" Append="ProtoBufIncludeDir" Condition="@(ProtocolBuffer) != ''">
        <Output ItemName="ClCompile" TaskParameter="OutputItemList"/>
      </AppendMetadata>
    
      <!--stage 3: use modified itemgroup-->
      <Message Text="@(ClCompile->'%(Identity): %(AdditionalIncludeDirectories)')"/>
    </Target>
    

    打印出来

    iamaninclude: /path/to/x;/path/to/y
    

    除非 ProtocolBuffer 不为空,否则它会打印

    iamaninclude: /path/to/x;/path/to/y;ProtoBufIncludeDir
    

    【讨论】:

    • 我遇到了同样的问题(尽管以编程方式设置PreprocessorDefinitions 而不是AdditionalIncludeDirectories)。天哪,这很神奇,但效果出奇的好。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-27
    • 2014-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多