Read about how to move you repeatable configuration from different .csproj files into single place
Quite often, when developing middle-to-large .NET applications or libraries, some part of the build configuration (same NuGet packages, common meta properties) starts to replicate across solution.
An example is the need to connect static analyzers. Usually, analyzers are needed in the whole solution, so it is rather difficult to include them separately in each project (
In addition to the banal inconvenience, when a new version of the package is released, you must manually update all project files in the solution. The same situation is for removing something common from all projects at once.
Directory.Build.targets comes to the rescue!
This feature was introduced in MSBuild 15 to allow general build settings to be moved into a separate file that will be applied to all projects within the solution where it is located.
Use case 1: Common author and version
The simplest example is that we have a solution that contains several projects, each of which must have an author and is released under the same version. In this case, you can create a
Directory.Build.props file in the root of the solution and add information indicating the author and version:
<!-- Directory.Build.props -->
Use case 2: Global static code analyzing
Another good example is installing StyleCop.Analyzers - a popular static code analysis package:
<!-- Directory.Build.props -->
<!-- 1 -->
<!-- 2 -->
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<!-- 3 -->
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" />
Lets look into each step:
- Add a link to the
MyRules.rulesetfile that contains the setting for the severity of the used analyzers rules
- Add a link to
stylecop.json, which contains the configuration of the
More details on the process of connecting
StyleCop.Analyzers with examples of configuration files can be found here.
How to decide which file (
.targets) should we use for our purpose?
According to this issue comment on official Microsoft docs repo:
...there's a widely-used convention:
.propsfiles are imported early in the import order.
.targetsfiles are imported late in the build order.
That's enforced by
<Project Sdk="Whatever">imports (the import of
Sdk.propscomes first, before all of the contents of the file, then
Sdk.targetscomes last, after all of the contents of the file). But that just formalized the longstanding convention...
But there is no guarantee that
.props files are always imported early and
.targets imported lately. It is just a common convention, so do not take it as 100% truth.
My advice is to use
Directory.Build.props for setting items, defaults, packages references and
Directory.Build.targets to set dependent (on other) properties, build targets, and overrides (to default properties in
global.json at solution level allows to unify and customize SDK version used across projects in solution.
It is very useful to strongly strict version that could be used to build and test solution. Sometimes there are bugs in SDK or unwanted behavior of some stuff, but you are sure that specific version and above works quite well.
In such case you could define
global.json at the root:
"rollForward": "disable" means that only specified version could be used to build project or solution. This property was introduced in .NET Core 3.0.
Full manual of using global.json files could be found here.
In the modern .NET world, there are existing opportunities for setting common global settings, allowing you to easily configure complex stuff in a single place, reducing the need for copy-paste to almost zero. There is not much information in the Web on such customization of the build process, so not every developer knows how to use such tools.
I hope, after reading this article, you have a general understanding of what can be achieved with the help of such simple manipulations. More information about all the things considered can be found on the links below.
- Customize your build by Microsoft
- Common MSBuild properties and items with Directory.Build.props by Thomas Levesque
- global.json overview by Microsoft