12
12
13
13
namespace Semmle . Extraction . CSharp . DependencyFetching
14
14
{
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
+
15
23
/// <summary>
16
24
/// Main implementation of the build analysis.
17
25
/// </summary>
18
- public sealed partial class DependencyManager : IDisposable
26
+ public sealed partial class DependencyManager : IDisposable , ICompilationInfoContainer
19
27
{
20
28
private readonly AssemblyCache assemblyCache ;
21
29
private readonly ILogger logger ;
22
30
private readonly IDiagnosticsWriter diagnosticsWriter ;
31
+ private readonly NugetPackageRestorer nugetPackageRestorer ;
32
+ private readonly IDotNet dotnet ;
33
+ private readonly FileContent fileContent ;
34
+ private readonly FileProvider fileProvider ;
23
35
24
36
// Only used as a set, but ConcurrentDictionary is the only concurrent set in .NET.
25
37
private readonly IDictionary < string , bool > usedReferences = new ConcurrentDictionary < string , bool > ( ) ;
@@ -30,17 +42,14 @@ public sealed partial class DependencyManager : IDisposable
30
42
private int conflictedReferences = 0 ;
31
43
private readonly DirectoryInfo sourceDir ;
32
44
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
+
38
46
private readonly TemporaryDirectory tempWorkingDirectory ;
39
47
private readonly bool cleanupTempWorkingDirectory ;
40
48
41
49
private readonly Lazy < Runtime > runtimeLazy ;
42
50
private Runtime Runtime => runtimeLazy . Value ;
43
- private readonly int threads = EnvironmentVariables . GetDefaultNumberOfThreads ( ) ;
51
+
52
+ internal static readonly int Threads = EnvironmentVariables . GetDefaultNumberOfThreads ( ) ;
44
53
45
54
/// <summary>
46
55
/// Performs C# dependency fetching.
@@ -73,26 +82,15 @@ public DependencyManager(string srcDir, ILogger logger)
73
82
$ "dependency-manager-{ DateTime . UtcNow : yyyyMMddHHmm} -{ Environment . ProcessId } .jsonc") ) ;
74
83
this . sourceDir = new DirectoryInfo ( srcDir ) ;
75
84
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 ) ;
81
89
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 = [ ] ;
96
94
97
95
void startCallback ( string s , bool silent )
98
96
{
@@ -104,7 +102,7 @@ void exitCallback(int ret, string msg, bool silent)
104
102
logger . Log ( silent ? Severity . Debug : Severity . Info , $ "Exit code { ret } { ( string . IsNullOrEmpty ( msg ) ? "" : $ ": { msg } ") } ") ;
105
103
}
106
104
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 =>
108
106
{
109
107
this . dotnetPath = installDir ;
110
108
return BuildScript . Success ;
@@ -121,13 +119,16 @@ void exitCallback(int ret, string msg, bool silent)
121
119
throw ;
122
120
}
123
121
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 ( ) ) ;
125
126
// Find DLLs in the .Net / Asp.Net Framework
126
127
// This needs to come after the nuget restore, because the nuget restore might fetch the .NET Core/Framework reference assemblies.
127
128
var frameworkLocations = AddFrameworkDlls ( dllLocations ) ;
128
129
129
130
assemblyCache = new AssemblyCache ( dllLocations , frameworkLocations , logger ) ;
130
- AnalyseSolutions ( allSolutions ) ;
131
+ AnalyseSolutions ( fileProvider . Solutions ) ;
131
132
132
133
foreach ( var filename in assemblyCache . AllAssemblies . Select ( a => a . Filename ) )
133
134
{
@@ -154,7 +155,7 @@ void exitCallback(int ret, string msg, bool silent)
154
155
shouldExtractWebViews )
155
156
{
156
157
CompilationInfos . Add ( ( "WebView extraction enabled" , "1" ) ) ;
157
- GenerateSourceFilesFromWebViews ( allNonBinaryFiles ) ;
158
+ GenerateSourceFilesFromWebViews ( ) ;
158
159
}
159
160
else
160
161
{
@@ -171,8 +172,8 @@ void exitCallback(int ret, string msg, bool silent)
171
172
logger . LogInfo ( "Build analysis summary:" ) ;
172
173
logger . LogInfo ( $ "{ nonGeneratedSources . Count , align } source files found on the filesystem") ;
173
174
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") ;
176
177
logger . LogInfo ( $ "{ usedReferences . Keys . Count , align } resolved references") ;
177
178
logger . LogInfo ( $ "{ unresolvedReferences . Count , align } unresolved references") ;
178
179
logger . LogInfo ( $ "{ conflictedReferences , align } resolved assembly conflicts") ;
@@ -182,8 +183,8 @@ void exitCallback(int ret, string msg, bool silent)
182
183
CompilationInfos . AddRange ( [
183
184
( "Source files on filesystem" , nonGeneratedSources . Count . ToString ( ) ) ,
184
185
( "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 ( ) ) ,
187
188
( "Resolved references" , usedReferences . Keys . Count . ToString ( ) ) ,
188
189
( "Unresolved references" , unresolvedReferences . Count . ToString ( ) ) ,
189
190
( "Resolved assembly conflicts" , conflictedReferences . ToString ( ) ) ,
@@ -229,11 +230,7 @@ private HashSet<string> AddFrameworkDlls(HashSet<AssemblyLookupLocation> dllLoca
229
230
230
231
private void RemoveNugetAnalyzerReferences ( )
231
232
{
232
- var packageFolder = packageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
233
- if ( packageFolder == null )
234
- {
235
- return ;
236
- }
233
+ var packageFolder = nugetPackageRestorer . PackageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
237
234
238
235
foreach ( var filename in usedReferences . Keys )
239
236
{
@@ -307,7 +304,7 @@ private void AddNetFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet
307
304
var packagesInPrioOrder = FrameworkPackageNames . NetFrameworks ;
308
305
309
306
var frameworkPaths = packagesInPrioOrder
310
- . Select ( ( s , index ) => ( Index : index , Path : GetPackageDirectory ( s , packageDirectory ) ) )
307
+ . Select ( ( s , index ) => ( Index : index , Path : GetPackageDirectory ( s ) ) )
311
308
. Where ( pair => pair . Path is not null )
312
309
. ToArray ( ) ;
313
310
@@ -338,11 +335,7 @@ private void AddNetFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet
338
335
if ( runtimeLocation is null )
339
336
{
340
337
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 ( ) ;
346
339
}
347
340
}
348
341
@@ -362,12 +355,7 @@ private void AddNetFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet
362
355
363
356
private void RemoveNugetPackageReference ( string packagePrefix , ISet < AssemblyLookupLocation > dllLocations )
364
357
{
365
- var packageFolder = packageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
366
- if ( packageFolder == null )
367
- {
368
- return ;
369
- }
370
-
358
+ var packageFolder = nugetPackageRestorer . PackageDirectory . DirInfo . FullName . ToLowerInvariant ( ) ;
371
359
var packagePathPrefix = Path . Combine ( packageFolder , packagePrefix . ToLowerInvariant ( ) ) ;
372
360
var toRemove = dllLocations . Where ( s => s . Path . StartsWith ( packagePathPrefix , StringComparison . InvariantCultureIgnoreCase ) ) ;
373
361
foreach ( var path in toRemove )
@@ -390,7 +378,7 @@ private void AddAspNetCoreFrameworkDlls(ISet<AssemblyLookupLocation> dllLocation
390
378
}
391
379
392
380
// 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 )
394
382
{
395
383
SelectNewestFrameworkPath ( aspNetCorePackage , "ASP.NET Core" , dllLocations , frameworkLocations ) ;
396
384
return ;
@@ -406,15 +394,20 @@ private void AddAspNetCoreFrameworkDlls(ISet<AssemblyLookupLocation> dllLocation
406
394
407
395
private void AddMicrosoftWindowsDesktopDlls ( ISet < AssemblyLookupLocation > dllLocations , ISet < string > frameworkLocations )
408
396
{
409
- if ( GetPackageDirectory ( FrameworkPackageNames . WindowsDesktopFramework , packageDirectory ) is string windowsDesktopApp )
397
+ if ( GetPackageDirectory ( FrameworkPackageNames . WindowsDesktopFramework ) is string windowsDesktopApp )
410
398
{
411
399
SelectNewestFrameworkPath ( windowsDesktopApp , "Windows Desktop App" , dllLocations , frameworkLocations ) ;
412
400
}
413
401
}
414
402
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 )
416
409
{
417
- return new DirectoryInfo ( root . DirInfo . FullName )
410
+ return new DirectoryInfo ( root . FullName )
418
411
. EnumerateDirectories ( packagePrefix + "*" , new EnumerationOptions { MatchCasing = MatchCasing . CaseInsensitive , RecurseSubdirectories = false } )
419
412
. FirstOrDefault ( ) ?
420
413
. FullName ;
@@ -467,15 +460,15 @@ private void GenerateSourceFileFromImplicitUsings()
467
460
}
468
461
}
469
462
470
- private void GenerateSourceFilesFromWebViews ( List < FileInfo > allFiles )
463
+ private void GenerateSourceFilesFromWebViews ( )
471
464
{
472
- var views = allFiles . SelectFileNamesByExtension ( ".cshtml" , ".razor" ) . ToArray ( ) ;
473
- if ( views . Length == 0 )
465
+ var views = fileProvider . RazorViews ;
466
+ if ( views . Count == 0 )
474
467
{
475
468
return ;
476
469
}
477
470
478
- logger . LogInfo ( $ "Found { views . Length } cshtml and razor files.") ;
471
+ logger . LogInfo ( $ "Found { views . Count } cshtml and razor files.") ;
479
472
480
473
if ( ! IsAspNetCoreDetected ( ) )
481
474
{
@@ -503,54 +496,6 @@ private void GenerateSourceFilesFromWebViews(List<FileInfo> allFiles)
503
496
}
504
497
}
505
498
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
-
554
499
/// <summary>
555
500
/// Creates a temporary directory with the given subfolder name.
556
501
/// 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)
674
619
675
620
private void AnalyseSolutions ( IEnumerable < string > solutions )
676
621
{
677
- Parallel . ForEach ( solutions , new ParallelOptions { MaxDegreeOfParallelism = threads } , solutionFile =>
622
+ Parallel . ForEach ( solutions , new ParallelOptions { MaxDegreeOfParallelism = Threads } , solutionFile =>
678
623
{
679
624
try
680
625
{
@@ -723,29 +668,11 @@ private void AnalyseProject(FileInfo project)
723
668
}
724
669
}
725
670
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
-
738
671
public void Dispose ( )
739
672
{
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 ( ) ;
748
674
diagnosticsWriter ? . Dispose ( ) ;
675
+ nugetPackageRestorer ? . Dispose ( ) ;
749
676
}
750
677
}
751
678
}
0 commit comments