Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/Build.UnitTests/Graph/ProjectGraph_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,19 @@ public class ProjectGraphTests : IDisposable
<ProjectReferenceTargets Include='A' Targets='AHelperInner;A' />
<ProjectReferenceTargets Include='A' Targets='AHelperOuter' OuterBuild='true' />
</ItemGroup>";

private const string ProjectReferenceTargetsWithMultitargetingDifferentInnerTargets = @"<ItemGroup>
<!-- Item order is important to ensure outer build targets are put in front of inner build ones -->
<ProjectReferenceTargets Include='A' Targets='AHelperInner' InnerBuild='true' />
<ProjectReferenceTargets Include='A' Targets='AHelperOuter' OuterBuild='true' />
</ItemGroup>";

private static string[] NonOuterBuildTargets = { "AHelperOuter", "AHelperInner", "A" };
private static string[] OuterBuildTargets = { "AHelperOuter" };
private static string[] InnerBuildTargets = { "AHelperInner" };

private const string OuterBuildSpecificationWithProjectReferenceTargets = MultitargetingSpecificationPropertyGroup + ProjectReferenceTargetsWithMultitargeting;
private const string OuterBuildSpecificationWithProjectReferenceTargetsAndDifferentInnerBuildTargets = MultitargetingSpecificationPropertyGroup + ProjectReferenceTargetsWithMultitargetingDifferentInnerTargets;

public ProjectGraphTests(ITestOutputHelper outputHelper)
{
Expand Down Expand Up @@ -1383,6 +1392,51 @@ void AssertMultitargetingNode(int projectNumber, ProjectGraph projectGraph, IRea
}
}

[Fact]
public void GetTargetListsSupportsDifferentTargetsForOuterAndInnerBuilds()
{
using (var env = TestEnvironment.Create())
{
var root1 = CreateProjectFile(
env: env,
projectNumber: 1,
projectReferences: new[] { 2 },
projectReferenceTargets: null,
defaultTargets: null,
extraContent: ProjectReferenceTargetsWithMultitargetingDifferentInnerTargets).Path;

CreateProjectFile(
env: env,
projectNumber: 2,
projectReferences: null,
projectReferenceTargets: null,
defaultTargets: null,
extraContent: OuterBuildSpecificationWithProjectReferenceTargetsAndDifferentInnerBuildTargets);

var projectGraph = new ProjectGraph(root1);

var dot = projectGraph.ToDot();

projectGraph.ProjectNodes.Count.ShouldBe(4);

IReadOnlyDictionary<ProjectGraphNode, ImmutableList<string>> targetLists = projectGraph.GetTargetLists(new List<string> { "A" });

targetLists.Count.ShouldBe(projectGraph.ProjectNodes.Count);
var root = GetFirstNodeWithProjectNumber(projectGraph, 1);

var outerBuild = GetOuterBuild(projectGraph, 2);
var innerBuilds = GetInnerBuilds(projectGraph, 2).ToArray();

targetLists[root].ShouldBe(new[] { "A" });
targetLists[outerBuild].ShouldBe(OuterBuildTargets);

foreach (var innerBuild in innerBuilds)
{
targetLists[innerBuild].ShouldBe(InnerBuildTargets);
}
}
}

[Fact]
public void GetTargetListsDefaultTargetsAreExpanded()
{
Expand Down
17 changes: 13 additions & 4 deletions src/Build/Graph/ProjectInterpretation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal sealed class ProjectInterpretation
private const string SetPlatformMetadataName = "SetPlatform";
private const string SetTargetFrameworkMetadataName = "SetTargetFramework";
private const string GlobalPropertiesToRemoveMetadataName = "GlobalPropertiesToRemove";
private const string ProjectReferenceTargetIsInnerBuildMetadataName = "InnerBuild";
private const string ProjectReferenceTargetIsOuterBuildMetadataName = "OuterBuild";
private const string InnerBuildReferenceItemName = "_ProjectSelfReference";
internal static string TransitiveReferenceItemName = "_TransitiveProjectReference";
Expand Down Expand Up @@ -467,11 +468,13 @@ private static void RemoveFromPropertyDictionary(
public readonly struct TargetsToPropagate
{
private readonly ImmutableList<TargetSpecification> _outerBuildTargets;
private readonly ImmutableList<TargetSpecification> _innerBuildTargets;
private readonly ImmutableList<TargetSpecification> _allTargets;

private TargetsToPropagate(ImmutableList<TargetSpecification> outerBuildTargets, ImmutableList<TargetSpecification> nonOuterBuildTargets)
private TargetsToPropagate(ImmutableList<TargetSpecification> outerBuildTargets, ImmutableList<TargetSpecification> innerBuildTargets, ImmutableList<TargetSpecification> nonOuterBuildTargets)
{
_outerBuildTargets = outerBuildTargets;
_innerBuildTargets = innerBuildTargets;

// This is used as the list of entry targets for both inner builds and non-multitargeting projects.
// It represents the concatenation of outer build targets and non outer build targets, in this order.
Expand All @@ -493,6 +496,7 @@ public static TargetsToPropagate FromProjectAndEntryTargets(ProjectInstance proj
{
ImmutableList<TargetSpecification>.Builder targetsForOuterBuild = ImmutableList.CreateBuilder<TargetSpecification>();
ImmutableList<TargetSpecification>.Builder targetsForInnerBuild = ImmutableList.CreateBuilder<TargetSpecification>();
ImmutableList<TargetSpecification>.Builder targetsForNonOuterBuild = ImmutableList.CreateBuilder<TargetSpecification>();

ICollection<ProjectItemInstance> projectReferenceTargets = project.GetItems(ItemTypeNames.ProjectReferenceTargets);

Expand All @@ -504,22 +508,27 @@ public static TargetsToPropagate FromProjectAndEntryTargets(ProjectInstance proj
{
string targetsMetadataValue = projectReferenceTarget.GetMetadataValue(ItemMetadataNames.ProjectReferenceTargetsMetadataName);
bool skipNonexistentTargets = MSBuildStringIsTrue(projectReferenceTarget.GetMetadataValue("SkipNonexistentTargets"));
bool targetsAreForInnerBuild = MSBuildStringIsTrue(projectReferenceTarget.GetMetadataValue(ProjectReferenceTargetIsInnerBuildMetadataName));
bool targetsAreForOuterBuild = MSBuildStringIsTrue(projectReferenceTarget.GetMetadataValue(ProjectReferenceTargetIsOuterBuildMetadataName));
TargetSpecification[] targets = ExpressionShredder.SplitSemiColonSeparatedList(targetsMetadataValue)
.Select(t => new TargetSpecification(t, skipNonexistentTargets)).ToArray();
if (targetsAreForOuterBuild)
{
targetsForOuterBuild.AddRange(targets);
}
else
else if(targetsAreForInnerBuild)
{
targetsForInnerBuild.AddRange(targets);
}
else
{
targetsForNonOuterBuild.AddRange(targets);
}
}
}
}

return new TargetsToPropagate(targetsForOuterBuild.ToImmutable(), targetsForInnerBuild.ToImmutable());
return new TargetsToPropagate(targetsForOuterBuild.ToImmutable(), targetsForInnerBuild.ToImmutable(), targetsForNonOuterBuild.ToImmutable());
}

public ImmutableList<string> GetApplicableTargetsForReference(ProjectGraphNode projectGraphNode)
Expand All @@ -535,7 +544,7 @@ ImmutableList<string> RemoveNonexistentTargetsIfSkippable(ImmutableList<TargetSp

return projectGraphNode.ProjectType switch
{
ProjectType.InnerBuild => RemoveNonexistentTargetsIfSkippable(_allTargets),
ProjectType.InnerBuild => RemoveNonexistentTargetsIfSkippable(_innerBuildTargets.Count > 0 ? _innerBuildTargets : _allTargets),
ProjectType.OuterBuild => RemoveNonexistentTargetsIfSkippable(_outerBuildTargets),
ProjectType.NonMultitargeting => RemoveNonexistentTargetsIfSkippable(_allTargets),
_ => throw new ArgumentOutOfRangeException(),
Expand Down