diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyDirectory.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyDirectory.cs new file mode 100644 index 00000000000..8efcb5d7d78 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyDirectory.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using Semmle.Util; +using Semmle.Util.Logging; + +namespace Semmle.Extraction.CSharp.DependencyFetching +{ + /// + /// A directory used for storing fetched dependencies. + /// When a specific directory is set via the dependency directory extractor option, + /// we store dependencies in that directory for caching purposes. + /// Otherwise, we create a temporary directory that is deleted upon disposal. + /// + public sealed class DependencyDirectory : IDisposable + { + private readonly string userReportedDirectoryPurpose; + private readonly ILogger logger; + private readonly bool attemptCleanup; + + public DirectoryInfo DirInfo { get; } + + public DependencyDirectory(string subfolderName, string userReportedDirectoryPurpose, ILogger logger) + { + this.logger = logger; + this.userReportedDirectoryPurpose = userReportedDirectoryPurpose; + + string path; + if (EnvironmentVariables.GetBuildlessDependencyDir() is string dir) + { + path = dir; + attemptCleanup = false; + } + else + { + path = FileUtils.GetTemporaryWorkingDirectory(out _); + attemptCleanup = true; + } + DirInfo = new DirectoryInfo(Path.Join(path, subfolderName)); + DirInfo.Create(); + } + + public void Dispose() + { + if (!attemptCleanup) + { + logger.LogInfo($"Keeping {userReportedDirectoryPurpose} directory {DirInfo.FullName} for possible caching purposes."); + return; + } + + try + { + DirInfo.Delete(true); + } + catch (Exception exc) + { + logger.LogInfo($"Couldn't delete {userReportedDirectoryPurpose} directory {exc.Message}"); + } + } + + public override string ToString() => DirInfo.FullName.ToString(); + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs index c77daa8899c..10d89b1e009 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs @@ -25,15 +25,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// /// The computed packages directory. - /// This will be in the Temp location + /// This will be in the Cached or Temp location /// so as to not trample the source tree. /// - private readonly TemporaryDirectory packageDirectory; + private readonly DependencyDirectory packageDirectory; /// /// Create the package manager for a specified source tree. /// - public NugetExeWrapper(FileProvider fileProvider, TemporaryDirectory packageDirectory, Semmle.Util.Logging.ILogger logger) + public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger) { this.fileProvider = fileProvider; this.packageDirectory = packageDirectory; diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index e0e1bc649fa..e2e548a46a9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -24,12 +24,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private readonly IDotNet dotnet; private readonly DependabotProxy? dependabotProxy; private readonly IDiagnosticsWriter diagnosticsWriter; - private readonly TemporaryDirectory legacyPackageDirectory; - private readonly TemporaryDirectory missingPackageDirectory; + private readonly DependencyDirectory legacyPackageDirectory; + private readonly DependencyDirectory missingPackageDirectory; private readonly ILogger logger; private readonly ICompilationInfoContainer compilationInfoContainer; - public TemporaryDirectory PackageDirectory { get; } + public DependencyDirectory PackageDirectory { get; } public NugetPackageRestorer( FileProvider fileProvider, @@ -48,9 +48,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching this.logger = logger; this.compilationInfoContainer = compilationInfoContainer; - PackageDirectory = new TemporaryDirectory(ComputeTempDirectoryPath("packages"), "package", logger); - legacyPackageDirectory = new TemporaryDirectory(ComputeTempDirectoryPath("legacypackages"), "legacy package", logger); - missingPackageDirectory = new TemporaryDirectory(ComputeTempDirectoryPath("missingpackages"), "missing package", logger); + PackageDirectory = new DependencyDirectory("packages", "package", logger); + legacyPackageDirectory = new DependencyDirectory("legacypackages", "legacy package", logger); + missingPackageDirectory = new DependencyDirectory("missingpackages", "missing package", logger); } public string? TryRestore(string package) diff --git a/csharp/extractor/Semmle.Util/EnvironmentVariables.cs b/csharp/extractor/Semmle.Util/EnvironmentVariables.cs index edce64a53fe..9f1519653de 100644 --- a/csharp/extractor/Semmle.Util/EnvironmentVariables.cs +++ b/csharp/extractor/Semmle.Util/EnvironmentVariables.cs @@ -76,5 +76,15 @@ namespace Semmle.Util { return Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OVERLAY_BASE_METADATA_OUT"); } + + /// + /// If set, returns the directory where buildless dependencies should be stored. + /// This is needed for caching dependencies. + /// + /// + public static string? GetBuildlessDependencyDir() + { + return Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_BUILDLESS_DEPENDENCY_DIR"); + } } }