MSBuildで2つのアイテム(ItemGroup
)を組み合わせてマトリクスを作成する方法について。 あるいはn個のアイテムとm個のアイテムから計n×m個のアイテムを生成する方法について。
例として、TargetFrameworks
とConfiguration
やRuntimeIdentifiers
の組み合わせすべてをItemGroup
として作成したい場合などを想定する。
2組のアイテムを組み合わせてn×mのマトリクスを作成する
1組目のアイテムn個を元に別のアイテムを新たに作成し、そのメタデータとして2組目のアイテムの値m個を持たせる。 これにより、2組のアイテムの値を組み合わせた計n×m個のアイテムを作成する。
3列と2行のアイテムからセル6個分のアイテムを作成する
<Project DefaultTargets="Build">
<!--
3列分と2行分を含む2組のアイテムから、各セルに対応する6個のアイテムを作成する例
| A B C
__|_________
1 | A1 B1 C1
2 | A2 B2 C2
-->
<Target Name="Build">
<!-- 3列分のアイテム定義 -->
<ItemGroup>
<Col Include="A" />
<Col Include="B" />
<Col Include="C" />
</ItemGroup>
<!-- 2行分のアイテム定義 -->
<ItemGroup>
<Row Include="1" />
<Row Include="2" />
</ItemGroup>
<!-- 列Rowと行Colから、それぞれを組み合わせたアイテムTableを作成する -->
<ItemGroup>
<!-- Colの内容をアイテムの内容としてTableアイテムを作成する -->
<Table Include="@(Col)">
<!-- メタデータとしてRowアイテムの各値を持たせる -->
<Row>%(Row.Identity)</Row>
</Table>
<!--
ここでは、まず値としてColアイテムの内容=A, B, Cを持つ3つのTableアイテムが作成される。
次に、それぞれのTableアイテムに対してメタデータRowを定義し、その値としてRowアイテムの内容=1, 2を持たせる。
結果として、アイテム3組、各アイテムにメタデータ2個となり、その組み合わせから計6個のTableアイテムが作成される。
-->
</ItemGroup>
<!-- アイテム値とメタデータの組み合わせを一覧として表示する -->
<Message Text="Table: %(Table.Identity)%(Table.Row)" Importance="high" />
<!-- Tableアイテムの値とメタデータを展開して、1次元のセル一覧となるアイテムCellを作成する -->
<ItemGroup>
<Cell Include="%(Table.Identity)%(Table.Row)" />
</ItemGroup>
<!-- Cellアイテムの内容を表示する -->
<Message Text="Cell: @(Cell)" Importance="high" />
<!-- プロパティTableCellsを作成し、値としてTableCellsアイテムの内容をカンマで区切った文字列を設定する -->
<PropertyGroup>
<TableCells>@(Cell, ',')</TableCells>
</PropertyGroup>
<!-- TableCellsプロパティに設定された内容を表示する -->
<Message Text="TableCells: $(TableCells)" Importance="high" />
</Target>
</Project>
実行結果
>dotnet msbuild -nologo
Table: A1
Table: B1
Table: C1
Table: A2
Table: B2
Table: C2
Cell: A1;B1;C1;A2;B2;C2
TableCells: A1,B1,C1,A2,B2,C2
2組のアイテムとメタデータを組み合わせてn×mのマトリクスを作成する
2組のアイテムとそのアイテムが持つメタデータから、それぞれの値を組み合わせた計n×m個のアイテムを作成する。
メタデータを含む場合も、先と同様に組み合わせてマトリクスを作成できる。
3列と3行のアイテムとメタデータから3×3の九九の表を作成する
<Project DefaultTargets="Build">
<!--
3×3の九九の表を作成する例
被乗数の値をもつ3列分と乗数の値を持つ3行分の要素を含む2組のアイテムから、
各セルの値として被乗数×乗数を乗算した9個の要素を含むアイテムを作成する例
| A B C
|(1)(2)(3)
_______________
1(1) | 1 2 3
2(2) | 2 4 6
3(3) | 3 6 9
-->
<Target Name="Build">
<!-- 列のアイテムとメタデータの定義 -->
<ItemGroup>
<Col Include="A" Value="1" />
<Col Include="B" Value="2" />
<Col Include="C" Value="3" />
</ItemGroup>
<!-- 行のアイテムとメタデータの定義 -->
<ItemGroup>
<Row Include="1" Value="1" />
<Row Include="2" Value="2" />
<Row Include="3" Value="3" />
</ItemGroup>
<!-- 列Rowと行Colから、それぞれを組み合わせたアイテムTableを作成する -->
<ItemGroup>
<!-- Colの内容をアイテムの内容としてTableアイテムを作成する -->
<Table Include="@(Col)">
<!-- メタデータとしてRowアイテムの各値とメタデータの値を持たせる -->
<RowName>%(Row.Identity)</RowName>
<RowValue>%(Row.Value)</RowValue>
</Table>
<!--
ここでは、まず値としてA, B, C(Colアイテムの内容)を持つ3つのTableアイテムが作成される。
このとき、ColアイテムのメタデータもそのままTableアイテムにコピーされる。
次に、それぞれのTableアイテムに対してメタデータRowとRowValueを定義し、その値としてRowアイテムとメタデータValueの値を持たせる。
結果として、アイテム3個、各アイテムにメタデータ3組となり、その組み合わせから計9個のTableアイテムが作成される。
-->
</ItemGroup>
<!-- アイテム値とメタデータの組み合わせを一覧として表示する -->
<Message Text="Table: %(Table.Identity)%(Table.RowName) = %(Table.Value)×%(Table.RowValue)" Importance="high" />
<!--
Tableアイテムの値とメタデータを展開して、1次元のセル一覧となるアイテムCellを作成する
また、セルに対応する値を計算してメタデータValueに値を代入する
-->
<ItemGroup>
<Cell Include="%(Table.Identity)%(Table.RowName)">
<Value>$([MSBuild]::Multiply(%(Table.Value), %(Table.RowValue)))</Value>
</Cell>
</ItemGroup>
<!-- アイテムCellの内容を一覧表示する -->
<Message Text="Cell %(Cell.Identity): Value=%(Cell.Value)" Importance="high" />
</Target>
</Project>
実行結果
>dotnet msbuild -nologo
Table: A1 = 1×1
Table: B1 = 2×1
Table: C1 = 3×1
Table: A2 = 1×2
Table: B2 = 2×2
Table: C2 = 3×2
Table: A3 = 1×3
Table: B3 = 2×3
Table: C3 = 3×3
Cell A1: Value=1
Cell B1: Value=2
Cell C1: Value=3
Cell A2: Value=2
Cell B2: Value=4
Cell C2: Value=6
Cell A3: Value=3
Cell B3: Value=6
Cell C3: Value=9
3組のプロパティ・アイテムから出力パスの組み合わせ一覧を作成する
応用・具体例として、build-configuration\target-framework\assembly-file
形式の出力パス一覧を作成する。
この例ではtarget-frameworkのみセミコロン区切りのプロパティTargetFrameworks
から取得する場合を考える。
Configuration・TargetFramework・AssemblyFileの3つのアイテムを組み合わせて出力パス一覧を生成する
<Project DefaultTargets="Build">
<PropertyGroup>
<TargetFrameworks>net6.0;net48;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<BuildConfiguration Include="Debug" />
<BuildConfiguration Include="Release" />
</ItemGroup>
<ItemGroup>
<OutputAssemblyFile Include="LibA.dll" />
<OutputAssemblyFile Include="LibB.dll" />
<OutputAssemblyFile Include="Exe.dll" />
</ItemGroup>
<Target Name="Build">
<!-- TargetFrameworks, BuildConfiguration, OutputAssemblyFileの各値を組み合わせたアイテムOutputFilePathを作成する -->
<ItemGroup>
<!-- セミコロン区切りの値を含むプロパティTargetFrameworksから、アイテム_TargetFrameworkを作成する -->
<_TargetFramework Include="$(TargetFrameworks)" />
<!-- BuildConfigurationと_TargetFrameworkを組み合わせたマトリクスとなるアイテムを作成する -->
<_Matrix1 Include="@(BuildConfiguration)">
<TargetFramework>%(_TargetFramework.Identity)</TargetFramework>
</_Matrix1>
<!-- 上記マトリクスを展開して1次元のアイテムに変換する -->
<_Flatten1 Include="%(_Matrix1.Identity)\%(_Matrix1.TargetFramework)\" />
<!-- 1次元化したConfigurationとTargetFrameworkのマトリクスと、OutputAssemblyFileを組み合わせたマトリクスとなるアイテムを作成する -->
<_Matrix2 Include="@(_Flatten1)">
<OutputAssemblyFile>%(OutputAssemblyFile.Identity)</OutputAssemblyFile>
</_Matrix2>
<!-- 上記マトリクスを展開して1次元のアイテムOutputFilePathに変換する -->
<OutputFilePath Include="%(_Matrix2.Identity)%(_Matrix2.OutputAssemblyFile)" />
</ItemGroup>
<Message Text="OutputFilePath: %(OutputFilePath.Identity)" Importance="high" />
</Target>
</Project>
実行結果
>dotnet msbuild -nologo | sort
OutputFilePath: Debug\net48\Exe.dll
OutputFilePath: Debug\net48\LibA.dll
OutputFilePath: Debug\net48\LibB.dll
OutputFilePath: Debug\net6.0\Exe.dll
OutputFilePath: Debug\net6.0\LibA.dll
OutputFilePath: Debug\net6.0\LibB.dll
OutputFilePath: Debug\netstandard2.1\Exe.dll
OutputFilePath: Debug\netstandard2.1\LibA.dll
OutputFilePath: Debug\netstandard2.1\LibB.dll
OutputFilePath: Release\net48\Exe.dll
OutputFilePath: Release\net48\LibA.dll
OutputFilePath: Release\net48\LibB.dll
OutputFilePath: Release\net6.0\Exe.dll
OutputFilePath: Release\net6.0\LibA.dll
OutputFilePath: Release\net6.0\LibB.dll
OutputFilePath: Release\netstandard2.1\Exe.dll
OutputFilePath: Release\netstandard2.1\LibA.dll
OutputFilePath: Release\netstandard2.1\LibB.dll