From 7d5d45579022d3cb6bc265af81d0bd844bd966db Mon Sep 17 00:00:00 2001 From: AliveDevil Date: Mon, 26 Aug 2024 11:32:49 +0200 Subject: [PATCH 1/3] Handle MSBuild properties NoWarn, TreatWarningsAsErrors, WarningsAsErrors --- .../IkvmToolTaskDiagnosticWriter.cs | 62 +++++++++++++++++-- .../targets/IKVM.Java.Core.NoTasks.targets | 3 + .../targets/IKVM.Java.Core.Tasks.targets | 3 + .../CompilerClassLoader.cs | 8 +-- .../IkvmImporterInternal.cs | 60 +++++++++++------- src/IKVM.Tools.Importer/StaticCompiler.cs | 2 +- .../Importer/IkvmImporterLauncher.cs | 2 +- 7 files changed, 108 insertions(+), 32 deletions(-) diff --git a/src/IKVM.MSBuild.Tasks/IkvmToolTaskDiagnosticWriter.cs b/src/IKVM.MSBuild.Tasks/IkvmToolTaskDiagnosticWriter.cs index 80ec27e7fb..e8f8f04db8 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmToolTaskDiagnosticWriter.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmToolTaskDiagnosticWriter.cs @@ -4,6 +4,8 @@ using IKVM.Tools.Runner; +using Microsoft.Build.Framework; + namespace IKVM.MSBuild.Tasks { @@ -38,24 +40,74 @@ public Task ReceiveAsync(IkvmToolDiagnosticEvent @event) if (@event == null) return Task.CompletedTask; + ( + IkvmToolDiagnosticEventLevel Level, + string Code, + string Message + ) structuredLog; + +#pragma warning disable IDE0079 // Unused suppression (net472 doesn't produce IDE0057) +#pragma warning disable IDE0057 // Suppress net6 & net8 analyzer for range-operations + // unspecified format: "Level: Message" + // MSBuild Format: "Level IKVM0000: Message" + // Longest preamble: "warning IKVM0000: " + // Some write """ + // warning IKVMC0000: Message + // (additional information) + // """ + // Skip these: + // - StdErr is mapped to Information + // - StdOut is mapped to Debug. + if (@event.Message.Length > 0 && @event.Message[0] is char first + && !char.IsWhiteSpace(first) + && @event.Message.IndexOf(": ", 0, 18) is { } firstColon and not -1) + { + structuredLog.Message = @event.Message.Substring(firstColon + 2); + + int levelLength; + if (@event.Message.IndexOf("IKVM", 0, firstColon, StringComparison.OrdinalIgnoreCase) is { } codeIndex and not -1) + { + levelLength = codeIndex - 1; + structuredLog.Code = @event.Message.Substring(codeIndex, 8 /* IKVM0000 */); + } + else + { + levelLength = firstColon; + structuredLog.Code = ""; + } + + structuredLog.Level = @event.Message.Substring(0, levelLength).ToUpperInvariant() switch + { + "ERROR" => IkvmToolDiagnosticEventLevel.Error, + "WARNING" => IkvmToolDiagnosticEventLevel.Warning, + _ => IkvmToolDiagnosticEventLevel.Information + }; + } + else + { + // Can't figure out level. + structuredLog = (@event.Level, null, @event.Message); + } +#pragma warning restore + try { - switch (@event.Level) + switch (structuredLog.Level) { case IkvmToolDiagnosticEventLevel.Debug: - logger.LogMessage(Microsoft.Build.Framework.MessageImportance.Low, @event.Message, @event.MessageArgs); + logger.LogMessage(null, structuredLog.Code, null, null, 0, 0, 0, 0, MessageImportance.Low, structuredLog.Message, @event.MessageArgs); writer?.WriteLine("DEBUG: " + @event.Message, @event.MessageArgs); break; case IkvmToolDiagnosticEventLevel.Information: - logger.LogMessage(Microsoft.Build.Framework.MessageImportance.Normal, @event.Message, @event.MessageArgs); + logger.LogMessage(null, structuredLog.Code, null, null, 0, 0, 0, 0, MessageImportance.Normal, structuredLog.Message, @event.MessageArgs); writer?.WriteLine("INFO: " + @event.Message, @event.MessageArgs); break; case IkvmToolDiagnosticEventLevel.Warning: - logger.LogWarning(@event.Message, @event.MessageArgs); + logger.LogWarning(null, structuredLog.Code, null, null, 0, 0, 0, 0, structuredLog.Message, @event.MessageArgs); writer?.WriteLine("WARN: " + @event.Message, @event.MessageArgs); break; case IkvmToolDiagnosticEventLevel.Error: - logger.LogWarning(@event.Message, @event.MessageArgs); + logger.LogError(null, structuredLog.Code, null, null, 0, 0, 0, 0, structuredLog.Message, @event.MessageArgs); writer?.WriteLine("ERROR: " + @event.Message, @event.MessageArgs); break; } diff --git a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets index 1f028d0d66..f95c9e7460 100644 --- a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets +++ b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets @@ -277,6 +277,9 @@ Value = b.ToString(); <_IkvmCompilerArgs Include="-nologo" /> <_IkvmCompilerArgs Include="-bootstrap" Condition=" '$(Bootstrap)' == 'true' " /> <_IkvmCompilerArgs Include="-debug:$(DebugType)" Condition=" '$(DebugType)' != 'none' " /> + <_IkvmCompilerArgs Include="-nowarn:$(NoWarn.Replace(';', ','))" Condition=" '$(NoWarn)' != '' " /> + <_IkvmCompilerArgs Include="-warnaserror" Condition=" '$(TreatWarningsAsErrors)' == 'true' " /> + <_IkvmCompilerArgs Include="-warnaserror:$(WarningsAsErrors.Replace(';', ','))" Condition=" '$(TreatWarningsAsErrors)' != 'true' And '$(WarningsAsErrors)' != '' " /> <_IkvmCompilerArgs Include="-assembly:$(AssemblyName)" /> <_IkvmCompilerArgs Include="-version:$(AssemblyVersion)" /> <_IkvmCompilerArgs Include="-runtime:$(IkvmRuntimeAssembly)" /> diff --git a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets index affb1f803e..27482bacbd 100644 --- a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets +++ b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets @@ -126,6 +126,9 @@ Platform="$(PlatformTarget.ToLowerInvariant())" Main="$(StartupObject)" Debug="$(DebugType)" + NoWarn="$(NoWarn.Replace(',', ';'))" + WarnAsError="$(TreatWarningsAsErrors)" + WarnAsErrorWarnings="$(WarningsAsErrors.Replace(',', ';'))" KeyFile="$(KeyOriginatorFile)" CompressResources="$(CompressResources)" ClassLoader="$(ClassLoader)" diff --git a/src/IKVM.Tools.Importer/CompilerClassLoader.cs b/src/IKVM.Tools.Importer/CompilerClassLoader.cs index 6c3f3d44f6..6d74873b7b 100644 --- a/src/IKVM.Tools.Importer/CompilerClassLoader.cs +++ b/src/IKVM.Tools.Importer/CompilerClassLoader.cs @@ -3419,8 +3419,8 @@ sealed class CompilerOptions internal uint fileAlignment; internal bool highentropyva; internal List sharedclassloader; // should *not* be deep copied in Copy(), because we want the list of all compilers that share a class loader - internal Dictionary suppressWarnings = new Dictionary(); - internal Dictionary errorWarnings = new Dictionary(); // treat specific warnings as errors + internal HashSet suppressWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); + internal HashSet errorWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); internal bool warnaserror; // treat all warnings as errors internal FileInfo writeSuppressWarningsFile; internal List proxies = new List(); @@ -3442,8 +3442,8 @@ internal CompilerOptions Copy() { copy.externalResources = new Dictionary(externalResources); } - copy.suppressWarnings = new Dictionary(suppressWarnings); - copy.errorWarnings = new Dictionary(errorWarnings); + copy.suppressWarnings = new(suppressWarnings, StringComparer.OrdinalIgnoreCase); + copy.errorWarnings = new(errorWarnings, StringComparer.OrdinalIgnoreCase); return copy; } diff --git a/src/IKVM.Tools.Importer/IkvmImporterInternal.cs b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs index a443d6544d..bd247a9933 100644 --- a/src/IKVM.Tools.Importer/IkvmImporterInternal.cs +++ b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs @@ -773,15 +773,7 @@ void ContinueParseCommandLine(RuntimeContext context, StaticCompiler compiler, I } else if (s.StartsWith("-nowarn:")) { - foreach (var w in s.Substring(8).Split(',')) - { - // lame way to chop off the leading zeroes - string ws = w; - while (ws.StartsWith("0")) - ws = ws.Substring(1); - - options.suppressWarnings[ws] = ws; - } + HandleWarnArg(options.suppressWarnings, s.Substring(8)); } else if (s == "-warnaserror") { @@ -789,15 +781,7 @@ void ContinueParseCommandLine(RuntimeContext context, StaticCompiler compiler, I } else if (s.StartsWith("-warnaserror:")) { - foreach (string w in s.Substring(13).Split(',')) - { - // lame way to chop off the leading zeroes - string ws = w; - while (ws.StartsWith("0")) - ws = ws.Substring(1); - - options.errorWarnings[ws] = ws; - } + HandleWarnArg(options.errorWarnings, s.Substring(13)); } else if (s.StartsWith("-runtime:")) { @@ -1479,7 +1463,7 @@ void ReportEvent(CompilerOptions options, in DiagnosticEvent evt) var key = evt.Diagnostic.Id.ToString(); for (int i = 0; ; i++) { - if (options.suppressWarnings.ContainsKey(key)) + if (options.suppressWarnings.Contains(key)) return; if (i == evt.Args.Length) @@ -1488,7 +1472,7 @@ void ReportEvent(CompilerOptions options, in DiagnosticEvent evt) key += ":" + evt.Args[i]; } - options.suppressWarnings.Add(key, key); + options.suppressWarnings.Add(key); if (options.writeSuppressWarningsFile != null) File.AppendAllText(options.writeSuppressWarningsFile.FullName, "-nowarn:" + key + Environment.NewLine); @@ -1505,7 +1489,7 @@ void ReportEvent(CompilerOptions options, in DiagnosticEvent evt) { DiagnosticLevel.Trace => "trace", DiagnosticLevel.Informational => "info", - DiagnosticLevel.Warning when options.errorWarnings.ContainsKey(key) || options.errorWarnings.ContainsKey(evt.Diagnostic.Id.ToString()) => "error", + DiagnosticLevel.Warning when options.errorWarnings.Contains(key) || options.errorWarnings.Contains(evt.Diagnostic.Id.ToString()) => "error", DiagnosticLevel.Warning => "warning", DiagnosticLevel.Error => "error", DiagnosticLevel.Fatal => "error", @@ -1526,6 +1510,40 @@ DiagnosticLevel.Warning when options.errorWarnings.ContainsKey(key) || options.e dst.WriteLine(options.path != null ? $" (in {options.path})" : ""); } + internal static void HandleWarnArg(ICollection target, string arg) + { + foreach (var w in arg.Split(',')) + { + // Strip IKVM prefix + int prefixStart = w.StartsWith("IKVM", StringComparison.OrdinalIgnoreCase) ? 5 : 0; + int contextIndex = w.IndexOf(':', prefixStart); + string context = string.Empty; + string parse; + if(contextIndex != -1) + { + // context includes ':' separator + context = w.Substring(contextIndex); + parse = w.Substring(prefixStart, contextIndex - prefixStart); + } + else + { + parse = w.Substring(prefixStart); + } + + if (!int.TryParse(parse, out var intResult)) + { + if (!Enum.TryParse(parse, out var namedResult)) + { + continue; // silently continue + } + + // Warnings are handled as int. + intResult = (int)namedResult; + } + + target.Add($"{intResult}{context}"); + } + } } } diff --git a/src/IKVM.Tools.Importer/StaticCompiler.cs b/src/IKVM.Tools.Importer/StaticCompiler.cs index 215cfafeb1..763688219a 100644 --- a/src/IKVM.Tools.Importer/StaticCompiler.cs +++ b/src/IKVM.Tools.Importer/StaticCompiler.cs @@ -242,7 +242,7 @@ internal void IssueMissingTypeMessage(Type type) internal void SuppressWarning(CompilerOptions options, Diagnostic diagnostic, string name) { - options.suppressWarnings[diagnostic.Id + ":" + name] = null; + options.suppressWarnings.Add($"{diagnostic.Id}:{name}"); } } diff --git a/src/IKVM.Tools.Runner/Importer/IkvmImporterLauncher.cs b/src/IKVM.Tools.Runner/Importer/IkvmImporterLauncher.cs index 2a4673a91b..d695a68f5c 100644 --- a/src/IKVM.Tools.Runner/Importer/IkvmImporterLauncher.cs +++ b/src/IKVM.Tools.Runner/Importer/IkvmImporterLauncher.cs @@ -299,7 +299,7 @@ public async Task ExecuteAsync(IkvmImporterOptions options, CancellationTok await LogEvent(IkvmToolDiagnosticEventLevel.Debug, "Executing {0} {1}", cli.TargetFilePath, cli.Arguments); // send output to MSBuild - cli = cli.WithStandardErrorPipe(PipeTarget.ToDelegate(i => LogEvent(IkvmToolDiagnosticEventLevel.Error, i))); + cli = cli.WithStandardErrorPipe(PipeTarget.ToDelegate(i => LogEvent(IkvmToolDiagnosticEventLevel.Warning, i))); cli = cli.WithStandardOutputPipe(PipeTarget.ToDelegate(i => LogEvent(IkvmToolDiagnosticEventLevel.Debug, i))); // combine manual cancellation with timeout From 49e98dfda2c774dd88573faa8cb0dfc709ddb79b Mon Sep 17 00:00:00 2001 From: AliveDevil Date: Mon, 26 Aug 2024 11:41:40 +0200 Subject: [PATCH 2/3] Fix prefix --- src/IKVM.Tools.Importer/IkvmImporterInternal.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IKVM.Tools.Importer/IkvmImporterInternal.cs b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs index bd247a9933..c894554818 100644 --- a/src/IKVM.Tools.Importer/IkvmImporterInternal.cs +++ b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs @@ -1515,7 +1515,7 @@ internal static void HandleWarnArg(ICollection target, string arg) foreach (var w in arg.Split(',')) { // Strip IKVM prefix - int prefixStart = w.StartsWith("IKVM", StringComparison.OrdinalIgnoreCase) ? 5 : 0; + int prefixStart = w.StartsWith("IKVM", StringComparison.OrdinalIgnoreCase) ? 4 : 0; int contextIndex = w.IndexOf(':', prefixStart); string context = string.Empty; string parse; From 7aeff14d6cbdb3034783b41e65f1ca496ba0783f Mon Sep 17 00:00:00 2001 From: AliveDevil Date: Mon, 26 Aug 2024 11:45:13 +0200 Subject: [PATCH 3/3] Remove enum name parsing --- src/IKVM.Tools.Importer/IkvmImporterInternal.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/IKVM.Tools.Importer/IkvmImporterInternal.cs b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs index c894554818..5ae7b80ee1 100644 --- a/src/IKVM.Tools.Importer/IkvmImporterInternal.cs +++ b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs @@ -1532,13 +1532,8 @@ internal static void HandleWarnArg(ICollection target, string arg) if (!int.TryParse(parse, out var intResult)) { - if (!Enum.TryParse(parse, out var namedResult)) - { - continue; // silently continue - } - - // Warnings are handled as int. - intResult = (int)namedResult; + // NamedResults aren't supported + continue; // silently continue } target.Add($"{intResult}{context}");