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 (5)

  1. We got a great question at msbuild@microsoft.com last week: What is the preferred method to retrieve

  2. Daniel says:

    How is this different to

    <PreviousResultsFiles>

     <ContentFiles Include="$(MSBuildProjectDirectory)**bin$(Configuration)*.Tests.dll"/>

    </PreviousResultsFiles>

    ?

    Cheers,

    Dan

  3. Thomas Gravgaard says:

    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.

  4. OgeGOon says:

    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.

  5. OgeGOon says:

    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 ?