Suppress pointless warnings coming from SBT (#3499)

This change introduces a custom LogManager for console that allows for
excluding certain log messages. The primarily reason for introducing
such LogManager/Appender is to stop issuing hundreds of pointless
warnings coming from the analyzing compiler (wrapper around javac) for
classes that are being generated by annotation processors.

The output looks like this:
```
[info] Cannot install GraalVM MBean due to Failed to load org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints
[info] compiling 129 Scala sources and 395 Java sources to /home/hubert/work/repos/enso/enso/engine/runtime/target/scala-2.13/classes ...
[warn] Unexpected javac output: warning: File for type 'org.enso.interpreter.runtime.type.ConstantsGen' created in the last round will not be subject to annotation processing.
[warn] 1 warning.
[info] [Use -Dgraal.LogFile=<path> to redirect Graal log output to a file.]
[info] Cannot install GraalVM MBean due to Failed to load org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints
[info] foojavac Filer
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.number.decimal.CeilMethodGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.resource.TakeNodeGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.error.ThrowErrorMethodGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.number.smallInteger.MultiplyMethodGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.warning.GetWarningsNodeGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.number.smallInteger.BitAndMethodGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.error.ErrorToTextNodeGen
[warn] Could not determine source for class org.enso.interpreter.node.expression.builtin.warning.GetValueMethodGen
[warn] Could not determine source for class org.enso.interpreter.runtime.callable.atom.AtomGen$MethodDispatchLibraryExports$Cached
....
```

The output now has over 500 of those and there will be more. Much more
(generated by our and Truffle processors).
There is no way to tell SBT that those are OK. One could potentially
think of splitting compilation into 3 stages (Java processors, Java and
Scala) but that will already complicate the non-trivial build definition
and we may still end up with the initial problem.
This is a fix to make it possible to get reasonable feedback from
compilation without scrolling mutliple screens *every single time*.

Also fixed a spurious warning in javac processor complaining about
creating files in the last round.

Related to https://www.pivotaltracker.com/story/show/182138198
This commit is contained in:
Hubert Plociniczak 2022-06-01 15:50:46 +02:00 committed by GitHub
parent ba5d6823a9
commit 31e3f39c55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 8 deletions

View File

@ -5,11 +5,8 @@ import sbt.Keys.{libraryDependencies, scalacOptions}
import sbt.addCompilerPlugin
import sbt.complete.DefaultParsers._
import sbt.complete.Parser
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
import src.main.scala.licenses.{
DistributionDescription,
SBTDistributionComponent
}
import sbtcrossproject.CrossPlugin.autoImport.{CrossType, crossProject}
import src.main.scala.licenses.{DistributionDescription, SBTDistributionComponent}
import java.io.File
@ -151,6 +148,7 @@ val packageBuilder = new DistributionPackage.Builder(
)
Global / onChangedBuildSource := ReloadOnSourceChanges
Global / excludeLintKeys += logManager
// ============================================================================
// === Compiler Options =======================================================
@ -1186,6 +1184,8 @@ lazy val runtime = (project in file("engine/runtime"))
.configs(Benchmark)
.settings(
frgaalJavaCompilerSetting,
Compile/logManager :=
sbt.internal.util.CustomLogManager.excludeMsg("Could not determine source for class ", Level.Warn),
version := ensoVersion,
commands += WithDebugCommand.withDebug,
cleanInstruments := FixInstrumentsGeneration.cleanInstruments.value,

View File

@ -4,6 +4,7 @@ import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
@ -18,6 +19,7 @@ import org.openide.util.lookup.ServiceProvider;
public class TypeProcessor extends BuiltinsMetadataProcessor<TypeProcessor.TypeMetadataEntry> {
private final Map<Filer, Map<String, BuiltinTypeConstr>> builtinTypes = new HashMap<>();
private JavaFileObject jfo = null;
private class BuiltinTypeConstr {
private String tpeName;
@ -50,6 +52,17 @@ public class TypeProcessor extends BuiltinsMetadataProcessor<TypeProcessor.TypeM
@Override
protected boolean handleProcess(
Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (jfo == null) {
// Create generator for Java source file once(!) so that it can be used at the last
// round of processing. Otherwise, javac complains that the generated file won't be
// used in further processing. That's fine, we know it won't.
try {
jfo = processingEnv.getFiler().createSourceFile(ConstantsGenFullClassname);
} catch (IOException e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
return false;
}
}
for (TypeElement annotation : annotations) {
Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
for (Element elt : annotatedElements) {
@ -85,9 +98,7 @@ public class TypeProcessor extends BuiltinsMetadataProcessor<TypeProcessor.TypeM
*/
@Override
protected void storeMetadata(Writer writer, Map<String, TypeMetadataEntry> pastEntries) throws IOException {
JavaFileObject gen = processingEnv.getFiler().createSourceFile(ConstantsGenFullClassname);
for (Filer f : builtinTypes.keySet()) {
System.out.println("foo" + f.toString());
for (Map.Entry<String, BuiltinTypeConstr> entry : builtinTypes.get(f).entrySet()) {
BuiltinTypeConstr constr = entry.getValue();
writer.append(
@ -104,7 +115,7 @@ public class TypeProcessor extends BuiltinsMetadataProcessor<TypeProcessor.TypeM
}
}
}
try (PrintWriter out = new PrintWriter(gen.openWriter())) {
try (PrintWriter out = new PrintWriter(jfo.openWriter())) {
out.println("package " + ConstantsGenPkg + ";");
out.println();
out.println("public class " + ConstantsGenClass + " {");

View File

@ -0,0 +1,33 @@
package sbt.internal.util
import sbt.internal.LogManager
import sbt.internal.util.ConsoleAppender.{Properties, noSuppressedMessage}
object CustomLogManager {
def excludeMsg(msgPrefix: String, level: sbt.Level.Value): LogManager = {
sbt.internal.LogManager.
withLoggers((_, _) => new CustomAppender(level, msgPrefix, ConsoleOut.systemOut))
}
/**
* Returns a custom ConsoleAppender that will skip log messages starting with a certain prefix.
*
* The only reason for such appender is to force SBT to keep quiet about certain kind of messages
* coming from the analyzing compiler (wrapper around java compiler) when it tries to match class files
* to source files. There is absolutely no way to tell SBT that that some sources are being generated from
* annotation processors and it will get them during the same compilation round. Nor can we easily
* suppress such warning.
*
* @param excludeLevel level of log message to exclude (together with prefix)
* @param prefix prefix of log message to exclude (together with log level)
* @param out object representing console output
*/
private final class CustomAppender(excludeLevel: sbt.Level.Value, prefix: String, out: ConsoleOut)
extends ConsoleAppender("out", Properties.from(out, Terminal.isAnsiSupported, Terminal.isAnsiSupported), noSuppressedMessage) {
override def appendLog(level: sbt.Level.Value, message: => String): Unit = {
if (excludeLevel != level || !message.startsWith(prefix)) {
super.appendLog(level, message)
}
}
}
}