Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filtering Item's Metadata in msbuild

Tags:

msbuild

I would like to create a new item collection with modified metadata. For example, change the delimiters of the ClCompile.AdditionalIncludeDirectories. In order to do so I first create an item collection from the AdditionalIncludeDirectories metadata, and then transform it:

<ItemGroup>
  <IncludeDirs0 Include="%(ClCompile.AdditionalIncludeDirectories)">
    <key>@(ClCompile)</key>
  </IncludeDirs0>
</ItemGroup>

<ItemGroup>
  <IncludeDirs Include="@(IncludeDirs0 -> '-I %(Identity)', ' ')">
    <key>%(IncludeDirs0.key)</key>
  </IncludeDirs>
</ItemGroup>

<ItemGroup>
  <Compile Include="@(ClCompile)">
    <IncludeDirs>@(IncludeDirs)</IncludeDirs>
  </Compile>
</ItemGroup>

The problem is how to filter IncludeDirs on Compile item group, such that each Compile item will have its right include dir. (so that ClCompile identity equals IncludeDirs key). I've tried adding a condition such as: Condition="'%(IncludeDirs.key)'=='%(ClCompile.Identity)'" but without any success.
I could have used the IncludeDirs directly: <Message Text="%(IncludeDirs.key) : @(IncludeDirs)"/> but I feel this misses the point, since the Compile collection should contain all the metadata.

What did I miss here?

like image 713
Amir Gonnen Avatar asked Feb 25 '23 02:02

Amir Gonnen


2 Answers

You can do it with one additional target which collects needed items. See MSBuild Batching.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ClCompile Include="1" />
    <ClCompile Include="2">
      <AdditionalIncludeDirectories>2.1;2.2;2.3</AdditionalIncludeDirectories>
    </ClCompile>
  </ItemGroup>              

  <Target Name="TransformClCompile"
          Inputs="%(ClCompile.Identity)"
          Outputs="_Non_Existent_Item_To_Batch_">
    <PropertyGroup>
        <IncludeDirs>%(ClCompile.AdditionalIncludeDirectories)</IncludeDirs>
    </PropertyGroup>
    <ItemGroup>
          <IncludeDirs Include="$(IncludeDirs)" />
          <Compile Include="@(ClCompile)">
            <IncludeDirs>@(IncludeDirs ->'-I %(Identity)', ' ')</IncludeDirs>
          </Compile>
    </ItemGroup>                
  </Target>

  <Target Name="Build" DependsOnTargets="TransformClCompile">
     <Message Text="compile %(Compile.Identity) %(Compile.IncludeDirs)" />
  </Target>
</Project>

Output:

  compile 1 
  compile 2 -I 2.1 -I 2.2 -I 2.3
like image 73
Sergio Rykov Avatar answered Mar 10 '23 11:03

Sergio Rykov


There are ways to do it more concisely in 4.0+, using Property Functions.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ClCompile Include="1" />
    <ClCompile Include="2">
      <AdditionalIncludeDirectories>2.1;2.2;2.3</AdditionalIncludeDirectories>
    </ClCompile>
  </ItemGroup>

  <Target Name="Build" >

   <Message Text="compile %(ClCompile.Identity)" Condition="'%(ClCompile.AdditionalIncludeDirectories)' ==''"/>

   <Message Text="compile %(ClCompile.Identity) /I $([System.String]::Join(' /I ', $([System.Text.RegularExpressions.Regex]::Split('%(ClCompile.AdditionalIncludeDirectories)', ';'))))" Condition="'%(ClCompile.AdditionalIncludeDirectories)' !=''"/>

  </Target>

</Project>

Output

compile 1
compile 2 /I 2.1 /I 2.2 /I 2.3

It isn't that pretty, but it's a little better, I think. The Regex.Split has to be used instead of String.Split because the latter needs an array of splitters and that's a little tricky to get.

The MSBuild binder needs some improvements, I think.

Dan (msbuild)

like image 35
cheerless bog Avatar answered Mar 10 '23 10:03

cheerless bog