1
1
using System ;
2
- using System . Collections . Immutable ;
3
2
using Microsoft . CodeAnalysis ;
4
3
using Microsoft . CodeAnalysis . CSharp ;
5
- using Microsoft . CodeAnalysis . Diagnostics ;
6
4
using System . IO ;
7
5
using System . Linq ;
8
6
using Semmle . Extraction . CSharp . Populators ;
9
- using System . Runtime . InteropServices ;
10
7
using System . Collections . Generic ;
11
- using System . Text ;
12
8
using System . Threading . Tasks ;
13
9
using System . Diagnostics ;
14
10
using Semmle . Util . Logging ;
@@ -40,27 +36,37 @@ public Analyser(IProgressMonitor pm, ILogger logger)
40
36
CSharpCompilation compilation ;
41
37
Layout layout ;
42
38
39
+ private bool init ;
43
40
/// <summary>
44
- /// Initialize the analyser.
41
+ /// Start initialization of the analyser.
45
42
/// </summary>
46
- /// <param name="commandLineArguments">Arguments passed to csc.</param>
47
- /// <param name="compilationIn">The Roslyn compilation.</param>
48
- /// <param name="options">Extractor options.</param>
49
43
/// <param name="roslynArgs">The arguments passed to Roslyn.</param>
50
- public void Initialize (
51
- CSharpCommandLineArguments commandLineArguments ,
52
- CSharpCompilation compilationIn ,
53
- Options options ,
54
- string [ ] roslynArgs )
44
+ /// <returns>A Boolean indicating whether to proceed with extraction.</returns>
45
+ public bool BeginInitialize ( string [ ] roslynArgs )
55
46
{
56
- compilation = compilationIn ;
47
+ return init = LogRoslynArgs ( roslynArgs , Extraction . Extractor . Version ) ;
48
+ }
57
49
50
+ /// <summary>
51
+ /// End initialization of the analyser.
52
+ /// </summary>
53
+ /// <param name="commandLineArguments">Arguments passed to csc.</param>
54
+ /// <param name="options">Extractor options.</param>
55
+ /// <param name="compilation">The Roslyn compilation.</param>
56
+ /// <returns>A Boolean indicating whether to proceed with extraction.</returns>
57
+ public void EndInitialize (
58
+ CSharpCommandLineArguments commandLineArguments ,
59
+ Options options ,
60
+ CSharpCompilation compilation )
61
+ {
62
+ if ( ! init )
63
+ throw new InternalError ( "EndInitialize called without BeginInitialize returning true" ) ;
58
64
layout = new Layout ( ) ;
59
65
this . options = options ;
60
-
66
+ this . compilation = compilation ;
61
67
extractor = new Extraction . Extractor ( false , GetOutputName ( compilation , commandLineArguments ) , Logger ) ;
68
+ LogDiagnostics ( ) ;
62
69
63
- LogDiagnostics ( roslynArgs ) ;
64
70
SetReferencePaths ( ) ;
65
71
66
72
CompilationErrors += FilteredDiagnostics . Count ( ) ;
@@ -110,7 +116,7 @@ public void InitializeStandalone(CSharpCompilation compilationIn, CommonOptions
110
116
layout = new Layout ( ) ;
111
117
extractor = new Extraction . Extractor ( true , null , Logger ) ;
112
118
this . options = options ;
113
- LogDiagnostics ( null ) ;
119
+ LogExtractorInfo ( Extraction . Extractor . Version ) ;
114
120
SetReferencePaths ( ) ;
115
121
}
116
122
@@ -205,11 +211,6 @@ static bool FileIsUpToDate(string src, string dest)
205
211
File . GetLastWriteTime ( dest ) >= File . GetLastWriteTime ( src ) ;
206
212
}
207
213
208
- bool FileIsCached ( string src , string dest )
209
- {
210
- return options . Cache && FileIsUpToDate ( src , dest ) ;
211
- }
212
-
213
214
/// <summary>
214
215
/// Extracts compilation-wide entities, such as compilations and compiler diagnostics.
215
216
/// </summary>
@@ -241,7 +242,7 @@ void DoAnalyseCompilation(string cwd, string[] args)
241
242
}
242
243
243
244
public void LogPerformance ( Entities . PerformanceMetrics p ) => compilationEntity . PopulatePerformance ( p ) ;
244
-
245
+
245
246
/// <summary>
246
247
/// Extract an assembly to a new trap file.
247
248
/// If the trap file exists, skip extraction to avoid duplicating
@@ -259,7 +260,7 @@ void DoAnalyseAssembly(PortableExecutableReference r)
259
260
var projectLayout = layout . LookupProjectOrDefault ( assemblyPath ) ;
260
261
using ( var trapWriter = projectLayout . CreateTrapWriter ( Logger , assemblyPath , true , options . TrapCompression ) )
261
262
{
262
- var skipExtraction = FileIsCached ( assemblyPath , trapWriter . TrapFile ) ;
263
+ var skipExtraction = options . Cache && File . Exists ( trapWriter . TrapFile ) ;
263
264
264
265
if ( ! skipExtraction )
265
266
{
@@ -430,29 +431,74 @@ public void Dispose()
430
431
public int TotalErrors => CompilationErrors + ExtractorErrors ;
431
432
432
433
/// <summary>
433
- /// Logs detailed information about this invocation,
434
- /// in the event that errors were detected.
434
+ /// Logs information about the extractor.
435
435
/// </summary>
436
- /// <param name="roslynArgs">The arguments passed to Roslyn.</param>
437
- public void LogDiagnostics ( string [ ] roslynArgs )
436
+ public void LogExtractorInfo ( string extractorVersion )
438
437
{
439
438
Logger . Log ( Severity . Info , " Extractor: {0}" , Environment . GetCommandLineArgs ( ) . First ( ) ) ;
440
- if ( extractor != null )
441
- Logger . Log ( Severity . Info , " Extractor version: {0}" , extractor . Version ) ;
442
-
439
+ Logger . Log ( Severity . Info , " Extractor version: {0}" , extractorVersion ) ;
443
440
Logger . Log ( Severity . Info , " Current working directory: {0}" , Directory . GetCurrentDirectory ( ) ) ;
441
+ }
442
+
443
+ /// <summary>
444
+ /// Logs information about the extractor, as well as the arguments to Roslyn.
445
+ /// </summary>
446
+ /// <param name="roslynArgs">The arguments passed to Roslyn.</param>
447
+ /// <returns>A Boolean indicating whether the same arguments have been logged previously.</returns>
448
+ public bool LogRoslynArgs ( string [ ] roslynArgs , string extractorVersion )
449
+ {
450
+ LogExtractorInfo ( extractorVersion ) ;
451
+ Logger . Log ( Severity . Info , $ " Arguments to Roslyn: { string . Join ( ' ' , roslynArgs ) } ") ;
444
452
445
- if ( roslynArgs != null )
453
+ var csharpLogDir = Extractor . GetCSharpLogDirectory ( ) ;
454
+ var tempFile = Path . Combine ( csharpLogDir , $ "csharp.{ Path . GetRandomFileName ( ) } .txt") ;
455
+
456
+ bool argsWritten ;
457
+ using ( var streamWriter = new StreamWriter ( new FileStream ( tempFile , FileMode . Append , FileAccess . Write ) ) )
446
458
{
447
- Logger . Log ( Severity . Info , $ " Arguments to Roslyn: { string . Join ( ' ' , roslynArgs ) } ") ;
459
+ streamWriter . WriteLine ( $ "# Arguments to Roslyn: { string . Join ( ' ' , roslynArgs . Where ( arg => ! arg . StartsWith ( '@' ) ) ) } ") ;
460
+ argsWritten = roslynArgs . WriteCommandLine ( streamWriter ) ;
461
+ }
462
+
463
+ var hash = FileUtils . ComputeFileHash ( tempFile ) ;
464
+ var argsFile = Path . Combine ( csharpLogDir , $ "csharp.{ hash } .txt") ;
448
465
449
- // Create a new file in the log folder.
450
- var argsFile = Path . Combine ( Extractor . GetCSharpLogDirectory ( ) , $ "csharp. { Path . GetRandomFileName ( ) } .txt ") ;
466
+ if ( argsWritten )
467
+ Logger . Log ( Severity . Info , $ " Arguments have been written to { argsFile } ") ;
451
468
452
- if ( roslynArgs . ArchiveCommandLine ( argsFile ) )
453
- Logger . Log ( Severity . Info , $ " Arguments have been written to { argsFile } ") ;
469
+ if ( File . Exists ( argsFile ) )
470
+ {
471
+ try
472
+ {
473
+ File . Delete ( tempFile ) ;
474
+ }
475
+ catch ( IOException e )
476
+ {
477
+ Logger . Log ( Severity . Warning , $ " Failed to remove { tempFile } : { e . Message } ") ;
478
+ }
479
+ return false ;
454
480
}
455
481
482
+ try
483
+ {
484
+ File . Move ( tempFile , argsFile ) ;
485
+ }
486
+ catch ( IOException e )
487
+ {
488
+ Logger . Log ( Severity . Warning , $ " Failed to move { tempFile } to { argsFile } : { e . Message } ") ;
489
+ }
490
+
491
+ return true ;
492
+ }
493
+
494
+
495
+ /// <summary>
496
+ /// Logs detailed information about this invocation,
497
+ /// in the event that errors were detected.
498
+ /// </summary>
499
+ /// <returns>A Boolean indicating whether to proceed with extraction.</returns>
500
+ public void LogDiagnostics ( )
501
+ {
456
502
foreach ( var error in FilteredDiagnostics )
457
503
{
458
504
Logger . Log ( Severity . Error , " Compilation error: {0}" , error ) ;
0 commit comments