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 (.csproj
) file.
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.props
and Directory.Build.targets
Directory.Build.props
/ 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 -->
<Project>
<PropertyGroup>
<Version>1.2.3</Version>
<Authors>John Doe</Authors>
</PropertyGroup>
</Project>
Use case 2: Global static code analyzing
Another good example is installing StyleCop.Analyzers - a popular static code analysis package:
<!-- Directory.Build.props -->
<Project>
<PropertyGroup>
<!-- 1 -->
<CodeAnalysisRuleset>$(MSBuildThisFileDirectory)MyRules.ruleset</CodeAnalysisRuleset>
</PropertyGroup>
<ItemGroup>
<!-- 2 -->
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<!-- 3 -->
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" />
</ItemGroup>
</Project>
Lets look into each step:
- Add a link to the
MyRules.ruleset
file that contains the setting for the severity of the used analyzers rules - Add
StyleCop.Analyzers
NuGet package. - Add a link to
stylecop.json
, which contains the configuration of theStyleCop.Analyzers
package itself.
More details on the process of connecting StyleCop.Analyzers
with examples of configuration files can be found here.
How to decide which file (.props
or .targets
) should we use for our purpose?
According to this issue comment on official Microsoft docs repo:
...there's a widely-used convention:
.props
files are imported early in the import order..targets
files are imported late in the build order.That's enforced by
<Project Sdk="Whatever">
imports (the import ofSdk.props
comes first, before all of the contents of the file, thenSdk.targets
comes 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 .props
file).
global.json
Placing 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:
{
"sdk": {
"version": "3.1.100",
"rollForward": "disable"
}
}
"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.
Summary
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.
See also:
- Customize your build by Microsoft
- Common MSBuild properties and items with Directory.Build.props by Thomas Levesque
- global.json overview by Microsoft