Skip to content

Commit

Permalink
Merge pull request #11090 from dependabot/dev/brettfo/nuget-install-d…
Browse files Browse the repository at this point in the history
…otnet-sdks

install .NET SDKs as specified by repo's `global.json` files
  • Loading branch information
randhircs authored Dec 16, 2024
2 parents 44b2066 + 4ebd9ba commit be2d4c8
Show file tree
Hide file tree
Showing 22 changed files with 506 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ await File.WriteAllTextAsync(projectPath, """
workingDirectory = Path.Join(workingDirectory, workingDirectoryPath);
}

var (exitCode, output, error) = await ProcessEx.RunAsync("dotnet", executableArgs, workingDirectory: workingDirectory);
var (exitCode, output, error) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(executableArgs, workingDirectory, new ExperimentsManager() { InstallDotnetSdks = false });
Assert.True(exitCode == 0, $"Error running update on unsupported SDK.\nSTDOUT:\n{output}\nSTDERR:\n{error}");

// verify project update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ private static async Task TestDiscoverAsync(string startingDirectory, string pro
var logger = new TestLogger();
var fullProjectPath = Path.Combine(testDirectory.DirectoryPath, projectPath);
var experimentsManager = new ExperimentsManager() { UseDirectDiscovery = true }; // the following method is direct discovery; this just makes the call to Validate... happy
var projectDiscovery = await SdkProjectDiscovery.DiscoverWithBinLogAsync(testDirectory.DirectoryPath, Path.GetDirectoryName(fullProjectPath)!, fullProjectPath, logger);
var projectDiscovery = await SdkProjectDiscovery.DiscoverWithBinLogAsync(testDirectory.DirectoryPath, Path.GetDirectoryName(fullProjectPath)!, fullProjectPath, experimentsManager, logger);
ValidateProjectResults(expectedProjects, projectDiscovery, experimentsManager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ private static byte[] CreateAssembly(string assemblyName, string assemblyVersion
</Project>
"""
);
var (exitCode, stdout, stderr) = ProcessEx.RunAsync("dotnet", ["msbuild", projectPath, "/t:_ReportCurrentSdkVersion"]).Result;
var experimentsManager = new ExperimentsManager();
var (exitCode, stdout, stderr) = ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["msbuild", projectPath, "/t:_ReportCurrentSdkVersion"], projectDir.FullName, experimentsManager).Result;
if (exitCode != 0)
{
throw new Exception($"Failed to report the current SDK version:\n{stdout}\n{stderr}");
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public async Task<AnalysisResult> RunAsync(string repoRoot, WorkspaceDiscoveryRe
dependenciesToUpdate,
updatedVersion,
nugetContext,
_experimentsManager,
_logger,
CancellationToken.None);
}
Expand Down Expand Up @@ -393,6 +394,7 @@ internal static async Task<ImmutableArray<Dependency>> FindUpdatedDependenciesAs
ImmutableHashSet<string> packageIds,
NuGetVersion updatedVersion,
NuGetContext nugetContext,
ExperimentsManager experimentsManager,
ILogger logger,
CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -432,6 +434,7 @@ internal static async Task<ImmutableArray<Dependency>> FindUpdatedDependenciesAs
packageIds,
updatedVersion,
nugetContext,
experimentsManager,
logger,
cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static async Task<ImmutableDictionary<NuGetFramework, ImmutableArray<Depe
ImmutableHashSet<string> packageIds,
NuGetVersion version,
NuGetContext nugetContext,
ExperimentsManager experimentsManager,
ILogger logger,
CancellationToken cancellationToken)
{
Expand All @@ -30,6 +31,7 @@ public static async Task<ImmutableDictionary<NuGetFramework, ImmutableArray<Depe
projectPath,
framework.ToString(),
packages,
experimentsManager,
logger);
var updatedDependencies = new List<Dependency>();
foreach (var dependency in dependencies)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private async Task<bool> TryRestoreMSBuildSdksAsync(string repoRootPath, string

_logger.Info($" Restoring MSBuild SDKs: {string.Join(", ", keys)}");

return await NuGetHelper.DownloadNuGetPackagesAsync(repoRootPath, workspacePath, msbuildSdks, logger);
return await NuGetHelper.DownloadNuGetPackagesAsync(repoRootPath, workspacePath, msbuildSdks, _experimentsManager, logger);
}

private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForDirectoryAsnyc(string repoRootPath, string workspacePath)
Expand Down Expand Up @@ -286,7 +286,7 @@ private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForProjectPathsAsy
_processedProjectPaths.Add(actualProjectPath);

var relativeProjectPath = Path.GetRelativePath(workspacePath, actualProjectPath).NormalizePathToUnix();
var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, actualProjectPath, _logger);
var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);
var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);

// Determine if there were unrestored MSBuildSdks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace NuGetUpdater.Core.Discover;

internal static class PackagesConfigDiscovery
{
public static async Task<PackagesConfigDiscoveryResult?> Discover(string repoRootPath, string workspacePath, string projectPath, ILogger logger)
public static async Task<PackagesConfigDiscoveryResult?> Discover(string repoRootPath, string workspacePath, string projectPath, ExperimentsManager experimentsManager, ILogger logger)
{
var projectDirectory = Path.GetDirectoryName(projectPath)!;
var additionalFiles = ProjectHelper.GetAllAdditionalFilesFromProject(projectPath, ProjectHelper.PathFormat.Full);
Expand All @@ -27,7 +27,7 @@ internal static class PackagesConfigDiscovery
.ToImmutableArray();

// generate `$(TargetFramework)` via MSBuild
var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, projectPath, logger);
var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, projectPath, experimentsManager, logger);

var additionalFilesRelative = additionalFiles.Select(p => Path.GetRelativePath(projectDirectory, p).NormalizePathToUnix()).ToImmutableArray();
return new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverAsync(s
{
if (experimentsManager.UseDirectDiscovery)
{
return await DiscoverWithBinLogAsync(repoRootPath, workspacePath, startingProjectPath, logger);
return await DiscoverWithBinLogAsync(repoRootPath, workspacePath, startingProjectPath, experimentsManager, logger);
}
else
{
return await DiscoverWithTempProjectAsync(repoRootPath, workspacePath, startingProjectPath, logger);
return await DiscoverWithTempProjectAsync(repoRootPath, workspacePath, startingProjectPath, experimentsManager, logger);
}
}

public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithBinLogAsync(string repoRootPath, string workspacePath, string startingProjectPath, ILogger logger)
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithBinLogAsync(string repoRootPath, string workspacePath, string startingProjectPath, ExperimentsManager experimentsManager, ILogger logger)
{
// N.b., there are many paths used in this function. The MSBuild binary log always reports fully qualified paths, so that's what will be used
// throughout until the very end when the appropriate kind of relative path is returned.
Expand Down Expand Up @@ -84,15 +84,15 @@ public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithBin
Dictionary<string, HashSet<string>> additionalFiles = new(PathComparer.Instance);
// projectPath, additionalFiles

var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, startingProjectPath, logger);
var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, startingProjectPath, experimentsManager, logger);
foreach (var tfm in tfms)
{
// create a binlog
var binLogPath = Path.Combine(Path.GetTempPath(), $"msbuild_{Guid.NewGuid():d}.binlog");
try
{
// TODO: once the updater image has all relevant SDKs installed, we won't have to sideline global.json anymore
var (exitCode, stdOut, stdErr) = await MSBuildHelper.SidelineGlobalJsonAsync(startingProjectDirectory, repoRootPath, async () =>
var (exitCode, stdOut, stdErr) = await MSBuildHelper.HandleGlobalJsonAsync(startingProjectDirectory, repoRootPath, experimentsManager, async () =>
{
// the built-in target `GenerateBuildDependencyFile` forces resolution of all NuGet packages, but doesn't invoke a full build
var dependencyDiscoveryTargetsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "DependencyDiscovery.targets");
Expand All @@ -102,10 +102,11 @@ public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithBin
startingProjectPath,
"/t:_DiscoverDependencies",
$"/p:TargetFramework={tfm}",
$"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath};CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
$"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath}",
$"/p:CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
$"/bl:{binLogPath}"
};
var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", args, workingDirectory: startingProjectDirectory);
var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory, experimentsManager);
return (exitCode, stdOut, stdErr);
}, logger, retainMSBuildSdks: true);
MSBuildHelper.ThrowOnUnauthenticatedFeed(stdOut);
Expand Down Expand Up @@ -411,7 +412,7 @@ Dictionary<string, HashSet<string>> topLevelPackagesPerProject
return property.Value;
}

public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithTempProjectAsync(string repoRootPath, string workspacePath, string projectPath, ILogger logger)
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithTempProjectAsync(string repoRootPath, string workspacePath, string projectPath, ExperimentsManager experimentsManager, ILogger logger)
{
// Determine which targets and props files contribute to the build.
var (buildFiles, projectTargetFrameworks) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(repoRootPath, projectPath);
Expand Down Expand Up @@ -476,7 +477,7 @@ public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithTem
dependencies = dependencies
.Select(d => d with { TargetFrameworks = tfms })
.ToImmutableArray();
var transitiveDependencies = await GetTransitiveDependencies(repoRootPath, projectPath, tfms, dependencies, logger);
var transitiveDependencies = await GetTransitiveDependencies(repoRootPath, projectPath, tfms, dependencies, experimentsManager, logger);
ImmutableArray<Dependency> allDependencies = dependencies.Concat(transitiveDependencies).Concat(sdkDependencies)
.OrderBy(d => d.Name)
.ToImmutableArray();
Expand Down Expand Up @@ -514,12 +515,19 @@ public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithTem
return results.ToImmutable();
}

private static async Task<ImmutableArray<Dependency>> GetTransitiveDependencies(string repoRootPath, string projectPath, ImmutableArray<string> tfms, ImmutableArray<Dependency> directDependencies, ILogger logger)
private static async Task<ImmutableArray<Dependency>> GetTransitiveDependencies(
string repoRootPath,
string projectPath,
ImmutableArray<string> tfms,
ImmutableArray<Dependency> directDependencies,
ExperimentsManager experimentsManager,
ILogger logger
)
{
Dictionary<string, Dependency> transitiveDependencies = new(StringComparer.OrdinalIgnoreCase);
foreach (var tfm in tfms)
{
var tfmDependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, directDependencies, logger);
var tfmDependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, directDependencies, experimentsManager, logger);
foreach (var dependency in tfmDependencies.Where(d => d.IsTransitive))
{
if (!transitiveDependencies.TryGetValue(dependency.Name, out var existingDependency))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ namespace NuGetUpdater.Core;

public record ExperimentsManager
{
public bool InstallDotnetSdks { get; init; } = false;
public bool UseLegacyDependencySolver { get; init; } = false;
public bool UseDirectDiscovery { get; init; } = false;

public Dictionary<string, object> ToDictionary()
{
return new()
{
["nuget_install_dotnet_sdks"] = InstallDotnetSdks,
["nuget_legacy_dependency_solver"] = UseLegacyDependencySolver,
["nuget_use_direct_discovery"] = UseDirectDiscovery,
};
Expand All @@ -22,6 +24,7 @@ public static ExperimentsManager GetExperimentsManager(Dictionary<string, object
{
return new ExperimentsManager()
{
InstallDotnetSdks = IsEnabled(experiments, "nuget_install_dotnet_sdks"),
UseLegacyDependencySolver = IsEnabled(experiments, "nuget_legacy_dependency_solver"),
UseDirectDiscovery = IsEnabled(experiments, "nuget_use_direct_discovery"),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ internal static class LockFileUpdater
public static async Task UpdateLockFileAsync(
string repoRootPath,
string projectPath,
ExperimentsManager experimentsManager,
ILogger logger)
{
var projectDirectory = Path.GetDirectoryName(projectPath)!;
await MSBuildHelper.SidelineGlobalJsonAsync(projectDirectory, repoRootPath, async () =>
await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
{
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["restore", "--force-evaluate", projectPath], workingDirectory: projectDirectory);
var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", "--force-evaluate", projectPath], projectDirectory, experimentsManager);
if (exitCode != 0)
{
logger.Error($" Lock file update failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
Expand Down
Loading

0 comments on commit be2d4c8

Please sign in to comment.