フォルダー別にビルドをカスタマイズする
MSBuild によってインポートされる特定のファイルを追加して、既定のプロパティ設定をオーバーライドし、カスタム ターゲットを追加できます。 これらのカスタマイズのスコープは、これらのファイルを配置する場所によってフォルダー レベルで制御できます。
この記事では、次のシナリオに適用できるカスタマイズについて説明します。
- ソリューション内の多くのプロジェクトのビルド設定をカスタマイズする
- 共通のファイル ディレクトリで多くのソリューションのビルド設定をカスタマイズする
- フォルダーの複雑な構造のサブフォルダーに対して異なるビルド設定をカスタマイズする
- 既定の設定、既定のビルド フォルダー、SDK によって設定されたその他の動作 (
Microsoft.Net.Sdk
など) をオーバーライドする - 任意の数のプロジェクトまたはソリューションに適用されるビルド ターゲットを追加またはカスタマイズする
C++ プロジェクトを使用している場合は、「C++ ビルドのカスタマイズ で説明されているメソッドを使用することもできます。
Directory.Build.props と Directory.Build.targets
すべてのプロジェクトに新しいプロパティを追加するには、ソースを含むルート フォルダーに Directory.Build.props という名前の 1 つのファイルで定義します。
MSBuild を実行すると、Microsoft.Common.props がディレクトリ構造内で Directory.Build.props ファイルを検索します。 それを見つけると、ファイルをインポートし、その中で定義されているプロパティを読み取ります。 Directory.Build.props は、ディレクトリの下にあるプロジェクトにカスタマイズを提供するユーザー定義ファイルです。
同様に、Microsoft.Common.targets は、Directory.Build.targetsを検索します。
Directory.Build.props は、インポートされたファイルのシーケンスの早い段階でインポートされます。これは、インポートで使用されるプロパティ(特に、ほとんどの .NET プロジェクト ファイルで .NET SDK を使用する場合など)、Sdk
属性を使用して暗黙的にインポートされるプロパティを設定する必要がある場合に重要です。
手記
Linux ベースのファイル システムは、大文字と小文字を区別します。 Directory.Build.props ファイル名の大文字と小文字が正確に一致していることを確認してください。一致していないと、ビルド プロセス中に検出されません。
Directory.Build.props の例
たとえば、Visual Studio ソリューション内のすべてのプロジェクトの出力ディレクトリを設定する Directory.Build.props ファイルを次に示します。 各プロジェクトの出力は、独自のプロジェクト名の下に配置されます。 この例では、Directory.Build.props ファイルはソリューション フォルダー内にあり、その下にはサブフォルダーに多数のプロジェクトがあります。 $(MSBuildProjectName)
プロパティは、各プロジェクトの名前を指定します。 Directory.Build.props ファイルは、独自のビルド中に各プロジェクトにインポートされるため、ソリューション内の個々のプロジェクトごとに適切な値に評価されます。
ソリューションをクリーンアップして、古い出力ファイルを削除します。
msbuild /t:Clean SolutionName.sln
Directory.Build.propsという名前の新しいファイルをリポジトリのルートに作成します。
次の XML をファイルに追加します。
<Project> <PropertyGroup> <OutDir>C:\output\$(MSBuildProjectName)</OutDir> </PropertyGroup> </Project>
手記
$(OutDir)
プロパティは出力への絶対パスであり、これを使用すると、.NET プロジェクトで通常使用される構成、ターゲット フレームワーク、またはランタイムのサブフォルダーの作成がバイパスされます。 通常のサブフォルダーをカスタム出力パスの下に作成する場合は、代わりにプロパティBaseOutputPath
を使用してみてください。MSBuild を実行します。 プロジェクトの既存の Microsoft.Common.props と Microsoft.Common.targets のインポートでは、Directory.Build.props ファイルを見つけてそれをインポートし、新しい出力フォルダーがそのフォルダーの下にあるすべてのプロジェクトに対して使用されます。
検索範囲
Directory.Build.props ファイルを検索すると、MSBuild はプロジェクトの場所 $(MSBuildProjectFullPath)
からディレクトリ構造を上方向に移動し、Directory.Build.props ファイルを見つけた後に停止します。 たとえば、$(MSBuildProjectFullPath)
が c:\users\username\code\test\case1 にした場合、MSBuild はそこで検索を開始し、次のディレクトリ構造のように、Directory.Build.props ファイルを見つけるまでディレクトリ構造を上方向に検索します。
c:\users\username\code\test\case1
c:\users\username\code\test
c:\users\username\code
c:\users\username
c:\users
c:\
ソリューション ファイルの場所は、Directory.Build.props とは無関係です。
インポート順序
Directory.Build.props は、Microsoft.Common.props 早い段階でインポートされ、後で定義されたプロパティは使用できません。 そのため、まだ定義されていない (および空と評価される) プロパティを参照することは避けてください。
Directory.Build.props で設定されたプロパティは、プロジェクト ファイル内またはインポートされたファイル内の別の場所でオーバーライドできるため、Directory.Build.props の設定は、プロジェクトの既定値を指定 と考える必要があります。
Directory.Build.targets は、NuGet パッケージから .targets
ファイルをインポートした後、Microsoft.Common.targets からインポートされます。 そのため、ほとんどのビルド ロジックで定義されているプロパティとターゲットをオーバーライドしたり、個々のプロジェクトが設定した内容に関係なく、すべてのプロジェクトのプロパティを設定したりできます。
プロパティを設定する必要がある場合、または以前の設定をオーバーライドする個々のプロジェクトのターゲットを定義する必要がある場合は、最終的なインポートの後に、そのロジックをプロジェクト ファイルに配置します。 SDK スタイルのプロジェクトでこれを行うには、最初に SDK スタイルの属性を同等のインポートに置き換える必要があります。 「MSBuild プロジェクト SDKを使用する方法」を参照してください。
手記
MSBuild エンジンは、プロジェクト (PreBuildEvent
を含む) のビルドの実行を開始する前に、評価中にインポートされたすべてのファイルを読み取ります。そのため、これらのファイルは、PreBuildEvent
やビルド プロセスの他の部分によって変更される予定はありません。 変更は、MSBuild.exe の次回の呼び出しまたは次の Visual Studio ビルドまで有効になりません。 また、ビルド プロセスに多数のプロジェクト ビルド (マルチターゲットプロジェクトやビルド依存プロジェクトと同様) が含まれている場合、Directory.build.propsを含むインポートされたファイルは、個々のプロジェクト ビルドごとに評価が行われるときに読み取られます。
ユース ケース: 複数レベルのマージ
この標準的なソリューション構造があるとします。
\
MySolution.sln
Directory.Build.props (1)
\src
Directory.Build.props (2-src)
\Project1
\Project2
\test
Directory.Build.props (2-test)
\Project1Tests
\Project2Tests
すべてのプロジェクト (1)の共通プロパティ、src プロジェクト の共通プロパティ (2-src)、および テスト プロジェクトの共通プロパティ (2 テスト)が望ましい場合があります。
MSBuild で "内部" ファイル (2 src と 2 テスト) を "outer" ファイル (1) と正しくマージするには、MSBuild が Directory.Build.props ファイルを見つけたら、さらにスキャンを停止することを考慮する必要があります。 スキャンを続行して外部ファイルにマージするには、次のコードを両方の内部ファイルに配置します。
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
MSBuild の一般的なアプローチの概要は次のとおりです。
- 特定のプロジェクトに対して、MSBuild はソリューション構造内で最初に見つかった Directory.Build.props を、ディレクトリの上層に向かって検索し、見つけたものを既定の設定と統合して、それ以上のスキャンを停止します。
- 複数レベルで検索して結合する場合は、"内部" ファイルから "外部" ファイルを
<Import...>
します (前のコードを参照)。 - "外部" ファイル自体もその上に何かをインポートしない場合は、そこでスキャンが停止します。
または簡単に言えば、何もインポートしない最初の Directory.Build.props は、MSBuild が停止する場所です。
インポート プロセスをより明示的に制御するには、プロパティ $(DirectoryBuildPropsPath)
、$(ImportDirectoryBuildProps)
、$(DirectoryBuildTargetsPath)
、および $(ImportDirectoryBuildTargets)
を使用します。 プロパティ $(DirectoryBuildPropsPath)
は、使用する Directory.Build.props
ファイルへのパスを指定します。同様に、$(DirectoryBuildTargetsPath)
は Directory.Build.targets
ファイルへのパスを指定します。
ブール型のプロパティ $(ImportDirectoryBuildProps)
と $(ImportDirectoryBuildTargets)
は既定で true
に設定されているため、通常、MSBuild はこれらのファイルを検索しますが、MSBuild がファイルをインポートできないように false
に設定できます。
例
この例では、前処理された出力を使用して、プロパティを設定する場所を決定します。
設定する特定のプロパティの使用状況を分析するには、/preprocess
または /pp
引数を指定して MSBuild を実行します。 出力テキストは、暗黙的にインポートされる Microsoft.Common.props などのシステム インポートや、独自のインポートなど、すべてのインポートの結果です。 この出力では、プロパティを設定する必要がある場所を、その値が使用される場所と関連付けて確認できます。
たとえば、単純な .NET Core または .NET 5 以降のコンソール アプリ プロジェクトがあり、通常は obj
中間出力フォルダーをカスタマイズするとします。 このパスを指定するプロパティは BaseIntermediateOutput
です。 TargetFramework
など、既に設定されている他のさまざまなプロパティと共にプロジェクト ファイル内の PropertyGroup
要素にこれを配置しようとすると、プロパティが有効にならないことがプロジェクトをビルドするときに検出されます。 /pp
オプションを使用して MSBuild を実行し、出力で BaseIntermediateOutputPath
を検索すると、その理由がわかります。 この場合、BaseIntermediateOutput
は読み取られ、Microsoft.Common.props
で使用されます。
Microsoft.Common.props にコメントがあり、プロパティ BaseIntermediateOutput
はここで、別のプロパティ (MSBuildProjectExtensionsPath
) によって使用される前に設定する必要があることが示されています。 また、BaseIntermediateOutputPath
が最初に設定されたときに、既存の値のチェックが行われ、未定義の場合は obj
に設定されることがわかります。
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
したがって、この配置は、このプロパティを設定するには、これより前の場所で指定する必要があることを示します。 前処理された出力のこのコードの直前に、Directory.Build.props
がインポートされていることがわかります。そのため、そこに BaseIntermediateOutputPath
設定でき、必要な効果を得るのに十分な早い段階で設定されます。
次の省略された前処理された出力は、BaseIntermediateOutput
設定を Directory.Build.props
に配置した結果を示しています。 標準インポートの先頭にあるコメントには、ファイル名と、通常、そのファイルがインポートされる理由に関する有用な情報が含まれます。
<?xml version="1.0" encoding="IBM437"?>
<!--
============================================================================================================================================
c:\source\repos\ConsoleApp9\ConsoleApp9\ConsoleApp9.csproj
============================================================================================================================================
-->
<Project DefaultTargets="Build">
<!--
============================================================================================================================================
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk">
This import was added implicitly because the Project element's Sdk attribute specified "Microsoft.NET.Sdk".
C:\Program Files\dotnet\sdk\7.0.200-preview.22628.1\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props
============================================================================================================================================
-->
<!--
***********************************************************************************************
Sdk.props
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
<PropertyGroup xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Indicate to other targets that Microsoft.NET.Sdk is being used.
This must be set here (as early as possible, before Microsoft.Common.props)
so that everything that follows can depend on it.
In particular, Directory.Build.props and nuget package props need to be able
to use this flag and they are imported by Microsoft.Common.props.
-->
<UsingMicrosoftNETSdk>true</UsingMicrosoftNETSdk>
<!--
Indicate whether the set of SDK defaults that makes SDK style project concise are being used.
For example: globbing, importing msbuild common targets.
Similar to the property above, it must be set here.
-->
<UsingNETSdkDefaults>true</UsingNETSdkDefaults>
</PropertyGroup>
<PropertyGroup Condition="'$(MSBuildProjectFullPath)' == '$(ProjectToOverrideProjectExtensionsPath)'" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<MSBuildProjectExtensionsPath>$(ProjectExtensionsPathForSpecifiedProject)</MSBuildProjectExtensionsPath>
</PropertyGroup>
<!--<Import Project="$(AlternateCommonProps)" Condition="'$(AlternateCommonProps)' != ''" />-->
<!--
============================================================================================================================================
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="'$(AlternateCommonProps)' == ''">
C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
============================================================================================================================================
-->
<!--
***********************************************************************************************
Microsoft.Common.props
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->
<PropertyGroup>
<ImportByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportByWildcardBeforeMicrosoftCommonProps>
<ImportByWildcardAfterMicrosoftCommonProps Condition="'$(ImportByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportByWildcardAfterMicrosoftCommonProps>
<ImportUserLocationsByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardBeforeMicrosoftCommonProps>
<ImportUserLocationsByWildcardAfterMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardAfterMicrosoftCommonProps>
<ImportDirectoryBuildProps Condition="'$(ImportDirectoryBuildProps)' == ''">true</ImportDirectoryBuildProps>
</PropertyGroup>
<!--
Determine the path to the directory build props file if the user did not disable $(ImportDirectoryBuildProps) and
they did not already specify an absolute path to use via $(DirectoryBuildPropsPath)
-->
<PropertyGroup Condition="'$(ImportDirectoryBuildProps)' == 'true' and '$(DirectoryBuildPropsPath)' == ''">
<_DirectoryBuildPropsFile Condition="'$(_DirectoryBuildPropsFile)' == ''">Directory.Build.props</_DirectoryBuildPropsFile>
<_DirectoryBuildPropsBasePath Condition="'$(_DirectoryBuildPropsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildPropsFile)'))</_DirectoryBuildPropsBasePath>
<DirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsBasePath)' != '' and '$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)'))</DirectoryBuildPropsPath>
</PropertyGroup>
<!--
============================================================================================================================================
<Import Project="$(DirectoryBuildPropsPath)" Condition="'$(ImportDirectoryBuildProps)' == 'true' and exists('$(DirectoryBuildPropsPath)')">
c:\source\repos\ConsoleApp9\Directory.Build.props
============================================================================================================================================
-->
<!-- Directory.build.props
-->
<PropertyGroup>
<BaseIntermediateOutputPath>myBaseIntermediateOutputPath</BaseIntermediateOutputPath>
</PropertyGroup>
<!--
============================================================================================================================================
</Import>
C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
============================================================================================================================================
-->
<!--
Prepare to import project extensions which usually come from packages. Package management systems will create a file at:
$(MSBuildProjectExtensionsPath)\$(MSBuildProjectFile).<SomethingUnique>.props
Each package management system should use a unique moniker to avoid collisions. It is a wild-card import so the package
management system can write out multiple files but the order of the import is alphabetic because MSBuild sorts the list.
-->
<PropertyGroup>
<!--
The declaration of $(BaseIntermediateOutputPath) had to be moved up from Microsoft.Common.CurrentVersion.targets
in order for the $(MSBuildProjectExtensionsPath) to use it as a default.
-->
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
<BaseIntermediateOutputPath Condition="!HasTrailingSlash('$(BaseIntermediateOutputPath)')">$(BaseIntermediateOutputPath)\</BaseIntermediateOutputPath>
<_InitialBaseIntermediateOutputPath>$(BaseIntermediateOutputPath)</_InitialBaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath Condition="'$(MSBuildProjectExtensionsPath)' == '' ">$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>
<!--
Import paths that are relative default to be relative to the importing file. However, since MSBuildExtensionsPath
defaults to BaseIntermediateOutputPath we expect it to be relative to the project directory. So if the path is relative
it needs to be made absolute based on the project directory.
-->
<MSBuildProjectExtensionsPath Condition="'$([System.IO.Path]::IsPathRooted($(MSBuildProjectExtensionsPath)))' == 'false'">$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))</MSBuildProjectExtensionsPath>
<MSBuildProjectExtensionsPath Condition="!HasTrailingSlash('$(MSBuildProjectExtensionsPath)')">$(MSBuildProjectExtensionsPath)\</MSBuildProjectExtensionsPath>
<ImportProjectExtensionProps Condition="'$(ImportProjectExtensionProps)' == ''">true</ImportProjectExtensionProps>
<_InitialMSBuildProjectExtensionsPath Condition=" '$(ImportProjectExtensionProps)' == 'true' ">$(MSBuildProjectExtensionsPath)</_InitialMSBuildProjectExtensionsPath>
</PropertyGroup>
...
関連コンテンツ
- ビルドをカスタマイズします。