MSBuild の各要素・アイテム・プロパティーの俯瞰と逆引き大辞典 を昔書きましたが…
Qiita では非常にコードを掲載しやすい有り難さがあるので、具体的に実装しようという話です。
ItemGroup をタスク (別々に実行) へ結びつける
つぎのように 1 入力 1 出力のコマンドは容易だと思いますが、
EncFile input.xml bin\Debug\output.bin
IV
や Key
などを個別に持たせるには?
EncFile2 encode IV Key input.xml bin\Debug\output.bin
メタデータを活用します。参考: プロジェクト ファイルで項目メタデータを参照する
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="15.0">
<PropertyGroup>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<ItemGroup>
<EncFile2 Include="first.xml">
<OutputTo>first.dat</OutputTo>
<IV>IV111</IV>
<Key>Key111</Key>
</EncFile2>
<EncFile2 Include="second.xml">
<OutputTo>second.dat</OutputTo>
<IV>IV222</IV>
<Key>Key222</Key>
</EncFile2>
</ItemGroup>
<Target Name="BuildEncFile"
AfterTargets="Build">
<Exec Command="EncFile2.exe ^
encode ^
"%(EncFile2.IV)" ^
"%(EncFile2.Key)" ^
"%(EncFile2.Identity)" ^
"$(OutputPath)%(EncFile2.OutputTo)""/>
</Target>
</Project>
%(EncFile2.Identity)
の意は アイテム EncFile2 のメタデータ Identity を参照
です。
実行してみます:
H:\Proj\MSBuildCSProj\1>msbuild
.NET Framework 向け Microsoft (R) Build Engine バージョン 15.9.21+g9802d43bc3
Copyright (C) Microsoft Corporation.All rights reserved.
2019/10/18 21:50:08 にビルドを開始しました。
ノード 1 上のプロジェクト "H:\Proj\MSBuildCSProj\1\1.csproj" (既定のターゲット)。
BuildEncFile:
EncFile2.exe ^
encode ^
"IV111" ^
"Key111" ^
"first.xml" ^
"bin\Debug\first.dat"
EncFile2.exe encode "IV111" "Key111" "first.xml" "bin\Debug\first.dat"
EncFile2.exe ^
encode ^
"IV222" ^
"Key222" ^
"second.xml" ^
"bin\Debug\second.dat"
EncFile2.exe encode "IV222" "Key222" "second.xml" "bin\Debug\second.dat"
プロジェクト "H:\Proj\MSBuildCSProj\1\1.csproj" (既定のターゲット) のビルドが完了しました。
ビルドに成功しました。
0 個の警告
0 エラー
経過時間 00:00:00.20
プロパティ関数で既定値を適用
プロパティ関数 を使い、既定値を適用できるようにしましょう。
<EncFile2 Include="first.xml" />
<EncFile2 Include="$([MSBuild]::ValueOrDefault(`$(firstYml)`, `first.yml`))" />
firstYml の指定は、set firstYml=1.yml
または msbuild /p:firstYml=1.yml
などです。
PDB 中に含まれるソースコードのパスを変更する
参考: PDB 中に含まれるソースコードのパスを相対パスに変えるための csproj 設定(Rosnly リポジトリ内を漁ってたらこういう書き方だった)
実施例:
<PropertyGroup>
<DeterministicSourceRoot>C:\yourApp\</DeterministicSourceRoot>
<RepoRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\'))</RepoRoot>
<PathMap>$(RepoRoot)=$(DeterministicSourceRoot)</PathMap>
</PropertyGroup>
ItemGroup をタスク (一度に実行) へ結びつける
参考: MSBuild バッチ
つぎのように、一度のコマンド実行で、複数ファイルを指定したい事案です:
signtool sign /a A.exe B.dll C.dll
アイテムの種類
を活用します。参考: プロジェクト ファイルの項目を参照する
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="15.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PreSignTarget Include="$(TargetPath)" />
<PreSignTarget Include="B.dll" />
<PreSignTarget Include="C.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="PreSignTarget"
BeforeTargets="CreateSetup">
<Exec Condition=" $(SignOptions) != '' "
Command="signtool.exe ^
sign ^
$(SignOptions) ^
@(PreSignTarget->'"%(FullPath)"', ' ')"
/>
</Target>
<Target Name="CreateSetup"
AfterTargets="Build">
</Target>
</Project>
@(PreSignTarget->'"%(FullPath)"', ' ')
は XML の都合で実体参照を用いますが、
@(PreSignTarget->'"%(FullPath)"', ' ')
のように解釈されるはずです。
アイテムの種類
を @(<ItemType>)
または @(<ItemType>, '<separator>')
の書式で展開できます。
参考: プロジェクト ファイルで項目メタデータを参照する
@(CppFiles -> '%(Filename).obj')
のような指定は 変換
と呼ばれます。
参考: メタデータを使用してアイテムの種類を変換する
実行してみます:
H:\Proj\MSBuildCSProj\2>set SIGNOPTIONS=/a
H:\Proj\MSBuildCSProj\2>msbuild /t:CreateSetup
.NET Framework 向け Microsoft (R) Build Engine バージョン 15.9.21+g9802d43bc3
Copyright (C) Microsoft Corporation.All rights reserved.
2019/10/18 22:02:52 にビルドを開始しました。
ノード 1 上のプロジェクト "H:\Proj\MSBuildCSProj\2\2.csproj" (CreateSetup ターゲット)。
PreSignTarget:
signtool.exe ^
sign ^
/a ^
"H:\Proj\MSBuildCSProj\2\bin\Debug\2.exe" "H:\Proj\MSBuildCSProj\2\B.dll" "H:\Proj\MSBuildCSProj\2\C.dll"
signtool.exe sign /a "H:\Proj\MSBuildCSProj\2\bin\Debug\2.exe" "H:\Proj\MSBuildCSProj\2\B.dll" "H:\Proj\MSBuildCSProj\2\C.dll"
プロジェクト "H:\Proj\MSBuildCSProj\2\2.csproj" (CreateSetup ターゲット) のビルドが完了しました。
ビルドに成功しました。
0 個の警告
0 エラー
経過時間 00:00:00.19
ItemGroup をタスク (個別実行と一度に実行) へ結びつける
NSIS で 2 種類 (通常版、スーパー版) のセットアップを作成したい。しかして署名は一度にしたい場合など。
実行イメージ:
makensis.exe /DOUTFILE=Setup_Normal.exe /DSUPER=0 Setup.nsi
makensis.exe /DOUTFILE=Setup_Super.exe /DSUPER=1 Setup.nsi
signtool.exe sign /a "Setup_Normal.exe" "Setup_Super.exe"
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="15.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<PropertyGroup>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<ItemGroup>
<MakeNsis Include="Setup.nsi">
<OutputTo>Setup_Normal.exe</OutputTo>
<Options>/DSUPER=0</Options>
</MakeNsis>
<MakeNsis Include="Setup.nsi">
<OutputTo>Setup_Super.exe</OutputTo>
<Options>/DSUPER=1</Options>
</MakeNsis>
</ItemGroup>
<Target Name="CreateSetup" AfterTargets="Build">
<Exec Command="makensis.exe /DOUTFILE=%(MakeNsis.OutputTo) %(MakeNsis.Options) %(MakeNsis.Identity)"/>
<Exec Command="signtool.exe sign $(SignOptions) @(MakeNsis->'"%(OutputTo)"', ' ')" Condition=" $(SignOptions) != '' "/>
</Target>
</Project>
実行してみました:
H:\Proj\MSBuildCSProj\3>msbuild
.NET Framework 向け Microsoft (R) Build Engine バージョン 15.9.21+g9802d43bc3
Copyright (C) Microsoft Corporation.All rights reserved.
2019/10/18 22:48:31 にビルドを開始しました。
ノード 1 上のプロジェクト "H:\Proj\MSBuildCSProj\3\3.csproj" (CreateSetup ターゲット)。
CreateSetup:
makensis.exe /DOUTFILE=Setup_Normal.exe /DSUPER=0 Setup.nsi
makensis.exe /DOUTFILE=Setup_Super.exe /DSUPER=1 Setup.nsi
signtool.exe sign /a "Setup_Normal.exe" "Setup_Super.exe"
プロジェクト "H:\Proj\MSBuildCSProj\3\3.csproj" (CreateSetup ターゲット) のビルドが完了しました。
ビルドに成功しました。
0 個の警告
0 エラー
経過時間 00:00:00.27
ItemGroup アイテム メタデータのテンプレート的な利用
<OutputTo>Setup_$(AssemblyVersion)_Normal.exe</OutputTo>
のように、
メタデータの指定をテンプレート化したいが…
Target の中でしか解決できないなど、$(AssemblyVersion)
を直ちに利用できない場合の回避策についてです。
Target 内部で %(MakeNsis.OutputToResolved)
を定義することで回避します。
<Target>
<ItemGroup>
<MakeNsis>
<OutputToResolved>...</OutputToResolved>
</MakeNsis>
</ItemGroup>
</Target>
有名な策として、String.Copy
という静的メソッドを用い、プロパティ関数の体で文字列を加工し出力する方法があります。
<OutputToResolved>$([System.String]::Copy('%(OutputTo)')</OutputToResolved>
<OutputToResolved>$([System.String]::Copy('%(OutputTo)').Replace('{VER}', '$(Ver)'))</OutputToResolved>
<OutputToResolved>$([System.String]::Copy('%(OutputTo)').Replace('{VER}', '$(Ver.Replace('.', '_'))'))</OutputToResolved>
出来上がりです:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="15.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<PropertyGroup>
<OutputPath>bin\Debug\</OutputPath>
<Ver>1.2.3</Ver>
</PropertyGroup>
<ItemGroup>
<MakeNsis Include="Setup.nsi">
<OutputTo>Setup_{VER}_Normal.exe</OutputTo>
<Options>/DSUPER=0</Options>
</MakeNsis>
<MakeNsis Include="Setup.nsi">
<OutputTo>Setup_{VER}_Super.exe</OutputTo>
<Options>/DSUPER=1</Options>
</MakeNsis>
</ItemGroup>
<Target Name="CreateSetup" AfterTargets="Build">
<ItemGroup>
<MakeNsis>
<OutputToResolved>$([System.String]::Copy('%(OutputTo)').Replace('{VER}', '$(Ver.Replace('.', '_'))'))</OutputToResolved>
</MakeNsis>
</ItemGroup>
<Exec Command="makensis.exe /DOUTFILE=%(MakeNsis.OutputToResolved) %(MakeNsis.Options) %(MakeNsis.Identity)"/>
<Exec Command="signtool.exe sign $(SignOptions) @(MakeNsis->'"%(OutputToResolved)"', ' ')" Condition=" $(SignOptions) != '' "/>
</Target>
</Project>
実行してみました:
H:\Proj\MSBuildCSProj\4>msbuild
.NET Framework 向け Microsoft (R) Build Engine バージョン 15.9.21+g9802d43bc3
Copyright (C) Microsoft Corporation.All rights reserved.
2019/10/27 3:25:40 にビルドを開始しました。
ノード 1 上のプロジェクト "H:\Proj\MSBuildCSProj\4\4.csproj" (既定のターゲット)。
CreateSetup:
makensis.exe /DOUTFILE=Setup_1_2_3_Normal.exe /DSUPER=0 Setup.nsi
makensis.exe /DOUTFILE=Setup_1_2_3_Super.exe /DSUPER=1 Setup.nsi
signtool.exe sign /a "Setup_1_2_3_Normal.exe" "Setup_1_2_3_Super.exe"
プロジェクト "H:\Proj\MSBuildCSProj\4\4.csproj" (既定のターゲット) のビルドが完了しました。
ビルドに成功しました。
0 個の警告
0 エラー
経過時間 00:00:00.28
nupkg で、ファイルをコピー
.NET Core スタイルの csproj で nupkg を構築し (dotnet pack
でビルド)、多数のファイルをビルド後にコピーする事案です。
ThePackage.csproj
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Pack="true" IncludeInPackage="false" Update="ThePackage.targets" PackagePath="build/ThePackage.targets">
</None>
<None Pack="true" IncludeInPackage="false" Update="GPL/**" PackagePath="binaryFiles">
</None>
</ItemGroup>
</Project>
ThePackage.targets
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ThePackageFiles Include="$(MSBuildThisFileDirectory)/../binaryFiles/**"/>
</ItemGroup>
<Target Name="CopyThePackage" AfterTargets="AfterBuild">
<Copy SourceFiles="@(ThePackageFiles)" DestinationFolder="$(OutDir)/%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>
</Project>
Notes:
- nupkg の /binaryFiles
フォルダを経由してコピーします。
- ThePackage
をプロジェクト名で置き換えます。