2013-08-19 10 views
5

devo uno script MSBuild che ho scritto per compilare i file di Google Protocol Buffer: (! Yay)Come si può modificare un ItemDefinitionGroup da una destinazione MSBuild?

<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> 

Questo compila i file perfettamente i buffer di protocollo, e li aggiunge agli ingressi del compilatore. Tuttavia, i miei altri file sorgente che vogliono includere i file .pb.h devono sapere dove questi file sono stati generati - quella posizione di generazione deve essere inserita nel percorso di inclusione.

Pertanto, se e solo se l'utente ha inserito un elemento <ProtocolBuffer da qualche parte nel loro scritto, voglio aggiungere la posizione generazione (in questo caso $(ProtoOutPath) a ClCompile di <AdditionalIncludeDirectories>.

È quello possibile o ho bisogno per creare file .cpp che vogliono usare questi bit generati saltare attraverso i cerchi

risposta

4

Leggi la tua domanda e pensa "non può essere così difficile." L'uomo, ho sbagliato. Innanzitutto ho pensato di mettere una condizione su di esso, ma ovviamente non è possibile utilizzare ItemGroup in condizioni di toplevel a causa dell'ordine di valutazione, quindi ho pensato che non sia possibile inserire un oggetto ItemDefinitionGroup in una destinazione (perché lì si possono usare le condizioni) e modificarlo lì. Poi ho fatto un paio di volte la mia tastiera sulla tastiera dopo aver capito che è probabilmente il motivo per cui hai posto la domanda:] (sapete che includere una directory inesistente non è un problema in quanto il compilatore lo ignorerà felicemente?)

Forse c'è una soluzione più semplice, ma alla fine ho capito: se non funziona nulla, il mio giocattolo preferito di msbuild aka CodeTaskFactory deve essere in grado di risolverlo. Lo fa (spero, non abbia completamente testato il risultato), ma non è affatto semplice. A questo punto, assicurati di richiamare il target Test da qualche parte prima del. Inizia la generazione di 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> 

Questo stampa

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

a meno che il ProtocolBuffer non è vuota, nel qual caso viene stampato

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

mi trovai di fronte con lo stesso problema (anche se l'impostazione 'PreprocessorDefinitions' di programmazione invece di' AdditionalIncludeDirectories'). Sacra vacca, questa è magia, ma funziona sorprendentemente bene. Grazie! –

Problemi correlati