Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

directory-build-organization — Claude Code skill: MSBuild layout

A skill that decides what belongs in Directory.Build.props, .targets and .rsp, so several projects stop repeating the same MSBuild policy.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add dotnet/skills --skill directory-build-organization -a claude-code
Open on GitHub

What directory-build-organization does

The skill sorts out which MSBuild settings live in Directory.Build.props, which in Directory.Build.targets and which in Directory.Packages.props. The order matters. .props is pulled in early by Microsoft.Common.props, before the project body, so a project can still override shared defaults. .targets arrive late through Microsoft.Common.targets and get the final say, which also means a project cannot override them.

It covers central package management with ManagePackageVersionsCentrally, GlobalPackageReference entries that reach every project, and multi-level setups. MSBuild auto-imports only the first file it finds walking upward, so chaining levels needs an explicit GetPathOfFileAbove import at the top of the inner file. One trap gets its own reference page: a condition on $(TargetFramework) inside .props silently misses in single-targeting projects, so that property belongs in .targets.

Scope is narrow on purpose. A one-project repo with no existing Directory.Build file is not the case for it, nor is a legacy-to-SDK migration or anything that is not MSBuild; property and condition semantics go to property-patterns. Ask how to organize and it reads the real project files, then hands back a proposed layout without touching them.

What it helps with

  • fold repeated MSBuild settings from several projects into one Directory.Build.props
  • move package versions into Directory.Packages.props
  • trace why a $(TargetFramework) condition never fires inside .props
  • chain nested build files with an explicit parent import
  • put shared MSBuild CLI flags in Directory.Build.rsp

Example prompts for the agent

  • Three projects repeat the same MSBuild policy. Propose a layout for `Directory.Build.props` and `.targets`, don't edit anything yet.
  • Audit our `Directory.Build.props` hierarchy: parent import, layer order, which file owns what.
  • Our `$(TargetFramework)` condition in `.props` does nothing. Explain it and tell me where it should go.

How to install directory-build-organization

1Skills CLI (Vercel)

npx skills add dotnet/skills --skill directory-build-organization -a claude-code

2Manually: put the files in place

~/.claude/skills/directory-build-organization/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/directory-build-organization/

From the same set5

The whole set →

Similar

assuredworkloads-environment-assessor official google Evaluates Google Cloud resources and Org Policies against data sovereignty standards. Recommends the right Assured Workloads framework before rollout. Claude Code Gemini CLI CX Codex 475installs gke-ai-troubleshooting-tpu-slice-mig-stuck-updating official google Diagnoses TPU Dynamic Slice creation failures and pending JobSets when the backing MIG is unstable. Use it for GKE issues tied to MIG updates or repairs. Claude Code Gemini CLI CX Codex 440installs asc-submission-health rorkai Checks whether an App Store version is ready, names each submission blocker and routes the fix through the asc CLI. Claude Code CX Codex Cursor +1 8,000installs gke-ai-troubleshooting-tpu-vm-repair-stuck official google Diagnoses GKE TPU VMs stuck in REPAIRING after host errors or maintenance. Use it when a TPU slice cannot be scheduled because a VM remains in repair. Claude Code Gemini CLI CX Codex 431installs asc-metadata-sync rorkai A skill for the asc CLI: pull App Store Connect metadata into local JSON, validate it, push it back. Useful when you are editing localizations, keywords or store assets. Claude Code CX Codex Cursor +1 7,800installs asc-xcode-build rorkai Teaches your agent to drive the asc CLI for Xcode archives, IPA and PKG export, and version bumps. Useful when a build has to reach App Store Connect or TestFlight. Claude Code CX Codex Cursor +1 7,600installs wizard Matt Pocock This skill creates an interactive bash wizard for manual setup steps, CI secrets, third-party dashboards, and one-off migrations. Claude Code 553,369installs hyperframes-cli heygen-com Controls HyperFrames video workflows from the CLI: audits layouts and contrast, edits timeline clips, captures snapshots, and renders locally or in cloud runtimes. Claude Code CX Codex Cursor +1 1,200,000installs

Questions about directory-build-organization

How do I install directory-build-organization in Claude Code?

Run npx skills add dotnet/skills --skill directory-build-organization -a claude-code. The skill lands at ~/.claude/skills/directory-build-organization/SKILL.md.

What does directory-build-organization do?

It maps out which properties, targets and package versions go into Directory.Build.props, .targets, .rsp and Directory.Packages.props, working from the actual project files rather than sibling folder names.

Does it help a repo with a single project?

No, not when there is no Directory.Build file there yet — that case sits outside the skill. It targets two or more projects sharing MSBuild policy, or an existing hierarchy with an import or layering defect.

Source text

SKILL.md · MIT
---
name: directory-build-organization
description: "USE ONLY for (1) two or more projects with repeated MSBuild policy, targets, or package versions, or (2) an existing Directory.Build.props/targets/rsp hierarchy with an import, placement, or layering defect. A one-project repo without an existing Directory.Build file is outside scope; do not load this skill. Covers centralizing shared policy and targets, Directory.Packages.props across projects, parent hierarchy/import structure, props-versus-targets placement, and folder/project exceptions. An existing hierarchy defect remains in scope when only one project is affected. For property value or condition semantics use property-patterns. Exclude legacy-to-SDK migration and non-MSBuild systems."
license: MIT
---

# 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.props` searches
  upward from each project and automatically imports the nearest
  `Directory.Build.props` early; project values can then override shared
  defaults. Explain the equivalent late `Microsoft.Common.targets` import when
  recommending `Directory.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 late
```

| 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](references/targetframework-props-pitfall.md) for the full explanation.

## Directory.Build.props

Good candidates: language settings, assembly/package metadata, build warnings, code analysis, common analyzers.

```xml
<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.

```xml
<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](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management) for details.

**Enable CPM in `Directory.Packages.props` at the repo root:**

```xml
<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 `msbuild` and `dotnet` CLI 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](references/multi-level-examples.md) for full file examples.

```xml
<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](references/common-patterns.md) for the directory layout and additional patterns (conditional settings by project type, post-pack validation).

## Workflow: Organizing Build Infrastructure

1. **Audit all `.csproj` files** — Catalog every `<PropertyGroup>`, `<ItemGroup>`, and custom `<Target>` across the solution. Note which settings repeat and which are project-specific.
2. **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.
3. **Create root `Directory.Build.targets`** — Move custom build targets, post-build validation, and any properties that depend on SDK-defined values (e.g., `OutputPath`, `TargetFramework` for single-targeting projects) here. These are imported after the SDK so all properties are available.
4. **Create `Directory.Packages.props`** — Enable Central Package Management (`ManagePackageVersionsCentrally`), list all `PackageVersion` entries, and remove `Version=` from `PackageReference` items in `.csproj` files.
5. **Set up multi-level hierarchy** — Create inner `Directory.Build.props` files for `src/` and `test/` folders with distinct settings. Use `GetPathOfFileAbove` to chain to the parent.
6. **Simplify `.csproj` files** — Remove all centralized properties, version attributes, and duplicated targets. Each project should only contain what is unique to it.
7. **Validate** — Run restore/build against the discovered `.sln`, `.slnx`,
   `.proj`, or project path, for example
   `dotnet restore <entrypoint> && dotnet build <entrypoint>`. If no root
   entrypoint exists, validate each discovered project explicitly instead of
   claiming a repo-root build. Use
   `dotnet msbuild <project> -pp:output.xml` to 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:

```bash
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.

© dotnet · MIT · dotnet/skills

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor