Understanding the Three-Level Target Chain Pattern in MSBuild

The three-level target chain pattern is a canonical MSBuild architecture where entry-point targets like Build delegate work through a property-defined sequence of Before, Core, and After phases, enabling safe pipeline extension without modifying core SDK logic.

The three-level target chain pattern is the standard mechanism used throughout MSBuild-based projects to structure complex build operations. According to the dotnet/skills repository documentation, this pattern separates extensibility hooks from critical build logic, allowing developers to inject custom steps while preserving the integrity of the build pipeline.

How the Three-Level Target Chain Works

In the plugins/dotnet-msbuild/skills/target-authoring/SKILL.md documentation, the pattern is described as a property-driven delegation system. Rather than hard-coding dependencies directly into target definitions, MSBuild uses properties like BuildDependsOn to declare ordered sequences of sub-targets.

1. Property Definition

The chain begins with a property group that defines three distinct phases:

<PropertyGroup>
  <BuildDependsOn>
    BeforeBuild;
    CoreBuild;
    AfterBuild
  </BuildDependsOn>
</PropertyGroup>

This property acts as a configurable pipeline. Because it uses standard property syntax, you can modify it using MSBuild's standard property manipulation mechanisms without editing the original target files.

2. Entry-Point Target Declaration

The main target references the property via DependsOnTargets:

<Target Name="Build"
        DependsOnTargets="$(BuildDependsOn)"
        Returns="@(TargetPathWithTargetPlatformMoniker)" />

This indirection ensures that the Build target remains stable while its constituent phases remain configurable.

3. Core Target Implementation

The CoreBuild target itself implements the same pattern recursively, delegating to $(CoreBuildDependsOn) and including error handling:

<PropertyGroup>
  <CoreBuildDependsOn>
    ResolveReferences;
    Compile;
    CopyFilesToOutputDirectory
  </CoreBuildDependsOn>
</PropertyGroup>

<Target Name="CoreBuild" DependsOnTargets="$(CoreBuildDependsOn)">
  <OnError ExecuteTargets="_TimeStampAfterCompile;PostBuildEvent"
           Condition="'$(RunPostBuildEvent)' == 'Always'" />
  <OnError ExecuteTargets="_CleanRecordFileWrites" />
</Target>

The OnError elements ensure cleanup executes even when compilation fails, maintaining file tracking integrity.

4. Extensibility Hooks

The BeforeBuild and AfterBuild targets ship as empty declarations specifically designed for user overrides:

<Target Name="BeforeBuild" />
<Target Name="AfterBuild" />

These hooks allow custom logic insertion at the start and end of the build process without requiring modifications to the Microsoft.Common.CurrentVersion.targets file or other SDK-provided implementations.

Practical Implementation Examples

Basic Three-Level Chain Structure

The complete pattern for a standard Build target appears as follows:

<PropertyGroup>
  <BuildDependsOn>
    BeforeBuild;
    CoreBuild;
    AfterBuild
  </BuildDependsOn>
</PropertyGroup>

<Target Name="Build"
        DependsOnTargets="$(BuildDependsOn)"
        Returns="@(TargetPathWithTargetPlatformMoniker)" />

<Target Name="BeforeBuild" />
<Target Name="AfterBuild" />

Extending the Chain Safely

To insert custom targets without overwriting SDK defaults, append to the dependency property:

<PropertyGroup>
  <CompileDependsOn>$(CompileDependsOn);MyCodeGenTarget</CompileDependsOn>
</PropertyGroup>

<Target Name="MyCodeGenTarget"
        Inputs="@(MySourceFiles)"
        Outputs="$(IntermediateOutputPath)generated.cs">
  <Exec Command="my-tool.exe -out $(IntermediateOutputPath)generated.cs" />
</Target>

This approach preserves the existing CompileDependsOn chain while adding your custom code generation step.

Source Reference

The canonical documentation for this pattern resides in plugins/dotnet-msbuild/skills/target-authoring/SKILL.md within the dotnet/skills repository. Reference implementations of the three-level target chain pattern also appear throughout Microsoft.Common.CurrentVersion.targets in the official MSBuild repository, which the skill documentation extracts and explains.

Summary

  • The three-level target chain pattern uses properties like BuildDependsOn to define ordered sequences of Before, Core, and After phases.
  • Entry-point targets (Build, Rebuild, Clean) delegate their work to these property-defined chains rather than hard-coding dependencies.
  • Core targets implement recursive dependency chains and include OnError elements for robust error handling and cleanup.
  • Empty hook targets (BeforeBuild, AfterBuild) provide safe extension points that do not conflict with SDK updates.
  • Property appending syntax ($(PropertyName);NewTarget) allows customization without overwriting default target sequences.

Frequently Asked Questions

What is the purpose of the Before and After targets?

The BeforeBuild and AfterBuild targets serve as intentional blank hooks that you can override in your own project files or imported targets. Because they exist as empty targets in the SDK, defining them in your project simply overrides the empty implementation, allowing you to inject custom logic at specific points in the build lifecycle without modifying core MSBuild logic.

How do I customize the build without overriding default targets?

Append your custom targets to the dependency properties using the $(PropertyName) reference syntax. For example, use <BuildDependsOn>$(BuildDependsOn);MyCustomTarget</BuildDependsOn> rather than redefining the entire BuildDependsOn list. This preserves the existing three-level chain while inserting your logic into the pipeline.

Why does CoreBuild use OnError elements?

The OnError elements in CoreBuild ensure that file tracking and cleanup operations execute even when the build fails. As implemented in the pattern, these elements trigger targets like _CleanRecordFileWrites and conditional post-build events, maintaining the integrity of the incremental build tracking system regardless of compilation success or failure.

Where is this pattern documented in the source code?

The formal documentation for the three-level target chain pattern exists in plugins/dotnet-msbuild/skills/target-authoring/SKILL.md within the dotnet/skills repository. This skill file extracts and documents the pattern from its primary implementation in Microsoft.Common.CurrentVersion.targets, providing authoritative guidance for MSBuild target authoring.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →