How To: Create Item Groups on the Fly
Here's another good question from our internal Visual Studio build conversion alias:
Suppose I have a target that produces an unpredictable number of output files and doesn’t use a Task Output to return that list to me. (Think compiling a .SLN file and it produces a set of XXX.Test.dll assemblies that I need for a later target.) When I use an ItemGroup to find this:
$(MSBuildProjectDirectory)\**\bin\$(Configuration)\*.Tests.dll
It works on the second run of the msbuild file, but the first time it is an empty item group. I’m guessing that this is because the itemgroup is resolved when the script is loaded and not when it is referenced from my later target. How do I fix this?
The original poster is correct: when MSBuild is launched the item groups are evaluated before targets are run. You can, however, create item groups on the fly using the <CreateItem> task. In the above case you'd do something like this as part of a target:
<CreateItem Include="$(MSBuildProjectDirectory)\**\bin\$(Configuration)\*.Tests.dll">
<Output TaskParameter="Include" ItemName="PreviousResultsFiles"/>
</CreateItem>
After that task executes you'll wind up with a new item group called PreviousResultsFiles. As always, complete documentation on the CreateItem task is available at MSDN.
[ Author: Neil Enns ]
Comments
Anonymous
January 03, 2007
We got a great question at msbuild@microsoft.com last week: What is the preferred method to retrieveAnonymous
October 06, 2008
How is this different to <PreviousResultsFiles> <ContentFiles Include="$(MSBuildProjectDirectory)**bin$(Configuration)*.Tests.dll"/> </PreviousResultsFiles> ? Cheers, DanAnonymous
March 03, 2009
Itemgroups are evaluated when the script is loaded. CreateItem when that task is run. If you for instance want to gather files created after a compilation task, they will not be in the ItemGroup.Anonymous
September 23, 2009
I want to use this method to create dynamically my list of "Test" MetaDataFile. So, I have added in the target "AfterGet" the following <CreateItem Include="$(LocalPath)**.vsmdi" AdditionalMetadata="TestList=BVT" > <Output TaskParameter="Include" ItemName="MetaDataFile" /> </CreateItem> This works fine as I can check with a <Message Text="MetaDataFile: %(MetaDataFile.Identity) - %(MetaDataFile.TestList)"/> All my .vsmdi are in the list. However, This item group in not taken into account as I can see in the BuildLog.txt for the target "ResolveTestFilesForEndToEndIteration" > Task "WorkspaceItemConverterTask" skipped, > due to false condition; ( '@(MetaDataFile)' > != '' ) was evaluated as ( '' != '' ). Why ? I can also verify in the log, the target "AfterGet" is well run before "ResolveTestFilesForEndToEndIteration" O.Anonymous
September 23, 2009
Sorry... I just found that using this in an override of the Target "BeforeTestConfiguration" works fine. Don't know why as the "AfterGet" is executed before "BeforeTestConfiguration". Has the ItemGroup "MetaDataFile" a kind of limited scope ?