如何从MSBuild目标修改ItemDefinitionGroup? [英] How can one modify an ItemDefinitionGroup from an MSBuild target?

查看:71
本文介绍了如何从MSBuild目标修改ItemDefinitionGroup?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个写的msbuild脚本来编译Google协议缓冲区文件:

I have an msbuild script I wrote to compile Google Protocol Buffers files:

<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文件的其他源文件需要知道这些文件的生成位置-生成位置需要放在包含路径上.

This compiles the protocol buffers files perfectly, and adds them to the compiler's inputs (yay!). However, my other source files that want to include the .pb.h files need to know where these files got generated -- that generation location needs to be put on the include path.

因此,当且仅当用户在脚本中的某个位置包含<ProtocolBuffer项时,我才想将生成位置(在本例中为$(ProtoOutPath)添加到ClCompile的<AdditionalIncludeDirectories>中.)

Therefore, if and only if the user has included a <ProtocolBuffer item somewhere in their script, I want to add the generation location (in this case $(ProtoOutPath) to ClCompile's <AdditionalIncludeDirectories>.

这是否可能,或者是否需要制作要使用这些生成的位的.cpp文件?

Is that possible or do I need to make .cpp files that want to use these generated bits jump through hoops?

推荐答案

阅读您的问题并认为不能那么难".伙计,我错了.首先,我以为只是在上面加上一个条件,但是由于评估顺序,当然不能在顶级条件中使用ItemGroups.然后我发现也不可能将ItemDefinitionGroup放在目标中(因为可以使用条件)并在那里进行修改.然后我意识到可能是您问这个问题的原因之后,我敲了几次键盘:[(顺便说一句,您知道包括一个不存在的目录并不是真正的问题,因为编译器会很乐意忽略它吗?)

Read your question and thought "can't be that hard". Man, was I wrong. First I thought just putting a condition on it, but of course one can't use ItemGroups in toplevel conditions because of evaluation order. Then I figured it's also not possible to put an ItemDefinitionGroup in a target (cause there one can use conditions) and modify it there. Then I bonked my head on the keyboard a couple of times after I realized that's probably why you asked the question :] (btw you know including a nonexisting directory is not really a problem since the compiler will happily ignore it?)

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

Maybe there's a simpler solution, but lastly I figured: if nothing works, my favourite msbuild toy aka CodeTaskFactory must be able to fix it. It does (I hope, didn't fully test the result), but it's not straightforward at all. Here you go, make sure to invoke the Test target somewhere before the C++ build starts.

<!--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不为空,否则在这种情况下它将打印

unless the ProtocolBuffer is not empty in which case it prints

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

这篇关于如何从MSBuild目标修改ItemDefinitionGroup?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆