mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Move Nuget restore logic from DependencyManager to dedicated class
This commit is contained in:
@@ -12,14 +12,26 @@ using Semmle.Util.Logging;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
public interface ICompilationInfoContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// List of `(key, value)` tuples, that are stored in the DB for telemetry purposes.
|
||||
/// </summary>
|
||||
List<(string, string)> CompilationInfos { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main implementation of the build analysis.
|
||||
/// </summary>
|
||||
public sealed partial class DependencyManager : IDisposable
|
||||
public sealed partial class DependencyManager : IDisposable, ICompilationInfoContainer
|
||||
{
|
||||
private readonly AssemblyCache assemblyCache;
|
||||
private readonly ILogger logger;
|
||||
private readonly IDiagnosticsWriter diagnosticsWriter;
|
||||
private readonly NugetPackageRestorer nugetPackageRestorer;
|
||||
private readonly IDotNet dotnet;
|
||||
private readonly FileContent fileContent;
|
||||
private readonly FileProvider fileProvider;
|
||||
|
||||
// Only used as a set, but ConcurrentDictionary is the only concurrent set in .NET.
|
||||
private readonly IDictionary<string, bool> usedReferences = new ConcurrentDictionary<string, bool>();
|
||||
@@ -30,18 +42,14 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private int conflictedReferences = 0;
|
||||
private readonly DirectoryInfo sourceDir;
|
||||
private string? dotnetPath;
|
||||
private readonly IDotNet dotnet;
|
||||
private readonly FileContent fileContent;
|
||||
private readonly TemporaryDirectory packageDirectory;
|
||||
private readonly TemporaryDirectory legacyPackageDirectory;
|
||||
private readonly TemporaryDirectory missingPackageDirectory;
|
||||
|
||||
private readonly TemporaryDirectory tempWorkingDirectory;
|
||||
private readonly FileProvider fileProvider;
|
||||
private readonly bool cleanupTempWorkingDirectory;
|
||||
|
||||
private readonly Lazy<Runtime> runtimeLazy;
|
||||
private Runtime Runtime => runtimeLazy.Value;
|
||||
private readonly int threads = EnvironmentVariables.GetDefaultNumberOfThreads();
|
||||
|
||||
internal static readonly int Threads = EnvironmentVariables.GetDefaultNumberOfThreads();
|
||||
|
||||
/// <summary>
|
||||
/// Performs C# dependency fetching.
|
||||
@@ -74,10 +82,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
$"dependency-manager-{DateTime.UtcNow:yyyyMMddHHmm}-{Environment.ProcessId}.jsonc"));
|
||||
this.sourceDir = new DirectoryInfo(srcDir);
|
||||
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "packages"));
|
||||
legacyPackageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "legacypackages"));
|
||||
missingPackageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "missingpackages"));
|
||||
|
||||
tempWorkingDirectory = new TemporaryDirectory(FileUtils.GetTemporaryWorkingDirectory(out cleanupTempWorkingDirectory));
|
||||
|
||||
this.fileProvider = new FileProvider(sourceDir, logger);
|
||||
@@ -112,8 +116,10 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
throw;
|
||||
}
|
||||
|
||||
nugetPackageRestorer = new NugetPackageRestorer(fileProvider, fileContent, dotnet, diagnosticsWriter, logger, this);
|
||||
|
||||
var dllLocations = fileProvider.Dlls.Select(x => new AssemblyLookupLocation(x)).ToHashSet();
|
||||
RestoreNugetPackages(dllLocations);
|
||||
dllLocations.UnionWith(nugetPackageRestorer.Restore());
|
||||
// Find DLLs in the .Net / Asp.Net Framework
|
||||
// This needs to come after the nuget restore, because the nuget restore might fetch the .NET Core/Framework reference assemblies.
|
||||
var frameworkLocations = AddFrameworkDlls(dllLocations);
|
||||
@@ -221,11 +227,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
private void RemoveNugetAnalyzerReferences()
|
||||
{
|
||||
var packageFolder = packageDirectory.DirInfo.FullName.ToLowerInvariant();
|
||||
if (packageFolder == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var packageFolder = nugetPackageRestorer.PackageDirectory.DirInfo.FullName.ToLowerInvariant();
|
||||
|
||||
foreach (var filename in usedReferences.Keys)
|
||||
{
|
||||
@@ -299,7 +301,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
var packagesInPrioOrder = FrameworkPackageNames.NetFrameworks;
|
||||
|
||||
var frameworkPaths = packagesInPrioOrder
|
||||
.Select((s, index) => (Index: index, Path: GetPackageDirectory(s, packageDirectory)))
|
||||
.Select((s, index) => (Index: index, Path: GetPackageDirectory(s)))
|
||||
.Where(pair => pair.Path is not null)
|
||||
.ToArray();
|
||||
|
||||
@@ -330,11 +332,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
if (runtimeLocation is null)
|
||||
{
|
||||
logger.LogInfo("No .NET Desktop Runtime location found. Attempting to restore the .NET Framework reference assemblies manually.");
|
||||
|
||||
if (TryRestorePackageManually(FrameworkPackageNames.LatestNetFrameworkReferenceAssemblies))
|
||||
{
|
||||
runtimeLocation = GetPackageDirectory(FrameworkPackageNames.LatestNetFrameworkReferenceAssemblies, missingPackageDirectory);
|
||||
}
|
||||
runtimeLocation = nugetPackageRestorer.TryRestoreLatestNetFrameworkReferenceAssemblies();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,12 +352,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
private void RemoveNugetPackageReference(string packagePrefix, ISet<AssemblyLookupLocation> dllLocations)
|
||||
{
|
||||
var packageFolder = packageDirectory.DirInfo.FullName.ToLowerInvariant();
|
||||
if (packageFolder == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var packageFolder = nugetPackageRestorer.PackageDirectory.DirInfo.FullName.ToLowerInvariant();
|
||||
var packagePathPrefix = Path.Combine(packageFolder, packagePrefix.ToLowerInvariant());
|
||||
var toRemove = dllLocations.Where(s => s.Path.StartsWith(packagePathPrefix, StringComparison.InvariantCultureIgnoreCase));
|
||||
foreach (var path in toRemove)
|
||||
@@ -382,7 +375,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
|
||||
// First try to find ASP.NET Core assemblies in the NuGet packages
|
||||
if (GetPackageDirectory(FrameworkPackageNames.AspNetCoreFramework, packageDirectory) is string aspNetCorePackage)
|
||||
if (GetPackageDirectory(FrameworkPackageNames.AspNetCoreFramework) is string aspNetCorePackage)
|
||||
{
|
||||
SelectNewestFrameworkPath(aspNetCorePackage, "ASP.NET Core", dllLocations, frameworkLocations);
|
||||
return;
|
||||
@@ -398,15 +391,20 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
private void AddMicrosoftWindowsDesktopDlls(ISet<AssemblyLookupLocation> dllLocations, ISet<string> frameworkLocations)
|
||||
{
|
||||
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework, packageDirectory) is string windowsDesktopApp)
|
||||
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework) is string windowsDesktopApp)
|
||||
{
|
||||
SelectNewestFrameworkPath(windowsDesktopApp, "Windows Desktop App", dllLocations, frameworkLocations);
|
||||
}
|
||||
}
|
||||
|
||||
private string? GetPackageDirectory(string packagePrefix, TemporaryDirectory root)
|
||||
private string? GetPackageDirectory(string packagePrefix)
|
||||
{
|
||||
return new DirectoryInfo(root.DirInfo.FullName)
|
||||
return GetPackageDirectory(packagePrefix, nugetPackageRestorer.PackageDirectory.DirInfo);
|
||||
}
|
||||
|
||||
internal static string? GetPackageDirectory(string packagePrefix, DirectoryInfo root)
|
||||
{
|
||||
return new DirectoryInfo(root.FullName)
|
||||
.EnumerateDirectories(packagePrefix + "*", new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, RecurseSubdirectories = false })
|
||||
.FirstOrDefault()?
|
||||
.FullName;
|
||||
@@ -495,22 +493,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a unique temp directory for the packages associated
|
||||
/// with this source tree. Use a SHA1 of the directory name.
|
||||
/// </summary>
|
||||
/// <returns>The full path of the temp directory.</returns>
|
||||
private static string ComputeTempDirectory(string srcDir, string subfolderName)
|
||||
{
|
||||
var bytes = Encoding.Unicode.GetBytes(srcDir);
|
||||
var sha = SHA1.HashData(bytes);
|
||||
var sb = new StringBuilder();
|
||||
foreach (var b in sha.Take(8))
|
||||
sb.AppendFormat("{0:x2}", b);
|
||||
|
||||
return Path.Combine(FileUtils.GetTemporaryWorkingDirectory(out var _), sb.ToString(), subfolderName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary directory with the given subfolder name.
|
||||
/// The created directory might be inside the repo folder, and it is deleted when the object is disposed.
|
||||
@@ -634,7 +616,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
private void AnalyseSolutions(IEnumerable<string> solutions)
|
||||
{
|
||||
Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = threads }, solutionFile =>
|
||||
Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = Threads }, solutionFile =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -683,7 +665,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose(TemporaryDirectory? dir, string name)
|
||||
public static void DisposeTempDirectory(TemporaryDirectory? dir, string name, ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -697,15 +679,13 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(packageDirectory, "package");
|
||||
Dispose(legacyPackageDirectory, "legacy package");
|
||||
Dispose(missingPackageDirectory, "missing package");
|
||||
if (cleanupTempWorkingDirectory)
|
||||
{
|
||||
Dispose(tempWorkingDirectory, "temporary working");
|
||||
DisposeTempDirectory(tempWorkingDirectory, "temporary working", logger);
|
||||
}
|
||||
|
||||
diagnosticsWriter?.Dispose();
|
||||
nugetPackageRestorer?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private static readonly HashSet<string> binaryFileExtensions = [".dll", ".exe"]; // TODO: add more binary file extensions.
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly Lazy<FileInfo[]> all;
|
||||
private readonly FileInfo[] all;
|
||||
private readonly Lazy<FileInfo[]> allNonBinary;
|
||||
private readonly Lazy<string[]> smallNonBinary;
|
||||
private readonly Lazy<string[]> sources;
|
||||
@@ -29,8 +29,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
SourceDir = sourceDir;
|
||||
this.logger = logger;
|
||||
|
||||
all = new Lazy<FileInfo[]>(GetAllFiles);
|
||||
allNonBinary = new Lazy<FileInfo[]>(() => all.Value.Where(f => !binaryFileExtensions.Contains(f.Extension.ToLowerInvariant())).ToArray());
|
||||
all = GetAllFiles();
|
||||
allNonBinary = new Lazy<FileInfo[]>(() => all.Where(f => !binaryFileExtensions.Contains(f.Extension.ToLowerInvariant())).ToArray());
|
||||
smallNonBinary = new Lazy<string[]>(() =>
|
||||
{
|
||||
var ret = SelectSmallFiles(allNonBinary.Value).SelectFileNames().ToArray();
|
||||
@@ -45,7 +45,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
globalJsons = new Lazy<string[]>(() => allNonBinary.Value.SelectFileNamesByName("global.json").ToArray());
|
||||
razorViews = new Lazy<string[]>(() => SelectTextFileNamesByExtension("razor view", ".cshtml", ".razor"));
|
||||
|
||||
rootNugetConfig = new Lazy<string?>(() => all.Value.SelectRootFiles(SourceDir).SelectFileNamesByName("nuget.config").FirstOrDefault());
|
||||
rootNugetConfig = new Lazy<string?>(() => all.SelectRootFiles(SourceDir).SelectFileNamesByName("nuget.config").FirstOrDefault());
|
||||
}
|
||||
|
||||
private string[] SelectTextFileNamesByExtension(string filetype, params string[] extensions)
|
||||
@@ -57,7 +57,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
private string[] SelectBinaryFileNamesByExtension(string filetype, params string[] extensions)
|
||||
{
|
||||
var ret = all.Value.SelectFileNamesByExtension(extensions).ToArray();
|
||||
var ret = all.SelectFileNamesByExtension(extensions).ToArray();
|
||||
logger.LogInfo($"Found {ret.Length} {filetype} files in {SourceDir}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ using Semmle.Util;
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
/// <summary>
|
||||
/// Manage the downloading of NuGet packages.
|
||||
/// Manage the downloading of NuGet packages with nuget.exe.
|
||||
/// Locates packages in a source tree and downloads all of the
|
||||
/// referenced assemblies to a temp folder.
|
||||
/// </summary>
|
||||
internal class NugetPackages : IDisposable
|
||||
internal class NugetExeWrapper : IDisposable
|
||||
{
|
||||
private readonly string? nugetExe;
|
||||
private readonly Util.Logging.ILogger logger;
|
||||
@@ -37,7 +37,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// <summary>
|
||||
/// Create the package manager for a specified source tree.
|
||||
/// </summary>
|
||||
public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory, Util.Logging.ILogger logger)
|
||||
public NugetExeWrapper(string sourceDir, TemporaryDirectory packageDirectory, Util.Logging.ILogger logger)
|
||||
{
|
||||
this.packageDirectory = packageDirectory;
|
||||
this.logger = logger;
|
||||
@@ -243,7 +243,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private void AddDefaultPackageSource(string nugetConfig)
|
||||
{
|
||||
logger.LogInfo("Adding default package source...");
|
||||
RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {DependencyManager.PublicNugetFeed} -ConfigFile \"{nugetConfig}\"", out var _);
|
||||
RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {NugetPackageRestorer.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out var _);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -3,36 +3,84 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Semmle.Util;
|
||||
using Semmle.Util.Logging;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
public sealed partial class DependencyManager
|
||||
internal sealed partial class NugetPackageRestorer : IDisposable
|
||||
{
|
||||
private void RestoreNugetPackages(HashSet<AssemblyLookupLocation> dllLocations)
|
||||
internal const string PublicNugetOrgFeed = "https://api.nuget.org/v3/index.json";
|
||||
|
||||
private readonly FileProvider fileProvider;
|
||||
private readonly FileContent fileContent;
|
||||
private readonly IDotNet dotnet;
|
||||
private readonly IDiagnosticsWriter diagnosticsWriter;
|
||||
private readonly TemporaryDirectory legacyPackageDirectory;
|
||||
private readonly TemporaryDirectory missingPackageDirectory;
|
||||
private readonly ILogger logger;
|
||||
private readonly ICompilationInfoContainer compilationInfoContainer;
|
||||
|
||||
public TemporaryDirectory PackageDirectory { get; }
|
||||
|
||||
public NugetPackageRestorer(
|
||||
FileProvider fileProvider,
|
||||
FileContent fileContent,
|
||||
IDotNet dotnet,
|
||||
IDiagnosticsWriter diagnosticsWriter,
|
||||
ILogger logger,
|
||||
ICompilationInfoContainer compilationInfoContainer)
|
||||
{
|
||||
this.fileProvider = fileProvider;
|
||||
this.fileContent = fileContent;
|
||||
this.dotnet = dotnet;
|
||||
this.diagnosticsWriter = diagnosticsWriter;
|
||||
this.logger = logger;
|
||||
this.compilationInfoContainer = compilationInfoContainer;
|
||||
|
||||
PackageDirectory = new TemporaryDirectory(ComputeTempDirectoryPath(fileProvider.SourceDir.FullName, "packages"));
|
||||
legacyPackageDirectory = new TemporaryDirectory(ComputeTempDirectoryPath(fileProvider.SourceDir.FullName, "legacypackages"));
|
||||
missingPackageDirectory = new TemporaryDirectory(ComputeTempDirectoryPath(fileProvider.SourceDir.FullName, "missingpackages"));
|
||||
}
|
||||
|
||||
public string? TryRestoreLatestNetFrameworkReferenceAssemblies()
|
||||
{
|
||||
if (TryRestorePackageManually(FrameworkPackageNames.LatestNetFrameworkReferenceAssemblies))
|
||||
{
|
||||
return DependencyManager.GetPackageDirectory(FrameworkPackageNames.LatestNetFrameworkReferenceAssemblies, missingPackageDirectory.DirInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public HashSet<AssemblyLookupLocation> Restore()
|
||||
{
|
||||
var assemblyLookupLocations = new HashSet<AssemblyLookupLocation>();
|
||||
var checkNugetFeedResponsiveness = EnvironmentVariables.GetBoolean(EnvironmentVariableNames.CheckNugetFeedResponsiveness);
|
||||
try
|
||||
{
|
||||
if (checkNugetFeedResponsiveness && !CheckFeeds())
|
||||
{
|
||||
// todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too.
|
||||
DownloadMissingPackagesFromSpecificFeeds(dllLocations);
|
||||
return;
|
||||
var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds();
|
||||
return unresponsiveMissingPackageLocation is null
|
||||
? []
|
||||
: [unresponsiveMissingPackageLocation];
|
||||
}
|
||||
|
||||
using (var nuget = new NugetPackages(sourceDir.FullName, legacyPackageDirectory, logger))
|
||||
using (var nuget = new NugetExeWrapper(fileProvider.SourceDir.FullName, legacyPackageDirectory, logger))
|
||||
{
|
||||
var count = nuget.InstallPackages();
|
||||
|
||||
if (nuget.PackageCount > 0)
|
||||
{
|
||||
CompilationInfos.Add(("packages.config files", nuget.PackageCount.ToString()));
|
||||
CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("packages.config files", nuget.PackageCount.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +105,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
|
||||
nugetPackageDllPaths.ExceptWith(excludedPaths);
|
||||
dllLocations.UnionWith(nugetPackageDllPaths.Select(p => new AssemblyLookupLocation(p)));
|
||||
assemblyLookupLocations.UnionWith(nugetPackageDllPaths.Select(p => new AssemblyLookupLocation(p)));
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
@@ -72,31 +120,30 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
var paths = dependencies
|
||||
.Paths
|
||||
.Select(d => Path.Combine(packageDirectory.DirInfo.FullName, d))
|
||||
.Select(d => Path.Combine(PackageDirectory.DirInfo.FullName, d))
|
||||
.ToList();
|
||||
dllLocations.UnionWith(paths.Select(p => new AssemblyLookupLocation(p)));
|
||||
assemblyLookupLocations.UnionWith(paths.Select(p => new AssemblyLookupLocation(p)));
|
||||
|
||||
LogAllUnusedPackages(dependencies);
|
||||
|
||||
if (checkNugetFeedResponsiveness)
|
||||
{
|
||||
DownloadMissingPackagesFromSpecificFeeds(dllLocations);
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadMissingPackages(dllLocations);
|
||||
}
|
||||
}
|
||||
var missingPackageLocation = checkNugetFeedResponsiveness
|
||||
? DownloadMissingPackagesFromSpecificFeeds()
|
||||
: DownloadMissingPackages();
|
||||
|
||||
internal const string PublicNugetFeed = "https://api.nuget.org/v3/index.json";
|
||||
if (missingPackageLocation is not null)
|
||||
{
|
||||
assemblyLookupLocations.Add(missingPackageLocation);
|
||||
}
|
||||
return assemblyLookupLocations;
|
||||
}
|
||||
|
||||
private List<string> GetReachableFallbackNugetFeeds()
|
||||
{
|
||||
var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet();
|
||||
if (fallbackFeeds.Count == 0)
|
||||
{
|
||||
fallbackFeeds.Add(PublicNugetFeed);
|
||||
logger.LogInfo($"No fallback Nuget feeds specified. Using default feed: {PublicNugetFeed}");
|
||||
fallbackFeeds.Add(PublicNugetOrgFeed);
|
||||
logger.LogInfo($"No fallback Nuget feeds specified. Using default feed: {PublicNugetOrgFeed}");
|
||||
}
|
||||
|
||||
logger.LogInfo($"Checking fallback Nuget feed reachability on feeds: {string.Join(", ", fallbackFeeds.OrderBy(f => f))}");
|
||||
@@ -130,7 +177,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
var projects = fileProvider.Solutions.SelectMany(solution =>
|
||||
{
|
||||
logger.LogInfo($"Restoring solution {solution}...");
|
||||
var res = dotnet.Restore(new(solution, packageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true));
|
||||
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true));
|
||||
if (res.Success)
|
||||
{
|
||||
successCount++;
|
||||
@@ -143,9 +190,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
return res.RestoredProjects;
|
||||
}).ToList();
|
||||
assets = assetFiles;
|
||||
CompilationInfos.Add(("Successfully restored solution files", successCount.ToString()));
|
||||
CompilationInfos.Add(("Failed solution restore with package source error", nugetSourceFailures.ToString()));
|
||||
CompilationInfos.Add(("Restored projects through solution files", projects.Count.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Successfully restored solution files", successCount.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Failed solution restore with package source error", nugetSourceFailures.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Restored projects through solution files", projects.Count.ToString()));
|
||||
return projects;
|
||||
}
|
||||
|
||||
@@ -161,10 +208,10 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
var nugetSourceFailures = 0;
|
||||
var assetFiles = new List<string>();
|
||||
var sync = new object();
|
||||
Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = threads }, project =>
|
||||
Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = DependencyManager.Threads }, project =>
|
||||
{
|
||||
logger.LogInfo($"Restoring project {project}...");
|
||||
var res = dotnet.Restore(new(project, packageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true));
|
||||
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true));
|
||||
lock (sync)
|
||||
{
|
||||
if (res.Success)
|
||||
@@ -179,26 +226,25 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
});
|
||||
assets = assetFiles;
|
||||
CompilationInfos.Add(("Successfully restored project files", successCount.ToString()));
|
||||
CompilationInfos.Add(("Failed project restore with package source error", nugetSourceFailures.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Successfully restored project files", successCount.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Failed project restore with package source error", nugetSourceFailures.ToString()));
|
||||
}
|
||||
|
||||
private void DownloadMissingPackagesFromSpecificFeeds(HashSet<AssemblyLookupLocation> dllLocations)
|
||||
private AssemblyLookupLocation? DownloadMissingPackagesFromSpecificFeeds()
|
||||
{
|
||||
var reachableFallbackFeeds = GetReachableFallbackNugetFeeds();
|
||||
if (reachableFallbackFeeds.Count > 0)
|
||||
{
|
||||
DownloadMissingPackages(dllLocations, fallbackNugetFeeds: reachableFallbackFeeds);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Skipping download of missing packages from specific feeds as no fallback Nuget feeds are reachable.");
|
||||
return DownloadMissingPackages(fallbackNugetFeeds: reachableFallbackFeeds);
|
||||
}
|
||||
|
||||
logger.LogWarning("Skipping download of missing packages from specific feeds as no fallback Nuget feeds are reachable.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private void DownloadMissingPackages(HashSet<AssemblyLookupLocation> dllLocations, IEnumerable<string>? fallbackNugetFeeds = null)
|
||||
private AssemblyLookupLocation? DownloadMissingPackages(IEnumerable<string>? fallbackNugetFeeds = null)
|
||||
{
|
||||
var alreadyDownloadedPackages = GetRestoredPackageDirectoryNames(packageDirectory.DirInfo);
|
||||
var alreadyDownloadedPackages = GetRestoredPackageDirectoryNames(PackageDirectory.DirInfo);
|
||||
var alreadyDownloadedLegacyPackages = GetRestoredLegacyPackageNames();
|
||||
|
||||
var notYetDownloadedPackages = new HashSet<PackageReference>(fileContent.AllPackages);
|
||||
@@ -213,7 +259,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
if (notYetDownloadedPackages.Count == 0)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
var multipleVersions = notYetDownloadedPackages
|
||||
@@ -229,17 +275,17 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
|
||||
logger.LogInfo($"Found {notYetDownloadedPackages.Count} packages that are not yet restored");
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "nugetconfig"));
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectoryPath(fileProvider.SourceDir.FullName, "nugetconfig"));
|
||||
var nugetConfig = fallbackNugetFeeds is null
|
||||
? GetNugetConfig()
|
||||
: CreateFallbackNugetConfig(fallbackNugetFeeds, tempDir.DirInfo.FullName);
|
||||
|
||||
CompilationInfos.Add(("Fallback nuget restore", notYetDownloadedPackages.Count.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Fallback nuget restore", notYetDownloadedPackages.Count.ToString()));
|
||||
|
||||
var successCount = 0;
|
||||
var sync = new object();
|
||||
|
||||
Parallel.ForEach(notYetDownloadedPackages, new ParallelOptions { MaxDegreeOfParallelism = threads }, package =>
|
||||
Parallel.ForEach(notYetDownloadedPackages, new ParallelOptions { MaxDegreeOfParallelism = DependencyManager.Threads }, package =>
|
||||
{
|
||||
var success = TryRestorePackageManually(package.Name, nugetConfig, package.PackageReferenceSource, tryWithoutNugetConfig: fallbackNugetFeeds is null);
|
||||
if (!success)
|
||||
@@ -253,9 +299,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
});
|
||||
|
||||
CompilationInfos.Add(("Successfully ran fallback nuget restore", successCount.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Successfully ran fallback nuget restore", successCount.ToString()));
|
||||
|
||||
dllLocations.Add(missingPackageDirectory.DirInfo.FullName);
|
||||
return missingPackageDirectory.DirInfo.FullName;
|
||||
}
|
||||
|
||||
private string? CreateFallbackNugetConfig(IEnumerable<string> fallbackNugetFeeds, string folderPath)
|
||||
@@ -318,10 +364,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
.ForEach(package => logger.LogInfo($"Unused package: {package}"));
|
||||
}
|
||||
|
||||
|
||||
private ICollection<string> GetAllPackageDirectories()
|
||||
{
|
||||
return new DirectoryInfo(packageDirectory.DirInfo.FullName)
|
||||
return new DirectoryInfo(PackageDirectory.DirInfo.FullName)
|
||||
.EnumerateDirectories("*", new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, RecurseSubdirectories = false })
|
||||
.Select(d => d.Name)
|
||||
.ToList();
|
||||
@@ -366,7 +411,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private bool TryRestorePackageManually(string package, string? nugetConfig = null, PackageReferenceSource packageReferenceSource = PackageReferenceSource.SdkCsProj, bool tryWithoutNugetConfig = true)
|
||||
{
|
||||
logger.LogInfo($"Restoring package {package}...");
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package, "missingpackages_workingdir"));
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectoryPath(package, "missingpackages_workingdir"));
|
||||
var success = dotnet.New(tempDir.DirInfo.FullName);
|
||||
if (!success)
|
||||
{
|
||||
@@ -534,14 +579,14 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
severity: DiagnosticMessage.TspSeverity.Warning
|
||||
));
|
||||
}
|
||||
CompilationInfos.Add(("All Nuget feeds reachable", allFeedsReachable ? "1" : "0"));
|
||||
compilationInfoContainer.CompilationInfos.Add(("All Nuget feeds reachable", allFeedsReachable ? "1" : "0"));
|
||||
|
||||
|
||||
var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet();
|
||||
if (inheritedFeeds.Count > 0)
|
||||
{
|
||||
logger.LogInfo($"Inherited Nuget feeds (not checked for reachability): {string.Join(", ", inheritedFeeds.OrderBy(f => f))}");
|
||||
CompilationInfos.Add(("Inherited Nuget feed count", inheritedFeeds.Count.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Inherited Nuget feed count", inheritedFeeds.Count.ToString()));
|
||||
}
|
||||
|
||||
return allFeedsReachable;
|
||||
@@ -627,5 +672,28 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
[GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex EnabledNugetFeed();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DependencyManager.DisposeTempDirectory(PackageDirectory, "package", logger);
|
||||
DependencyManager.DisposeTempDirectory(legacyPackageDirectory, "legacy package", logger);
|
||||
DependencyManager.DisposeTempDirectory(missingPackageDirectory, "missing package", logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a unique temp directory for the packages associated
|
||||
/// with this source tree. Use a SHA1 of the directory name.
|
||||
/// </summary>
|
||||
/// <returns>The full path of the temp directory.</returns>
|
||||
private static string ComputeTempDirectoryPath(string srcDir, string subfolderName)
|
||||
{
|
||||
var bytes = Encoding.Unicode.GetBytes(srcDir);
|
||||
var sha = SHA1.HashData(bytes);
|
||||
var sb = new StringBuilder();
|
||||
foreach (var b in sha.Take(8))
|
||||
sb.AppendFormat("{0:x2}", b);
|
||||
|
||||
return Path.Combine(FileUtils.GetTemporaryWorkingDirectory(out var _), sb.ToString(), subfolderName);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user