-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Prevent duplicate targets Fixes #5071 #5109
Conversation
Duplicate targets break the build; this prevents MSBuild's default targets from being added if they already exist.
AddReferencesBuildTask(target, targetName, outputItem); | ||
if (!traversalProject.Targets.Select(target => target.Key).Contains(targetName ?? "Build")) | ||
{ | ||
ProjectTargetInstance target = traversalProject.AddTarget(targetName ?? "Build", string.Empty, string.Empty, outputItemAsItem, null, string.Empty, string.Empty, string.Empty, string.Empty, false /* legacy target returns behaviour */); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fix looks right, but can you add a unit test that proves it does what we think?
src/Build.UnitTests/Construction/SolutionProjectGenerator_Tests.cs
Outdated
Show resolved
Hide resolved
src/Build.UnitTests/Construction/SolutionProjectGenerator_Tests.cs
Outdated
Show resolved
Hide resolved
@@ -1969,8 +1969,11 @@ private static void AddTraversalReferencesTarget(ProjectInstance traversalProjec | |||
outputItemAsItem = "@(" + outputItem + ")"; | |||
} | |||
|
|||
ProjectTargetInstance target = traversalProject.AddTarget(targetName ?? "Build", String.Empty, String.Empty, outputItemAsItem, null, String.Empty, String.Empty, String.Empty, String.Empty, false /* legacy target returns behaviour */); | |||
AddReferencesBuildTask(target, targetName, outputItem); | |||
if (!traversalProject.Targets.Select(target => target.Key).Contains(targetName ?? "Build")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extract targetName ?? "Build"
to a variable since it's used twice
instances.ShouldHaveSingleItem(); | ||
if (!name.Equals("name.that.does.Not.Affect.The.Build.targets")) | ||
{ | ||
instances[0].Targets["Build"].AfterTargets.ShouldBe("NonsenseTarget"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is right. When defined in before
, the dummy target from the bug should be overridden by the generated target "in the body" of the metaproject.
Have you validated that this change behaves correctly using the repro steps from #5071 (comment)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think this was a design issue. I thought it would be best for the user's version to override the generated version no matter what. I can change it.
…s.cs Co-Authored-By: Rainer Sigwald <raines@microsoft.com>
…s.cs Co-Authored-By: Rainer Sigwald <raines@microsoft.com>
In 16.3, the default version overrode before/after.<sln>.targets versions. This changes back to that mode.
if (traversalProject.Targets.Select(target => target.Key).Contains(correctedTargetName)) | ||
{ | ||
traversalProject.RemoveTarget(correctedTargetName); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just do this unconditionally; RemoveTarget
doesn't throw if it's not there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this depends on the "how did pre-16.4 behave" question WRT having stub targets in before/after.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry—I should have mentioned this rather than just pushing the change. In 16.3, if you have a before...targets or an after...targets with a Build target, the Build target is overwritten by the generated target.
I've just run into the situation where I can no longer redefine the Build target, for example via UPDATE: I've come up with a workaround of sorts: <Target
Name="_CustomBeforeBuild"
BeforeTargets="Build"
Outputs="@(CollectedBuildOutput)">
<!-- DO YOUR ACUTAL Build TARGET WORK HERE (or via DependsOnTargets etc) -->
<!-- Remove all @(ProjectReference) which effectively turns the default Build target into a no-op -->
<ItemGroup>
<_OriginalProjectReference Include="@(ProjectReference)"/>
<ProjectReference Remove="@(ProjectReference)" />
</ItemGroup>
</Target>
<Target
Name="_CustomAfterBuild"
AfterTargets="Build">
<!-- Restore all @(ProjectReference) in case another target is due to be executed after Build -->
<ItemGroup>
<ProjectReference Include="@(_OriginalProjectReference)" />
</ItemGroup>
</Target> |
Can you define the Build target by putting it in a separate file that you import in Directory.Build/Solution.props? I think Directory.Solution.targets comes in after the dummy Build target was added. |
@Forgind Apologies for the delay, vacation. No, your suggestion does not work. The custom These lines in However, An earlier version of |
Duplicate targets break the build; this prevents MSBuild's default targets from being added if they already exist.
Fixes #5071