はじめに
今回はDirectory.Build.props
とDirectory.Build.targets
を用いて特定フォルダ以下の.csproj
に対して共通設定を記述する方法を紹介したいと思います。
概要
Directory.Build.props
とDirectory.Build.targets
を用いることで特定のディレクトリ配下にあるすべてのプロジェクトに対して、共通のプロパティ、アイテム、ターゲットを一元的に定義することができます。
- Directory.Build.props :
csproj
が読み込まれる前に適応される。既定値を設定できる - Directory.Build.targets :
csproj
が読み込まれる後に適応される。最終的に採用する値を設定できる
注意点
.csproj
のあるディレクトリから親ディレクトリへと順に探索をしていき、最初に見つかった Directory.Build.props (または Directory.Build.targets) ファイルをインポートします。
つまり、デフォルトでは最も「近い」ディレクトリにあるファイルのみが自動的に適用されるのでご注意ください。
When searching for a Directory.Build.props file, MSBuild walks the directory structure upwards from your project location $(MSBuildProjectFullPath), stopping after it locates a Directory.Build.props file. For example, if your $(MSBuildProjectFullPath) was c:\users\username\code\test\case1, MSBuild would start searching there and then search the directory structure upward until it located a Directory.Build.props file, as in the following directory structure.
// DeepL翻訳
MSBuild は、Directory.Build.props ファイルを検索するとき、プロジェクトの場所 $(MSBuildProjectFullPath) からディレクトリ構造を上方向に検索し、Directory.Build.props ファイルが見つかったら停止します。
Customize your build by folder or solution - MSBuild | Microsoft Learn
利用例
特にDirectory.Build.props
は複数プロジェクトがあるような場合だとよく見かける気がします。例えばCySharpさんだと以下のように作者情報やパッケージバージョンなどを記述しています。
<Project> <PropertyGroup> <!-- NuGet Packaging --> <PackageVersion>$(Version)</PackageVersion> <Company>Cysharp</Company> <Authors>Cysharp</Authors> <Copyright>© Cysharp, Inc.</Copyright> <PackageProjectUrl>https://github.com/Cysharp/ZLinq</PackageProjectUrl> <RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl> <RepositoryType>git</RepositoryType> <PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageIcon>Icon.png</PackageIcon> <SignAssembly>true</SignAssembly> <PackageReadmeFile>README.md</PackageReadmeFile> <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)opensource.snk</AssemblyOriginatorKeyFile> <GenerateDocumentationFile>true</GenerateDocumentationFile> <NoWarn>1591</NoWarn> <!-- NuGet deterministic build --> <Deterministic>true</Deterministic> <ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild> <!-- NuGet SourceLink --> <PublishRepositoryUrl>true</PublishRepositoryUrl> <EmbedUntrackedSources>true</EmbedUntrackedSources> <IncludeSymbols>true</IncludeSymbols> <SymbolPackageFormat>snupkg</SymbolPackageFormat> <!-- NuGet Missing Compiler Flags --> <Features>strict</Features> </PropertyGroup> <PropertyGroup Condition="'$(ContinuousIntegrationBuild)' == 'true'"> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <!-- CS9074: The 'scoped' modifier of parameter 'span' doesn't match overridden or implemented member.--> <WarningsNotAsErrors>$(WarningsNotAsErrors);CS9074</WarningsNotAsErrors> </PropertyGroup> <ItemGroup> <None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" /> </ItemGroup> <PropertyGroup> <SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage> </PropertyGroup> </Project>
ZLinq/Directory.Build.props at main · Cysharp/ZLinq · GitHub
ちなみに
この手法はInterceptors
というC#12からの機能を使う際に、結構有効活用できる知識だったりするみたいです。
andrewlock.net
InterceptorsNamespaces
という定義を一括で書き込むみたいな感じです。