Visual Studio のビルド プロセスは、プロジェクト ファイルにインポートされる一連の MSBuild .targets ファイルによって定義されます。 通常、Visual Studio プロジェクトとして SDK を使用する場合、これらのインポートは暗黙的です。 これらのインポートされたファイルの 1 つである Microsoft.Common.targets を拡張して、ビルド プロセス内のいくつかのポイントでカスタム タスクを実行できます。 この記事では、Visual Studio のビルド プロセスを拡張するために使用できる 3 つの方法について説明します。
カスタム ターゲットを作成し、
BeforeTargets属性とAfterTargets属性を使用して実行するタイミングを指定します。共通ターゲットで定義されている
DependsOnプロパティをオーバーライドします。共通ターゲット (Microsoft.Common.targets またはインポートするファイル) で定義されている特定の定義済みターゲットをオーバーライドします。
AfterTargets と BeforeTargets
カスタム ターゲットの AfterTargets 属性と BeforeTargets 属性を使用して、実行するタイミングを指定できます。
次の例は、 AfterTargets 属性を使用して、出力ファイルで何かを行うカスタム ターゲットを追加する方法を示しています。 この場合、出力ファイルは新しいフォルダー CustomOutput にコピーされます。 この例では、CustomClean属性を使用して、カスタム ビルド操作によって作成されたファイルを BeforeTargets ターゲットでクリーンアップし、CoreClean ターゲットの前にカスタム クリーン操作を実行するように指定する方法も示します。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
</PropertyGroup>
<Target Name="CustomAfterBuild" AfterTargets="Build">
<ItemGroup>
<_FilesToCopy Include="$(OutputPath)**\*"/>
</ItemGroup>
<Message Text="_FilesToCopy: @(_FilesToCopy)" Importance="high"/>
<Message Text="DestFiles:
@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(_FilesToCopy)"
DestinationFiles=
"@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>
<Target Name="CustomClean" BeforeTargets="CoreClean">
<Message Text="Inside Custom Clean" Importance="high"/>
<ItemGroup>
<_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
</ItemGroup>
<Delete Files='@(_CustomFilesToDelete)'/>
</Target>
</Project>
警告
定義済みのターゲットは SDK インポートによって上書きされるため、そのターゲットとは異なる名前を使用してください (例えば、ここでのカスタムビルドターゲットは CustomAfterBuild であり、AfterBuild ではありません)。 定義済みのターゲットの一覧については、この記事の最後にある 表 を参照してください。
DependsOn プロパティを拡張する
ビルド プロセスを拡張するもう 1 つの方法は、 DependsOn プロパティ ( BuildDependsOn など) を使用して、標準ターゲットの前に実行するターゲットを指定することです。
このメソッドは、定義済みのターゲットをオーバーライドする場合に適しています。これについては、次のセクションで説明します。 定義済みのターゲットのオーバーライドは、引き続きサポートされている古いメソッドですが、MSBuild はターゲットの定義を順番に評価するため、プロジェクトをインポートする別のプロジェクトが、既にオーバーライドしたターゲットをオーバーライドできないようにする方法はありません。 そのため、たとえば、プロジェクト ファイルで定義された最後の AfterBuild ターゲットは、他のすべてのプロジェクトがインポートされた後、ビルド中に使用されます。
共通ターゲット全体の属性で使用される DependsOn プロパティをオーバーライドすることで、ターゲットが意図せずに上書きされるのを防ぐことができます。 たとえば、Build ターゲットには、DependsOnTargetsの"$(BuildDependsOn)"属性値が含まれています。 考えてみてください。
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
この XML は、 Build ターゲットを実行する前に、 BuildDependsOn プロパティで指定されたすべてのターゲットを最初に実行する必要があることを示します。
BuildDependsOn プロパティは次のように定義されます。
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
プロジェクト ファイルの末尾に BuildDependsOn という名前の別のプロパティを宣言することで、このプロパティ値をオーバーライドできます。 SDK スタイルのプロジェクトでは、明示的なインポートを使用する必要があることを意味します。 最後のインポートの後に プロパティを配置できるように、暗黙的なインポートDependsOnインポートに関するセクションを参照してください。 前の BuildDependsOn プロパティを新しいプロパティに含めることで、ターゲット リストの先頭と末尾に新しいターゲットを追加できます。 例えば次が挙げられます。
<PropertyGroup>
<BuildDependsOn>
MyCustomTarget1;
$(BuildDependsOn);
MyCustomTarget2
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTarget1">
<Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
<Message Text="Running MyCustomTarget2..."/>
</Target>
プロジェクト ファイルをインポートするプロジェクトでは、行ったカスタマイズを上書きすることなく、これらのプロパティをさらに拡張できます。
DependsOn プロパティをオーバーライドするには
オーバーライドする共通ターゲットの定義済みの
DependsOnプロパティを特定します。 一般的にオーバーライドされるDependsOnプロパティの一覧については、次の表を参照してください。プロジェクト ファイルの末尾にあるプロパティまたはプロパティの別のインスタンスを定義します。 新しいプロパティに元のプロパティ (
$(BuildDependsOn)など) を含めます。プロパティ定義の前または後にカスタム ターゲットを定義します。
プロジェクト ファイルをビルドします。
一般的によくオーバーライドされる DependsOn プロパティ
| プロパティ名 | 追加されたターゲットは、この時点より前に実行されます。 |
|---|---|
BuildDependsOn |
メイン ビルド エントリ ポイント。 ビルド プロセス全体の前または後にカスタム ターゲットを挿入する場合は、このプロパティをオーバーライドします。 |
RebuildDependsOn |
Rebuild
|
RunDependsOn |
最終的なビルド出力の実行 (.EXEの場合) |
CompileDependsOn |
コンパイル (Compileターゲット)。 コンパイル手順の前または後にカスタム プロセスを挿入する場合は、このプロパティをオーバーライドします。 |
CreateSatelliteAssembliesDependsOn |
サテライト アセンブリの作成 |
CleanDependsOn |
Cleanターゲット (すべての中間ビルド出力と最終的なビルド出力の削除)。 カスタム ビルド プロセスからの出力をクリーンアップする場合は、このプロパティをオーバーライドします。 |
PostBuildEventDependsOn |
PostBuildEvent ターゲット |
PublishBuildDependsOn |
ビルド発行 |
ResolveAssemblyReferencesDependsOn |
ResolveAssemblyReferencesターゲット (特定の依存関係の推移閉包を見つける)。
ResolveAssemblyReferenceを参照してください。 |
例: BuildDependsOn と CleanDependsOn
次の例は、 BeforeTargets と AfterTargets の例に似ていますが、同様の機能を実現する方法を示しています。
BuildDependsOnを使用してビルドを拡張し、ビルド後に出力ファイルをコピーする独自のタスク CustomAfterBuildを追加し、CustomCleanを使用して対応するCleanDependsOn タスクも追加します。
この例では、これは SDK スタイルのプロジェクトです。 この記事で前述した SDK スタイルのプロジェクトに関するメモで説明したように、Visual Studio がプロジェクト ファイルを生成するときに使用する Sdk 属性の代わりに、手動インポート方法を使用する必要があります。
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"/>
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"/>
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);CustomAfterBuild
</BuildDependsOn>
<CleanDependsOn>
$(CleanDependsOn);CustomClean
</CleanDependsOn>
<_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
</PropertyGroup>
<Target Name="CustomAfterBuild">
<ItemGroup>
<_FilesToCopy Include="$(OutputPath)**\*"/>
</ItemGroup>
<Message Importance="high" Text="_FilesToCopy: @(_FilesToCopy)"/>
<Message Text="DestFiles:
@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(_FilesToCopy)"
DestinationFiles="@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>
<Target Name="CustomClean">
<Message Importance="high" Text="Inside Custom Clean"/>
<ItemGroup>
<_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
</ItemGroup>
<Delete Files="@(_CustomFilesToDelete)"/>
</Target>
</Project>
要素の順序は重要です。
BuildDependsOn要素とCleanDependsOn要素は、標準の SDK ターゲット ファイルをインポートした後に表示される必要があります。
定義済みのターゲットをオーバーライドする
共通の .targets ファイルには、ビルド プロセスの主要なターゲットの前後に呼び出される一連の定義済みの空のターゲットが含まれています。 たとえば、MSBuild はメイン BeforeBuild ターゲットの前に CoreBuild ターゲットを呼び出し、AfterBuild ターゲットの後に CoreBuild ターゲットを呼び出します。 既定では、共通ターゲット内の空のターゲットは何も行いませんが、プロジェクト ファイルで必要なターゲットを定義することで、既定の動作をオーバーライドできます。 この記事で前述したメソッドをお勧めしますが、このメソッドを使用する古いコードが発生する可能性があります。
プロジェクトで SDK ( Microsoft.Net.Sdk など) を使用する場合は、「明示的インポートと暗黙的インポート」で説明されているように、暗黙的インポートから明示的インポートに変更 を加える必要があります。
定義済みのターゲットをオーバーライドするには
プロジェクトで
Sdk属性を使用する場合は、明示的なインポート構文に変更します。 明示的インポートと暗黙的インポートを参照してください。オーバーライドする一般的なターゲットで定義済みのターゲットを特定します。 安全にオーバーライドできるターゲットの完全な一覧については、次の表を参照してください。
プロジェクト ファイルの最後、
</Project>タグの直前、および明示的な SDK インポートの直後にターゲットを定義します。 例えば次が挙げられます。<Project> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> ... <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> <Target Name="BeforeBuild"> <!-- Insert tasks to run before build here --> </Target> <Target Name="AfterBuild"> <!-- Insert tasks to run after build here --> </Target> </Project>最上位の
Sdk要素のProject属性が削除されていることに注意してください。プロジェクト ファイルをビルドします。
定義済みターゲットの表
次の表は、オーバーライドできる共通ターゲット内のすべてのターゲットを示しています。
| ターゲット名 | 説明 |
|---|---|
BeforeCompile、AfterCompile |
これらのターゲットのいずれかに挿入されたタスクは、コア コンパイルが実行される前または後に実行されます。 ほとんどのカスタマイズは、これら 2 つのターゲットのいずれかで行われます。 |
BeforeBuild、AfterBuild |
これらのターゲットのいずれかに挿入されたタスクは、ビルド内の他のすべてのタスクの前または後に実行されます。
手記:BeforeBuildターゲットとAfterBuildターゲットは、ほとんどのプロジェクト ファイルの末尾にあるコメントで既に定義されているため、ビルド前およびビルド後のイベントをプロジェクト ファイルに簡単に追加できます。 |
BeforeRebuild、AfterRebuild |
これらのターゲットのいずれかに挿入されたタスクは、コアリビルド機能が呼び出される前または後に実行されます。
Microsoft.Common.targets でのターゲット実行の順序は、BeforeRebuild、Clean、Build、AfterRebuildです。 |
BeforeClean、AfterClean |
これらのターゲットのいずれかに挿入されたタスクは、コア クリーン機能が呼び出される前または後に実行されます。 |
BeforePublish、AfterPublish |
これらのターゲットのいずれかに挿入されたタスクは、コア発行機能が呼び出される前または後に実行されます。 |
BeforeResolveReferences、AfterResolveReferences |
これらのターゲットのいずれかに挿入されたタスクは、アセンブリ参照が解決される前または後に実行されます。 |
BeforeResGen、AfterResGen |
これらのターゲットのいずれかに挿入されたタスクは、リソースが生成される前または後に実行されます。 |
ビルド システムと .NET SDK にはさらに多くのターゲットがあります。 MSBuild ターゲット (SDK と既定のビルド ターゲット) を参照してください。
カスタム ターゲットのベスト プラクティス
DependsOnTargetsプロパティとBeforeTargetsプロパティは両方とも、ターゲットを別のターゲットの前に実行する必要があることを指定できますが、両方とも異なるシナリオで必要になります。 依存関係の要件を指定するターゲットが異なります。 独自のターゲットのみを制御でき、システム ターゲットまたはその他のインポートされたターゲットを安全に変更できないため、選択した方法が制約されます。
カスタム ターゲットを作成するときは、次の一般的なガイドラインに従って、ターゲットが意図した順序で実行されるようにします。
DependsOnTargets属性を使用して、ターゲットを実行する前に実行する必要があるターゲットを指定します。 制御するターゲットのチェーンの場合、各ターゲットはチェーンの前のメンバーをDependsOnTargetsで指定できます。前に実行する必要があるターゲットを制御していないターゲットには、
BeforeTargetsを使用します (ビルドの早い段階で実行する必要があるターゲットのBeforeTargets="PrepareForBuild"など)。必要な出力が使用可能であることを保証する制御しないターゲットには、
AfterTargetsを使用します。 たとえば、参照のリストを変更するAfterTargets="ResolveReferences"を指定します。これらを組み合わせて使用できます。 たとえば、
DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile"のようにします。
明示的インポートと暗黙的インポート
Visual Studio によって生成されるプロジェクトでは、通常、プロジェクト要素に対して Sdk 属性が使用されます。 これらの種類のプロジェクトは、SDK スタイルのプロジェクトと呼ばれます。
「MSBuild プロジェクト SDK の使用」を参照してください。 次に例を示します。
<Project Sdk="Microsoft.Net.Sdk">
プロジェクトで Sdk 属性を使用すると、2 つのインポートが暗黙的に追加され、1 つはプロジェクト ファイルの先頭に、1 つは最後に追加されます。
暗黙的なインポートは、次のような import ステートメントをプロジェクト ファイルの最初の行として、 Project 要素の後に付けるのと同じです。
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
次の import ステートメントをプロジェクト ファイルの最後の行として使用します。
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
この構文は、 明示的な SDK インポートと呼ばれます。 この明示的な構文を使用する場合は、プロジェクト要素の Sdk 属性を省略する必要があります。
暗黙的な SDK のインポートは、次のような古いプロジェクト ファイルの一般的なコンストラクトである特定の "共通" .props または .targets ファイルをインポートすることと同じです。
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
そして
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
このような古い参照は、このセクションで前に示した明示的な SDK 構文に置き換える必要があります。
明示的な SDK 構文を使用すると、最初のインポートの前または最終的な SDK インポートの後に独自のコードを追加できます。 つまり、インポートされた .props ファイルで最初のインポートが有効になる前にプロパティを設定することで動作を変更できます。また、最終的なインポート後に SDK .targets ファイルのいずれかで定義されているターゲットをオーバーライドできます。 このメソッドを使用すると、次に説明するように、 BeforeBuild または AfterBuild をオーバーライドできます。
次のステップ
ビルドをカスタマイズするために、MSBuild で実行できる操作は他にもあります。 「ビルドのカスタマイズ」を参照してください。
関連コンテンツ
- Visual Studio の統合
- MSBuild の概念
- MSBuild のターゲット
- .targets ファイル
- ターゲットのビルド順序