Skip to content

AfterTargets hook runs before target body when Condition-skipped target re-runs with Condition true #13274

@sbomer

Description

@sbomer

This may be by design (see referenced issues below) but I found this very confusing, so wanted to make sure we have an issue for this specific problem.

Repro

<Project>
  <Target Name="Build" DependsOnTargets="Work;Enable;Second" />
  <Target Name="Work" Condition=" '$(Enabled)' == 'true' ">
    <Message Importance="high" Text="1. Work ran" />
  </Target>
  <Target Name="Hook" AfterTargets="Work">
    <Message Importance="high" Text="2. Hook ran" />
  </Target>
  <Target Name="Enable">
    <PropertyGroup>
      <Enabled>true</Enabled>
    </PropertyGroup>
  </Target>
  <Target Name="Second" DependsOnTargets="Work" />
</Project>
$ dotnet msbuild repro.proj -v:n

Expected: 1. Work ran before 2. Hook ran
Actual: 2. Hook ran before 1. Work ran

Hook fires on the first (Condition-false) invocation of Work, before Work has ever executed. When Work actually runs on the second invocation (after Enable sets the property), Hook does not fire again.

Analysis

Two documented behaviors interact to produce this:

  1. AfterTargets fires on skipped targets — "After the target is executed or skipped, any other target that lists it in an AfterTargets attribute is run."
  2. Condition-skipped targets can re-run — "If a target is skipped because its Condition evaluates to false, it can still be executed if invoked later with Condition true."

The target gets a second chance, but its hooks don't.

This came up while investigating an incremental build issue in xamarin-android.

Related

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions