Directory Build Organization
.NET (Microsoft)OfficialPublished by .NET (Microsoft)
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to managing build infrastructure in .NET repositories by utilizing Directory.Build.props, Directory.Build.targets, Directory.Packages.props, and Directory.Build.rsp files. It enables developers to centralize shared policies, package versions, and build targets, ensuring consistency across multiple projects. The skill covers the evaluation order of these files, how to implement multi-level hierarchies, and how to leverage Central Package Management (CPM) to maintain a single source of truth for dependencies.
What it does
- Centralizes shared MSBuild properties and metadata in root Directory.Build.props files.
- Manages custom build targets and late-bound logic using Directory.Build.targets.
- Implements Central Package Management via Directory.Packages.props to unify dependency versions.
- Configures global MSBuild CLI arguments using Directory.Build.rsp.
- Establishes multi-level import hierarchies for folder-specific build configurations.
- Provides guidance on cleaning up redundant settings from individual project files.
When to use it
- When two or more projects in a repository share repeated MSBuild policies, targets, or package versions.
- When an existing Directory.Build hierarchy contains import, placement, or layering defects.
- When you need to standardize build flags or analyzer configurations across a large solution.
- When you want to simplify project files by removing duplicated boilerplate code.
Advantages
- Reduces configuration drift by enforcing consistent build policies across the repository.
- Simplifies project maintenance by centralizing dependency versions and build logic.
- Prevents bin/obj folder conflicts by standardizing artifact output layouts.
- Improves build reproducibility through shared CLI arguments and global package references.
Usage examples
- “How can I centralize my NuGet package versions across all projects in my solution?”
- The agent will guide you to create a Directory.Packages.props file at the repo root, enable ManagePackageVersionsCentrally, and define PackageVersion items for your dependencies.
- “My projects have duplicated build warnings and company metadata. How do I clean this up?”
- The agent will help you move these shared properties into a root Directory.Build.props file and remove the redundant entries from your individual .csproj files.
- “I need to run a custom validation target after every build. Where should I put it?”
- The agent will instruct you to place the custom Target in a Directory.Build.targets file to ensure it is imported late in the evaluation process.
- “How do I debug why my Directory.Build.props settings are being overridden?”
- The agent will provide the command 'dotnet msbuild -pp:output.xml MyProject.csproj' to help you inspect the preprocessed project output and identify the source of the override.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/dotnet/directory-build-organization/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip directory-build-organization.zip -d .agents/skills
Documentation by .NET (Microsoft)
Organizing Build Infrastructure with Directory.Build Files
Intent control
For "should we", "how should we organize", "recommend", "advise", or review requests, inspect the actual project files and return a proposed layout only. Do not create or edit files unless the user explicitly asks to apply, move, centralize, or clean up the configuration.
Before summarizing, verify which project retains each project-specific property, item, or target. Name the actual file; do not infer it from sibling names.
Recommend a verification command only after discovering a real
.sln,.slnx,.proj, or project file at that path. If no repo-root entry point exists, give commands for the discovered projects or state that the entry point was not found.Every advice response must explain that
Microsoft.Common.propssearches upward from each project and automatically imports the nearestDirectory.Build.propsearly; project values can then override shared defaults. Explain the equivalent lateMicrosoft.Common.targetsimport when recommendingDirectory.Build.targets.
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:
Microsoft.Common.props imports Directory.Build.props early
→ project body and package/project imports evaluate
→ Microsoft.Common.targets imports Directory.Build.targets lateUse .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:MSB3277Works with both
msbuildanddotnetCLI in modern .NET versionsGreat 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 restore/build against the discovered
.sln,.slnx,.proj, or project path, for exampledotnet restore <entrypoint> && dotnet build <entrypoint>. If no root entrypoint exists, validate each discovered project explicitly instead of claiming a repo-root build. Usedotnet msbuild <project> -pp:output.xmlto inspect the final merged view.
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.csprojThis expands all imports inline so you can see exactly where each property is set and what the final evaluated value is.
Files
- ATTRIBUTION.mdAdded by AutoGineer870 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md10.8 KiB
- references/common-patterns.md1.4 KiB
- references/multi-level-examples.md3.9 KiB
- references/targetframework-props-pitfall.md3.3 KiB
LICENSE
The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- .NET (Microsoft)
- Publisher
- .NET (Microsoft) (Official)
- Source
- dotnet/skills · plugins/dotnet-msbuild/skills/directory-build-organization · commit 96c3d68
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by .NET (Microsoft) or .NET (Microsoft).
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download directory-build-organization.zip