Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new TargetBuiltReasons #10092

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Add new TargetBuiltReasons #10092

wants to merge 5 commits into from

Conversation

maridematte
Copy link
Contributor

Fixes #9467

Context

When building targets we only specify a reason when it has direct control over the flow. So some targets have no built reason at the end of the build and can get very confusing on large builds.

Changes Made

Added options for TargetBuiltReason: Initial target, entry target, default target.
These are added to TargetSpecification so they can be referenced in different points of the build.

@maridematte maridematte marked this pull request as ready for review April 30, 2024 16:36
@maridematte maridematte self-assigned this Apr 30, 2024
Copy link
Member

@JanKrivanek JanKrivanek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for diving into this!

It looks as a good direction. I believe we should test the case of explicit entry target (or implicit default target) and identical initial target - as then the same target should be executed twice - for 2 different reasons. The current implementation seem to not cover those cases (both executions would be attributed to initial target).
Other than that - I like the solution - simple and effective

@rainersigwald
Copy link
Member

I believe we should test the case of explicit entry target (or implicit default target) and identical initial target - as then the same target should be executed twice - for 2 different reasons.

Not sure I understand this. A target must be executed only once, for exactly one reason--even if it is put in the target stack multiple times for multiple reasons. Right?

@JanKrivanek
Copy link
Member

I believe we should test the case of explicit entry target (or implicit default target) and identical initial target - as then the same target should be executed twice - for 2 different reasons.

Not sure I understand this. A target must be executed only once, for exactly one reason--even if it is put in the target stack multiple times for multiple reasons. Right?

I believe for InitialTargets we do not deduplicate this and we document it so as well - but will double check (afk now though). In general - target can execute multiple times if not properly input/output attributed, right? Or maybe I'm just confused :-)

@rainersigwald
Copy link
Member

In general - target can execute multiple times if not properly input/output attributed, right?

Within a single project instance in a single build, the engine should never run the target again, at most emitting Target "{0}" skipped. Previously built successfully. to the log.

@JanKrivanek
Copy link
Member

Sorry - yes, you are correct - I was totally wrong on this.
I still have some recollection that even for skipped we could set the BuiltReason. But I can be wrong again. @maridematte - please ignore my comment until I check this on my end with code at hand

@JanKrivanek
Copy link
Member

Seems like TargetSkippedBuildEventArgs has BuiltReason as well: https://github.com/dotnet/msbuild/blob/main/src/Framework/TargetSkippedEventArgs.cs#L102 It's being populated from the TargetEntry populated here. It's probably not used in the viewer - as viewer doesn't seem to display built reason for skipped targets (@KirillOsenkov ?). But even despite that it might not be bad idea to populate the information properly - we just need to pass a tupple list instead of a string list into the BuildTargets

@KirillOsenkov
Copy link
Member

Reminds me of this case I think:
image

/// The target was defined as an initial target of the project.
/// </summary>
InitialTarget,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe remove the empty line. As a rule in C# there shouldn't be two consecutive empty lines.

TargetEntry newEntry;
if (buildReason == TargetBuiltReason.None)
{
newEntry = new TargetEntry(_requestEntry, this as ITargetBuilderCallback, targetSpecification, baseLookup, parentTargetEntry, targetSpecification._targetBuiltReason, _componentHost, stopProcessingOnCompletion);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to avoid having two lines of code both creating TargetEntry. I think it'll be better if you mutate the buildReason variable and pass it as an argument, so you can avoid duplicating lines 757 and 761.

@KirillOsenkov
Copy link
Member

It is indeed pretty elegant and simpler than I thought it would be!

I filed a viewer issue to expose the TargetBuiltReason for skipped targets.

Would be nice to check if we can pass through this information for skipped targets.

Jan, not sure what you mean by passing a tuple instead of a string list, can you link to the source code?

@KirillOsenkov
Copy link
Member

Also, be very careful here:

if (buildReason == TargetBuiltReason.BeforeTargets || buildReason == TargetBuiltReason.DependsOn || buildReason == TargetBuiltReason.None)

We should reason about this check very carefully, because now instead of None it could be something else. If the intent here was to check for "anything else but AfterTargets", the condition needs to be updated for the new enum values.

Need a lot of careful scrutiny here, because we might accidentally introduce a very bad regression.

@KirillOsenkov
Copy link
Member

PushTargets seems like a very non-trivial algorithm. I'd sleep better at night if it was covered by unit-tests. Wondering if we already have tests covering this method?

We need to trace the control flow in this method and carefully determine which branch of the if/else is (or should be) taken for each of the new enum values.

/// <summary>
/// The target was the default target of the project
/// </summary>
DefaultTarget,
Copy link
Member

@danmoseley danmoseley May 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pluralization should match the xml attributes. So DefaultTargets and InitialTargets like AfterTargets.
Also EntryTargets as that can be several (/t:a;b)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, to clarify, all three new enum fields should be plural, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@JanKrivanek
Copy link
Member

Jan, not sure what you mean by passing a tuple instead of a string list, can you link to the source code?

tl;dr;: Setting the reason should be responsibility of the code that requested the build (and hence knows the reason)

Basically the BuildTargets is an internal contract and called from a single place in code (not considering the unit tests):

https://github.com/dotnet/msbuild/blob/f25414599dcb07202a9f8bfd8125d04a6e5c14be/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs#L1210C21-L1210C31

So it should be very easy to transfer the responsibility of deciding about the build reason from the BuildTargets method, which actually doesn't have the knowledge (and the proposal now is just best guessing) to the caller (above) who is requesting the targets to be build and hence has the exact knowledge of the reason.
In such case the BuildTargets would accept e.g. (string, BuiltReason)[] targets instead of current string[] targets - which is easily accomodable on a caller side, removes the uncertainity on the callee side and is not breaking any contracts.

(but we already agreed on this offline with @maridematte)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TargetBuiltReason should have a field for InitialTargets
5 participants