2013-08-19 51 views
5

我有一個MSBuild腳本我寫信給谷歌編譯Protocol Buffers的文件:(!耶)如何從MSBuild目標修改ItemDefinitionGroup?

<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文件的其他源文件需要知道這些文件的生成位置 - 生成位置需要放在包含路徑中。

因此,當且僅當用戶已經包括了<ProtocolBuffer項目的地方在他們的劇本,我想補充的生成位置(在這種情況下$(ProtoOutPath)到ClCompile的<AdditionalIncludeDirectories>

那是可能的,或者我需要使想要使用這些生成的位的.cpp文件跳過循環?

回答

4

閱讀您的問題和想法「不能那麼難」人,是我錯了首先,我認爲只是在它的條件,但當然,由於評估順序,不能在頂級條件中使用ItemGroups,那麼我認爲也不可能將ItemDefinitionGroup放入目標中(在那裏可以使用條件)並在那裏修改它。然後,我bonked我的頭在鍵盤上了幾次後,我意識到這可能就是你問的問題:(?順便說一句,你知道其中一個不存在的目錄是不是一個真正的問題,因爲編譯器會很樂意忽略它)

也許有一個更簡單的解決方案,但最後我想:如果什麼都不起作用,我最喜歡的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 
+1

我面臨着同樣的問題(雖然設置'PreprocessorDefinitions'編程而不是' AdditionalIncludeDirectories')。聖牛這是神奇的,但它出奇的好。謝謝! –