最近、ローカルビルドとサーバービルド(CI、Nightly、Weekly)用の柔軟なビルドスクリプトを作成するために、MSBuildの学習を開始しました。私の経験に基づいて、ビルドスクリプトは非常に厄介なものになる可能性があることを知っていました。いくつかのガイダンスを持っている私の会社でさえ、すべてのターゲットとそれらがどのように連携するかを知ることは苦痛でした。確かに、それは長期的なプロセスでした。何かが必要で、十分な時間がなく、怠惰で乱雑になり始めます。しかし、拡張性と読みやすさを容易にするために、MSBuildスクリプトをどのように構成すればよいのでしょうか。特に、ターゲットDependsOnTargets、BeforeTargets、AfterTargetsの間の3つの関係は、自分の足を撃つのに役立ちました。
2 に答える
私のMSBuildブックには、興味があれば、MSBuildで再利用可能な要素を作成する方法に基づいたセクションがあります。ここでもコメントをします。この内容は本の内容とは異なります。
MSBuildファイルを作成するときは、何をどのように分離するかに注意する必要があります。それを少し説明するために、マネージドVSプロジェクトが箱から出してどのように機能するかを調べてみましょう(これは再利用可能な要素の優れたモデルです)。
C#/ VBプロジェクトを作成すると、.csprojファイルが取得されます。この.csprojファイルには、主にプロパティとアイテムが含まれています。そのファイルには単一のターゲットはありません。これらのファイルには、ビルドされるものが含まれています(ビルドに関連するいくつかの設定とともに)。プロジェクトファイルの下部に、インポートステートメントがあります。このインポートにより、プロジェクトの構築方法がもたらされます。
インポートされるファイルは次のとおりです。
- Microsoft.CSharp / VisualBasic / FSharp.targets
- Microsoft.common.targets
この場合、Microsoft.common.targetsは、すべての管理対象言語の全体的なビルドプロセスを定義します。次に、Microsoft.CSharp.targets(または他の言語固有の.targetsファイルの1つ)が、特定の言語固有のツールを呼び出す方法の空白を埋めます。
DependsOnTargetsとBefore/AfterTargetsの比較
上記の回答では、「本当に必要な場合を除いて、たとえば2つの場合を除いて、DependsOnTargetsを避けることをお勧めします」と述べています。私はこれに同意しません。これがDependsOnTargetsとBefore/AfterTargetsの私の見解です。
DependsOnTargetsを使用する場合
- 実行するターゲットのワークフローを作成しようとしている場合
- 他のターゲットを最初に実行しないとターゲットが機能しない場合
- 目的の操作に基づいて特定のステップで異なるターゲットを注入する必要がある場合
の場合にBefore/AfterTargetsを使用する
- ターゲットが存在するファイルを所有しておらず、拡張可能なDependsOnTargetsプロパティがない場合
- ターゲットがいつ実行されるかに関係なく、特定のターゲットの前/後にターゲットを実行する必要があります
違いを少し引き出すために、Webプロジェクトを検討してください。Webプロジェクトの場合、.csproj/.vbprojが処理するワークフローは2つあります。
- 建てる
- 公開
ビルドターゲットの前に実行されるターゲットのリストにターゲットを追加したい場合は、公開シナリオでのみBuildDependsOnプロパティを動的に更新できます。Before/AfterTargetsではこれを行うことはできません。
理想的な世界では、各ターゲットは次のwrtDependsOnTargetsを持ちます。
- すべてのターゲットには、プロパティによってフィードされるDependsOnTargets属性があります
- 各DependsOnTargetsは、常に既存の値をプロパティ定義の前に追加します
例えば
<MyTargetDependsOn>
$(MyTargetDependsOn);
Target1;
Target2
</MyTargetDependsOn>
残念ながら、多くのターゲットはこのパターンに従わないため、DependsOnTargetsは多くの場合水中で死んでいます。
MSBuildスクリプトを作成するときは、Before / AfterTargetsを使用することを選択する必要がある明確な理由がない限り、常にDependsOnTargetsを使用します。私は(当時Microsoftにいなかったので、デザインの本当の理由についての洞察はありません)Before / AfterTargetsは、ユーザーがターゲットを注入して、ターゲットの前/後に実行できるようにするために実際に作成されたと感じています。所有し、作成者は上記のパターンを使用しませんでした。
さて、私がすでに言ったように、私は私自身の経験をしました:)
- オブジェクト指向プログラミングの場合と同様に、すべてのターゲットは単一のファイルになります:)また、共通のプロパティとアイテムグループは、独自のファイルに残しておく必要があります(たとえば、構築するすべてのプロジェクトを含むProjects.conf 、ツールへのすべてのパスを含むEnvironment.conf 、拡張パック)。ご覧のとおり、これはさまざまなビルドセット(ローカルビルドやサーバービルドなど)に非常に便利です。
- 必要なすべてのパス、ファイル、およびプロパティを定義します。DRYの原則に従わないでください。これは難しいことですが、私の経験から、すでに定義されている値に基づいて新しい値を構築することは困難です。したがって、 .csprojファイルと.nuspecファイルを含むプロジェクトがある場合は、わざわざ両方を明示的に宣言しないでください。
- Initターゲットを宣言します。説明は次のとおりです。
- Clean、Build、Test、Package、Deploy、Archiveなど、ビルド中に処理する必要のあるいくつかの主要なターゲットを宣言します。それらを相互に依存させます(DependsOnTargets )。テストとパッケージは、たとえばビルドターゲットによって異なります。それらはすべてInitターゲットに依存している必要があります。
- 健全性チェックはルール1の例外です。それらは、必要な場所と同じファイルに属します。たとえば、Pac kageCheckターゲットは、.nuspecファイルが存在することを監視し、 InitをAfterTargetsとして宣言します。これにより、ビルドを高速で失敗させることができます。
- 他のすべてのターゲットは、BeforeTargets、AfterTargetsを使用して並べ替えられます。本当に必要な場合を除いて、 DependsOnTargetsを避けることをお勧めします。たとえば、 Buildの前に2つのターゲットを処理する必要があるが、相互に順序がある場合などです。
- サーバーとローカルのビルドスクリプトでは、必要なものすべてと、パブリックAPIである必要があるものをインポートするだけです。私が言ったように、すべてのターゲットは単一のファイルになります(1)。これで、 UpdateAssemblyVersionターゲットがある場合、 Build in AfterTargets(5)を宣言する必要があります。インポートを追加/削除するだけで、そのターゲットをアクティブ化/非アクティブ化できます。
- MSBuildExtensionPackを利用してください。彼らは本当に役に立ちます!しかし、あなた自身のタスクを書くことを恐れないでください!
これが私のルールです。コメントや補足をいただければ幸いです。