diff --git a/src/Basic.CompilerLog.UnitTests/Basic.CompilerLog.UnitTests.csproj b/src/Basic.CompilerLog.UnitTests/Basic.CompilerLog.UnitTests.csproj index ba5c1fc..b2293c6 100644 --- a/src/Basic.CompilerLog.UnitTests/Basic.CompilerLog.UnitTests.csproj +++ b/src/Basic.CompilerLog.UnitTests/Basic.CompilerLog.UnitTests.csproj @@ -36,6 +36,9 @@ MetadataVersion1.console.complog + + linux-console.complog + diff --git a/src/Basic.CompilerLog.UnitTests/CompilerLogReaderTests.cs b/src/Basic.CompilerLog.UnitTests/CompilerLogReaderTests.cs index cdc63f8..e97737c 100644 --- a/src/Basic.CompilerLog.UnitTests/CompilerLogReaderTests.cs +++ b/src/Basic.CompilerLog.UnitTests/CompilerLogReaderTests.cs @@ -354,13 +354,15 @@ public void MetadataCompat(string resourceName) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { using var stream = ResourceLoader.GetResourceStream(resourceName); - using var reader = CompilerLogReader.Create(stream, leaveOpen: true, BasicAnalyzerHostOptions.None); - foreach (var compilerCall in reader.ReadAllCompilerCalls()) - { - var data = reader.ReadCompilationData(compilerCall); - var result = data.EmitToMemory(); - Assert.True(result.Success); - } + Assert.Throws(() => CompilerLogReader.Create(stream, leaveOpen: true, BasicAnalyzerHostOptions.None)); } } + + [Fact] + public void Disposed() + { + var reader = CompilerLogReader.Create(Fixture.ConsoleComplogPath.Value); + reader.Dispose(); + Assert.Throws(() => reader.ReadCompilationData(0)); + } } diff --git a/src/Basic.CompilerLog.UnitTests/ProgramTests.cs b/src/Basic.CompilerLog.UnitTests/ProgramTests.cs index ceeb30e..edcd8f7 100644 --- a/src/Basic.CompilerLog.UnitTests/ProgramTests.cs +++ b/src/Basic.CompilerLog.UnitTests/ProgramTests.cs @@ -257,6 +257,22 @@ public void ResponseProjectFilter() Assert.Contains("Program.cs", File.ReadAllLines(rsp)); } + [Fact] + public void ResponseOnCompilerLog() + { + var complogPath = Path.Combine(RootDirectory, "msbuild.complog"); + Assert.Empty(CompilerLogUtil.ConvertBinaryLog(Fixture.SolutionBinaryLogPath, complogPath)); + Assert.Equal(Constants.ExitSuccess, RunCompLog($"rsp {complogPath} -p console.csproj")); + } + + [Fact] + public void ResponseOnInvalidFileType() + { + var (exitCode, output) = RunCompLogEx($"rsp data.txt -p console.csproj"); + Assert.Equal(Constants.ExitFailure, exitCode); + Assert.Contains("Not a valid log", output); + } + [Fact] public void ResponseAll() { @@ -283,6 +299,19 @@ public void ResponseBadOption() Assert.Contains("complog rsp [OPTIONS]", output); } + [Fact] + public void ResponseLinuxComplog() + { + var path = Path.Combine(RootDirectory, "console.complog"); + File.WriteAllBytes(path, ResourceLoader.GetResourceBlob("linux-console.complog")); + var (exitCode, output) = RunCompLogEx($"rsp {path}"); + Assert.Equal(Constants.ExitSuccess, exitCode); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Contains("generated on different operating system", output); + } + } + [Theory] [InlineData("")] [InlineData("--exclude-analyzers")] diff --git a/src/Basic.CompilerLog.UnitTests/Resources/linux-console.complog b/src/Basic.CompilerLog.UnitTests/Resources/linux-console.complog new file mode 100644 index 0000000..b0551b1 Binary files /dev/null and b/src/Basic.CompilerLog.UnitTests/Resources/linux-console.complog differ diff --git a/src/Basic.CompilerLog.UnitTests/UsingAllCompilerLogTests.cs b/src/Basic.CompilerLog.UnitTests/UsingAllCompilerLogTests.cs index 9f32e25..621c0b6 100644 --- a/src/Basic.CompilerLog.UnitTests/UsingAllCompilerLogTests.cs +++ b/src/Basic.CompilerLog.UnitTests/UsingAllCompilerLogTests.cs @@ -59,6 +59,20 @@ public async Task EmitToMemory() } } + [Fact] + public async Task CommandLineArguments() + { + await foreach (var complogPath in Fixture.GetAllCompilerLogs(TestOutputHelper)) + { + TestOutputHelper.WriteLine(complogPath); + using var reader = CompilerLogReader.Create(complogPath, options: BasicAnalyzerHostOptions.None); + foreach (var data in reader.ReadAllCompilerCalls()) + { + Assert.NotEmpty(data.GetArguments()); + } + } + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/src/Basic.CompilerLog.Util/CompilerLogReader.cs b/src/Basic.CompilerLog.Util/CompilerLogReader.cs index fe73946..dc123b4 100644 --- a/src/Basic.CompilerLog.Util/CompilerLogReader.cs +++ b/src/Basic.CompilerLog.Util/CompilerLogReader.cs @@ -25,27 +25,34 @@ namespace Basic.CompilerLog.Util; -public abstract class CompilerLogReader : IDisposable +public sealed class CompilerLogReader : IDisposable { public static int LatestMetadataVersion => Metadata.LatestMetadataVersion; + /// + /// Stores the underlying archive this reader is using. Do not use directly. Instead + /// use which will throw if the reader is disposed + /// + private ZipArchive _zipArchiveCore; + private readonly Dictionary _refMap = new(); private readonly Dictionary _mvidToRefInfoMap = new(); private readonly Dictionary _analyzersMap = new(); private readonly bool _ownsCompilerLogState; - private readonly Dictionary _compilationInfoMap = new(); + private readonly Dictionary _compilationInfoMap = new(); public BasicAnalyzerHostOptions BasicAnalyzerHostOptions { get; } internal CompilerLogState CompilerLogState { get; } - internal ZipArchive ZipArchive { get; private set; } internal Metadata Metadata { get; } internal int Count => Metadata.Count; public int MetadataVersion => Metadata.MetadataVersion; public bool IsWindowsLog => Metadata.IsWindows == true; + public bool IsDisposed => _zipArchiveCore is null; + internal ZipArchive ZipArchive => !IsDisposed ? _zipArchiveCore : throw new ObjectDisposedException(nameof(CompilerLogReader)); - internal CompilerLogReader(ZipArchive zipArchive, Metadata metadata, BasicAnalyzerHostOptions basicAnalyzersOptions, CompilerLogState? state) + private CompilerLogReader(ZipArchive zipArchive, Metadata metadata, BasicAnalyzerHostOptions basicAnalyzersOptions, CompilerLogState? state) { - ZipArchive = zipArchive; + _zipArchiveCore = zipArchive; CompilerLogState = state ?? new CompilerLogState(); _ownsCompilerLogState = state is null; BasicAnalyzerHostOptions = basicAnalyzersOptions; @@ -77,8 +84,8 @@ public static CompilerLogReader Create( var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen); var metadata = ReadMetadata(); return metadata.MetadataVersion switch { - 1 => new CompilerLogReaderVersion1(zipArchive, metadata, options, state), - 2 => new CompilerLogReaderVersion2(zipArchive, metadata, options, state), + 1 => throw new ArgumentException("Version 1 compiler logs are no longer supported"), + 2 => new CompilerLogReader(zipArchive, metadata, options, state), _ => throw GetInvalidCompilerLogFileException(), }; @@ -108,15 +115,73 @@ public static CompilerLogReader Create( return Create(stream, leaveOpen: false, options, state); } - private protected abstract object ReadCompilationInfo(int index); + private CompilationInfoPack ReadCompilationInfo(int index) + { + using var stream = ZipArchive.OpenEntryOrThrow(GetCompilerEntryName(index)); + return MessagePackSerializer.Deserialize(stream); + } - private protected abstract CompilerCall ReadCompilerCallCore(int index, object rawInfo); + private CompilerCall ReadCompilerCallCore(int index, CompilationInfoPack pack) + { + return new CompilerCall( + pack.ProjectFilePath, + pack.CompilerCallKind, + pack.TargetFramework, + pack.IsCSharp, + new Lazy(() => GetContentPack(pack.CommandLineArgsHash)), + index); + } - private protected abstract RawCompilationData ReadRawCompilationDataCore(int index, object rawInfo); + private RawCompilationData ReadRawCompilationDataCore(int index, CompilationInfoPack pack) + { + var dataPack = GetContentPack(pack.CompilationDataPackHash); + + var references = dataPack + .References + .Select(x => new RawReferenceData(x.Mvid, x.Aliases, x.EmbedInteropTypes)) + .ToList(); + var analyzers = dataPack + .Analyzers + .Select(x => new RawAnalyzerData(x.Mvid, x.FilePath)) + .ToList(); + var contents = dataPack + .ContentList + .Select(x => new RawContent(x.Item2.FilePath, x.Item2.ContentHash, (RawContentKind)x.Item1)) + .ToList(); + var resources = dataPack + .Resources + .Select(x => new RawResourceData(x.ContentHash, CreateResourceDescription(this, x))) + .ToList(); + + return new RawCompilationData( + index, + compilationName: dataPack.ValueMap["compilationName"], + assemblyFileName: dataPack.ValueMap["assemblyFileName"]!, + xmlFilePath: dataPack.ValueMap["xmlFilePath"], + outputDirectory: dataPack.ValueMap["outputDirectory"], + dataPack.ChecksumAlgorithm, + references, + analyzers, + contents, + resources, + pack.IsCSharp, + dataPack.IncludesGeneratedText); + + static ResourceDescription CreateResourceDescription(CompilerLogReader reader, ResourcePack pack) + { + var dataProvider = () => + { + var bytes = reader.GetContentBytes(pack.ContentHash); + return new MemoryStream(bytes); + }; - private protected abstract (EmitOptions, ParseOptions, CompilationOptions) ReadCompilerOptionsCore(int index, object rawInfo); + return string.IsNullOrEmpty(pack.FileName) + ? new ResourceDescription(pack.Name, dataProvider, pack.IsPublic) + : new ResourceDescription(pack.Name, pack.FileName, dataProvider, pack.IsPublic); + } + } - private object GetOrReadCompilationInfo(int index) + private CompilationInfoPack GetOrReadCompilationInfo(int index) { if (!_compilationInfoMap.TryGetValue(index, out var info)) { @@ -154,8 +219,33 @@ public List ReadAllCompilerCalls(Func? predica public (EmitOptions EmitOptions, ParseOptions ParseOptions, CompilationOptions CompilationOptions) ReadCompilerOptions(CompilerCall compilerCall) { var index = GetIndex(compilerCall); - var info = GetOrReadCompilationInfo(index); - return ReadCompilerOptionsCore(index, info); + var pack = GetOrReadCompilationInfo(index); + return ReadCompilerOptions(pack); + } + + private (EmitOptions EmitOptions, ParseOptions ParseOptions, CompilationOptions CompilationOptions) ReadCompilerOptions(CompilationInfoPack pack) + { + var emitOptions = MessagePackUtil.CreateEmitOptions(GetContentPack(pack.EmitOptionsHash)); + ParseOptions parseOptions; + CompilationOptions compilationOptions; + if (pack.IsCSharp) + { + var parseTuple = GetContentPack<(ParseOptionsPack, CSharpParseOptionsPack)>(pack.ParseOptionsHash); + parseOptions = MessagePackUtil.CreateCSharpParseOptions(parseTuple.Item1, parseTuple.Item2); + + var optionsTuple = GetContentPack<(CompilationOptionsPack, CSharpCompilationOptionsPack)>(pack.CompilationOptionsHash); + compilationOptions = MessagePackUtil.CreateCSharpCompilationOptions(optionsTuple.Item1, optionsTuple.Item2); + } + else + { + var parseTuple = GetContentPack<(ParseOptionsPack, VisualBasicParseOptionsPack)>(pack.ParseOptionsHash); + parseOptions = MessagePackUtil.CreateVisualBasicParseOptions(parseTuple.Item1, parseTuple.Item2); + + var optionsTuple = GetContentPack<(CompilationOptionsPack, VisualBasicCompilationOptionsPack, ParseOptionsPack, VisualBasicParseOptionsPack)>(pack.CompilationOptionsHash); + compilationOptions = MessagePackUtil.CreateVisualBasicCompilationOptions(optionsTuple.Item1, optionsTuple.Item2, optionsTuple.Item3, optionsTuple.Item4); + } + + return (emitOptions, parseOptions, compilationOptions); } public CompilationData ReadCompilationData(int index) => @@ -163,11 +253,10 @@ public CompilationData ReadCompilationData(int index) => public CompilationData ReadCompilationData(CompilerCall compilerCall) { - var index = GetIndex(compilerCall); - var info = GetOrReadCompilationInfo(index); + var pack = GetOrReadCompilationInfo(GetIndex(compilerCall)); var rawCompilationData = ReadRawCompilationData(compilerCall); var referenceList = GetMetadataReferences(rawCompilationData.References); - var (emitOptions, rawParseOptions, compilationOptions) = ReadCompilerOptionsCore(index, info); + var (emitOptions, rawParseOptions, compilationOptions) = ReadCompilerOptions(pack); var hashAlgorithm = rawCompilationData.ChecksumAlgorithm; var sourceTextList = new List<(SourceText SourceText, string Path)>(); @@ -606,13 +695,13 @@ internal void CopyAssemblyBytes(Guid mvid, Stream destination) public void Dispose() { - if (ZipArchive is null) + if (IsDisposed) { return; } ZipArchive.Dispose(); - ZipArchive = null!; + _zipArchiveCore = null!; if (_ownsCompilerLogState) { diff --git a/src/Basic.CompilerLog.Util/CompilerLogReaderVersion1.cs b/src/Basic.CompilerLog.Util/CompilerLogReaderVersion1.cs deleted file mode 100644 index 3e9edc3..0000000 --- a/src/Basic.CompilerLog.Util/CompilerLogReaderVersion1.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.IO.Compression; -using Basic.CompilerLog.Util; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; -using CompilationInfo = System.Tuple; -using static Basic.CompilerLog.Util.CommonUtil; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.VisualBasic; -using Microsoft.CodeAnalysis.Emit; -using System.Collections.Immutable; - -namespace Basic.CompilerLog.Util; - -internal sealed class CompilerLogReaderVersion1 : CompilerLogReader -{ - internal CompilerLogReaderVersion1(ZipArchive zipArchive, Metadata metadata, BasicAnalyzerHostOptions basicAnalyzersOptions, CompilerLogState? state) - :base(zipArchive, metadata, basicAnalyzersOptions, state) - { - - } - - private protected override object ReadCompilationInfo(int index) - { - using var reader = Polyfill.NewStreamReader(ZipArchive.OpenEntryOrThrow(GetCompilerEntryName(index)), ContentEncoding, leaveOpen: false); - var compilerCall = ReadCompilerCallCore(reader, index); - var commandLineArguments = compilerCall.ParseArguments(); - return new CompilationInfo(compilerCall, commandLineArguments); - } - - private protected override CompilerCall ReadCompilerCallCore(int index, object rawInfo) - { - var info = (CompilationInfo)rawInfo; - return info.Item1; - } - - private CompilerCall ReadCompilerCallCore(StreamReader reader, int index) - { - var projectFile = reader.ReadLineOrThrow(); - var isCSharp = reader.ReadLineOrThrow() == "C#"; - var targetFramework = reader.ReadLineOrThrow(); - if (string.IsNullOrEmpty(targetFramework)) - { - targetFramework = null; - } - - var kind = (CompilerCallKind)Enum.Parse(typeof(CompilerCallKind), reader.ReadLineOrThrow()); - var count = int.Parse(reader.ReadLineOrThrow()); - var arguments = new string[count]; - for (int i = 0; i < count; i++) - { - arguments[i] = reader.ReadLineOrThrow(); - } - - return new CompilerCall(projectFile, kind, targetFramework, isCSharp, new Lazy(() => arguments), index); - } - - private protected override RawCompilationData ReadRawCompilationDataCore(int index, object rawInfo) - { - var info = (CompilationInfo)rawInfo; - var args = info.Item2; - using var reader = Polyfill.NewStreamReader(ZipArchive.OpenEntryOrThrow(GetCompilerEntryName(index)), ContentEncoding, leaveOpen: false); - var compilerCall = ReadCompilerCallCore(reader, index); - - var references = new List(); - var analyzers = new List(); - var contents = new List(); - var resources = new List(); - var readGeneratedFiles = false; - - while (reader.ReadLine() is string line) - { - var colonIndex = line.IndexOf(':'); - switch (line.AsSpan().Slice(0, colonIndex)) - { - case "m": - ParseMetadataReference(line); - break; - case "a": - ParseAnalyzer(line); - break; - case "source": - ParseContent(line, RawContentKind.SourceText); - break; - case "generated": - ParseContent(line, RawContentKind.GeneratedText); - break; - case "generatedResult": - readGeneratedFiles = ParseBool(); - break; - case "config": - ParseContent(line, RawContentKind.AnalyzerConfig); - break; - case "text": - ParseContent(line, RawContentKind.AdditionalText); - break; - case "embed": - ParseContent(line, RawContentKind.Embed); - break; - case "embedline": - ParseContent(line, RawContentKind.EmbedLine); - break; - case "link": - ParseContent(line, RawContentKind.SourceLink); - break; - case "ruleset": - ParseContent(line, RawContentKind.RuleSet); - break; - case "appconfig": - ParseContent(line, RawContentKind.AppConfig); - break; - case "win32manifest": - ParseContent(line, RawContentKind.Win32Manifest); - break; - case "win32resource": - ParseContent(line, RawContentKind.Win32Resource); - break; - case "cryptokeyfile": - ParseContent(line, RawContentKind.CryptoKeyFile); - break; - case "r": - ParseResource(line); - break; - case "win32icon": - ParseContent(line, RawContentKind.Win32Icon); - break; - default: - throw new InvalidOperationException($"Unrecognized line: {line}"); - } - - bool ParseBool() => - colonIndex + 1 < line.Length && - line[colonIndex + 1] == '1'; - } - - var assemblyFileName = Path.GetFileNameWithoutExtension(compilerCall.ProjectFileName); - var data = new RawCompilationData( - index, - args.CompilationName, - assemblyFileName, - args.DocumentationPath, - args.OutputDirectory, - args.ChecksumAlgorithm, - references, - analyzers, - contents, - resources, - isCSharp: compilerCall.IsCSharp, - readGeneratedFiles); - - return data; - - void ParseMetadataReference(string line) - { - var items = line.Split(':'); - if (items.Length == 5 && - Guid.TryParse(items[1], out var mvid) && - int.TryParse(items[2], out var kind)) - { - var embedInteropTypes = items[3] == "1"; - - var aliases = ImmutableArray.Empty; - if (!string.IsNullOrEmpty(items[4])) - { - aliases = items[4].Split(',').ToImmutableArray(); - } - - references.Add(new RawReferenceData( - mvid, - aliases, - embedInteropTypes)); - return; - } - - throw new InvalidOperationException(); - } - - void ParseContent(string line, RawContentKind kind) - { - var items = line.Split(':', count: 3); - contents.Add(new(items[2], items[1], kind)); - } - - void ParseResource(string line) - { - var items = line.Split(':', count: 5); - var fileName = items[4]; - var isPublic = bool.Parse(items[3]); - var contentHash = items[1]; - var dataProvider = () => - { - var bytes = GetContentBytes(contentHash); - return new MemoryStream(bytes); - }; - - var d = string.IsNullOrEmpty(fileName) - ? new ResourceDescription(items[2], dataProvider, isPublic) - : new ResourceDescription(items[2], fileName, dataProvider, isPublic); - resources.Add(new(contentHash, d)); - } - - void ParseAnalyzer(string line) - { - var items = line.Split(':', count: 3); - var mvid = Guid.Parse(items[1]); - analyzers.Add(new RawAnalyzerData(mvid, items[2])); - } - } - - private protected override (EmitOptions, ParseOptions, CompilationOptions) ReadCompilerOptionsCore(int index, object rawInfo) - { - var info = (CompilationInfo)rawInfo; - var args = info.Item2; - return (args.EmitOptions, args.ParseOptions, args.CompilationOptions); - } -} \ No newline at end of file diff --git a/src/Basic.CompilerLog.Util/CompilerLogReaderVersion2.cs b/src/Basic.CompilerLog.Util/CompilerLogReaderVersion2.cs deleted file mode 100644 index b5b09f5..0000000 --- a/src/Basic.CompilerLog.Util/CompilerLogReaderVersion2.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.CodeDom.Compiler; -using System.IO.Compression; -using Basic.CompilerLog.Util; -using Basic.CompilerLog.Util.Serialize; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Emit; -using static Basic.CompilerLog.Util.CommonUtil; - -namespace Basic.CompilerLog.Util; - -internal sealed class CompilerLogReaderVersion2 : CompilerLogReader -{ - internal CompilerLogReaderVersion2(ZipArchive zipArchive, Metadata metadata, BasicAnalyzerHostOptions basicAnalyzersOptions, CompilerLogState? state) - :base(zipArchive, metadata, basicAnalyzersOptions, state) - { - - } - - private protected override object ReadCompilationInfo(int index) - { - using var stream = ZipArchive.OpenEntryOrThrow(GetCompilerEntryName(index)); - return MessagePack.MessagePackSerializer.Deserialize(stream); - } - - private protected override CompilerCall ReadCompilerCallCore(int index, object rawInfo) - { - var pack = (CompilationInfoPack)rawInfo; - return new CompilerCall( - pack.ProjectFilePath, - pack.CompilerCallKind, - pack.TargetFramework, - pack.IsCSharp, - new Lazy(() => GetContentPack(pack.CommandLineArgsHash)), - index); - } - - private protected override RawCompilationData ReadRawCompilationDataCore(int index, object rawInfo) - { - var pack = (CompilationInfoPack)rawInfo; - var dataPack = GetContentPack(pack.CompilationDataPackHash); - - var references = dataPack - .References - .Select(x => new RawReferenceData(x.Mvid, x.Aliases, x.EmbedInteropTypes)) - .ToList(); - var analyzers = dataPack - .Analyzers - .Select(x => new RawAnalyzerData(x.Mvid, x.FilePath)) - .ToList(); - var contents = dataPack - .ContentList - .Select(x => new RawContent(x.Item2.FilePath, x.Item2.ContentHash, (RawContentKind)x.Item1)) - .ToList(); - var resources = dataPack - .Resources - .Select(x => new RawResourceData(x.ContentHash, CreateResourceDescription(this, x))) - .ToList(); - - return new RawCompilationData( - index, - compilationName: dataPack.ValueMap["compilationName"], - assemblyFileName: dataPack.ValueMap["assemblyFileName"]!, - xmlFilePath: dataPack.ValueMap["xmlFilePath"], - outputDirectory: dataPack.ValueMap["outputDirectory"], - dataPack.ChecksumAlgorithm, - references, - analyzers, - contents, - resources, - pack.IsCSharp, - dataPack.IncludesGeneratedText); - - static ResourceDescription CreateResourceDescription(CompilerLogReader reader, ResourcePack pack) - { - var dataProvider = () => - { - var bytes = reader.GetContentBytes(pack.ContentHash); - return new MemoryStream(bytes); - }; - - return string.IsNullOrEmpty(pack.FileName) - ? new ResourceDescription(pack.Name, dataProvider, pack.IsPublic) - : new ResourceDescription(pack.Name, pack.FileName, dataProvider, pack.IsPublic); - } - } - - private protected override (EmitOptions, ParseOptions, CompilationOptions) ReadCompilerOptionsCore(int index, object rawInfo) - { - var pack = (CompilationInfoPack)rawInfo; - var emitOptions = MessagePackUtil.CreateEmitOptions(GetContentPack(pack.EmitOptionsHash)); - ParseOptions parseOptions; - CompilationOptions compilationOptions; - if (pack.IsCSharp) - { - var parseTuple = GetContentPack<(ParseOptionsPack, CSharpParseOptionsPack)>(pack.ParseOptionsHash); - parseOptions = MessagePackUtil.CreateCSharpParseOptions(parseTuple.Item1, parseTuple.Item2); - - var optionsTuple = GetContentPack<(CompilationOptionsPack, CSharpCompilationOptionsPack)>(pack.CompilationOptionsHash); - compilationOptions = MessagePackUtil.CreateCSharpCompilationOptions(optionsTuple.Item1, optionsTuple.Item2); - } - else - { - var parseTuple = GetContentPack<(ParseOptionsPack, VisualBasicParseOptionsPack)>(pack.ParseOptionsHash); - parseOptions = MessagePackUtil.CreateVisualBasicParseOptions(parseTuple.Item1, parseTuple.Item2); - - var optionsTuple = GetContentPack<(CompilationOptionsPack, VisualBasicCompilationOptionsPack, ParseOptionsPack, VisualBasicParseOptionsPack)>(pack.CompilationOptionsHash); - compilationOptions = MessagePackUtil.CreateVisualBasicCompilationOptions(optionsTuple.Item1, optionsTuple.Item2, optionsTuple.Item3, optionsTuple.Item4); - } - - return (emitOptions, parseOptions, compilationOptions); - } -} \ No newline at end of file diff --git a/src/Basic.CompilerLog.Util/CompilerLogUtil.cs b/src/Basic.CompilerLog.Util/CompilerLogUtil.cs index 5d7bed6..cceef92 100644 --- a/src/Basic.CompilerLog.Util/CompilerLogUtil.cs +++ b/src/Basic.CompilerLog.Util/CompilerLogUtil.cs @@ -109,18 +109,6 @@ public static ConvertBinaryLogResult TryConvertBinaryLog(Stream binaryLogStream, return new ConvertBinaryLogResult(success, included, diagnostics); } - public static List ReadAllCompilerCalls(string compilerLogFilePath, Func? predicate = null) - { - using var compilerLogStream = new FileStream(compilerLogFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); - return ReadAllCompilerCalls(compilerLogStream, predicate); - } - - public static List ReadAllCompilerCalls(Stream compilerLogStream, Func? predicate = null) - { - using var reader = CompilerLogReader.Create(compilerLogStream); - return reader.ReadAllCompilerCalls(predicate); - } - private static Exception CreateException(string message, IEnumerable diagnostics) { var builder = new StringBuilder(); diff --git a/src/Basic.CompilerLog/Program.cs b/src/Basic.CompilerLog/Program.cs index 4ece268..07aa5a7 100644 --- a/src/Basic.CompilerLog/Program.cs +++ b/src/Basic.CompilerLog/Program.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis; using Mono.Options; using StructuredLogViewer; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.Loader; using System.Text; @@ -11,22 +12,13 @@ var (command, rest) = args.Length == 0 ? ("help", Enumerable.Empty()) : (args[0], args.Skip(1)); -// a CancellationToken that is canceled when the user hits Ctrl+C. -var cts = new CancellationTokenSource(); - -Console.CancelKeyPress += (s, e) => -{ - WriteLine("Canceling..."); - cts.Cancel(); - e.Cancel = true; -}; try { return command.ToLower() switch { "create" => RunCreate(rest), - "replay" => RunReplay(rest, cts.Token), + "replay" => RunReplay(rest), "export" => RunExport(rest), "ref" => RunReferences(rest), "rsp" => RunResponseFile(rest), @@ -35,8 +27,8 @@ "help" => RunHelp(rest), // Older option names - "diagnostics" => RunReplay(rest, cts.Token), - "emit" => RunReplay(rest, cts.Token), + "diagnostics" => RunReplay(rest), + "emit" => RunReplay(rest), _ => RunBadCommand(command) }; } @@ -170,9 +162,8 @@ int RunPrint(IEnumerable args) } using var compilerLogStream = GetOrCreateCompilerLogStream(extra); - var compilerCalls = CompilerLogUtil.ReadAllCompilerCalls( - compilerLogStream, - options.FilterCompilerCalls); + using var reader = GetCompilerLogReader(compilerLogStream, leaveOpen: true); + var compilerCalls = reader.ReadAllCompilerCalls(options.FilterCompilerCalls); foreach (var compilerCall in compilerCalls) { @@ -350,7 +341,7 @@ int RunResponseFile(IEnumerable args) return ExitSuccess; } - var compilerCalls = GetCompilerCalls(extra, options.FilterCompilerCalls); + var (disposable, compilerCalls) = GetCompilerCalls(extra, options.FilterCompilerCalls); baseOutputPath = GetBaseOutputPath(baseOutputPath); WriteLine($"Generating response files in {baseOutputPath}"); Directory.CreateDirectory(baseOutputPath); @@ -365,6 +356,7 @@ int RunResponseFile(IEnumerable args) ExportUtil.ExportRsp(compilerCall, writer, singleLine); } + disposable.Dispose(); return ExitSuccess; } catch (OptionException e) @@ -379,9 +371,26 @@ void PrintUsage() WriteLine("complog rsp [OPTIONS] msbuild.complog"); options.WriteOptionDescriptions(Out); } + + (IDisposable disposable, List) GetCompilerCalls(List extra, Func? predicate) + { + var logFilePath = GetLogFilePath(extra); + var stream = new FileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); + var ext = Path.GetExtension(logFilePath); + if (ext is ".binlog") + { + return (stream, BinaryLogUtil.ReadAllCompilerCalls(stream, new(), predicate)); + } + else + { + Debug.Assert(ext is ".complog"); + var reader = GetCompilerLogReader(stream, leaveOpen: false); + return (reader, reader.ReadAllCompilerCalls(predicate)); + } + } } -int RunReplay(IEnumerable args, CancellationToken cancellationToken) +int RunReplay(IEnumerable args) { var baseOutputPath = ""; var severity = DiagnosticSeverity.Warning; @@ -428,8 +437,6 @@ int RunReplay(IEnumerable args, CancellationToken cancellationToken) for (int i = 0; i < compilerCalls.Count; i++) { - cancellationToken.ThrowIfCancellationRequested(); - var compilerCall = compilerCalls[i]; Write($"{compilerCall.GetDiagnosticName()} ..."); @@ -442,11 +449,11 @@ int RunReplay(IEnumerable args, CancellationToken cancellationToken) { var path = GetOutputPath(baseOutputPath, compilerCalls, i, "emit"); Directory.CreateDirectory(path); - emitResult = compilationData.EmitToDisk(path, cancellationToken); + emitResult = compilationData.EmitToDisk(path); } else { - emitResult = compilationData.EmitToMemory(cancellationToken); + emitResult = compilationData.EmitToMemory(); } WriteLine(emitResult.Success ? "Success" : "Error"); @@ -533,22 +540,6 @@ passed after --. return ExitSuccess; } -List GetCompilerCalls(List extra, Func? predicate) -{ - var logFilePath = GetLogFilePath(extra); - using var stream = new FileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var ext = Path.GetExtension(logFilePath); - switch (ext) - { - case ".binlog": - return BinaryLogUtil.ReadAllCompilerCalls(stream, new(), predicate); - case ".complog": - return CompilerLogUtil.ReadAllCompilerCalls(stream, predicate); - default: - throw new Exception($"Unrecognized file extension: {ext}"); - } -} - CompilerLogReader GetCompilerLogReader(Stream compilerLogStream, bool leaveOpen, BasicAnalyzerHostOptions? options = null) { var reader = CompilerLogReader.Create(compilerLogStream, leaveOpen, options);