Skip to content

Commit 6bfaa90

Browse files
committed
C#: Avoid explicitly restoring the projects in the restored solution files.
1 parent aaaf6f8 commit 6bfaa90

File tree

5 files changed

+82
-28
lines changed

5 files changed

+82
-28
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
103103
progressMonitor.MissingNuGet();
104104
}
105105

106-
Restore(solutions);
107-
Restore(allProjects);
106+
var restoredProjects = RestoreSolutions(solutions);
107+
var projects = allProjects.Except(restoredProjects);
108+
RestoreProjects(projects);
108109
DownloadMissingPackages(allFiles);
109110
}
110111

@@ -351,16 +352,26 @@ private void AnalyseProject(FileInfo project)
351352

352353
}
353354

354-
private bool Restore(string target, string? pathToNugetConfig = null) =>
355-
dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
355+
private bool RestoreProject(string project, string? pathToNugetConfig = null) =>
356+
dotnet.RestoreProjectToDirectory(project, packageDirectory.DirInfo.FullName, pathToNugetConfig);
356357

357-
private void Restore(IEnumerable<string> targets, string? pathToNugetConfig = null)
358-
{
359-
foreach (var target in targets)
360-
{
361-
Restore(target, pathToNugetConfig);
362-
}
363-
}
358+
private bool RestoreSolution(string solution, out IList<string> projects) =>
359+
dotnet.RestoreSolutionToDirectory(solution, packageDirectory.DirInfo.FullName, out projects);
360+
361+
/// <summary>
362+
/// Executes `dotnet restore` on all solution files in solutions.
363+
/// Returns a list of projects that are up to date with respect to restore.
364+
/// </summary>
365+
/// <param name="solutions">A list of paths to solution files.</param>
366+
private IEnumerable<string> RestoreSolutions(IEnumerable<string> solutions) =>
367+
solutions.SelectMany(solution =>
368+
{
369+
RestoreSolution(solution, out var restoredProjects);
370+
return restoredProjects;
371+
});
372+
373+
private void RestoreProjects(IEnumerable<string> projects) =>
374+
Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, project => RestoreProject(project));
364375

365376
private void DownloadMissingPackages(List<FileInfo> allFiles)
366377
{
@@ -401,10 +412,9 @@ private void DownloadMissingPackages(List<FileInfo> allFiles)
401412
continue;
402413
}
403414

404-
success = Restore(tempDir.DirInfo.FullName, nugetConfig);
415+
success = RestoreProject(tempDir.DirInfo.FullName, nugetConfig);
405416

406417
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
407-
408418
if (!success)
409419
{
410420
progressMonitor.FailedToRestoreNugetPackage(package);

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.IO;
5+
using System.Linq;
6+
using System.Text.RegularExpressions;
57
using Semmle.Util;
68

79
namespace Semmle.Extraction.CSharp.DependencyFetching
810
{
911
/// <summary>
1012
/// Utilities to run the "dotnet" command.
1113
/// </summary>
12-
internal class DotNet : IDotNet
14+
internal partial class DotNet : IDotNet
1315
{
1416
private readonly ProgressMonitor progressMonitor;
1517
private readonly string dotnet;
@@ -41,7 +43,7 @@ private ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardO
4143
private bool RunCommand(string args)
4244
{
4345
progressMonitor.RunningProcess($"{dotnet} {args}");
44-
using var proc = Process.Start(this.MakeDotnetStartInfo(args, redirectStandardOutput: false));
46+
using var proc = Process.Start(MakeDotnetStartInfo(args, redirectStandardOutput: false));
4547
proc?.WaitForExit();
4648
var exitCode = proc?.ExitCode ?? -1;
4749
if (exitCode != 0)
@@ -52,14 +54,51 @@ private bool RunCommand(string args)
5254
return true;
5355
}
5456

55-
public bool RestoreToDirectory(string projectOrSolutionFile, string packageDirectory, string? pathToNugetConfig = null)
57+
private bool RunCommand(string args, out IList<string> output)
5658
{
57-
var args = $"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true";
59+
progressMonitor.RunningProcess($"{dotnet} {args}");
60+
var pi = MakeDotnetStartInfo(args, redirectStandardOutput: true);
61+
var exitCode = pi.ReadOutput(out output);
62+
if (exitCode != 0)
63+
{
64+
progressMonitor.CommandFailed(dotnet, args, exitCode);
65+
return false;
66+
}
67+
return true;
68+
}
69+
70+
private static string GetRestoreArgs(string projectOrSolutionFile, string packageDirectory) =>
71+
$"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true";
72+
73+
public bool RestoreProjectToDirectory(string projectFile, string packageDirectory, string? pathToNugetConfig = null)
74+
{
75+
var args = GetRestoreArgs(projectFile, packageDirectory);
5876
if (pathToNugetConfig != null)
77+
{
5978
args += $" --configfile \"{pathToNugetConfig}\"";
79+
}
6080
return RunCommand(args);
6181
}
6282

83+
public bool RestoreSolutionToDirectory(string solutionFile, string packageDirectory, out IList<string> projects)
84+
{
85+
var args = GetRestoreArgs(solutionFile, packageDirectory);
86+
args += " --verbosity normal";
87+
if (RunCommand(args, out var output))
88+
{
89+
var regex = RestoreProjectRegex();
90+
projects = output
91+
.Select(line => regex.Match(line))
92+
.Where(match => match.Success)
93+
.Select(match => match.Groups[1].Value)
94+
.ToList();
95+
return true;
96+
}
97+
98+
projects = new List<string>();
99+
return false;
100+
}
101+
63102
public bool New(string folder)
64103
{
65104
var args = $"new console --no-restore --output \"{folder}\"";
@@ -78,22 +117,21 @@ public bool AddPackage(string folder, string package)
78117

79118
private IList<string> GetListed(string args, string artifact)
80119
{
81-
progressMonitor.RunningProcess($"{dotnet} {args}");
82-
var pi = this.MakeDotnetStartInfo(args, redirectStandardOutput: true);
83-
var exitCode = pi.ReadOutput(out var artifacts);
84-
if (exitCode != 0)
120+
if (RunCommand(args, out var artifacts))
85121
{
86-
progressMonitor.CommandFailed(dotnet, args, exitCode);
87-
return new List<string>();
122+
progressMonitor.LogInfo($"Found {artifact}s: {string.Join("\n", artifacts)}");
123+
return artifacts;
88124
}
89-
progressMonitor.LogInfo($"Found {artifact}s: {string.Join("\n", artifacts)}");
90-
return artifacts;
125+
return new List<string>();
91126
}
92127

93128
public bool Exec(string execArgs)
94129
{
95130
var args = $"exec {execArgs}";
96131
return RunCommand(args);
97132
}
133+
134+
[GeneratedRegex("Restored\\s+(.+\\.csproj)", RegexOptions.Compiled)]
135+
private static partial Regex RestoreProjectRegex();
98136
}
99137
}

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNet.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
44
{
55
internal interface IDotNet
66
{
7-
bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null);
7+
bool RestoreProjectToDirectory(string project, string directory, string? pathToNugetConfig = null);
8+
bool RestoreSolutionToDirectory(string solution, string directory, out IList<string> projects);
89
bool New(string folder);
910
bool AddPackage(string folder, string package);
1011
IList<string> GetListedRuntimes();

csharp/extractor/Semmle.Extraction.Tests/FileContent.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Xunit;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using Semmle.Util.Logging;
54
using Semmle.Extraction.CSharp.DependencyFetching;
65

csharp/extractor/Semmle.Extraction.Tests/Runtime.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ public DotNetStub(IList<string> runtimes, IList<string> sdks)
1818

1919
public bool New(string folder) => true;
2020

21-
public bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null) => true;
21+
public bool RestoreProjectToDirectory(string project, string directory, string? pathToNugetConfig = null) => true;
22+
23+
public bool RestoreSolutionToDirectory(string solution, string directory, out IList<string> projects)
24+
{
25+
projects = new List<string>();
26+
return true;
27+
}
2228

2329
public IList<string> GetListedRuntimes() => runtimes;
2430

0 commit comments

Comments
 (0)