{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/target-authoring",
  "version": "1.0.0",
  "name": "target-authoring",
  "description": "Canonical patterns for writing custom MSBuild targets. Only activate in MSBuild/.NET build context. USE FOR: diagnosing and fixing custom target authoring anti-patterns, reviewing MSBuild target definitions for correctness, diagnosing broken SDK target chains across files (e.g., Directory.Build.targets silently redefining SDK targets), fixing targets that replace CompileDependsOn instead of extending it with $(CompileDependsOn), fixing query targets that return stale results due to Outputs vs Returns misuse, fixing missing Inputs/Outputs causing unnecessary rebuilds, fixing missing FileWrites registration. Covers DependsOnTargets vs BeforeTargets vs AfterTargets, the Build→CoreBuild three-level pattern, hooking into the build pipeline, the $(XxxDependsOn) chain-extension pattern. DO NOT USE FOR: incremental build tuning (use incremental-build), parallelization (use build-parallelism), general anti-patterns (use msbuild-antipatterns), non-MSBuild build systems.",
  "system_prompt_fragment": "# Custom Target Authoring Patterns\n\nCanonical patterns from `Microsoft.Common.CurrentVersion.targets` in the MSBuild repository.\n\n## The Three-Level Target Chain\n\nEvery major entry point (Build, Rebuild, Clean) delegates to a **property** listing its dependencies, which chains through Before → Core → After:\n\n```xml\n<PropertyGroup>\n  <BuildDependsOn>\n    BeforeBuild;\n    CoreBuild;\n    AfterBuild\n  </BuildDependsOn>\n</PropertyGroup>\n\n<Target Name=\"Build\"\n    Condition=\" '$(_InvalidConfigurationWarning)' != 'true' \"\n    DependsOnTargets=\"$(BuildDependsOn)\"\n    Returns=\"@(TargetPathWithTargetPlatformMoniker)\" />\n\n<!-- Empty extensibility targets — users override these -->\n<Target Name=\"BeforeBuild\" />\n<Target Name=\"AfterBuild\" />\n```\n\n`CoreBuild` delegates to `$(CoreBuildDependsOn)` and includes error handlers:\n\n```xml\n<Target Name=\"CoreBuild\" DependsOnTargets=\"$(CoreBuildDependsOn)\">\n  <OnError ExecuteTargets=\"_TimeStampAfterCompile;PostBuildEvent\"\n      Condition=\"'$(RunPostBuildEvent)' == 'Always'\" />\n  <OnError ExecuteTargets=\"_CleanRecordFileWrites\" />\n</Target>\n```\n\n### Rules\n\n- Delegate to a property (`DependsOnTargets=\"$(MyTargetDependsOn)\"`), not hardcoded targets.\n- `OnError` goes inside the orchestrating target to ensure cleanup runs even on failure.\n- Empty Before/After targets are extensibility points. Users override them; SDKs never put logic in them.\n\n## Chain Extension — Append, Never Overwrite\n\nWhen adding a custom target to an existing chain, **append** to the `DependsOn` property:\n\n```xml\n<!-- GOOD: Append to existing chain -->\n<PropertyGroup>\n  <CompileDependsOn>$(CompileDependsOn);MyCodeGenTarget</CompileDependsOn>\n</PropertyGroup>\n\n<!-- BAD: Overwrites the entire chain, dropping SDK targets -->\n<PropertyGroup>\n  <CompileDependsOn>MyCodeGenTarget</CompileDependsOn>\n</PropertyGroup>\n```\n\n## DependsOnTargets vs BeforeTargets vs AfterTargets\n\n| Mechanism | Defined in | Best for |\n|---|---|---|\n| `DependsOnTargets` | The target that needs deps | Target explicitly requires others |\n| `BeforeTargets` | The injecting target | Insert before a target you don't own |\n| `AfterTargets` | The injecting target | Insert after a target you don't own |\n\nValidation targets use `BeforeTargets` to intercept all entry points:\n\n```xml\n<Target Name=\"_CheckForInvalidConfigurationAndPlatform\"\n    BeforeTargets=\"$(BuildDependsOn);Build;$(RebuildDependsOn);Rebuild;$(CleanDependsOn);Clean\">\n</Target>\n```\n\n**Rules:**\n\n- Use `DependsOnTargets` when your target needs specific prerequisites.\n- Use `BeforeTargets`/`AfterTargets` when injecting into a pipeline you don't own.\n- Prefer `BeforeTargets=\"CoreCompile\"` over modifying `$(CompileDependsOn)` when you don't control the targets file.\n\n## Returns vs Outputs\n\n```xml\n<!-- Build returns items for consumption by referencing projects -->\n<Target Name=\"Build\"\n    DependsOnTargets=\"$(BuildDependsOn)\"\n    Returns=\"@(TargetPathWithTargetPlatformMoniker)\" />\n\n<!-- GetTargetPath is a lightweight query target -->\n<Target Name=\"GetTargetPath\" Returns=\"@(TargetPathWithTargetPlatformMoniker)\" />\n```\n\n- **`Returns`** specifies what the MSBuild task receives when calling this project. Use for inter-project communication.\n- **`Outputs`** on inner targets is for incrementality (timestamp checks). Use for up-to-date detection.\n- Never mix the two purposes. Query targets (`GetTargetPath`, `GetTargetFrameworks`) should use `Returns`, not `Outputs`.\n\n## Target Naming Conventions\n\n| Pattern | Meaning | Example |\n|---|---|---|\n| `_PrefixedName` | Internal/private target | `_TimeStampBeforeCompile` |\n| `CoreXxx` | The actual implementation | `CoreBuild`, `CoreCompile` |\n| `BeforeXxx` / `AfterXxx` | Empty extensibility hooks | `BeforeBuild`, `AfterCompile` |\n| `PrepareXxx` | Setup/validation phase | `PrepareForBuild` |\n| `ResolveXxx` | Discovery/resolution phase | `ResolveReferences` |\n| `GetXxx` | Lightweight query (no side effects) | `GetTargetPath` |\n\n## Complete Custom Target Template\n\n```xml\n<!-- 1. Define the DependsOn chain for extensibility -->\n<PropertyGroup>\n  <MyFeatureDependsOn>\n    _ValidateMyFeatureInputs;\n    BeforeMyFeature;\n    CoreMyFeature;\n    AfterMyFeature\n  </MyFeatureDependsOn>\n</PropertyGroup>\n\n<!-- 2. Outer target with Returns for inter-project communication -->\n<Target Name=\"MyFeature\"\n    DependsOnTargets=\"$(MyFeatureDependsOn)\"\n    Returns=\"@(MyFeatureOutput)\" />\n\n<!-- 3. Empty extensibility points -->\n<Target Name=\"BeforeMyFeature\" />\n<Target Name=\"AfterMyFeature\" />\n\n<!-- 4. Core implementation with Inputs/Outputs for incrementality -->\n<Target Name=\"CoreMyFeature\"\n    Inputs=\"$(MSBuildAllProjects);@(MyFeatureInput)\"\n    Outputs=\"$(IntermediateOutputPath)myfeature.generated.cs\">\n  <Exec Command=\"my-tool.exe -o $(IntermediateOutputPath)myfeature.generated.cs\" />\n  <!-- 5. Register outputs for clean tracking -->\n  <ItemGroup>\n    <Compile Include=\"$(IntermediateOutputPath)myfeature.generated.cs\" />\n    <FileWrites Include=\"$(IntermediateOutputPath)myfeature.generated.cs\" />\n  </ItemGroup>\n</Target>\n\n<!-- 6. Validation target runs first in the dependency chain -->\n<Target Name=\"_ValidateMyFeatureInputs\">\n  <Error Text=\"MyFeatureInput items are required.\"\n         Condition=\"'@(MyFeatureInput)' == ''\" />\n</Target>\n```\n\n## Common Pitfalls\n\n- **Overwriting `DependsOn` properties** drops SDK targets silently. Always include `$(ExistingProperty)` when appending.\n- **Using `Outputs` on query targets** causes MSBuild to skip them when \"up to date,\" returning stale data. Use `Returns`.\n- **Defining targets in `.props`** means `BeforeTargets` on SDK targets have nothing to hook into yet. Move targets to `.targets`.\n- **Forgetting `OnError`** in orchestrating targets means file tracking fails on build errors, breaking subsequent incremental builds.",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/target-authoring"
  ],
  "tags": [
    "dotnet-msbuild",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/target-authoring/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/target-authoring/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}