# Understanding the Three-Level Target Chain Pattern in MSBuild

> Uncover the three-level target chain pattern in MSBuild, a powerful architecture for safe pipeline extension. Learn how Build delegates work through Before, Core, and After phases.

- Repository: [.NET Platform/skills](https://github.com/dotnet/skills)
- Tags: deep-dive
- Published: 2026-07-11

---

**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`](https://github.com/dotnet/skills/blob/main/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:

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

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

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

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

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

```xml
<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`](https://github.com/dotnet/skills/blob/main/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`](https://github.com/dotnet/skills/blob/main/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.