directory-build-organization について
.NETのビルド設定を整理し、NuGetのバージョンを一元管理する開発者向けのガイドです。.propsと.targetsの使い分け、ディレクトリ階層ごとのインポート、評価済み設定の確認方法を説明します。
主な機能
- 01.propsと.targetsの役割の区別
- 02Directory.Packages.propsによるバージョン集約
- 03階層別のビルドファイル構成
- 04TargetFramework条件の注意点
- 05プリプロセス出力による評価済み設定の確認
活用シーン
- 01複数プロジェクトで重複するビルド設定の集約
- 02NuGetパッケージバージョンの一元管理
- 03srcとtestの階層構成の計画
- 04インポート順や条件が反映されない問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill directory-build-organizationインストール後、Claude Code で/directory-build-organizationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/common-patterns.mdmulti-level-examples.mdtargetframework-props-pitfall.md詳細
Directory.Build ファイルでビルドインフラストラクチャを整理する
意図の確認
- 「〜すべきか」「どのように整理すべきか」「推奨して」「アドバイスして」などの依頼やレビュー依頼では、実際のプロジェクトファイルを確認し、提案する構成のみを返します。ユーザーから設定の適用、移動、一元化、または整理を明示的に依頼されない限り、ファイルを作成または編集しないでください。
- 要約する前に、プロジェクト固有の各プロパティ、項目、またはターゲットがどのプロジェクトに残るかを確認してください。実際のファイル名を示し、兄弟ファイル名から推測しないでください。
- 実際の
.sln、.slnx、.proj、またはプロジェクトファイルがそのパスにあることを確認した後にのみ、検証コマンドを推奨してください。リポジトリルートにエントリポイントがない場合は、確認できたプロジェクト用のコマンドを示すか、エントリポイントが見つからなかったことを明記してください。 - すべてのアドバイスでは、
Microsoft.Common.propsが各プロジェクトから上方向に検索し、最も近いDirectory.Build.propsを早い段階で自動的にインポートすることを説明してください。その後、プロジェクトの値で共有デフォルト値を上書きできます。Directory.Build.targetsを推奨する場合は、同等の遅いMicrosoft.Common.targetsのインポートについても説明してください。
Directory.Build.props と Directory.Build.targets の違い
評価時にインポートされるタイミングが異なるため、どちらのファイルを使うかを理解することが重要です。
評価順序:
Microsoft.Common.props imports Directory.Build.props early
→ project body and package/project imports evaluate
→ Microsoft.Common.targets imports Directory.Build.targets late
.props を使う用途 | .targets を使う用途 |
|---|---|
| プロパティのデフォルト値の設定 | カスタムビルドターゲット |
| 共通項目の定義 | 後から適用するプロパティの上書き |
| プロジェクトで上書き可能なプロパティ | ビルド後の処理 |
| アセンブリ/パッケージのメタデータ | 最終値に対する条件付きロジック |
| アナライザーの PackageReferences | SDK で定義されたプロパティに依存するターゲット |
経験則: プロパティと項目は .props に、カスタムターゲットと後から適用するロジックは .targets に記述します。
.props はプロジェクトファイルより前にインポートされるため、プロジェクトからそこで設定した値を上書きできます。.targets はすべての処理の後にインポートされるため、最終的な決定権を持ちます。ただし、プロジェクトから .targets の値を上書きすることはできません。
⚠️ 重要:.props と .targets での TargetFramework の利用可否
.props ファイルで $(TargetFramework) を使ったプロパティ条件を指定すると、単一ターゲットのプロジェクトではプロパティが .props の評価時に空になるため、条件が暗黙に失敗します。TFM に依存するプロパティは、代わりに .targets に移動してください。ItemGroup と Target の条件には影響しません。
詳しい説明は、targetframework-props-pitfall.md を参照してください。
Directory.Build.props
適した用途:言語設定、アセンブリ/パッケージのメタデータ、ビルド警告、コード分析、共通アナライザー。
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Company>Contoso</Company>
<Authors>Contoso Engineering</Authors>
</PropertyGroup>
</Project>
ここには記述しないでください: プロジェクト固有の TFM、プロジェクト固有の PackageReferences、ターゲット/ビルドロジック、または SDK で定義された値に依存するプロパティ(.props の評価時には利用できません)。
Directory.Build.targets
適した用途:カスタムビルドターゲット、後から適用するプロパティの上書き(SDK プロパティに依存する値)、ビルド後の検証。
<Project>
<Target Name="ValidateProjectSettings" BeforeTargets="Build">
<Error Text="All libraries must target netstandard2.0 or higher"
Condition="'$(OutputType)' == 'Library' AND '$(TargetFramework)' == 'net472'" />
</Target>
<PropertyGroup>
<DocumentationFile Condition="'$(IsPackable)' == 'true'">$(OutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>
Directory.Packages.props(Central Package Management)
Central Package Management(CPM)を使うと、すべての NuGet パッケージバージョンの信頼できる情報源を一元化できます。詳しくは https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management を参照してください。
リポジトリルートの Directory.Packages.props で CPM を有効にします:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="xunit" Version="2.9.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
</ItemGroup>
</Project>
Directory.Build.rsp
ディレクトリツリー配下のすべてのビルドに適用される、既定の MSBuild CLI 引数が含まれます。
Directory.Build.rsp の例:
/maxcpucount
/nodeReuse:false
/consoleLoggerParameters:Summary;ForceNoAlign
/warnAsMessage:MSB3277
- 最新の .NET バージョンでは、
msbuildとdotnetの両方の CLI で動作します - CI とローカルビルドのフラグを統一するのに適しています
- 引数はそれぞれ別の行に記述します
複数階層の Directory.Build ファイル
MSBuild は、プロジェクトディレクトリから上の階層へたどって見つかった最初の Directory.Build.props(または .targets)のみを自動インポートします。複数階層を連結するには、内側のファイルの先頭で親ファイルを明示的にインポートします。ファイルの完全な例については、multi-level-examples を参照してください。
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="Exists('$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))')" />
</Project>
レイアウト例:
repo/
Directory.Build.props ← repo-wide (lang version, company info, analyzers)
Directory.Build.targets ← repo-wide targets
Directory.Packages.props ← central package versions
src/
Directory.Build.props ← src-specific (imports repo-level, sets IsPackable=true)
test/
Directory.Build.props ← test-specific (imports repo-level, sets IsPackable=false, adds test packages)
成果物の出力レイアウト(.NET 8 以降)
Directory.Build.props で <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath> を設定すると、単一の artifacts/ フォルダー内に、プロジェクト名ごとに分けられた bin/、obj/、publish/ ディレクトリが自動的に生成されます。これにより、デフォルトで bin/obj の競合を回避できます。ディレクトリのレイアウトと追加のパターン(プロジェクトの種類に応じた条件付き設定、パック後の検証)については、common-patterns を参照してください。
ワークフロー:ビルドインフラストラクチャの整理
- すべての
.csprojファイルを監査する — ソリューション全体のすべての<PropertyGroup>、<ItemGroup>、カスタム<Target>を一覧にします。繰り返し使われている設定と、プロジェクト固有の設定を確認します。 - ルートに
Directory.Build.propsを作成する — 共通のプロパティのデフォルト値(LangVersion、Nullable、TreatWarningsAsErrors、メタデータ)をここに移動します。これらはプロジェクトファイルより前にインポートされるため、プロジェクト側で上書きできます。 - ルートに
Directory.Build.targetsを作成する — カスタムビルドターゲット、ビルド後の検証、SDK で定義された値に依存するプロパティ(例:単一ターゲットのプロジェクトにおけるOutputPath、TargetFramework)をここに移動します。これらは SDK の後にインポートされるため、すべてのプロパティを利用できます。 Directory.Packages.propsを作成する — 一元管理されたパッケージ管理(ManagePackageVersionsCentrally)を有効にし、すべてのPackageVersionエントリを記述して、.csprojファイルのPackageReference項目からVersion=を削除します。- 複数階層の構成を設定する —
src/フォルダーとtest/フォルダーに、異なる設定を持つ内側のDirectory.Build.propsファイルを作成します。親ファイルとの連結にはGetPathOfFileAboveを使用します。 .csprojファイルを簡素化する — 一元管理されたプロパティ、バージョン属性、重複したターゲットをすべて削除します。各プロジェクトには、そのプロジェクト固有の内容のみを記述します。- 検証する — 見つかった
.sln、.slnx、.proj、またはプロジェクトのパスを対象に、復元とビルドを実行します。例:dotnet restore <entrypoint> && dotnet build <entrypoint>。ルートのエントリポイントが存在しない場合は、リポジトリのルートからビルドできると主張せず、見つかった各プロジェクトを個別に検証します。最終的に統合された内容を確認するには、dotnet msbuild <project> -pp:output.xmlを使用します。
トラブルシューティング
| 問題 | 原因 | 解決方法 |
|---|---|---|
Directory.Build.props が読み込まれない | ファイル名の大文字・小文字が異なる(Linux/macOS では完全一致が必要) | 大文字・小文字を含め、正確なファイル名が Directory.Build.props(大文字の D、B)であることを確認します |
.props のプロパティがプロジェクトに反映されない | プロジェクトがインポート後に同じプロパティを設定している | プロパティを Directory.Build.targets に移動し、プロジェクトの後に設定します |
| 複数階層のインポートが機能しない | 内側のファイルで GetPathOfFileAbove を使ったインポートが抜けている | 内側のファイルの先頭に <Import> 要素を追加します(複数階層のセクションを参照) |
SDK の値を使うプロパティが .props で空になる | .props の評価時点では SDK のプロパティがまだ定義されていない | SDK の後にインポートされる .targets に移動します |
Directory.Packages.props が見つからない | ファイルがリポジトリのルートにない、または名前が完全一致していない | Directory.Packages.props という名前で、プロジェクトディレクトリと同じ階層かそれより上に配置する必要があります |
$(TargetFramework) を使ったプロパティ条件が .props で一致しない | 単一ターゲットのプロジェクトでは、.props の評価時点で TargetFramework がまだ設定されていない | プロパティを .targets に移動するか、代わりに遅い段階で評価される ItemGroup/Target の条件を使用します |
診断: プリプロセス済みのプロジェクト出力を使うと、すべてのインポートと最終的なプロパティ値を確認できます。
dotnet msbuild -pp:output.xml MyProject.csproj
これにより、すべてのインポートがインラインに展開され、各プロパティがどこで設定されているか、最終的に評価された値が何かを正確に確認できます。