7

I'm desperately curious why I am unable to create an item in a global scope based on a metadata condition which works as expected inside a target. For instance, this works as expected:

<ItemGroup>
    <TestItems Include="TestItem1">
        <TestFlag>true</TestFlag>
    </TestItems>
    <TestItems Include="TestItem2">
        <TestFlag>false</TestFlag>
    </TestItems>
</ItemGroup>

<Target Name="Default">

    <Message Text="@(TestItems)" />
    <Message Text="@(TestItems)" Condition="'%(TestItems.TestFlag)'=='true'" />

    <ItemGroup>
        <FilteredTestItems Include="@(TestItems)" Condition="'%(TestItems.TestFlag)'=='true'" />
    </ItemGroup>

    <Message Text="@(FilteredTestItems)" />
    <Message Text="@(FilteredTestItems)" Condition="'%(FilteredTestItems.TestFlag)'=='true'" />

</Target>

and produces the following output:

TestItem1;TestItem2 TestItem1 TestItem1 TestItem1

And this works as expected:

<ItemGroup>
    <TestItems Include="TestItem1">
        <TestFlag>true</TestFlag>
    </TestItems>
    <TestItems Include="TestItem2">
        <TestFlag>false</TestFlag>
    </TestItems>
</ItemGroup>

<ItemGroup>
    <FilteredTestItems Include="@(TestItems)" Condition="'false'=='true'" />
</ItemGroup>

<Target Name="Default">

    <Message Text="@(TestItems)" />
    <Message Text="@(TestItems)" Condition="'%(TestItems.TestFlag)'=='true'" />


    <Message Text="@(FilteredTestItems)" />
    <Message Text="@(FilteredTestItems)" Condition="'%(FilteredTestItems.TestFlag)'=='true'" />

</Target>

Producing the following output:

TestItem1;TestItem2 TestItem1

But this:

<ItemGroup>
    <TestItems Include="TestItem1">
        <TestFlag>true</TestFlag>
    </TestItems>
    <TestItems Include="TestItem2">
        <TestFlag>false</TestFlag>
    </TestItems>
</ItemGroup>

<ItemGroup>
    <FilteredTestItems Include="@(TestItems)" Condition="'%(TestItems.TestFlag)'=='true'" />
</ItemGroup>

Produces the following MSBuild error:

temp.proj(13,45): error MSB4090: Found an unexpected character '%' at position 2 in condition "'%(TestItems.TestFlag)'=='true'".

So what gives? Certainly I can work around it, but what exactly am I not understanding about ItemGroup, metadata and/or the global scope?

Amandalishus
  • 521
  • 2
  • 11

1 Answers1

4

The item group condition works outside a target, but batching doesn't (that's the "%" operator). Batching is used when you call a task, and since you can only call a task from inside a target, it makes sense for batching to also only work inside a target.

You might ask why the item group works inside the target since it's not a task. Prior to MSBuild 3.5, you weren't allowed item groups inside targets at all; you had to call CreateItem instead. In versions 3.5 and 4.0, using item groups that way is allowed, but I think it's just syntactic sugar for calling the CreateItem task, so your condition works because there is a task behind the scenes.

Rory MacLeod
  • 11,012
  • 7
  • 41
  • 43
  • Sure, you can't use batching outside a target. I think it's because the parser only read the global scope once (so it has no clue about what's defined in it when it parses => no batching possible). As for the difference between ItemGroup and CreateItem you can read these topics : http://stackoverflow.com/questions/937681/createitem-vs-itemgroup and http://stackoverflow.com/questions/3891065/msbuild-itemgroup-include-exclude-pattern-issue – Benjamin Baumann Jan 06 '11 at 10:19
  • 2
    The docs (now?) say that [`CreateItem` is deprecated](http://msdn.microsoft.com/en-us/library/s2y3e43x%28v=vs.90%29), so I would avoid using it unless necessary (I think it expands parameters more agressively than `ItemGroup` does) because, in the very distant future, perhaps it won’t be included in .net ;-). Thus, it probably isn’t “syntactic sugar”. – binki Aug 16 '13 at 21:07