MSBuild: Inherit Directory.Build.props files

Microsoft Build ("MSBuild") now ships the feature where the special project root files Directory.Build.props and Directory.Build.targets are applied for all the projects discovered in inherited folders.

By default, once MSBuild finds one file for each suffix, it stops processing all ancestor folders for additional files with the same name. Nested or inherited Directory.Build.(props|targerts) files are not supported out of the box.

To enable nested or inherited Directory.Build.(props|targets); the directive, <Import /> , must be used in child files. The MSBuild documentation covers how to do this without notating that the <Import /> directive must be within the `<Project></Project>` element, which may be confusing if you're starting to dig into customizing MSBuild projects.

Single Level Example

Let’s say you want to enable different build properties for src and test directories for your project as outlined below for different Rosalyn analyzer rule sets:

  • project

    • Directory.Build.props

    • src

      • Directory.Build.props

      • ProjectA

    • test

      • Directory.Build.props

      • ProjectA.Tests

In Directory.Build.props files in the child directory, the key item to add is the <Import /> directive inside the <Project> element with an [MsBuild] function to get the file path of the ancestor folder above the current directory.

Functions in MSBuild projects are called similar to static functions in PowerShell, with the type wrapped in square brackets followed by a double colon and then paren with arguments e.g. [Type]::Method('arg1', '$(BuildProp1)')

<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

</Project>

Once the above is set, you can import the base props or target file(s) and add any overrides or addition properties for the child file. The example below uses different Rosalyn analyzer rule sets based upon the build configuration value.

<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup Condition="$(Configuration) == 'Release'">

<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../nm-release.ruleset</CodeAnalysisRuleSet>

</PropertyGroup>

<PropertyGroup Condition="$(Configuration) == 'Debug'">

<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../nm-debug.ruleset</CodeAnalysisRuleSet>

</PropertyGroup>

</Project>

Now you are set to use nested Directory.Build.(props|targets) files. 🐱‍🐉