Compiler API refactored:

* exec(messageCollector, arguments) introduced
* exception-catching logic moved closer to the entry point
* PrintingMessageCollector localized
This commit is contained in:
Andrey Breslav
2013-02-12 20:43:08 +04:00
parent cf87f55085
commit 3293e08d97
5 changed files with 127 additions and 61 deletions
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.cli.common.messages;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Set;
public class MessageSeverityCollector implements MessageCollector {
private final MessageCollector delegate;
private final Set<CompilerMessageSeverity> reportedSeverities = Sets.newHashSet();
public MessageSeverityCollector(@NotNull MessageCollector delegate) {
this.delegate = delegate;
}
@Override
public void report(
@NotNull CompilerMessageSeverity severity,
@NotNull String message,
@NotNull CompilerMessageLocation location
) {
delegate.report(severity, message, location);
reportedSeverities.add(severity);
}
public boolean anyReported(@NotNull CompilerMessageSeverity... severities) {
return reportedSeverities.containsAll(Arrays.asList(severities));
}
}
@@ -18,38 +18,36 @@ package org.jetbrains.jet.cli.common.messages;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
public class PrintingMessageCollector implements MessageCollector {
private boolean verbose;
private final boolean verbose;
private final PrintStream errStream;
private final MessageRenderer messageRenderer;
// File path (nullable) -> error message
private final Multimap<String, String> groupedMessages = LinkedHashMultimap.create();
private final Set<CompilerMessageSeverity> reportedSeverities = Sets.newHashSet();
public PrintingMessageCollector(PrintStream errStream,
MessageRenderer messageRenderer,
boolean verbose) {
public PrintingMessageCollector(
@NotNull PrintStream errStream,
@NotNull MessageRenderer messageRenderer,
boolean verbose
) {
this.verbose = verbose;
this.errStream = errStream;
this.messageRenderer = messageRenderer;
}
@Override
public void report(@NotNull CompilerMessageSeverity severity,
public void report(
@NotNull CompilerMessageSeverity severity,
@NotNull String message,
@NotNull CompilerMessageLocation location) {
reportedSeverities.add(severity);
@NotNull CompilerMessageLocation location
) {
String text = messageRenderer.render(severity, message, location);
if (severity == CompilerMessageSeverity.LOGGING || severity == CompilerMessageSeverity.OUTPUT) {
@@ -73,16 +71,4 @@ public class PrintingMessageCollector implements MessageCollector {
}
}
}
public boolean isVerbose() {
return verbose;
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public boolean anyReported(@NotNull CompilerMessageSeverity... severities) {
return reportedSeverities.containsAll(Arrays.asList(severities));
}
}
@@ -20,16 +20,14 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;
import com.sampullara.cli.Args;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.jet.cli.common.messages.MessageRenderer;
import org.jetbrains.jet.cli.common.messages.PrintingMessageCollector;
import org.jetbrains.jet.cli.common.messages.*;
import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentException;
import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentUtil;
import org.jetbrains.jet.config.CompilerConfiguration;
import java.io.PrintStream;
import static org.jetbrains.jet.cli.common.ExitCode.COMPILATION_ERROR;
import static org.jetbrains.jet.cli.common.ExitCode.INTERNAL_ERROR;
import static org.jetbrains.jet.cli.common.ExitCode.OK;
@@ -99,30 +97,50 @@ public abstract class CLICompiler<A extends CompilerArguments> {
* Executes the compiler on the parsed arguments
*/
@NotNull
public ExitCode exec(final PrintStream errStream, A arguments) {
public ExitCode exec(@NotNull PrintStream errStream, @NotNull A arguments) {
if (arguments.isHelp()) {
usage(errStream);
return OK;
}
System.setProperty("java.awt.headless", "true");
final MessageRenderer messageRenderer = getMessageRenderer(arguments);
MessageRenderer messageRenderer = getMessageRenderer(arguments);
errStream.print(messageRenderer.renderPreamble());
printVersionIfNeeded(errStream, arguments, messageRenderer);
PrintingMessageCollector messageCollector = new PrintingMessageCollector(errStream, messageRenderer, arguments.isVerbose());
Disposable rootDisposable = CompileEnvironmentUtil.createMockDisposable();
try {
return doExecute(arguments, messageCollector, rootDisposable);
return exec(messageCollector, arguments);
}
finally {
messageCollector.printToErrStream();
errStream.print(messageRenderer.renderConclusion());
Disposer.dispose(rootDisposable);
}
}
@NotNull
public ExitCode exec(@NotNull MessageCollector messageCollector, @NotNull A arguments) {
try {
Disposable rootDisposable = CompileEnvironmentUtil.createMockDisposable();
try {
MessageSeverityCollector severityCollector = new MessageSeverityCollector(messageCollector);
ExitCode code = doExecute(arguments, severityCollector, rootDisposable);
return severityCollector.anyReported(CompilerMessageSeverity.ERROR) ? COMPILATION_ERROR : code;
}
finally {
Disposer.dispose(rootDisposable);
}
}
catch (Throwable t) {
messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(t),
CompilerMessageLocation.NO_LOCATION);
return INTERNAL_ERROR;
}
}
//TODO: can't declare parameters as not null due to KT-1863
@NotNull
protected abstract ExitCode doExecute(A arguments, PrintingMessageCollector messageCollector, Disposable rootDisposable);
protected abstract ExitCode doExecute(A arguments, MessageCollector messageCollector, Disposable rootDisposable);
//TODO: can we make it private?
@NotNull
@@ -145,6 +163,9 @@ public abstract class CLICompiler<A extends CompilerArguments> {
* Useful main for derived command line tools
*/
public static void doMain(@NotNull CLICompiler compiler, @NotNull String[] args) {
// We depend on swing (indirectly through PSI or something), so we want to declare headless mode,
// to avoid accidentally starting the UI thread
System.setProperty("java.awt.headless", "true");
ExitCode exitCode = doMainNoExit(compiler, args);
if (exitCode != OK) {
System.exit(exitCode.getCode());
@@ -34,7 +34,7 @@ import org.jetbrains.jet.cli.common.ExitCode;
import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.jet.cli.common.messages.PrintingMessageCollector;
import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.config.CommonConfigurationKeys;
import org.jetbrains.jet.config.CompilerConfiguration;
@@ -67,7 +67,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
@NotNull
@Override
protected ExitCode doExecute(K2JSCompilerArguments arguments, PrintingMessageCollector messageCollector, Disposable rootDisposable) {
protected ExitCode doExecute(K2JSCompilerArguments arguments, MessageCollector messageCollector, Disposable rootDisposable) {
if (arguments.sourceFiles == null) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Specify sources location via -sourceFiles", NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
@@ -99,10 +99,10 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
}
MainCallParameters mainCallParameters = arguments.createMainCallParameters();
return translateAndGenerateOutputFile(mainCallParameters, messageCollector, environmentForJS, config, outputFile);
return translateAndGenerateOutputFile(mainCallParameters, environmentForJS, config, outputFile);
}
private static void reportCompiledSourcesList(@NotNull PrintingMessageCollector messageCollector,
private static void reportCompiledSourcesList(@NotNull MessageCollector messageCollector,
@NotNull JetCoreEnvironment environmentForJS) {
List<JetFile> files = environmentForJS.getSourceFiles();
Iterable<String> fileNames = Iterables.transform(files, new Function<JetFile, String>() {
@@ -121,23 +121,22 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
}
@NotNull
private static ExitCode translateAndGenerateOutputFile(@NotNull MainCallParameters mainCall,
@NotNull PrintingMessageCollector messageCollector,
@NotNull JetCoreEnvironment environmentForJS, @NotNull Config config, @NotNull String outputFile) {
private static ExitCode translateAndGenerateOutputFile(
@NotNull MainCallParameters mainCall,
@NotNull JetCoreEnvironment environmentForJS,
@NotNull Config config,
@NotNull String outputFile
) {
try {
K2JSTranslator.translateWithMainCallParametersAndSaveToFile(mainCall, environmentForJS.getSourceFiles(), outputFile, config);
}
catch (Exception e) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Exception while translating:\n" + e.getMessage(),
CompilerMessageLocation.NO_LOCATION);
// TODO we should report the exception nicely to the collector so it can report
// for example inside a mvn plugin we need to see the stack trace
return ExitCode.INTERNAL_ERROR;
throw new RuntimeException(e);
}
return messageCollector.anyReported(CompilerMessageSeverity.ERROR) ? COMPILATION_ERROR : OK;
return OK;
}
private static boolean analyzeAndReportErrors(@NotNull PrintingMessageCollector messageCollector,
private static boolean analyzeAndReportErrors(@NotNull MessageCollector messageCollector,
@NotNull final List<JetFile> sources, @NotNull final Config config) {
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
analyzerWithCompilerReport.analyzeAndReport(new Function0<AnalyzeExhaust>() {
@@ -58,7 +58,7 @@ public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
@Override
@NotNull
protected ExitCode doExecute(K2JVMCompilerArguments arguments, PrintingMessageCollector messageCollector, Disposable rootDisposable) {
protected ExitCode doExecute(K2JVMCompilerArguments arguments, MessageCollector messageCollector, Disposable rootDisposable) {
KotlinPaths paths = arguments.kotlinHome != null
? new KotlinPathsFromHomeDir(new File(arguments.kotlinHome))
: PathUtil.getKotlinPathsForCompiler();
@@ -127,10 +127,9 @@ public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
File outputDir = arguments.outputDir != null ? new File(arguments.outputDir) : null;
if (arguments.module != null) {
boolean oldVerbose = messageCollector.isVerbose();
messageCollector.setVerbose(false);
List<Module> modules = CompileEnvironmentUtil.loadModuleScript(paths, arguments.module, messageCollector);
messageCollector.setVerbose(oldVerbose);
MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector);
List<Module> modules = CompileEnvironmentUtil.loadModuleScript(paths, arguments.module, sanitizedCollector);
File directory = new File(arguments.module).getParentFile();
KotlinToJVMBytecodeCompiler.compileModules(configuration, modules,
directory, jar, outputDir,
@@ -145,18 +144,13 @@ public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
JetCoreEnvironment environment = new JetCoreEnvironment(rootDisposable, configuration);
KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
}
return messageCollector.anyReported(CompilerMessageSeverity.ERROR) ? COMPILATION_ERROR : OK;
return OK;
}
catch (CompilationException e) {
messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e),
MessageUtil.psiElementToMessageLocation(e.getElement()));
return INTERNAL_ERROR;
}
catch (Throwable t) {
messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(t),
CompilerMessageLocation.NO_LOCATION);
return INTERNAL_ERROR;
}
}
@@ -180,7 +174,7 @@ public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
// probably relates to KT-1863... well, may be not
@NotNull
@Override
public ExitCode exec(PrintStream errStream, K2JVMCompilerArguments arguments) {
public ExitCode exec(@NotNull PrintStream errStream, @NotNull K2JVMCompilerArguments arguments) {
return super.exec(errStream, arguments);
}
@@ -214,4 +208,21 @@ public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
}
return annotationsPath;
}
private static class FilteringMessageCollector implements MessageCollector {
private final MessageCollector messageCollector;
public FilteringMessageCollector(@NotNull MessageCollector messageCollector) {
this.messageCollector = messageCollector;
}
@Override
public void report(
@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location
) {
if (!(severity == CompilerMessageSeverity.LOGGING || severity == CompilerMessageSeverity.OUTPUT)) {
messageCollector.report(severity, message, location);
}
}
}
}