VC PlusPlus:MSBuild バッチ処理変数
概要
バッチ処理は%(Batch)のように記述する変数です。
バッチ処理の定義の基礎
例えば、以下のように書いた<ItemGroup>~</ItemGroup>があったとしたら
<ItemGroup>
<Batch Include="1" Element="en-US" />
<Batch Include="2" Element="ja-JP" />
</ItemGroup>
%(Element)というバッチ処理がアイテムグループBatchの中に設定されたことになり、Batchの中のバッチとして扱う範囲の呼び出しができます。Includeに示した値はファイルなので、プロジェクトで読み込むには、vcxprojファイルのあるフォルダと同じ階層に1や2という名前のファイルが必要になります。Projectタグ直下に置くItemGroupの中のタグには最初の定義で要素IncludeかUpdateかRemoveが必須です。例えば以下のような使い方ができます。
<Target Name="BatchTarget">
<ItemGroup>
<Batch Condition=" '%(Element)' == 'en-US' ">
<ConditionEnUSBatch>true</ConditionEnUSBatch>
</Batch>
</ItemGroup>
<Message Text="Batch = @(Batch->'%(Element)')" Importance="High" />
</Target>
出力結果
Batch = en-US;ja-JP
この時、実は%(Element)だけではなく、%(Identity)というものもBatchの中設定されていてIncludeという要素の値に対応しています。%(Element)がen-USを示す内容のときだけConditionの中で示された比較が成立し、%(ConditionEnUSBatch)もBatchの中にできていて、以下のようにして出力することができます。
<Target Name="BatchTarget">
<ItemGroup>
<Batch Condition=" '%(Element)' == 'en-US' ">
<ConditionEnUSBatch>true</ConditionEnUSBatch>
</Batch>
</ItemGroup>
<Message Text="Batch = {@(Batch->' Element = %(Element), Identity = %(Identity), ConditionEnUSBatch = %(ConditionEnUSBatch)')}" Importance="High" />
</Target>
出力結果
Batch = { Element = en-US, Identity = 1, ContitionEnUSBatch = true; Element = ja-JP, Identity = 2, ContitionEnUSBatch =}
と表現できることになります。
さらに、このようにBatchの中のという指定をするために使った@(Batch->' ')を以下のように記述することもできます。
<Target Name="BatchTarget">
<ItemGroup>
<Batch Condition=" '%(Element)' == 'en-US' ">
<ConditionEnUSBatch>true</ConditionEnUSBatch>
</Batch>
</ItemGroup>
<Message Text="%(Batch.Element)" Importance="High" />
</Target>
出力結果
en-US
ja-JP
このように、%(Batch.Element)とすると、バッチ処理の機能が働き、その要素の分だけメッセージ出力処理が繰り返されます。@(Batch->' ')の中にバッチがあった時はその中のメッセージが;区切りで繰り返される感じになります。
バッチ処理の例外
以下のようにTarget内で定義したItemGroupでバッチを呼び出しても評価順序が正しくないため思い通りの結果は得られません。ちゅういして使いましょうとマイクロソフトのWebsiteに記載されていたので、そこは注意したいですね。
<Target Name="Test" AfterTargets="AfterResourceCompile">
<ItemGroup>
<BatchItem Include='a/a.txt' BatchPath='%(Filename)%(Extension)' />
<BatchItem Include='a/b.txt' BatchPath='%(Filename)%(Extension)' />
<BatchItem Include='b/a.txt' BatchPath='%(Filename)%(Extension)' />
</ItemGroup>
<Message Text="BatchItem=[@(i)]" Importance='High' />
<Message Text="BatchItem->Include=[@(BatchItem->'%(Identity)')]" Importance='High' />
<Message Text="BatchItem->BatchPath=[@(BatchItem->'%(BatchPath)')]" Importance='High' />
</Target>
実行結果
BatchItem=[a/a.txt;a/b.txt;b/a.txt;b/a.txt]
BatchItem->Include=[a/a.txt;a/b.txt;b/a.txt;b/a.txt]
BatchItem->BatchPath=[;b.txt;a.txt;a.txt]
%(Filename)や%(Extension)ように、定義しなくても使えるバッチというのもあります。が、上記のように、Targetの中でバッチを使うとまだ評価されていないだとかスコープだとかの都合で動作が保証されず思い通りの結果は得られません。
以下のようにすると期待していたものに近い結果が得られます。
<ItemGroup>
<BatchItem Include='a/a.txt' BatchPath='%(Filename)%(Extension)' />
<BatchItem Include='a/b.txt' BatchPath='%(Filename)%(Extension)' />
<BatchItem Include='b/a.txt' BatchPath='%(Filename)%(Extension)' />
</ItemGroup>
<Target Name="Test" AfterTargets="AfterResourceCompile">
<Message Text="BatchItem=[@(BatchItem)]" Importance='High' />
<Message Text="BatchItem->Include=[@(BatchItem->'%(Identity)')]" Importance='High' />
<Message Text="BatchItem->BatchPath=[@(BatchItem->'%(BatchPath)')]" Importance='High' />
</Target>
実行結果
BatchItem=[a/a.txt;a/b.txt;b/a.txt]
BatchItem->Include=[a/a.txt;a/b.txt;b/a.txt]
BatchItem->BatchPath=[a.txt;b.txt;a.txt]