1
- using System ;
1
+ using Semmle . Util ;
2
+ using System ;
2
3
using System . Collections . Generic ;
3
4
using System . IO ;
4
5
using System . Linq ;
5
6
using Semmle . Extraction . CSharp . Standalone ;
6
7
using System . Threading . Tasks ;
8
+ using System . Collections . Concurrent ;
9
+ using System . Text ;
10
+ using System . Security . Cryptography ;
7
11
8
12
namespace Semmle . BuildAnalyser
9
13
{
@@ -42,20 +46,18 @@ interface IBuildAnalysis
42
46
/// <summary>
43
47
/// Main implementation of the build analysis.
44
48
/// </summary>
45
- class BuildAnalysis : IBuildAnalysis
49
+ class BuildAnalysis : IBuildAnalysis , IDisposable
46
50
{
47
51
private readonly AssemblyCache assemblyCache ;
48
52
private readonly NugetPackages nuget ;
49
53
private readonly IProgressMonitor progressMonitor ;
50
- private HashSet < string > usedReferences = new HashSet < string > ( ) ;
51
- private readonly HashSet < string > usedSources = new HashSet < string > ( ) ;
52
- private readonly HashSet < string > missingSources = new HashSet < string > ( ) ;
53
- private readonly Dictionary < string , string > unresolvedReferences = new Dictionary < string , string > ( ) ;
54
+ private readonly IDictionary < string , bool > usedReferences = new ConcurrentDictionary < string , bool > ( ) ;
55
+ private readonly IDictionary < string , bool > sources = new ConcurrentDictionary < string , bool > ( ) ;
56
+ private readonly IDictionary < string , string > unresolvedReferences = new ConcurrentDictionary < string , string > ( ) ;
54
57
private readonly DirectoryInfo sourceDir ;
55
58
private int failedProjects , succeededProjects ;
56
59
private readonly string [ ] allSources ;
57
60
private int conflictedReferences = 0 ;
58
- private readonly object mutex = new object ( ) ;
59
61
60
62
/// <summary>
61
63
/// Performs a C# build analysis.
@@ -77,7 +79,7 @@ public BuildAnalysis(Options options, IProgressMonitor progress)
77
79
ToArray ( ) ;
78
80
79
81
var dllDirNames = options . DllDirs . Select ( Path . GetFullPath ) . ToList ( ) ;
80
- PackageDirectory = TemporaryDirectory . CreateTempDirectory ( sourceDir . FullName ) ;
82
+ PackageDirectory = new TemporaryDirectory ( ComputeTempDirectory ( sourceDir . FullName ) ) ;
81
83
82
84
if ( options . UseNuGet )
83
85
{
@@ -97,23 +99,22 @@ public BuildAnalysis(Options options, IProgressMonitor progress)
97
99
{
98
100
dllDirNames . Add ( Runtime . Runtimes . First ( ) ) ;
99
101
}
100
-
102
+
103
+ // These files can sometimes prevent `dotnet restore` from working correctly.
104
+ using ( new FileRenamer ( sourceDir . GetFiles ( "global.json" , SearchOption . AllDirectories ) ) )
105
+ using ( new FileRenamer ( sourceDir . GetFiles ( "Directory.Build.props" , SearchOption . AllDirectories ) ) )
101
106
{
102
- // These files can sometimes prevent `dotnet restore` from working correctly.
103
- using ( new FileRenamer ( sourceDir . GetFiles ( "global.json" , SearchOption . AllDirectories ) ) )
104
- using ( new FileRenamer ( sourceDir . GetFiles ( "Directory.Build.props" , SearchOption . AllDirectories ) ) )
105
- {
106
- var solutions = options . SolutionFile != null ?
107
- new [ ] { options . SolutionFile } :
108
- sourceDir . GetFiles ( "*.sln" , SearchOption . AllDirectories ) . Select ( d => d . FullName ) ;
107
+ var solutions = options . SolutionFile != null ?
108
+ new [ ] { options . SolutionFile } :
109
+ sourceDir . GetFiles ( "*.sln" , SearchOption . AllDirectories ) . Select ( d => d . FullName ) ;
109
110
110
- RestoreSolutions ( solutions ) ;
111
- dllDirNames . Add ( PackageDirectory . DirInfo . FullName ) ;
112
- assemblyCache = new BuildAnalyser . AssemblyCache ( dllDirNames , progress ) ;
113
- AnalyseSolutions ( solutions ) ;
111
+ RestoreSolutions ( solutions ) ;
112
+ dllDirNames . Add ( PackageDirectory . DirInfo . FullName ) ;
113
+ assemblyCache = new BuildAnalyser . AssemblyCache ( dllDirNames , progress ) ;
114
+ AnalyseSolutions ( solutions ) ;
114
115
115
- usedReferences = new HashSet < string > ( assemblyCache . AllAssemblies . Select ( a => a . Filename ) ) ;
116
- }
116
+ foreach ( var filename in assemblyCache . AllAssemblies . Select ( a => a . Filename ) )
117
+ UseReference ( filename ) ;
117
118
}
118
119
119
120
ResolveConflicts ( ) ;
@@ -124,7 +125,7 @@ public BuildAnalysis(Options options, IProgressMonitor progress)
124
125
}
125
126
126
127
// Output the findings
127
- foreach ( var r in usedReferences )
128
+ foreach ( var r in usedReferences . Keys )
128
129
{
129
130
progressMonitor . ResolvedReference ( r ) ;
130
131
}
@@ -146,6 +147,25 @@ public BuildAnalysis(Options options, IProgressMonitor progress)
146
147
DateTime . Now - startTime ) ;
147
148
}
148
149
150
+ /// <summary>
151
+ /// Computes a unique temp directory for the packages associated
152
+ /// with this source tree. Use a SHA1 of the directory name.
153
+ /// </summary>
154
+ /// <param name="srcDir"></param>
155
+ /// <returns>The full path of the temp directory.</returns>
156
+ private static string ComputeTempDirectory ( string srcDir )
157
+ {
158
+ var bytes = Encoding . Unicode . GetBytes ( srcDir ) ;
159
+
160
+ using var sha1 = new SHA1CryptoServiceProvider ( ) ;
161
+ var sha = sha1 . ComputeHash ( bytes ) ;
162
+ var sb = new StringBuilder ( ) ;
163
+ foreach ( var b in sha . Take ( 8 ) )
164
+ sb . AppendFormat ( "{0:x2}" , b ) ;
165
+
166
+ return Path . Combine ( Path . GetTempPath ( ) , "GitHub" , "packages" , sb . ToString ( ) ) ;
167
+ }
168
+
149
169
/// <summary>
150
170
/// Resolves conflicts between all of the resolved references.
151
171
/// If the same assembly name is duplicated with different versions,
@@ -154,7 +174,7 @@ public BuildAnalysis(Options options, IProgressMonitor progress)
154
174
void ResolveConflicts ( )
155
175
{
156
176
var sortedReferences = usedReferences .
157
- Select ( r => assemblyCache . GetAssemblyInfo ( r ) ) .
177
+ Select ( r => assemblyCache . GetAssemblyInfo ( r . Key ) ) .
158
178
OrderBy ( r => r . Version ) .
159
179
ToArray ( ) ;
160
180
@@ -165,7 +185,9 @@ void ResolveConflicts()
165
185
finalAssemblyList [ r . Name ] = r ;
166
186
167
187
// Update the used references list
168
- usedReferences = new HashSet < string > ( finalAssemblyList . Select ( r => r . Value . Filename ) ) ;
188
+ usedReferences . Clear ( ) ;
189
+ foreach ( var r in finalAssemblyList . Select ( r => r . Value . Filename ) )
190
+ UseReference ( r ) ;
169
191
170
192
// Report the results
171
193
foreach ( var r in sortedReferences )
@@ -194,8 +216,7 @@ void ReadNugetFiles()
194
216
/// <param name="reference">The filename of the reference.</param>
195
217
void UseReference ( string reference )
196
218
{
197
- lock ( mutex )
198
- usedReferences . Add ( reference ) ;
219
+ usedReferences [ reference ] = true ;
199
220
}
200
221
201
222
/// <summary>
@@ -204,27 +225,18 @@ void UseReference(string reference)
204
225
/// <param name="sourceFile">The source file.</param>
205
226
void UseSource ( FileInfo sourceFile )
206
227
{
207
- if ( sourceFile . Exists )
208
- {
209
- lock ( mutex )
210
- usedSources . Add ( sourceFile . FullName ) ;
211
- }
212
- else
213
- {
214
- lock ( mutex )
215
- missingSources . Add ( sourceFile . FullName ) ;
216
- }
228
+ sources [ sourceFile . FullName ] = sourceFile . Exists ;
217
229
}
218
230
219
231
/// <summary>
220
232
/// The list of resolved reference files.
221
233
/// </summary>
222
- public IEnumerable < string > ReferenceFiles => this . usedReferences ;
234
+ public IEnumerable < string > ReferenceFiles => this . usedReferences . Keys ;
223
235
224
236
/// <summary>
225
237
/// The list of source files used in projects.
226
238
/// </summary>
227
- public IEnumerable < string > ProjectSourceFiles => usedSources ;
239
+ public IEnumerable < string > ProjectSourceFiles => sources . Where ( s => s . Value ) . Select ( s => s . Key ) ;
228
240
229
241
/// <summary>
230
242
/// All of the source files in the source directory.
@@ -240,7 +252,7 @@ void UseSource(FileInfo sourceFile)
240
252
/// List of source files which were mentioned in project files but
241
253
/// do not exist on the file system.
242
254
/// </summary>
243
- public IEnumerable < string > MissingSourceFiles => missingSources ;
255
+ public IEnumerable < string > MissingSourceFiles => sources . Where ( s => ! s . Value ) . Select ( s => s . Key ) ;
244
256
245
257
/// <summary>
246
258
/// Record that a particular reference couldn't be resolved.
@@ -250,8 +262,7 @@ void UseSource(FileInfo sourceFile)
250
262
/// <param name="projectFile">The project file making the reference.</param>
251
263
void UnresolvedReference ( string id , string projectFile )
252
264
{
253
- lock ( mutex )
254
- unresolvedReferences [ id ] = projectFile ;
265
+ unresolvedReferences [ id ] = projectFile ;
255
266
}
256
267
257
268
readonly TemporaryDirectory PackageDirectory ;
@@ -276,7 +287,7 @@ void AnalyseProject(FileInfo project)
276
287
277
288
try
278
289
{
279
- IProjectFile csProj = new CsProjFile ( project ) ;
290
+ var csProj = new CsProjFile ( project ) ;
280
291
281
292
foreach ( var @ref in csProj . References )
282
293
{
@@ -309,14 +320,6 @@ void AnalyseProject(FileInfo project)
309
320
310
321
}
311
322
312
- /// <summary>
313
- /// Delete packages directory.
314
- /// </summary>
315
- public void Cleanup ( )
316
- {
317
- PackageDirectory ? . Cleanup ( ) ;
318
- }
319
-
320
323
void Restore ( string projectOrSolution )
321
324
{
322
325
int exit = DotNet . RestoreToDirectory ( projectOrSolution , PackageDirectory . DirInfo . FullName ) ;
@@ -345,13 +348,18 @@ public void AnalyseSolutions(IEnumerable<string> solutions)
345
348
{
346
349
var sln = new SolutionFile ( solutionFile ) ;
347
350
progressMonitor . AnalysingSolution ( solutionFile ) ;
348
- AnalyseProjectFiles ( sln . Projects . Select ( p => new FileInfo ( p ) ) . Where ( p => p . Exists ) . ToArray ( ) ) ;
351
+ AnalyseProjectFiles ( sln . Projects . Select ( p => new FileInfo ( p ) ) . Where ( p => p . Exists ) ) ;
349
352
}
350
353
catch ( Microsoft . Build . Exceptions . InvalidProjectFileException ex )
351
354
{
352
355
progressMonitor . FailedProjectFile ( solutionFile , ex . BaseMessage ) ;
353
356
}
354
357
} ) ;
355
358
}
359
+
360
+ public void Dispose ( )
361
+ {
362
+ PackageDirectory ? . Dispose ( ) ;
363
+ }
356
364
}
357
365
}
0 commit comments