c++ - How can one modify an ItemDefinitionGroup from an MSBuild target? -
i have msbuild script wrote 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=""$(protoc)" --proto_path="$([system.io.path]::getdirectoryname(%(protocolbuffer.protopath)))" --cpp_out="$(protooutpath)" "%(protocolbuffer.fullpath)" --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>
this compiles protocol buffers files perfectly, , adds them compiler's inputs (yay!). however, other source files want include .pb.h
files need know these files got generated -- generation location needs put on include path.
therefore, if , if user has included <protocolbuffer
item somewhere in script, want add generation location (in case $(protooutpath)
clcompile's <additionalincludedirectories>
.
is possible or need make .cpp files want use these generated bits jump through hoops?
read question , thought "can't hard". man, wrong. first thought putting condition on it, of course 1 can't use itemgroups in toplevel conditions because of evaluation order. figured it's not possible put itemdefinitiongroup in target (cause there 1 can use conditions) , modify there. bonked head on keyboard couple of times after realized that's why asked question :] (btw know including nonexisting directory not problem since compiler happily ignore it?)
maybe there's simpler solution, lastly figured: if nothing works, favourite msbuild toy aka codetaskfactory must able fix it. (i hope, didn't test result), it's not straightforward @ all. here go, make sure invoke test target somewhere before c++ build starts.
<!--uncomment below define protocolbuffers--> <!--<itemgroup> <protocolbuffer include="whitelist.proto" /> <protocolbuffer include="whitelist2.proto" /> </itemgroup>--> <!--suppose these default include files defined in c++ project--> <itemdefinitiongroup label="defaultincludes"> <clcompile> <additionalincludedirectories>/path/to/x;/path/to/y</additionalincludedirectories> </clcompile> </itemdefinitiongroup> <!--include @ least 1 item can play it--> <itemgroup> <clcompile include="iamaninclude"/> </itemgroup> <!--use code append 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 itemgroup, clear it: if output taskparameter itemgroup, apparently content gets appended group instead of replacing it. found no documentation whatsoever though???--> <itemgroup condition="@(protocolbuffer) != ''"> <clcompilecopy include="@(clcompile)"/> <clcompile remove="@(clcompile)"/> </itemgroup> <!--stage 2: append 'protobufincludedir' additionalincludedirectories, , append result 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>
this prints
iamaninclude: /path/to/x;/path/to/y
unless protocolbuffer not empty in case prints
iamaninclude: /path/to/x;/path/to/y;protobufincludedir