How to use MSBuild ItemGroup members as individual items like Properties

msbuild

So I'm kinda getting the hang of writing in MSBuild.

I like the idea storing things in ItemGroups because its easy to iterate, and you can have multple fields.

That makes them sort of like a class (more like a struct) but they respond to the iteration syntax of targets and tasks in a way that feels like a lambda expression.

However I continue to run into situation where I want to access a value of a particular item in an item group, that is, to access it like a property.

In that case I run into the issue of how to isolate a single item within the group.

When the item group is batching in a target or task, because of addressing using a metadata name or because of addressing with the common itemgroup name, you can utilize the value of the 'current' item i.e. @(ItemsName) or %(MetaDataName).

But I want to do things like: use an Item group as a System.Configuration class that contains the values of the entries in a section of a config file. Therefore the normal thing would be to name the ItemGroup itself to match the section name in the config file. however, the ItemGroup is not an addressable element that is accessible through the build engine interface, only the items themselves are addressable.

It might be nice to individually name the items in an ItemGroup rather than name them all the same and use the Include or a metadata field like to distinguish among them. This makes them behave like properties in that they are individually addressable as distinct items. so you could easily use their values in Conditions this way: '@(UniqueItemName->'%(Value)').

However, then the iterable features are essentially lost.

To narrow this down, presume i have a config file that gets read into an Item group by and xml task so that the element names in a section become the name of the items in the item group and attributes of each config file element are attributes that become metadata:

<configItemFlag name="displayDebugMessages" value="true" note="use with abandon" />
<configItemFlag name="displaySecurityValueMessages" value="false" note="use with caution" />

When I want to test this in a Condition, I need to narrow it down to something like this:

<Messge Text="Debug Message: you are debugging!" Condition="'@(configItemFlag->'%(Name)')' == 'displayDebugMessages' AND '@(configItemFlag->'%(Value)')' == 'true'/>

But this only evaluates the comparison and frequently does not evaluate to a single boolean.

So is there any way to syntacticly get this down to a dependable test?

Best Answer

Does this work for what you are trying to do?

<ItemGroup>
  <ConfigItemFlag Include="displayDebugMessages">
    <Value>true</Value>
    <Note>use with abandon</Note>
  </ConfigItemFlag>
  <ConfigItemFlag Include="displaySecurityValueMessages">
    <Value>false</Value>
    <Note>use with caution</Note>
  </ConfigItemFlag>
</ItemGroup>

<Target Name="Build">
  <Message
    Condition="
      '%(ConfigItemFlag.Identity)' == 'displayDebugMessages' AND
      '%(Value)' == 'true'"
    Text="Debug Message: you are debugging, %(Note)!"
    /> 
</Target>

Output:

Build:
  Debug Message: you are debugging, use with abandon!

(response to comment)

...the only thing I can offer to be able to use meta as properties isn't all that great, unless the target will make heavy use of them throughout. Basically it involves flattening each item to properties by batching on the item and creating local properties with each batch.

<Target Name="BuildOne"
  Outputs="%(ConfigItemFlag.Identity)">
  <!-- flatten this batch to properties -->
  <PropertyGroup>
    <_Identity>%(ConfigItemFlag.Identity)</_Identity>
    <_Value>%(ConfigItemFlag.Value)</_Value>
    <_Note>%(ConfigItemFlag.Note)</_Note>
  </PropertyGroup>
  <!-- use meta as properties -->
  <Message
    Condition="
      '$(_Identity)' == 'displayDebugMessages' AND
      '$(_Value)' == 'true'"
    Text="Debug Message: you are debugging, $(_Note)!"
    /> 
</Target>
<Target Name="Build" DependsOnTargets="BuildOne"
  />
Related Topic