{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/directory-build-organization",
  "version": "1.0.0",
  "name": "directory-build-organization",
  "description": "Guide for organizing MSBuild infrastructure with Directory.Build.props, Directory.Build.targets, Directory.Packages.props, and Directory.Build.rsp. Only activate in MSBuild/.NET build context. USE FOR: structuring multi-project repos, centralizing build settings, implementing NuGet Central Package Management (CPM) with ManagePackageVersionsCentrally, consolidating duplicated properties across .csproj files, setting up multi-level Directory.Build hierarchy with GetPathOfFileAbove, understanding evaluation order (Directory.Build.props → SDK .props → .csproj → SDK .targets → Directory.Build.targets). Critical pitfall: $(TargetFramework) conditions in .props silently fail for single-targeting projects — must use .targets. DO NOT USE FOR: non-MSBuild build systems, migrating legacy projects to SDK-style (use msbuild-modernization), single-project solutions with no shared settings. INVOKES: no tools — pure knowledge skill.",
  "system_prompt_fragment": "# Organizing Build Infrastructure with Directory.Build Files\n\n## Directory.Build.props vs Directory.Build.targets\n\nUnderstanding which file to use is critical. They differ in **when** they are imported during evaluation:\n\n**Evaluation order:**\n\n```\nDirectory.Build.props → SDK .props → YourProject.csproj → SDK .targets → Directory.Build.targets\n```\n\n| Use `.props` for | Use `.targets` for |\n|---|---|\n| Setting property defaults | Custom build targets |\n| Common item definitions | Late-bound property overrides |\n| Properties projects can override | Post-build steps |\n| Assembly/package metadata | Conditional logic on final values |\n| Analyzer PackageReferences | Targets that depend on SDK-defined properties |\n\n**Rule of thumb:** Properties and items go in `.props`. Custom targets and late-bound logic go in `.targets`.\n\nBecause `.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.\n\n### ⚠️ Critical: TargetFramework Availability in .props vs .targets\n\n**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.\n\nSee [targetframework-props-pitfall.md](references/targetframework-props-pitfall.md) for the full explanation.\n\n## Directory.Build.props\n\nGood candidates: language settings, assembly/package metadata, build warnings, code analysis, common analyzers.\n\n```xml\n<Project>\n  <PropertyGroup>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>\n    <Company>Contoso</Company>\n    <Authors>Contoso Engineering</Authors>\n  </PropertyGroup>\n</Project>\n```\n\n**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).\n\n## Directory.Build.targets\n\nGood candidates: custom build targets, late-bound property overrides (values depending on SDK properties), post-build validation.\n\n```xml\n<Project>\n  <Target Name=\"ValidateProjectSettings\" BeforeTargets=\"Build\">\n    <Error Text=\"All libraries must target netstandard2.0 or higher\"\n           Condition=\"'$(OutputType)' == 'Library' AND '$(TargetFramework)' == 'net472'\" />\n  </Target>\n\n  <PropertyGroup>\n    <!-- DocumentationFile depends on OutputPath, which is set by the SDK -->\n    <DocumentationFile Condition=\"'$(IsPackable)' == 'true'\">$(OutputPath)$(AssemblyName).xml</DocumentationFile>\n  </PropertyGroup>\n</Project>\n```\n\n## Directory.Packages.props (Central Package Management)\n\nCentral 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.\n\n**Enable CPM in `Directory.Packages.props` at the repo root:**\n\n```xml\n<Project>\n  <PropertyGroup>\n    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageVersion Include=\"Microsoft.Extensions.Logging\" Version=\"8.0.0\" />\n    <PackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageVersion Include=\"xunit\" Version=\"2.9.0\" />\n    <PackageVersion Include=\"xunit.runner.visualstudio\" Version=\"2.8.2\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!-- GlobalPackageReference applies to ALL projects — great for analyzers -->\n    <GlobalPackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.556\" />\n    <GlobalPackageReference Include=\"Microsoft.CodeAnalysis.NetAnalyzers\" Version=\"8.0.0\" />\n  </ItemGroup>\n</Project>\n```\n\n## Directory.Build.rsp\n\nContains default MSBuild CLI arguments applied to all builds under the directory tree.\n\n**Example `Directory.Build.rsp`:**\n\n```\n/maxcpucount\n/nodeReuse:false\n/consoleLoggerParameters:Summary;ForceNoAlign\n/warnAsMessage:MSB3277\n```\n\n- Works with both `msbuild` and `dotnet` CLI in modern .NET versions\n- Great for enforcing consistent CI and local build flags\n- Each argument goes on its own line\n\n## Multi-level Directory.Build Files\n\nMSBuild 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.\n\n```xml\n<Project>\n  <Import Project=\"$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))\"\n         Condition=\"Exists('$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))')\" />\n\n  <!-- Inner-level overrides go here -->\n</Project>\n```\n\n**Example layout:**\n\n```\nrepo/\n  Directory.Build.props          ← repo-wide (lang version, company info, analyzers)\n  Directory.Build.targets        ← repo-wide targets\n  Directory.Packages.props       ← central package versions\n  src/\n    Directory.Build.props        ← src-specific (imports repo-level, sets IsPackable=true)\n  test/\n    Directory.Build.props        ← test-specific (imports repo-level, sets IsPackable=false, adds test packages)\n```\n\n## Artifact Output Layout (.NET 8+)\n\nSet `<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).\n\n## Workflow: Organizing Build Infrastructure\n\n1. **Audit all `.csproj` files** — Catalog every `<PropertyGroup>`, `<ItemGroup>`, and custom `<Target>` across the solution. Note which settings repeat and which are project-specific.\n2. **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.\n3. **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.\n4. **Create `Directory.Packages.props`** — Enable Central Package Management (`ManagePackageVersionsCentrally`), list all `PackageVersion` entries, and remove `Version=` from `PackageReference` items in `.csproj` files.\n5. **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.\n6. **Simplify `.csproj` files** — Remove all centralized properties, version attributes, and duplicated targets. Each project should only contain what is unique to it.\n7. **Validate** — Run `dotnet restore && dotnet build` and verify no regressions. Use `dotnet msbuild -pp:output.xml` to inspect the final merged view if needed.\n\n## Troubleshooting\n\n| Problem | Cause | Fix |\n|---|---|---|\n| `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) |\n| 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 |\n| 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) |\n| 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 |\n| `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 |\n| 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) |\n\n**Diagnosis:** Use the preprocessed project output to see all imports and final property values:\n\n```bash\ndotnet msbuild -pp:output.xml MyProject.csproj\n```\n\nThis expands all imports inline so you can see exactly where each property is set and what the final evaluated value is.",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/directory-build-organization"
  ],
  "tags": [
    "dotnet-msbuild",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/directory-build-organization/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/directory-build-organization/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}