Directory.Build.props - Centralize your builds

2/13/2022
3 minute read

The Directory.Build.props works almost like a "normal" csproj file. You can define default dependencies or even apply implicit usings. That is for example very convenient when you have a test project where you might always want to import Moq and XUnit namespace due to your setup.

How does it work?

It is simple. MSBuild will traverse from your current csproj path upwards to file-hierarchy and selects the first More to that later Directory.Build.props it can find and includes it. Have us a look at the following file-structure:

src/
+- Infrastructure/
¦  +- Infrastructure.csproj
+- Tools/
¦  +- Tools.csproj
+- Web/
¦  +- Web.csproj
+- Directoy.Build.props

Under our src there is directly the Directory.Build.props and then we have some folders which contain the "real" projects. Those "real" projects automatically import our Directory.Build.props.

Let us have a look at an example:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<ItemGroup>
      <PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      </PackageReference>
      <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      </PackageReference>
    </ItemGroup>

    <ItemGroup>
        <AdditionalFiles Include="$(SolutionDir)\stylecop.json" Link="stylecop.json" />
    </ItemGroup>
    <PropertyGroup>
        <CodeAnalysisRuleSet>$(SolutionDir)\stylecop.analyzers.ruleset</CodeAnalysisRuleSet>
    </PropertyGroup>

    <PropertyGroup>
        <WarningLevel>5</WarningLevel>
        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    </PropertyGroup>

</Project>

This is taken directly from my blog github repository: here

So in our case Infrastructure.csproj, Web.csproj and Tools.csproj will automatically include SonarAnalyzer.CSharp and StyleCop.Analyzers as nuget package. If I update the version in my Directory.Build.props all of them get automatically updated!

Another example:
Let's say you have the following test setup:

tests/
+- IntegrationTests/
¦  +- IntegrationTest.csproj
+- TestUtilities/
¦  +- TestUtilities.csproj
+- UnitTests/
¦  +- UnitTest.csproj
+- Directory.Build.props

As the nature of tests you will most likely use one test framework throughout all of your tests plus the same mocking framework. I personally love XUnit for various reasons and Moq as mocking library. Therefore I want to use them in all my test projects. So my Directory.Build.props would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>
    <PackageReference Include="FluentAssertions" Version="6.5.1" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0-preview-20220131-20" />
    <PackageReference Include="Moq" Version="4.16.1" />
    <PackageReference Include="xunit" Version="2.4.2-pre.12" />
  </ItemGroup>

  <ItemGroup Label="Implicit usings">
    <Using Include="FluentAssertions" />
    <Using Include="Moq" />
    <Using Include="Xunit" />
  </ItemGroup>

</Project>

Taken from here

Super convenient, isn't it?

Now let's say the Tools project does not need all this includes and stuff or in general: How can I exclude a project?. That is quite easy as we are using the normal syntax we would use in a csproj: Condition. Taking the example from above, let us exclude the TestUtilities.csproj:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup Condition="$(MSBuildProjectName) != 'TestUtilities'">
    <PackageReference Include="FluentAssertions" Version="6.5.1" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0-preview-20220131-20" />
    <PackageReference Include="Moq" Version="4.16.1" />
    <PackageReference Include="xunit" Version="2.4.2-pre.12" />
  </ItemGroup>

  <ItemGroup Label="Implicit usings" Condition="$(MSBuildProjectName) != 'TestUtilities'">
    <Using Include="FluentAssertions" />
    <Using Include="Moq" />
    <Using Include="Xunit" />
  </ItemGroup>

</Project>

Also very simple. Just add the name of your project without the "csproj" file extension.

Multiple Directory.Build.props

What happens if we have multiple Directory.Build.props?

tests/
+- IntegrationTests/
¦  +- IntegrationTest.csproj
+- TestUtilities/
¦  +- TestUtilities.csproj
+- UnitTests/
¦  +- UnitTest.csproj
+- Directory.Build.props
Directory.Build.props

Now we have another Directory.Build.props one level above the "tests" folder. Well this one will not automatically get included. You have to tell MSBuild it should continue scanning. You can do this by adding this to the inner Directory.Build.props (the one under "tests").

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

Conclusion

Directory.Build.props gives you a handy tool the centralize common dependencies and configurations.

Further read here on the Microsoft documentation

error NETSDK1194: The "--output" option isn't supported when building a solution.

Did you see the following error in recent days in your build-pipeline:

error NETSDK1194: The "--output" option isn't supported when building a solution.

If so - that is not necessarily your fault at all! Microsoft released a new SDK version, which breaks your builds. Let's see why and what we can do to tackle that.

From Zero to Production - Generate everything with a single button

This blog post will show you how to setup, from scratch, your GitHub repository so you can in a matter of a single click:

  • Run tests and build your application
  • Release the application for example to nuget
  • Create a Release on GitHub with Release notes
  • Update the documentation utilizing GitHub Pages and DocFx

Therefore we will build a "template" repository you can take as a foundation.

Central nuget store for your UNO Platform App

With the newest UNO Platform update to .NET 6 we can leverage some cool things to simplify our dependency management. We combine .NET 6 with the new feature called Directory.Build.props to make that work.

An error has occurred. This application may no longer respond until reloaded. Reload x