|
8 | 8 | using System.Text;
|
9 | 9 | using System.Threading;
|
10 | 10 | using System.Threading.Tasks;
|
| 11 | +using Basic.CompilerLog.Util; |
11 | 12 | using Microsoft.CodeAnalysis;
|
12 | 13 | using Microsoft.CodeAnalysis.CSharp;
|
13 | 14 | using Microsoft.CodeAnalysis.Text;
|
@@ -102,55 +103,154 @@ public static ExitCode Run(string[] args)
|
102 | 103 |
|
103 | 104 | try
|
104 | 105 | {
|
105 |
| - if (options.ProjectsToLoad.Any()) |
| 106 | + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); |
| 107 | + var pathTransformer = new PathTransformer(canonicalPathCache); |
| 108 | + |
| 109 | + if (options.BinaryLogPath is string binlogPath) |
106 | 110 | {
|
107 |
| - AddSourceFilesFromProjects(options.ProjectsToLoad, options.CompilerArguments, logger); |
| 111 | + logger.LogInfo(" Running binary log analysis."); |
| 112 | + return RunBinaryLogAnalysis(analyzerStopwatch, options, binlogPath, logger, canonicalPathCache, pathTransformer); |
108 | 113 | }
|
109 |
| - |
110 |
| - var compilerVersion = new CompilerVersion(options); |
111 |
| - if (compilerVersion.SkipExtraction) |
| 114 | + else |
112 | 115 | {
|
113 |
| - logger.LogWarning($" Unrecognized compiler '{compilerVersion.SpecifiedCompiler}' because {compilerVersion.SkipReason}"); |
114 |
| - return ExitCode.Ok; |
| 116 | + logger.LogInfo(" Running tracing analysis."); |
| 117 | + return RunTracingAnalysis(analyzerStopwatch, options, logger, canonicalPathCache, pathTransformer); |
115 | 118 | }
|
| 119 | + } |
| 120 | + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] |
| 121 | + { |
| 122 | + logger.LogError($" Unhandled exception: {ex}"); |
| 123 | + return ExitCode.Errors; |
| 124 | + } |
| 125 | + } |
116 | 126 |
|
117 |
| - var workingDirectory = Directory.GetCurrentDirectory(); |
118 |
| - var compilerArgs = options.CompilerArguments.ToArray(); |
| 127 | + private static ExitCode RunBinaryLogAnalysis(Stopwatch stopwatch, Options options, string binlogPath, ILogger logger, CanonicalPathCache canonicalPathCache, PathTransformer pathTransformer) |
| 128 | + { |
| 129 | + logger.LogInfo($"Reading compiler calls from binary log {binlogPath}"); |
| 130 | + try |
| 131 | + { |
| 132 | + using var fileStream = new FileStream(binlogPath, FileMode.Open, FileAccess.Read, FileShare.Read); |
| 133 | + using var reader = BinaryLogReader.Create(fileStream); |
119 | 134 |
|
120 |
| - var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); |
121 |
| - var pathTransformer = new PathTransformer(canonicalPathCache); |
| 135 | + // Filter out compiler calls that aren't interesting for examination |
| 136 | + static bool filter(CompilerCall compilerCall) |
| 137 | + { |
| 138 | + return compilerCall.IsCSharp && |
| 139 | + compilerCall.Kind == CompilerCallKind.Regular; |
| 140 | + } |
122 | 141 |
|
123 |
| - using var analyser = new TracingAnalyser(new LogProgressMonitor(logger), logger, pathTransformer, canonicalPathCache, options.AssemblySensitiveTrap); |
| 142 | + var allCompilationData = reader.ReadAllCompilationData(filter); |
| 143 | + var allFailed = true; |
124 | 144 |
|
125 |
| - var compilerArguments = CSharpCommandLineParser.Default.Parse( |
126 |
| - compilerVersion.ArgsWithResponse, |
127 |
| - workingDirectory, |
128 |
| - compilerVersion.FrameworkPath, |
129 |
| - compilerVersion.AdditionalReferenceDirectories |
130 |
| - ); |
| 145 | + logger.LogInfo($" Found {allCompilationData.Count} compilations in binary log"); |
131 | 146 |
|
132 |
| - if (compilerArguments is null) |
| 147 | + foreach (var compilationData in allCompilationData) |
133 | 148 | {
|
134 |
| - var sb = new StringBuilder(); |
135 |
| - sb.Append(" Failed to parse command line: ").AppendList(" ", compilerArgs); |
136 |
| - logger.LogError(sb.ToString()); |
137 |
| - ++analyser.CompilationErrors; |
138 |
| - return ExitCode.Failed; |
139 |
| - } |
| 149 | + if (compilationData.GetCompilationAfterGenerators() is not CSharpCompilation compilation) |
| 150 | + { |
| 151 | + logger.LogError(" Compilation data is not C#"); |
| 152 | + continue; |
| 153 | + } |
140 | 154 |
|
141 |
| - if (!analyser.BeginInitialize(compilerVersion.ArgsWithResponse)) |
142 |
| - { |
143 |
| - logger.LogInfo("Skipping extraction since files have already been extracted"); |
144 |
| - return ExitCode.Ok; |
| 155 | + var compilerCall = compilationData.CompilerCall; |
| 156 | + var diagnosticName = compilerCall.GetDiagnosticName(); |
| 157 | + logger.LogInfo($" Processing compilation {diagnosticName} at {compilerCall.ProjectDirectory}"); |
| 158 | + var compilerArgs = compilerCall.GetArguments(); |
| 159 | + |
| 160 | + var compilationIdentifierPath = string.Empty; |
| 161 | + try |
| 162 | + { |
| 163 | + compilationIdentifierPath = FileUtils.ConvertPathToSafeRelativePath( |
| 164 | + Path.GetRelativePath(Directory.GetCurrentDirectory(), compilerCall.ProjectDirectory)); |
| 165 | + } |
| 166 | + catch (ArgumentException exc) |
| 167 | + { |
| 168 | + logger.LogWarning($" Failed to get relative path for {compilerCall.ProjectDirectory} from current working directory {Directory.GetCurrentDirectory()}: {exc.Message}"); |
| 169 | + } |
| 170 | + |
| 171 | + var args = reader.ReadCommandLineArguments(compilerCall); |
| 172 | + var generatedSyntaxTrees = compilationData.GetGeneratedSyntaxTrees(); |
| 173 | + |
| 174 | + using var analyser = new BinaryLogAnalyser(new LogProgressMonitor(logger), logger, pathTransformer, canonicalPathCache, options.AssemblySensitiveTrap); |
| 175 | + |
| 176 | + var exit = Analyse(stopwatch, analyser, options, |
| 177 | + references => [() => compilation.References.ForEach(r => references.Add(r))], |
| 178 | + (analyser, syntaxTrees) => [() => syntaxTrees.AddRange(compilation.SyntaxTrees)], |
| 179 | + (syntaxTrees, references) => compilation, |
| 180 | + (compilation, options) => analyser.Initialize( |
| 181 | + compilerCall.ProjectDirectory, |
| 182 | + compilerArgs?.ToArray() ?? [], |
| 183 | + TracingAnalyser.GetOutputName(compilation, args), |
| 184 | + compilation, |
| 185 | + generatedSyntaxTrees, |
| 186 | + Path.Combine(compilationIdentifierPath, diagnosticName), |
| 187 | + options), |
| 188 | + () => { }); |
| 189 | + |
| 190 | + switch (exit) |
| 191 | + { |
| 192 | + case ExitCode.Ok: |
| 193 | + allFailed &= false; |
| 194 | + logger.LogInfo($" Compilation {diagnosticName} succeeded"); |
| 195 | + break; |
| 196 | + case ExitCode.Errors: |
| 197 | + allFailed &= false; |
| 198 | + logger.LogWarning($" Compilation {diagnosticName} had errors"); |
| 199 | + break; |
| 200 | + case ExitCode.Failed: |
| 201 | + logger.LogWarning($" Compilation {diagnosticName} failed"); |
| 202 | + break; |
| 203 | + } |
145 | 204 | }
|
| 205 | + return allFailed ? ExitCode.Failed : ExitCode.Ok; |
| 206 | + } |
| 207 | + catch (IOException ex) |
| 208 | + { |
| 209 | + logger.LogError($"Failed to open binary log: {ex.Message}"); |
| 210 | + return ExitCode.Failed; |
| 211 | + } |
| 212 | + } |
146 | 213 |
|
147 |
| - return AnalyseTracing(workingDirectory, compilerArgs, analyser, compilerArguments, options, analyzerStopwatch); |
| 214 | + private static ExitCode RunTracingAnalysis(Stopwatch analyzerStopwatch, Options options, ILogger logger, CanonicalPathCache canonicalPathCache, PathTransformer pathTransformer) |
| 215 | + { |
| 216 | + if (options.ProjectsToLoad.Any()) |
| 217 | + { |
| 218 | + AddSourceFilesFromProjects(options.ProjectsToLoad, options.CompilerArguments, logger); |
148 | 219 | }
|
149 |
| - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] |
| 220 | + |
| 221 | + var compilerVersion = new CompilerVersion(options); |
| 222 | + if (compilerVersion.SkipExtraction) |
150 | 223 | {
|
151 |
| - logger.LogError($" Unhandled exception: {ex}"); |
152 |
| - return ExitCode.Errors; |
| 224 | + logger.LogWarning($" Unrecognized compiler '{compilerVersion.SpecifiedCompiler}' because {compilerVersion.SkipReason}"); |
| 225 | + return ExitCode.Ok; |
| 226 | + } |
| 227 | + |
| 228 | + var workingDirectory = Directory.GetCurrentDirectory(); |
| 229 | + var compilerArgs = options.CompilerArguments.ToArray(); |
| 230 | + using var analyser = new TracingAnalyser(new LogProgressMonitor(logger), logger, pathTransformer, canonicalPathCache, options.AssemblySensitiveTrap); |
| 231 | + var compilerArguments = CSharpCommandLineParser.Default.Parse( |
| 232 | + compilerVersion.ArgsWithResponse, |
| 233 | + workingDirectory, |
| 234 | + compilerVersion.FrameworkPath, |
| 235 | + compilerVersion.AdditionalReferenceDirectories |
| 236 | + ); |
| 237 | + |
| 238 | + if (compilerArguments is null) |
| 239 | + { |
| 240 | + var sb = new StringBuilder(); |
| 241 | + sb.Append(" Failed to parse command line: ").AppendList(" ", compilerArgs); |
| 242 | + logger.LogError(sb.ToString()); |
| 243 | + ++analyser.CompilationErrors; |
| 244 | + return ExitCode.Failed; |
| 245 | + } |
| 246 | + |
| 247 | + if (!analyser.BeginInitialize(compilerVersion.ArgsWithResponse)) |
| 248 | + { |
| 249 | + logger.LogInfo("Skipping extraction since files have already been extracted"); |
| 250 | + return ExitCode.Ok; |
153 | 251 | }
|
| 252 | + |
| 253 | + return AnalyseTracing(workingDirectory, compilerArgs, analyser, compilerArguments, options, analyzerStopwatch); |
154 | 254 | }
|
155 | 255 |
|
156 | 256 | private static void AddSourceFilesFromProjects(IEnumerable<string> projectsToLoad, IList<string> compilerArguments, ILogger logger)
|
|
0 commit comments