Skip to content

Commit 0b2744b

Browse files
authored
GetCopyToOutputDirectoryItemsGraphPredictor: Add support for MSBuildCopyContentTransitively (#120)
1 parent cac2e3b commit 0b2744b

File tree

2 files changed

+64
-13
lines changed

2 files changed

+64
-13
lines changed

src/BuildPrediction/Predictors/GetCopyToOutputDirectoryItemsGraphPredictor.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.IO;
67
using Microsoft.Build.Execution;
78
using Microsoft.Build.Graph;
@@ -15,23 +16,44 @@ public sealed class GetCopyToOutputDirectoryItemsGraphPredictor : IProjectGraphP
1516
{
1617
internal const string UseCommonOutputDirectoryPropertyName = "UseCommonOutputDirectory";
1718
internal const string OutDirPropertyName = "OutDir";
19+
internal const string MSBuildCopyContentTransitivelyPropertyName = "MSBuildCopyContentTransitively";
1820

1921
/// <inheritdoc/>
2022
public void PredictInputsAndOutputs(ProjectGraphNode projectGraphNode, ProjectPredictionReporter predictionReporter)
23+
{
24+
string outDir = projectGraphNode.ProjectInstance.GetPropertyValue(OutDirPropertyName);
25+
HashSet<ProjectGraphNode> visitedNodes = new();
26+
PredictInputsAndOutputs(projectGraphNode, outDir, predictionReporter, visitedNodes);
27+
}
28+
29+
private static void PredictInputsAndOutputs(
30+
ProjectGraphNode projectGraphNode,
31+
string outDir,
32+
ProjectPredictionReporter predictionReporter,
33+
HashSet<ProjectGraphNode> visitedNodes)
2134
{
2235
ProjectInstance projectInstance = projectGraphNode.ProjectInstance;
2336

2437
// The GetCopyToOutputDirectoryItems target gets called on all dependencies, unless UseCommonOutputDirectory is set to true.
2538
var useCommonOutputDirectory = projectInstance.GetPropertyValue(UseCommonOutputDirectoryPropertyName);
2639
if (!useCommonOutputDirectory.Equals("true", StringComparison.OrdinalIgnoreCase))
2740
{
28-
string outDir = projectInstance.GetPropertyValue(OutDirPropertyName);
41+
bool copyContentTransitively = projectInstance.GetPropertyValue(MSBuildCopyContentTransitivelyPropertyName).Equals("true", StringComparison.OrdinalIgnoreCase);
2942

30-
// Note that GetCopyToOutputDirectoryItems effectively only is able to go one project reference deep despite being recursive as
31-
// it uses @(_MSBuildProjectReferenceExistent) to recurse, which is not set in the recursive calls.
32-
// See: https://github.com/microsoft/msbuild/blob/master/src/Tasks/Microsoft.Common.CurrentVersion.targets
3343
foreach (ProjectGraphNode dependency in projectGraphNode.ProjectReferences)
3444
{
45+
if (!visitedNodes.Add(dependency))
46+
{
47+
// Avoid duplicate predictions
48+
continue;
49+
}
50+
51+
// If transitive, recurse
52+
if (copyContentTransitively)
53+
{
54+
PredictInputsAndOutputs(dependency, outDir, predictionReporter, visitedNodes);
55+
}
56+
3557
// Process each item type considered in GetCopyToOutputDirectoryItems. Yes, Compile is considered.
3658
ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, ContentItemsPredictor.ContentItemName, outDir, predictionReporter);
3759
ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, EmbeddedResourceItemsPredictor.EmbeddedResourceItemName, outDir, predictionReporter);

src/BuildPredictionTests/Predictors/GetCopyToOutputDirectoryItemsGraphPredictorTests.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.IO;
67
using Microsoft.Build.Construction;
78
using Microsoft.Build.Prediction.Predictors;
@@ -77,8 +78,10 @@ public void UseCommonOutputDirectory()
7778
.AssertNoPredictions();
7879
}
7980

80-
[Fact]
81-
public void WithCopy()
81+
[Theory]
82+
[InlineData(false)]
83+
[InlineData(true)]
84+
public void WithCopy(bool copyContentTransitively)
8285
{
8386
string projectFile = Path.Combine(_rootDir, @"src\project.csproj");
8487
ProjectRootElement projectRootElement = ProjectRootElement.Create(projectFile);
@@ -89,7 +92,12 @@ public void WithCopy()
8992
ProjectRootElement dep2 = CreateDependencyProject("dep2", shouldCopy);
9093
ProjectRootElement dep3 = CreateDependencyProject("dep3", shouldCopy);
9194

92-
// The main project depends on 1 and 2; 2 depends on 3; 3 depends on 1. Note that this should *not* be transitive
95+
projectRootElement.AddProperty(GetCopyToOutputDirectoryItemsGraphPredictor.MSBuildCopyContentTransitivelyPropertyName, copyContentTransitively.ToString());
96+
dep1.AddProperty(GetCopyToOutputDirectoryItemsGraphPredictor.MSBuildCopyContentTransitivelyPropertyName, copyContentTransitively.ToString());
97+
dep2.AddProperty(GetCopyToOutputDirectoryItemsGraphPredictor.MSBuildCopyContentTransitivelyPropertyName, copyContentTransitively.ToString());
98+
dep3.AddProperty(GetCopyToOutputDirectoryItemsGraphPredictor.MSBuildCopyContentTransitivelyPropertyName, copyContentTransitively.ToString());
99+
100+
// The main project depends on 1 and 2; 2 depends on 3; 3 depends on 1.
93101
projectRootElement.AddItem("ProjectReference", @"..\dep1\dep1.proj");
94102
projectRootElement.AddItem("ProjectReference", @"..\dep2\dep2.proj");
95103
dep2.AddItem("ProjectReference", @"..\dep3\dep3.proj");
@@ -100,8 +108,8 @@ public void WithCopy()
100108
dep2.Save();
101109
dep3.Save();
102110

103-
var expectedInputFiles = new[]
104-
{
111+
List<PredictedItem> expectedInputFiles =
112+
[
105113
new PredictedItem(@"dep1\dep1.xml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
106114
new PredictedItem(@"dep1\dep1.resx", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
107115
new PredictedItem(@"dep1\dep1.cs", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
@@ -112,10 +120,10 @@ public void WithCopy()
112120
new PredictedItem(@"dep2\dep2.cs", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
113121
new PredictedItem(@"dep2\dep2.txt", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
114122
new PredictedItem(@"dep2\dep2.xaml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
115-
};
123+
];
116124

117-
var expectedOutputFiles = new[]
118-
{
125+
List<PredictedItem> expectedOutputFiles =
126+
[
119127
new PredictedItem(@"src\bin\dep1.xml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
120128
new PredictedItem(@"src\bin\dep1.resx", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
121129
new PredictedItem(@"src\bin\dep1.cs", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
@@ -126,7 +134,28 @@ public void WithCopy()
126134
new PredictedItem(@"src\bin\dep2.cs", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
127135
new PredictedItem(@"src\bin\dep2.txt", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
128136
new PredictedItem(@"src\bin\dep2.xaml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
129-
};
137+
];
138+
139+
if (copyContentTransitively)
140+
{
141+
expectedInputFiles.AddRange(
142+
[
143+
new PredictedItem(@"dep3\dep3.xml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
144+
new PredictedItem(@"dep3\dep3.resx", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
145+
new PredictedItem(@"dep3\dep3.cs", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
146+
new PredictedItem(@"dep3\dep3.txt", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
147+
new PredictedItem(@"dep3\dep3.xaml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
148+
]);
149+
150+
expectedOutputFiles.AddRange(
151+
[
152+
new PredictedItem(@"src\bin\dep3.xml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
153+
new PredictedItem(@"src\bin\dep3.resx", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
154+
new PredictedItem(@"src\bin\dep3.cs", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
155+
new PredictedItem(@"src\bin\dep3.txt", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
156+
new PredictedItem(@"src\bin\dep3.xaml", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
157+
]);
158+
}
130159

131160
new GetCopyToOutputDirectoryItemsGraphPredictor()
132161
.GetProjectPredictions(projectFile)

0 commit comments

Comments
 (0)