Skip to content

Commit 342fed5

Browse files
committed
Add DotnetSdkPredictor
1 parent def3651 commit 342fed5

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.IO;
7+
using Microsoft.Build.Execution;
8+
9+
namespace Microsoft.Build.Prediction.Predictors;
10+
11+
/// <summary>
12+
/// Makes predictions based on using Microsoft.NET.Sdk.
13+
/// </summary>
14+
public sealed class DotnetSdkPredictor : IProjectPredictor
15+
{
16+
internal const string UsingMicrosoftNETSdkPropertyName = "UsingMicrosoftNETSdk";
17+
18+
private readonly ConcurrentDictionary<string, bool> _globalJsonExistenceCache = new(PathComparer.Instance);
19+
20+
/// <inheritdoc/>
21+
public void PredictInputsAndOutputs(
22+
ProjectInstance projectInstance,
23+
ProjectPredictionReporter predictionReporter)
24+
{
25+
var usingMicrosoftNETSdk = projectInstance.GetPropertyValue(UsingMicrosoftNETSdkPropertyName);
26+
if (!usingMicrosoftNETSdk.Equals("true", StringComparison.OrdinalIgnoreCase))
27+
{
28+
return;
29+
}
30+
31+
// Microsoft.NET.Sdk reads global.json.
32+
string currentProbeDirectory = projectInstance.Directory;
33+
while (currentProbeDirectory != null)
34+
{
35+
string globalJsonPath = Path.Combine(currentProbeDirectory, "global.json");
36+
bool globalJsonPathExists = _globalJsonExistenceCache.GetOrAdd(globalJsonPath, File.Exists);
37+
if (globalJsonPathExists)
38+
{
39+
predictionReporter.ReportInputFile(globalJsonPath);
40+
break;
41+
}
42+
43+
currentProbeDirectory = Path.GetDirectoryName(currentProbeDirectory);
44+
}
45+
}
46+
}

src/BuildPrediction/ProjectPredictors.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public static class ProjectPredictors
6060
/// <item><see cref="ModuleDefinitionFilePredictor"/></item>
6161
/// <item><see cref="CppContentFilesProjectOutputGroupPredictor"/></item>
6262
/// <item><see cref="LinkItemsPredictor"/></item>
63+
/// <item><see cref="DotnetSdkPredictor"/></item>
6364
/// </list>
6465
/// </remarks>
6566
/// <returns>A collection of <see cref="IProjectPredictor"/>.</returns>
@@ -106,6 +107,7 @@ public static class ProjectPredictors
106107
new ModuleDefinitionFilePredictor(),
107108
new CppContentFilesProjectOutputGroupPredictor(),
108109
new LinkItemsPredictor(),
110+
new DotnetSdkPredictor(),
109111
//// NOTE! When adding a new predictor here, be sure to update the doc comment above.
110112
};
111113

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.IO;
6+
using Microsoft.Build.Construction;
7+
using Microsoft.Build.Execution;
8+
using Microsoft.Build.Prediction.Predictors;
9+
using Xunit;
10+
11+
namespace Microsoft.Build.Prediction.Tests.Predictors;
12+
13+
public class DotnetSdkPredictorTests
14+
{
15+
private readonly string _rootDir;
16+
17+
public DotnetSdkPredictorTests()
18+
{
19+
// Isolate each test into its own folder
20+
_rootDir = Path.Combine(Directory.GetCurrentDirectory(), nameof(DotnetSdkPredictorTests), Guid.NewGuid().ToString());
21+
Directory.CreateDirectory(_rootDir);
22+
}
23+
24+
[Fact]
25+
public void GlobalJsonExistsAdjacent()
26+
{
27+
ProjectRootElement projectRootElement = ProjectRootElement.Create(Path.Combine(_rootDir, @"src\project.csproj"));
28+
projectRootElement.AddProperty(DotnetSdkPredictor.UsingMicrosoftNETSdkPropertyName, "true");
29+
30+
Directory.CreateDirectory(Path.Combine(_rootDir, "src"));
31+
File.WriteAllText(Path.Combine(_rootDir, @"src\global.json"), "{}");
32+
33+
// Extraneous one above, which is not predicted as an input.
34+
File.WriteAllText(Path.Combine(_rootDir, "global.json"), "{}");
35+
36+
ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement);
37+
38+
var expectedInputFiles = new[]
39+
{
40+
new PredictedItem(@"src\global.json", nameof(DotnetSdkPredictor)),
41+
};
42+
43+
new DotnetSdkPredictor()
44+
.GetProjectPredictions(projectInstance)
45+
.AssertPredictions(
46+
projectInstance,
47+
expectedInputFiles.MakeAbsolute(_rootDir),
48+
null,
49+
null,
50+
null);
51+
}
52+
53+
[Fact]
54+
public void GlobalJsonExistsAbove()
55+
{
56+
ProjectRootElement projectRootElement = ProjectRootElement.Create(Path.Combine(_rootDir, @"src\project.csproj"));
57+
projectRootElement.AddProperty(DotnetSdkPredictor.UsingMicrosoftNETSdkPropertyName, "true");
58+
59+
File.WriteAllText(Path.Combine(_rootDir, "global.json"), "{}");
60+
61+
ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement);
62+
63+
var expectedInputFiles = new[]
64+
{
65+
new PredictedItem(@"global.json", nameof(DotnetSdkPredictor)),
66+
};
67+
68+
new DotnetSdkPredictor()
69+
.GetProjectPredictions(projectInstance)
70+
.AssertPredictions(
71+
projectInstance,
72+
expectedInputFiles.MakeAbsolute(_rootDir),
73+
null,
74+
null,
75+
null);
76+
}
77+
78+
[Fact]
79+
public void NoGlobalJsonExists()
80+
{
81+
ProjectRootElement projectRootElement = ProjectRootElement.Create(Path.Combine(_rootDir, @"src\project.csproj"));
82+
projectRootElement.AddProperty(DotnetSdkPredictor.UsingMicrosoftNETSdkPropertyName, "true");
83+
84+
ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement);
85+
86+
new DotnetSdkPredictor()
87+
.GetProjectPredictions(projectInstance)
88+
.AssertPredictions(
89+
projectInstance,
90+
null,
91+
null,
92+
null,
93+
null);
94+
}
95+
96+
[Fact]
97+
public void NotUsingDotnetSdk()
98+
{
99+
ProjectRootElement projectRootElement = ProjectRootElement.Create(Path.Combine(_rootDir, @"src\project.csproj"));
100+
101+
Directory.CreateDirectory(Path.Combine(_rootDir, "src"));
102+
File.WriteAllText(Path.Combine(_rootDir, "global.json"), "{}");
103+
104+
ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement);
105+
106+
new DotnetSdkPredictor()
107+
.GetProjectPredictions(projectInstance)
108+
.AssertPredictions(
109+
projectInstance,
110+
null,
111+
null,
112+
null,
113+
null);
114+
}
115+
}

0 commit comments

Comments
 (0)