Build Configuration (MSBuild)
Repository-wide MSBuild infrastructure for versioning, packaging, code analysis, and centralised dependency management.
Overview
The ploch-common repository uses a layered set of MSBuild files to centralise build behaviour across all projects in the solution. Rather than repeating settings in individual .csproj files, the configuration is declared once and inherited automatically by every project in the repository.
The infrastructure covers five concerns: project defaults (nullable, language version, output type), NuGet packaging, automatic versioning via Nerdbank.GitVersioning, source debugging via SourceLink, and centralised package version management. Analyser enforcement is layered on top through shared .props files imported from the sibling mrploch-development repository.
Note: src/Common.MSBuild/ is a placeholder directory reserved for a future custom MSBuild tasks project. No deliverable content exists there yet. The documentation below describes the build infrastructure that is currently active.
Key Files
| File | Purpose |
|---|---|
Directory.Build.props |
Inherited by every project; sets defaults for packaging, documentation, nullable, analysers, versioning tools |
Directory.Packages.props |
Enables Central Package Management (ManagePackageVersionsCentrally=true) and imports shared version definitions |
version.json |
Nerdbank.GitVersioning (NBGV) configuration; controls version scheme, public release branches, and NuGet SemVer 2 format |
../mrploch-development/dependencies/*.props |
Shared package version definitions and global analyser references imported from the workspace sibling |
Directory.Build.props
Directory.Build.props is automatically imported by MSBuild for every project under the repository root. It sets the following properties:
Project identity
<Authors>Kris Ploch</Authors>
<Company>Ploch</Company>
<Product>Ploch.Common</Product>
<PackageProjectUrl>https://common.github.ploch.dev/</PackageProjectUrl>
<RepositoryUrl>https://github.com/mrploch/ploch-common.git</RepositoryUrl>
<Copyright>Kris Ploch $([System.DateTime]::Now.Year)</Copyright>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
Code quality
<Nullable>enable</Nullable>
<LangVersion>default</LangVersion>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
Test project detection
Test projects are identified automatically by name suffix. The condition $(MSBuildProjectName.EndsWith('Tests')) is evaluated at import time:
<IsTestProject>$(MSBuildProjectName.EndsWith('Tests'))</IsTestProject>
Test projects have packaging and XML documentation disabled; non-test projects have both enabled:
<!-- Test projects -->
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<IsPackable>false</IsPackable>
<!-- Library projects -->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Versioning and source debugging
Every project automatically gets Nerdbank.GitVersioning and SourceLink references via GlobalPackageReference:
<PackageReference Include="Nerdbank.GitVersioning" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" />
SourceLink is configured to embed source information in symbols:
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Deterministic builds are activated automatically in CI:
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
Directory.Packages.props
Central Package Management is enabled globally:
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
With this enabled, individual .csproj files declare <PackageReference> elements without a Version attribute. Versions are resolved centrally from this file and the imported .props files. Adding a version attribute to a project-level reference is a build error.
The file imports five shared version catalogues from mrploch-development/dependencies/:
| Imported file | Contents |
|---|---|
MicrosoftExtensions.Net9.Packages.props |
EF Core, Hosting, DI, Configuration, ASP.NET Core |
Ploch.Packages.props |
Ploch organisation packages (GenericRepository, Data.Model) |
Common.Packages.props |
Ardalis, AutoMapper, FastEndpoints, Newtonsoft.Json, Dawn.Guard, etc. |
Testing.Packages.props |
xUnit, FluentAssertions, Moq, AutoFixture, Coverlet |
Analyzers.Global.Packages.props |
StyleCop, Roslynator, SonarAnalyzer, MS NetAnalyzers (as GlobalPackageReference) |
The two repository-specific versions pinned directly in Directory.Packages.props are:
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.115" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
Nerdbank.GitVersioning (version.json)
Version numbers are derived from the git history using Nerdbank.GitVersioning. The version.json file at the repository root defines the version policy:
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "3.1-prerelease",
"assemblyVersion": {
"precision": "revision"
},
"versionHeightOffset": -1,
"nuGetPackageVersion": {
"semVer": 2.0
},
"publicReleaseRefSpec": [
"^refs/heads/master$",
"^refs/tags/v\\d+\\.\\d+\\.\\d+"
],
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
Key points:
- The base version is
3.1. NBGV appends the commit height as a third segment automatically (e.g.3.1.42). TheversionHeightOffsetof-1shifts the computed commit height down by one. - Builds from
masteror a tag matchingv\d+\.\d+\.\d+are treated as public releases; all other branches produce pre-release packages. - Assembly versions use four-part precision (
Major.Minor.Patch.Revision). - NuGet packages use SemVer 2 format, which enables pre-release metadata such as
3.1.42-g1a2b3c4. - Cloud build number stamping is enabled so the CI build number reflects the calculated version.
To inspect the current computed version locally:
nbgv get-version
To prepare a release commit (sets the version and creates a tag):
nbgv prepare-release
Shared Analyser Configuration
The Analyzers.Global.Packages.props file imported via Directory.Packages.props uses GlobalPackageReference to inject the following analysers into every project in the solution without any per-project declaration:
| Analyser | Purpose |
|---|---|
StyleCop.Analyzers |
Enforces code style and documentation conventions |
Roslynator.Analyzers |
Broad set of code quality and refactoring diagnostics |
SonarAnalyzer.CSharp |
SonarQube rules for bug detection and code smell identification |
Microsoft.CodeAnalysis.NetAnalyzers |
Official .NET platform analysers |
Microsoft.VisualStudio.Threading.Analyzers |
Detects async/await and threading anti-patterns |
codecracker.CSharp |
Additional C# code quality rules |
All analyser packages are configured with PrivateAssets=all so they are build-time only and do not appear as transitive dependencies in published packages.
Build Diagnostics Target
Directory.Build.props includes an opt-in PrintSettings target that logs key MSBuild property values at high importance before each project builds. It is gated behind the PrintBuildSettings property and is off by default so CI logs stay readable. Enable it for a targeted diagnostic build:
dotnet build Ploch.Common.slnx -p:PrintBuildSettings=true
Sample output:
Building Ploch.Common with settings:
MSBuildProjectName -> 'Ploch.Common'
Configuration -> 'Release'
IsTestProject -> 'False'
GeneratePackageOnBuild -> 'True'
GenerateDocumentationFile -> 'True'
IsPackable -> 'True'
Configuration
No installation step is required. The configuration files are repository-level infrastructure. Any project added to the solution automatically inherits all settings by virtue of MSBuild's directory-traversal import rules for Directory.Build.props and Directory.Packages.props.
To add a new package version to the central catalogue, add a <PackageVersion> entry to Directory.Packages.props (for repository-specific packages) or to the appropriate file in mrploch-development/dependencies/ (for workspace-wide versions).
To change the base version, edit the "version" field in version.json.
Related Libraries
- Ploch.Common — Core utility library
- Ploch.Common.DependencyInjection — ServicesBundle pattern for modular DI registration