
Build Organization for MSBuild
FreeStreamline your MSBuild project structure with ease.
Free · Opens the source repo
What Build Organization for MSBuild does
The Build Organization for MSBuild skill provides a comprehensive guide for structuring and managing your MSBuild infrastructure using various configuration files. It focuses on the effective use of Directory.Build.props, Directory.Build.targets, Directory.Packages.props, and Directory.Build.rsp to centralize build settings across multi-project repositories. This skill is particularly beneficial for developers and teams working with complex solutions that require consistent build configurations and package management.
With this skill, you can learn the critical differences between .props and .targets files, including their evaluation order and appropriate use cases. For instance, .props files are ideal for setting default properties and common item definitions, while .targets files are used for custom build targets and late-bound property overrides. Understanding these distinctions helps in avoiding common pitfalls, such as the silent failure of $(TargetFramework) conditions in .props files for single-targeting projects.
Additionally, the skill covers the implementation of Central Package Management (CPM) through Directory.Packages.props, allowing you to manage NuGet package versions from a single location. This is essential for maintaining consistency across multiple projects and simplifying dependency management. The multi-level Directory.Build configuration is also addressed, enabling you to create a hierarchical structure that can adapt to various project needs while minimizing duplication of settings.
Overall, this skill is designed for developers who want to enhance their MSBuild workflows, improve project organization, and ensure that their build processes are efficient and maintainable.
When to use it
Use this skill when structuring multi-project repositories or centralizing build configurations in MSBuild.
When not to use it
Avoid this skill for non-MSBuild systems or single-project solutions without shared settings.
What you can build with it
Centralizing Build Settings
Use this skill to consolidate build settings across multiple projects in a repository, improving maintainability.
Implementing Central Package Management
Leverage Central Package Management to manage NuGet package versions from a single source, reducing version conflicts.
Structuring Multi-Project Repositories
Organize complex solutions with a multi-level Directory.Build hierarchy to streamline build configurations.
How to install Build Organization for MSBuild
View source1. Install with the skills CLI
npx skills add dotnet/skills/directory-build-organization --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by dotnetOrganizing Build Infrastructure with Directory.Build Files
Directory.Build.props vs Directory.Build.targets
Understanding which file to use is critical. They differ in when they are imported during evaluation:
Evaluation order:
Directory.Build.props → SDK .props → YourProject.csproj → SDK .targets → Directory.Build.targets
Use .props for | Use .targets for |
|---|---|
| Setting property defaults | Custom build targets |
| Common item definitions | Late-bound property overrides |
| Properties projects can override | Post-build steps |
| Assembly/package metadata | Conditional logic on final values |
| Analyzer PackageReferences | Targets that depend on SDK-defined properties |
Rule of thumb: Properties and items go in .props. Custom targets and late-bound logic go in .targets.
Because .props is imported before the project file, the project can override any value set there. Because .targets is imported after everything, it gets the final say—but projects cannot override .targets values.
⚠️ Critical: TargetFramework Availability in .props vs .targets
Property conditions on $(TargetFramework) in .props files silently fail for single-targeting projects — the property is empty during .props evaluation. Move TFM-conditional properties to .targets instead. ItemGroup and Target conditions are not affected.
See targetframework-props-pitfall.md for the full explanation.
Directory.Build.props
Good candidates: language settings, assembly/package metadata, build warnings, code analysis, common analyzers.
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Company>Contoso</Company>
<Authors>Contoso Engineering</Authors>
</PropertyGroup>
</Project>
Do NOT put here: project-specific TFMs, project-specific PackageReferences, targets/build logic, or properties depending on SDK-defined values (not available during .props evaluation).
Directory.Build.targets
Good candidates: custom build targets, late-bound property overrides (values depending on SDK properties), post-build validation.
<Project>
<Target Name="ValidateProjectSettings" BeforeTargets="Build">
<Error Text="All libraries must target netstandard2.0 or higher"
Condition="'$(OutputType)' == 'Library' AND '$(TargetFramework)' == 'net472'" />
</Target>
<PropertyGroup>
<!-- DocumentationFile depends on OutputPath, which is set by the SDK -->
<DocumentationFile Condition="'$(IsPackable)' == 'true'">$(OutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>
Directory.Packages.props (Central Package Management)
Central Package Management (CPM) provides a single source of truth for all NuGet package versions. See https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management for details.
Enable CPM in Directory.Packages.props at the repo root:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="xunit" Version="2.9.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<!-- GlobalPackageReference applies to ALL projects — great for analyzers -->
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
</ItemGroup>
</Project>
Directory.Build.rsp
Contains default MSBuild CLI arguments applied to all builds under the directory tree.
Example Directory.Build.rsp:
/maxcpucount
/nodeReuse:false
/consoleLoggerParameters:Summary;ForceNoAlign
/warnAsMessage:MSB3277
- Works with both
msbuildanddotnetCLI in modern .NET versions - Great for enforcing consistent CI and local build flags
- Each argument goes on its own line
Multi-level Directory.Build Files
MSBuild only auto-imports the first Directory.Build.props (or .targets) it finds walking up from the project directory. To chain multiple levels, explicitly import the parent at the top of the inner file. See multi-level-examples for full file examples.
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="Exists('$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))')" />
<!-- Inner-level overrides go here -->
</Project>
Example layout:
repo/
Directory.Build.props ← repo-wide (lang version, company info, analyzers)
Directory.Build.targets ← repo-wide targets
Directory.Packages.props ← central package versions
src/
Directory.Build.props ← src-specific (imports repo-level, sets IsPackable=true)
test/
Directory.Build.props ← test-specific (imports repo-level, sets IsPackable=false, adds test packages)
Artifact Output Layout (.NET 8+)
Set <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath> in Directory.Build.props to automatically produce project-name-separated bin/, obj/, and publish/ directories under a single artifacts/ folder, avoiding bin/obj clashes by default. See common-patterns for the directory layout and additional patterns (conditional settings by project type, post-pack validation).
Workflow: Organizing Build Infrastructure
- Audit all
.csprojfiles — Catalog every<PropertyGroup>,<ItemGroup>, and custom<Target>across the solution. Note which settings repeat and which are project-specific. - Create root
Directory.Build.props— Move shared property defaults (LangVersion, Nullable, TreatWarningsAsErrors, metadata) here. These are imported before the project file so projects can override them. - Create root
Directory.Build.targets— Move custom build targets, post-build validation, and any properties that depend on SDK-defined values (e.g.,OutputPath,TargetFrameworkfor single-targeting projects) here. These are imported after the SDK so all properties are available. - Create
Directory.Packages.props— Enable Central Package Management (ManagePackageVersionsCentrally), list allPackageVersionentries, and removeVersion=fromPackageReferenceitems in.csprojfiles. - Set up multi-level hierarchy — Create inner
Directory.Build.propsfiles forsrc/andtest/folders with distinct settings. UseGetPathOfFileAboveto chain to the parent. - Simplify
.csprojfiles — Remove all centralized properties, version attributes, and duplicated targets. Each project should only contain what is unique to it. - Validate — Run
dotnet restore && dotnet buildand verify no regressions. Usedotnet msbuild -pp:output.xmlto inspect the final merged view if needed.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
Directory.Build.props isn't picked up | File name casing wrong (exact match required on Linux/macOS) | Verify exact casing: Directory.Build.props (capital D, B) |
Properties from .props are ignored by projects | Project sets the same property after the import | Move the property to Directory.Build.targets to set it after the project |
| Multi-level import doesn't work | Missing GetPathOfFileAbove import in inner file | Add the <Import> element at the top of the inner file (see Multi-level section) |
Properties using SDK values are empty in .props | SDK properties aren't defined yet during .props evaluation | Move to .targets which is imported after the SDK |
Directory.Packages.props not found | File not at repo root or not named exactly | Must be named Directory.Packages.props and at or above the project directory |
Property condition on $(TargetFramework) doesn't match in .props | TargetFramework isn't set yet for single-targeting projects during .props evaluation | Move property to .targets, or use ItemGroup/Target conditions instead (which evaluate late) |
Diagnosis: Use the preprocessed project output to see all imports and final property values:
dotnet msbuild -pp:output.xml MyProject.csproj
This expands all imports inline so you can see exactly where each property is set and what the final evaluated value is.
Frequently asked questions about Build Organization for MSBuild
Similar skills
Turborepo
Optimized build system for JavaScript/TypeScript monorepos.
Azure Pipelines Validation
Streamline your Azure DevOps pipeline changes locally.
Azure Developer CLI
Streamline your Azure project workflows with best practices.
Azure Container Registry CLI
Manage Azure Container Registry resources with ease.
Aspire
Build and orchestrate polyglot distributed applications seamlessly.
Vercel CLI
Manage and deploy Vercel projects from the command line.
