1212
1313namespace Semmle . Extraction . CSharp . DependencyFetching
1414{
15+ public interface ICompilationInfoContainer
16+ {
17+ /// <summary>
18+ /// List of `(key, value)` tuples, that are stored in the DB for telemetry purposes.
19+ /// </summary>
20+ List < ( string , string ) > CompilationInfos { get ; }
21+ }
22+
1523 /// <summary>
1624 /// Main implementation of the build analysis.
1725 /// </summary>
18- public sealed partial class DependencyManager : IDisposable
26+ public sealed partial class DependencyManager : IDisposable , ICompilationInfoContainer
1927 {
2028 private readonly AssemblyCache assemblyCache ;
2129 private readonly ILogger logger ;
2230 private readonly IDiagnosticsWriter diagnosticsWriter ;
31+ private readonly NugetPackageRestorer nugetPackageRestorer ;
32+ private readonly IDotNet dotnet ;
33+ private readonly FileContent fileContent ;
34+ private readonly FileProvider fileProvider ;
2335
2436 // Only used as a set, but ConcurrentDictionary is the only concurrent set in .NET.
2537 private readonly IDictionary < string , bool > usedReferences = new ConcurrentDictionary < string , bool > ( ) ;
@@ -30,17 +42,14 @@ public sealed partial class DependencyManager : IDisposable
3042 private int conflictedReferences = 0 ;
3143 private readonly DirectoryInfo sourceDir ;
3244 private string ? dotnetPath ;
33- private readonly IDotNet dotnet ;
34- private readonly FileContent fileContent ;
35- private readonly TemporaryDirectory packageDirectory ;
36- private readonly TemporaryDirectory legacyPackageDirectory ;
37- private readonly TemporaryDirectory missingPackageDirectory ;
45+
3846 private readonly TemporaryDirectory tempWorkingDirectory ;
3947 private readonly bool cleanupTempWorkingDirectory ;
4048
4149 private readonly Lazy < Runtime > runtimeLazy ;
4250 private Runtime Runtime => runtimeLazy . Value ;
43- private readonly int threads = EnvironmentVariables . GetDefaultNumberOfThreads ( ) ;
51+
52+ internal static readonly int Threads = EnvironmentVariables . GetDefaultNumberOfThreads ( ) ;
4453
4554 /// <summary>
4655 /// Performs C# dependency fetching.
@@ -73,26 +82,15 @@ public DependencyManager(string srcDir, ILogger logger)
7382 $ "dependency-manager-{ DateTime . UtcNow : yyyyMMddHHmm} -{ Environment . ProcessId } .jsonc") ) ;
7483 this . sourceDir = new DirectoryInfo ( srcDir ) ;
7584
76- packageDirectory = new TemporaryDirectory ( ComputeTempDirectory ( sourceDir . FullName , "packages" ) ) ;
77- legacyPackageDirectory = new TemporaryDirectory ( ComputeTempDirectory ( sourceDir . FullName , "legacypackages" ) ) ;
78- missingPackageDirectory = new TemporaryDirectory ( ComputeTempDirectory ( sourceDir . FullName , "missingpackages" ) ) ;
79-
80- tempWorkingDirectory = new TemporaryDirectory ( FileUtils . GetTemporaryWorkingDirectory ( out cleanupTempWorkingDirectory ) ) ;
85+ tempWorkingDirectory = new TemporaryDirectory (
86+ FileUtils . GetTemporaryWorkingDirectory ( out cleanupTempWorkingDirectory ) ,
87+ "temporary working" ,
88+ logger ) ;
8189
82- logger . LogInfo ( $ "Finding files in { srcDir } ...") ;
83-
84- var allFiles = GetAllFiles ( ) . ToList ( ) ;
85- var binaryFileExtensions = new HashSet < string > ( new [ ] { ".dll" , ".exe" } ) ; // TODO: add more binary file extensions.
86- var allNonBinaryFiles = allFiles . Where ( f => ! binaryFileExtensions . Contains ( f . Extension . ToLowerInvariant ( ) ) ) . ToList ( ) ;
87- var smallNonBinaryFiles = allNonBinaryFiles . SelectSmallFiles ( logger ) . SelectFileNames ( ) . ToList ( ) ;
88- this . fileContent = new FileContent ( logger , smallNonBinaryFiles ) ;
89- this . nonGeneratedSources = allNonBinaryFiles . SelectFileNamesByExtension ( ".cs" ) . ToList ( ) ;
90- this . generatedSources = new ( ) ;
91- var allProjects = allNonBinaryFiles . SelectFileNamesByExtension ( ".csproj" ) . ToList ( ) ;
92- var allSolutions = allNonBinaryFiles . SelectFileNamesByExtension ( ".sln" ) . ToList ( ) ;
93- var dllLocations = allFiles . SelectFileNamesByExtension ( ".dll" ) . Select ( x => new AssemblyLookupLocation ( x ) ) . ToHashSet ( ) ;
94-
95- logger . LogInfo ( $ "Found { allFiles . Count } files, { nonGeneratedSources . Count } source files, { allProjects . Count } project files, { allSolutions . Count } solution files, { dllLocations . Count } DLLs.") ;
90+ this . fileProvider = new FileProvider ( sourceDir , logger ) ;
91+ this . fileContent = new FileContent ( logger , this . fileProvider . SmallNonBinary ) ;
92+ this . nonGeneratedSources = fileProvider . Sources . ToList ( ) ;
93+ this . generatedSources = [ ] ;
9694
9795 void startCallback ( string s , bool silent )
9896 {
@@ -104,7 +102,7 @@ void exitCallback(int ret, string msg, bool silent)
104102 logger . Log ( silent ? Severity . Debug : Severity . Info , $ "Exit code { ret } { ( string . IsNullOrEmpty ( msg ) ? "" : $ ": { msg } ") } ") ;
105103 }
106104
107- DotNet . WithDotNet ( SystemBuildActions . Instance , logger , smallNonBinaryFiles , tempWorkingDirectory . ToString ( ) , shouldCleanUp : false , ensureDotNetAvailable : true , version : null , installDir =>
105+ DotNet . WithDotNet ( SystemBuildActions . Instance , logger , fileProvider . GlobalJsons , tempWorkingDirectory . ToString ( ) , shouldCleanUp : false , ensureDotNetAvailable : true , version : null , installDir =>
108106 {
109107 this . dotnetPath = installDir ;
110108 return BuildScript . Success ;
@@ -121,13 +119,16 @@ void exitCallback(int ret, string msg, bool silent)
121119 throw ;
122120 }
123121
124- RestoreNugetPackages ( allNonBinaryFiles , allProjects , allSolutions , dllLocations ) ;
122+ nugetPackageRestorer = new NugetPackageRestorer ( fileProvider , fileContent , dotnet , diagnosticsWriter , logger , this ) ;
123+
124+ var dllLocations = fileProvider . Dlls . Select ( x => new AssemblyLookupLocation ( x ) ) . ToHashSet ( ) ;
125+ dllLocations . UnionWith ( nugetPackageRestorer . Restore ( ) ) ;
125126 // Find DLLs in the .Net / Asp.Net Framework
126127 // This needs to come after the nuget restore, because the nuget restore might fetch the .NET Core/Framework reference assemblies.
127128 var frameworkLocations = AddFrameworkDlls ( dllLocations ) ;
128129
129130 assemblyCache = new AssemblyCache ( dllLocations , frameworkLocations , logger ) ;
130- AnalyseSolutions ( allSolutions ) ;
131+ AnalyseSolutions ( fileProvider . Solutions ) ;
131132
132133 foreach ( var filename in assemblyCache . AllAssemblies . Select ( a => a . Filename ) )
133134 {
@@ -154,7 +155,7 @@ void exitCallback(int ret, string msg, bool silent)
154155 shouldExtractWebViews )
155156 {
156157 CompilationInfos . Add ( ( "WebView extraction enabled" , "1" ) ) ;
157- GenerateSourceFilesFromWebViews ( allNonBinaryFiles ) ;
158+ GenerateSourceFilesFromWebViews ( ) ;
158159 }
159160 else
160161 {
@@ -171,8 +172,8 @@ void exitCallback(int ret, string msg, bool silent)
171172 logger . LogInfo ( "Build analysis summary:" ) ;
172173 logger . LogInfo ( $ "{ nonGeneratedSources . Count , align } source files found on the filesystem") ;
173174 logger . LogInfo ( $ "{ generatedSources . Count , align } source files have been generated") ;
174- logger . LogInfo ( $ "{ allSolutions . Count , align } solution files found on the filesystem") ;
175- logger . LogInfo ( $ "{ allProjects . Count , align } project files found on the filesystem") ;
175+ logger . LogInfo ( $ "{ fileProvider . Solutions . Count , align } solution files found on the filesystem") ;
176+ logger . LogInfo ( $ "{ fileProvider . Projects . Count , align } project files found on the filesystem") ;
176177 logger . LogInfo ( $ "{ usedReferences . Keys . Count , align } resolved references") ;
177178 logger . LogInfo ( $ "{ unresolvedReferences . Count , align } unresolved references") ;
178179 logger . LogInfo ( $ "{ conflictedReferences , align } resolved assembly conflicts") ;
@@ -182,8 +183,8 @@ void exitCallback(int ret, string msg, bool silent)
182183 CompilationInfos . AddRange ( [
183184 ( "Source files on filesystem" , nonGeneratedSources . Count . ToString ( ) ) ,
184185 ( "Source files generated" , generatedSources . Count . ToString ( ) ) ,
185- ( "Solution files on filesystem" , allSolutions . Count . ToString ( ) ) ,
186- ( "Project files on filesystem" , allProjects . Count . ToString ( ) ) ,
186+ ( "Solution files on filesystem" , fileProvider . Solutions . Count . ToString ( ) ) ,
187+ ( "Project files on filesystem" , fileProvider . Projects . Count . ToString ( ) ) ,
187188 ( "Resolved references" , usedReferences . Keys . Count . ToString ( ) ) ,
188189 ( "Unresolved references" , unresolvedReferences . Count . ToString ( ) ) ,
189190 ( "Resolved assembly conflicts" , conflictedReferences . ToString ( ) ) ,
@@ -229,11 +230,7 @@ private HashSet<string> AddFrameworkDlls(HashSet<AssemblyLookupLocation> dllLoca
229230
230231 private void RemoveNugetAnalyzerReferences ( )
231232 {
232- var packageFolder = packageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
233- if ( packageFolder == null )
234- {
235- return ;
236- }
233+ var packageFolder = nugetPackageRestorer . PackageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
237234
238235 foreach ( var filename in usedReferences . Keys )
239236 {
@@ -307,7 +304,7 @@ private void AddNetFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet
307304 var packagesInPrioOrder = FrameworkPackageNames . NetFrameworks ;
308305
309306 var frameworkPaths = packagesInPrioOrder
310- . Select ( ( s , index ) => ( Index : index , Path : GetPackageDirectory ( s , packageDirectory ) ) )
307+ . Select ( ( s , index ) => ( Index : index , Path : GetPackageDirectory ( s ) ) )
311308 . Where ( pair => pair . Path is not null )
312309 . ToArray ( ) ;
313310
@@ -338,11 +335,7 @@ private void AddNetFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet
338335 if ( runtimeLocation is null )
339336 {
340337 logger . LogInfo ( "No .NET Desktop Runtime location found. Attempting to restore the .NET Framework reference assemblies manually." ) ;
341-
342- if ( TryRestorePackageManually ( FrameworkPackageNames . LatestNetFrameworkReferenceAssemblies ) )
343- {
344- runtimeLocation = GetPackageDirectory ( FrameworkPackageNames . LatestNetFrameworkReferenceAssemblies , missingPackageDirectory ) ;
345- }
338+ runtimeLocation = nugetPackageRestorer . TryRestoreLatestNetFrameworkReferenceAssemblies ( ) ;
346339 }
347340 }
348341
@@ -362,12 +355,7 @@ private void AddNetFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet
362355
363356 private void RemoveNugetPackageReference ( string packagePrefix , ISet < AssemblyLookupLocation > dllLocations )
364357 {
365- var packageFolder = packageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
366- if ( packageFolder == null )
367- {
368- return ;
369- }
370-
358+ var packageFolder = nugetPackageRestorer . PackageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
371359 var packagePathPrefix = Path . Combine ( packageFolder , packagePrefix . ToLowerInvariant ( ) ) ;
372360 var toRemove = dllLocations . Where ( s => s . Path . StartsWith ( packagePathPrefix , StringComparison . InvariantCultureIgnoreCase ) ) ;
373361 foreach ( var path in toRemove )
@@ -390,7 +378,7 @@ private void AddAspNetCoreFrameworkDlls(ISet<AssemblyLookupLocation> dllLocation
390378 }
391379
392380 // First try to find ASP.NET Core assemblies in the NuGet packages
393- if ( GetPackageDirectory ( FrameworkPackageNames . AspNetCoreFramework , packageDirectory ) is string aspNetCorePackage )
381+ if ( GetPackageDirectory ( FrameworkPackageNames . AspNetCoreFramework ) is string aspNetCorePackage )
394382 {
395383 SelectNewestFrameworkPath ( aspNetCorePackage , "ASP.NET Core" , dllLocations , frameworkLocations ) ;
396384 return ;
@@ -406,15 +394,20 @@ private void AddAspNetCoreFrameworkDlls(ISet<AssemblyLookupLocation> dllLocation
406394
407395 private void AddMicrosoftWindowsDesktopDlls ( ISet < AssemblyLookupLocation > dllLocations , ISet < string > frameworkLocations )
408396 {
409- if ( GetPackageDirectory ( FrameworkPackageNames . WindowsDesktopFramework , packageDirectory ) is string windowsDesktopApp )
397+ if ( GetPackageDirectory ( FrameworkPackageNames . WindowsDesktopFramework ) is string windowsDesktopApp )
410398 {
411399 SelectNewestFrameworkPath ( windowsDesktopApp , "Windows Desktop App" , dllLocations , frameworkLocations ) ;
412400 }
413401 }
414402
415- private string ? GetPackageDirectory ( string packagePrefix , TemporaryDirectory root )
403+ private string ? GetPackageDirectory ( string packagePrefix )
404+ {
405+ return GetPackageDirectory ( packagePrefix , nugetPackageRestorer . PackageDirectory . DirInfo ) ;
406+ }
407+
408+ internal static string ? GetPackageDirectory ( string packagePrefix , DirectoryInfo root )
416409 {
417- return new DirectoryInfo ( root . DirInfo . FullName )
410+ return new DirectoryInfo ( root . FullName )
418411 . EnumerateDirectories ( packagePrefix + "*" , new EnumerationOptions { MatchCasing = MatchCasing . CaseInsensitive , RecurseSubdirectories = false } )
419412 . FirstOrDefault ( ) ?
420413 . FullName ;
@@ -467,15 +460,15 @@ private void GenerateSourceFileFromImplicitUsings()
467460 }
468461 }
469462
470- private void GenerateSourceFilesFromWebViews ( List < FileInfo > allFiles )
463+ private void GenerateSourceFilesFromWebViews ( )
471464 {
472- var views = allFiles . SelectFileNamesByExtension ( ".cshtml" , ".razor" ) . ToArray ( ) ;
473- if ( views . Length == 0 )
465+ var views = fileProvider . RazorViews ;
466+ if ( views . Count == 0 )
474467 {
475468 return ;
476469 }
477470
478- logger . LogInfo ( $ "Found { views . Length } cshtml and razor files.") ;
471+ logger . LogInfo ( $ "Found { views . Count } cshtml and razor files.") ;
479472
480473 if ( ! IsAspNetCoreDetected ( ) )
481474 {
@@ -503,54 +496,6 @@ private void GenerateSourceFilesFromWebViews(List<FileInfo> allFiles)
503496 }
504497 }
505498
506- private IEnumerable < FileInfo > GetAllFiles ( )
507- {
508- IEnumerable < FileInfo > files = sourceDir . GetFiles ( "*.*" , new EnumerationOptions { RecurseSubdirectories = true } ) ;
509-
510- if ( dotnetPath != null )
511- {
512- files = files . Where ( f => ! f . FullName . StartsWith ( dotnetPath , StringComparison . OrdinalIgnoreCase ) ) ;
513- }
514-
515- files = files . Where ( f =>
516- {
517- try
518- {
519- if ( f . Exists )
520- {
521- return true ;
522- }
523-
524- logger . LogWarning ( $ "File { f . FullName } could not be processed.") ;
525- return false ;
526- }
527- catch ( Exception ex )
528- {
529- logger . LogWarning ( $ "File { f . FullName } could not be processed: { ex . Message } ") ;
530- return false ;
531- }
532- } ) ;
533-
534- files = new FilePathFilter ( sourceDir , logger ) . Filter ( files ) ;
535- return files ;
536- }
537-
538- /// <summary>
539- /// Computes a unique temp directory for the packages associated
540- /// with this source tree. Use a SHA1 of the directory name.
541- /// </summary>
542- /// <returns>The full path of the temp directory.</returns>
543- private static string ComputeTempDirectory ( string srcDir , string subfolderName )
544- {
545- var bytes = Encoding . Unicode . GetBytes ( srcDir ) ;
546- var sha = SHA1 . HashData ( bytes ) ;
547- var sb = new StringBuilder ( ) ;
548- foreach ( var b in sha . Take ( 8 ) )
549- sb . AppendFormat ( "{0:x2}" , b ) ;
550-
551- return Path . Combine ( FileUtils . GetTemporaryWorkingDirectory ( out var _ ) , sb . ToString ( ) , subfolderName ) ;
552- }
553-
554499 /// <summary>
555500 /// Creates a temporary directory with the given subfolder name.
556501 /// The created directory might be inside the repo folder, and it is deleted when the object is disposed.
@@ -674,7 +619,7 @@ private void ResolveConflicts(IEnumerable<string> frameworkPaths)
674619
675620 private void AnalyseSolutions ( IEnumerable < string > solutions )
676621 {
677- Parallel . ForEach ( solutions , new ParallelOptions { MaxDegreeOfParallelism = threads } , solutionFile =>
622+ Parallel . ForEach ( solutions , new ParallelOptions { MaxDegreeOfParallelism = Threads } , solutionFile =>
678623 {
679624 try
680625 {
@@ -723,29 +668,11 @@ private void AnalyseProject(FileInfo project)
723668 }
724669 }
725670
726- public void Dispose ( TemporaryDirectory ? dir , string name )
727- {
728- try
729- {
730- dir ? . Dispose ( ) ;
731- }
732- catch ( Exception exc )
733- {
734- logger . LogInfo ( $ "Couldn't delete { name } directory { exc . Message } ") ;
735- }
736- }
737-
738671 public void Dispose ( )
739672 {
740- Dispose ( packageDirectory , "package" ) ;
741- Dispose ( legacyPackageDirectory , "legacy package" ) ;
742- Dispose ( missingPackageDirectory , "missing package" ) ;
743- if ( cleanupTempWorkingDirectory )
744- {
745- Dispose ( tempWorkingDirectory , "temporary working" ) ;
746- }
747-
673+ tempWorkingDirectory ? . Dispose ( ) ;
748674 diagnosticsWriter ? . Dispose ( ) ;
675+ nugetPackageRestorer ? . Dispose ( ) ;
749676 }
750677 }
751678}
0 commit comments