|
| 1 | +# Directory.Build.props and Directory.Build.targets |
| 2 | + |
| 3 | +## What they are |
| 4 | + |
| 5 | +`Directory.Build.props` and `Directory.Build.targets` are MSBuild files that |
| 6 | +are automatically imported into every project in the directory tree beneath them. |
| 7 | +The key difference is *when* they're imported: |
| 8 | + |
| 9 | +| File | Import point | Use for | |
| 10 | +|------|-------------|---------| |
| 11 | +| `Directory.Build.props` | Early (before SDK defaults) | Properties that configure the SDK | |
| 12 | +| `Directory.Build.targets` | Late (after NuGet targets) | Custom build targets, post-build actions | |
| 13 | + |
| 14 | +Placing them at the repository root means all projects share the same settings |
| 15 | +automatically — no property repetition in individual `.csproj` files. |
| 16 | + |
| 17 | +Create them quickly via the CLI: |
| 18 | +```bash |
| 19 | +dotnet new buildprops # creates Directory.Build.props |
| 20 | +``` |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +## Recommended Directory.Build.props for .NET 10 |
| 25 | + |
| 26 | +```xml |
| 27 | +<Project> |
| 28 | + <PropertyGroup> |
| 29 | + <!-- Framework & Language --> |
| 30 | + <TargetFramework>net10.0</TargetFramework> |
| 31 | + <LangVersion>latest</LangVersion> |
| 32 | + <Nullable>enable</Nullable> |
| 33 | + <ImplicitUsings>enable</ImplicitUsings> |
| 34 | + |
| 35 | + <!-- Code quality — all three enforce style/analysis at build time --> |
| 36 | + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> |
| 37 | + <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> |
| 38 | + <EnableNETAnalyzers>true</EnableNETAnalyzers> |
| 39 | + <AnalysisLevel>latest</AnalysisLevel> |
| 40 | + |
| 41 | + <!-- Source generation — exposes generated files in obj/ for debugging --> |
| 42 | + <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> |
| 43 | + |
| 44 | + <!-- Package metadata (for libraries) --> |
| 45 | + <Authors>Your Name</Authors> |
| 46 | + <Copyright>Copyright (c) 2025 Your Name</Copyright> |
| 47 | + </PropertyGroup> |
| 48 | + |
| 49 | + <!-- Deterministic + SourceLink for reproducible builds --> |
| 50 | + <PropertyGroup> |
| 51 | + <Deterministic>true</Deterministic> |
| 52 | + <PublishRepositoryUrl>true</PublishRepositoryUrl> |
| 53 | + <EmbedUntrackedSources>true</EmbedUntrackedSources> |
| 54 | + <IncludeSymbols>true</IncludeSymbols> |
| 55 | + <SymbolPackageFormat>snupkg</SymbolPackageFormat> |
| 56 | + </PropertyGroup> |
| 57 | + |
| 58 | + <!-- CI-only: reproducible builds on GitHub Actions --> |
| 59 | + <PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'"> |
| 60 | + <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> |
| 61 | + </PropertyGroup> |
| 62 | + |
| 63 | + <!-- Shared analyzer packages — applied to every project automatically --> |
| 64 | + <ItemGroup> |
| 65 | + <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" /> |
| 66 | + </ItemGroup> |
| 67 | +</Project> |
| 68 | +``` |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## Property explanations |
| 73 | + |
| 74 | +**`LangVersion=latest`** — automatically uses the latest C# version supported by |
| 75 | +the installed SDK (C# 13 in .NET 9, C# 14 in .NET 10). Never pin to a specific |
| 76 | +number; you'll miss new features unnecessarily. |
| 77 | + |
| 78 | +**`Nullable=enable`** — enables nullable reference types. This is one of the |
| 79 | +highest-value safety features in modern C#. With it enabled you'll get compile- |
| 80 | +time warnings for potential null dereferences. |
| 81 | + |
| 82 | +**`TreatWarningsAsErrors=true`** — turns all warnings into errors. Without this, |
| 83 | +warnings accumulate silently and never get fixed. Pair it with `<NoWarn>` for |
| 84 | +intentional suppression of specific warnings: |
| 85 | +```xml |
| 86 | +<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- suppress missing XML docs --> |
| 87 | +``` |
| 88 | + |
| 89 | +**`EnforceCodeStyleInBuild=true`** — causes `.editorconfig` style rules to |
| 90 | +produce build errors/warnings rather than just IDE squiggles. This makes code |
| 91 | +style enforceable in CI. |
| 92 | + |
| 93 | +**`AnalysisLevel=latest`** — enables the newest generation of Roslyn code |
| 94 | +quality rules as soon as they're available in the SDK. |
| 95 | + |
| 96 | +**`EmitCompilerGeneratedFiles=true`** — writes source-generated files (e.g., |
| 97 | +from `System.Text.Json`, `RegexGenerator`, `LoggerMessage`) into `obj/` so you |
| 98 | +can inspect them. Harmless and very helpful for debugging generators. |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +## Directory.Build.targets |
| 103 | + |
| 104 | +Use `.targets` for things that must run *after* the project and NuGet imports, |
| 105 | +or that define build targets: |
| 106 | + |
| 107 | +```xml |
| 108 | +<Project> |
| 109 | + <!-- Enforce code analysis across all projects --> |
| 110 | + <PropertyGroup> |
| 111 | + <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> |
| 112 | + </PropertyGroup> |
| 113 | + |
| 114 | + <!-- Custom post-build target — e.g., log output path --> |
| 115 | + <Target Name="LogBuildOutput" AfterTargets="Build"> |
| 116 | + <Message Importance="high" Text="Built $(MSBuildProjectName) → $(TargetPath)" /> |
| 117 | + </Target> |
| 118 | +</Project> |
| 119 | +``` |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +## Multi-level merging |
| 124 | + |
| 125 | +By default MSBuild stops scanning upward after finding the first |
| 126 | +`Directory.Build.props`. To support per-folder overrides that also inherit |
| 127 | +from the root file, add this to the inner file: |
| 128 | + |
| 129 | +```xml |
| 130 | +<!-- tests/Directory.Build.props --> |
| 131 | +<Project> |
| 132 | + <!-- Import the root settings first --> |
| 133 | + <Import Project="$([MSBuild]::GetPathOfFileAbove( |
| 134 | + 'Directory.Build.props', |
| 135 | + '$(MSBuildThisFileDirectory)../'))" |
| 136 | + Condition="'' != $([MSBuild]::GetPathOfFileAbove( |
| 137 | + 'Directory.Build.props', |
| 138 | + '$(MSBuildThisFileDirectory)../'))" /> |
| 139 | + |
| 140 | + <!-- Test-only overrides --> |
| 141 | + <PropertyGroup> |
| 142 | + <IsTestProject>true</IsTestProject> |
| 143 | + </PropertyGroup> |
| 144 | +</Project> |
| 145 | +``` |
| 146 | + |
| 147 | +This is useful for applying different settings to `src/` vs `tests/`. |
| 148 | + |
| 149 | +--- |
| 150 | + |
| 151 | +## Overriding in individual projects |
| 152 | + |
| 153 | +Settings from `Directory.Build.props` are defaults. Any project can override |
| 154 | +them by setting the property in its own `.csproj`: |
| 155 | + |
| 156 | +```xml |
| 157 | +<Project Sdk="Microsoft.NET.Sdk"> |
| 158 | + <PropertyGroup> |
| 159 | + <!-- This project targets multiple frameworks --> |
| 160 | + <TargetFrameworks>net10.0;netstandard2.0</TargetFrameworks> |
| 161 | + |
| 162 | + <!-- Disable TreatWarningsAsErrors for generated code projects --> |
| 163 | + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> |
| 164 | + </PropertyGroup> |
| 165 | +</Project> |
| 166 | +``` |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## Troubleshooting |
| 171 | + |
| 172 | +**Property not taking effect**: Run `dotnet msbuild /pp:preprocessed.xml MyProj.csproj` |
| 173 | +to see the full merged file with import order. Look for where your property is set |
| 174 | +vs where it's overridden. |
| 175 | + |
| 176 | +**File silently ignored on Linux/macOS**: The filename must match exactly — |
| 177 | +`Directory.Build.props` (capital B and capital P). Linux filesystems are |
| 178 | +case-sensitive. |
| 179 | + |
| 180 | +**Visual Studio not picking up changes**: Close and reopen the solution, or |
| 181 | +right-click → Reload Project after editing `.props` or `.targets` files. |
0 commit comments