Scope and order of evaluation of Items in MsBuild

2019-07-28 06:06发布

问题:

I wonder why in the following code, MsBuild refuses to set the Suffix Metadata. It does work with a CreateItem task instead of the ItemGroup Declaration (because CreateItem is computed at build time) but I can't do this here because this code is in a "property file" : the project has no target, it's just a bunch of properties/items I include in real projects.

<ItemGroup>
        <Layout Include="Bla">
            <PartnerCode>bla</PartnerCode>
        </Layout>
        <Layout Include="Bli">
            <PartnerCode>bli</PartnerCode>
        </Layout>
</ItemGroup>

<ItemGroup Condition="'$(LayoutENV)'=='Preprod'">
        <LayoutFolder Include="Preprod">
            <Destination>..\Compil\layout\pre\</Destination>
        </LayoutFolder>
</ItemGroup>


<ItemGroup>
    <Destinations Include="@(LayoutFolder)" >
        <Suffix>%(Layout.PartnerCode)</Suffix>
    </Destinations>
</ItemGroup>

Destinations is well built but the Suffix Metadata is not set.

As for now, I have duplicated the Destinations Definition in every project I needed it but it's not very clean. If someone has a better solution, I'm interested!

回答1:

With MSBuild 4 you can use metadata from previous items in item declaration like this :

<ItemGroup>
  <Layout Include="Bla">
      <PartnerCode>bla</PartnerCode>
  </Layout>
  <Layout Include="Bli">
      <PartnerCode>bli</PartnerCode>
  </Layout>
</ItemGroup>

<ItemGroup>
  <Destinations Include="@(Layout)" >
      <Suffix>%(PartnerCode)</Suffix>
  </Destinations>
</ItemGroup>

(It's strange that you batch on LayoutFolder and try to get Layout metadata. What value do you want as Suffix bla or bli?)



回答2:

It appears that I try to set Metadata dynamically outside a target which is impossible. I try to set the Suffix Metadata by batching over Layout items but Layout items are not properly set when the batching is done. The batching is done when msbuild parse my property files, it does not wait for Layout to be declared.

Nevertheless, like MadGnome pointed out, I can batch over LayoutFolder (which is the source items for my includes) because MSBuild does wait for it to be declared.



回答3:

The issue you're encountering is that you're referring to metadata in a list. The %(Layout.PartnerCode) iterates through the ItemGroup of "Layout", which in this case returns 2 items. Even with 1 it causes undesired, unexpected results, as you're pointing to a list. MSBuild returns two meta tags and doesn't know which one you would want to have. The result being that it chooses none instead... or.. well, MSBuild ends up setting it to nothing.

I'd suggest setting a default ItemDefinition, like this (MSBuild 3.5)

<ItemDefinitionGroup>
  <Layout>
    <PartnerCode>%(Identity)</PartnerCode>
    <Suffix>%(PartnerCode)</Suffix>
    <Destination Condition="'$(LayoutENV)'=='Preprod'">..\Compile\layout\pre\</Destination>
  </Layout>
</ItemDefinitionGroup>

And then define them as you would have.

<ItemGroup>
    <Layout Include="Bla" />
    <Layout Include="Bli" />
    <Layout Include="Bloop">
        <PartnerCode>B2</PartnerCode>
        <Suffix>%(PartnerCode)</Suffix>
    </Layout>
</ItemGroup>

Sidenotes


Note. Metadata seems to be only parsed once per definition group / itemgroup, so if you're setting PartnerCode, you'd also have to reset Suffix, as seen in the second example. I am not familiar with the behaviour in MSBuild 3.5, but it is the case in MSBuild 4.0.

Note. I'm assuming that you want your filename as a suffix, Identity does the trick, see here "MSBuild Well-known Item Metadata": (https://msdn.microsoft.com/en-us/library/ms164313.aspx), if this is not the case, you can always follow the custom override example or write your own function based on it. Read more on stuff like that here "MSBuild Property Functions": (https://msdn.microsoft.com/en-us/library/dd633440.aspx)