はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】Directory.Build.propsとDirectory.Build.targetsを用いて特定フォルダ以下のcsprojに対して共通設定を記述する

はじめに

今回はDirectory.Build.propsDirectory.Build.targetsを用いて特定フォルダ以下の.csprojに対して共通設定を記述する方法を紹介したいと思います。

概要

Directory.Build.propsDirectory.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という定義を一括で書き込むみたいな感じです。