3

Help me understand, why target A (and D) is run while target B is not when MSBuild is invoked like this:

msbuild /target:C 

on this simple project (XML header omitted):

<Target Name="A" BeforeTargets="C"> <Message Text="Inside target A" /> </Target> <Target Name="B"> <Message Text="Inside target B" /> </Target> <Target Name="C" DependsOnTargets="B" Condition="'False'"> <Message Text="Inside target C" /> </Target> <Target Name="D" AfterTargets="C"> <Message Text="Inside target D" /> </Target> 

According to target build order:

  • The Condition attribute of the target is evaluated. If the Condition attribute is present and evaluates to false, the target isn't executed and has no further effect on the build.
  • Before a target is executed, its DependsOnTargets targets are run.
  • Before a target is executed, any target that lists it in a BeforeTargets attribute is run.
  • ...
  • After a target is executed or skipped, any target that lists it in an AfterTargets attribute is run.

which is why it is only logical to conclude that since predicate Before a target is executed is the same for both DependsOnTargets and BeforeTargets attributes, either both A and B should be run, or neither.

As far as my understanding goes, neither A nor B should be run since MSBuild is asked to run target C, which is conditional and Condition evaluates to false.


P.S. MSBuild 15.5.180.51428

4
  • Why did you tag with MSBuild 4.0 when you explicitly say you are using MSBuild 15.5? Same with your answer. Commented Jan 22, 2018 at 12:16
  • @CamiloTerevinto Because attributes in question are features of MSBuild 4.0 and I thought that it was relevant. Commented Jan 22, 2018 at 12:18
  • So you think that the code hasn't changed at all between MSBuild 4.0 and 15.5 which is over 7 years newer? Commented Jan 22, 2018 at 12:19
  • @CamiloTerevinto Not since the 2009, it didn't. This issue is somewhat covered in second edition of "Inside The Microsoft Build Engine" which was released in 2009. I referenced current version of MSBuild to indicate that this is still a case in latest shipped version of MSBuild. Commented Jan 22, 2018 at 12:34

1 Answer 1

4

This seems to be a case of peculiar difference between MSBuild 4.0 BeforeTargets, AfterTargets attributes versus older DependsOnTargets attribute.

Short answer

BeforeTargets and AfterTargets are run regardless of the Condition whereas DependsOnTargets (and target itself) are skipped when Condition evaluates to false.

Long answer

Investigation of TargetBuilder::ProcessTargetStack() source code showed that

  1. Targets specified with AfterTargets and BeforeTargets attributes are unconditionally pushed onto stack of targets to be built via afterTargets and beforeTargets lists returned by GetTargetsWhichRunAfter() and GetTargetsWhichRunBefore() methods.

  2. Targets specified with DependsOnTargets attribute are pushed when non-null onto stack of targets via dependencies list returned by TargetEntry::GetDependencies() method.

  3. Condition attribute is actually evaluated only inside TargetEntry::GetDependencies() method which returns empty list when condition is present and resolves to false, and list of dependencies otherwise.

Description in target build order seems to contradict the logic behind scheduling; the correct order seems to be:

  • Before a target is executed, any target that lists it in a BeforeTargets attribute is run.
  • The Condition attribute of the target is evaluated. If the Condition attribute is present and evaluates to false, the target and its DependsOnTargets targets are not executed.
  • ...
  • After a target is executed or skipped, any target that lists it in an AfterTargets attribute is run.

(Native speakers can probably document this better.)

P.S. Correct me if I'm wrong, but I come from C background and this check

if (null != dependencies) 

seems redundant to me because dependencies variable is initialised as

dependencies = currentTargetEntry.GetDependencies(...) 

and GetDependencies returns initialised instance of List<..> regardless of condition:

List<...> GetDependencies(...) { bool condition = ConditionEvaluator.EvaluateCondition(...); if (!condition) { ... return new List<...>(); } ... List<...> dependencyTargets = new List<...>(); ... return dependencyTargets; } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.