org.jetbrains.kotlin.cli.common.CLICompiler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2016 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.kotlin.cli.common;
import com.google.common.base.Predicates;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.concurrency.AppScheduledExecutorService;
import kotlin.collections.ArraysKt;
import kotlin.jvm.functions.Function1;
import org.fusesource.jansi.AnsiConsole;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.common.arguments.ArgumentUtilsKt;
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
import org.jetbrains.kotlin.cli.common.messages.*;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentException;
import org.jetbrains.kotlin.cli.jvm.compiler.CompilerJarLocator;
import org.jetbrains.kotlin.config.*;
import org.jetbrains.kotlin.progress.CompilationCanceledException;
import org.jetbrains.kotlin.progress.CompilationCanceledStatus;
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
import org.jetbrains.kotlin.utils.StringsKt;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import static org.jetbrains.kotlin.cli.common.ExitCode.*;
public abstract class CLICompiler {
static private void setIdeaIoUseFallback() {
if (SystemInfo.isWindows) {
Properties properties = System.getProperties();
properties.setProperty("idea.io.use.nio2", Boolean.TRUE.toString());
if (!(SystemInfo.isJavaVersionAtLeast("1.7") && !"1.7.0-ea".equals(SystemInfo.JAVA_VERSION))) {
properties.setProperty("idea.io.use.fallback", Boolean.TRUE.toString());
}
}
}
@NotNull
public ExitCode exec(@NotNull PrintStream errStream, @NotNull String... args) {
return exec(errStream, Services.EMPTY, MessageRenderer.PLAIN_RELATIVE_PATHS, args);
}
// Used via reflection in CompilerRunnerUtil#invokeExecMethod and in Eclipse plugin (see KotlinCLICompiler)
@SuppressWarnings("UnusedDeclaration")
@NotNull
public ExitCode execAndOutputXml(@NotNull PrintStream errStream, @NotNull Services services, @NotNull String... args) {
return exec(errStream, services, MessageRenderer.XML, args);
}
// Used via reflection in KotlinCompilerBaseTask
@SuppressWarnings("UnusedDeclaration")
@NotNull
public ExitCode execFullPathsInMessages(@NotNull PrintStream errStream, @NotNull String[] args) {
return exec(errStream, Services.EMPTY, MessageRenderer.PLAIN_FULL_PATHS, args);
}
@Nullable
private A parseArguments(@NotNull PrintStream errStream, @NotNull MessageRenderer messageRenderer, @NotNull String[] args) {
try {
A arguments = createArguments();
parseArguments(args, arguments);
return arguments;
}
catch (IllegalArgumentException e) {
errStream.println(e.getMessage());
Usage.print(errStream, createArguments(), false);
}
catch (Throwable t) {
errStream.println(messageRenderer.render(
CompilerMessageSeverity.EXCEPTION,
OutputMessageUtil.renderException(t),
CompilerMessageLocation.NO_LOCATION)
);
}
return null;
}
@SuppressWarnings("WeakerAccess") // Used in maven (see KotlinCompileMojoBase.java)
public void parseArguments(@NotNull String[] args, @NotNull A arguments) {
ArgumentUtilsKt.parseArguments(args, arguments);
}
@NotNull
protected abstract A createArguments();
@NotNull
private ExitCode exec(
@NotNull PrintStream errStream,
@NotNull Services services,
@NotNull MessageRenderer messageRenderer,
@NotNull String[] args
) {
K2JVMCompiler.Companion.resetInitStartTime();
A arguments = parseArguments(errStream, messageRenderer, args);
if (arguments == null) {
return INTERNAL_ERROR;
}
if (arguments.help || arguments.extraHelp) {
Usage.print(errStream, createArguments(), arguments.extraHelp);
return OK;
}
MessageCollector collector = new PrintingMessageCollector(errStream, messageRenderer, arguments.verbose);
try {
if (PlainTextMessageRenderer.COLOR_ENABLED) {
AnsiConsole.systemInstall();
}
errStream.print(messageRenderer.renderPreamble());
return exec(collector, services, arguments);
}
finally {
errStream.print(messageRenderer.renderConclusion());
if (PlainTextMessageRenderer.COLOR_ENABLED) {
AnsiConsole.systemUninstall();
}
}
}
@SuppressWarnings("WeakerAccess") // Used in maven (see KotlinCompileMojoBase.java)
@NotNull
public ExitCode exec(@NotNull MessageCollector messageCollector, @NotNull Services services, @NotNull A arguments) {
printVersionIfNeeded(messageCollector, arguments);
if (arguments.suppressWarnings) {
messageCollector = new FilteringMessageCollector(messageCollector, Predicates.equalTo(CompilerMessageSeverity.WARNING));
}
reportUnknownExtraFlags(messageCollector, arguments);
reportUnsupportedJavaVersion(messageCollector, arguments);
GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector);
CompilerConfiguration configuration = new CompilerConfiguration();
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, groupingCollector);
setupCommonArgumentsAndServices(configuration, arguments, services);
setupPlatformSpecificArgumentsAndServices(configuration, arguments, services);
try {
ExitCode exitCode = OK;
int repeatCount = 1;
if (arguments.repeat != null) {
try {
repeatCount = Integer.parseInt(arguments.repeat);
}
catch (NumberFormatException ignored) {
}
}
CompilationCanceledStatus canceledStatus = services.get(CompilationCanceledStatus.class);
ProgressIndicatorAndCompilationCanceledStatus.setCompilationCanceledStatus(canceledStatus);
for (int i = 0; i < repeatCount; i++) {
if (i > 0) {
K2JVMCompiler.Companion.resetInitStartTime();
}
Disposable rootDisposable = Disposer.newDisposable();
try {
setIdeaIoUseFallback();
ExitCode code = doExecute(arguments, configuration, rootDisposable);
exitCode = groupingCollector.hasErrors() ? COMPILATION_ERROR : code;
}
catch (CompilationCanceledException e) {
messageCollector.report(CompilerMessageSeverity.INFO, "Compilation was canceled", CompilerMessageLocation.NO_LOCATION);
return ExitCode.OK;
}
catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof CompilationCanceledException) {
messageCollector
.report(CompilerMessageSeverity.INFO, "Compilation was canceled", CompilerMessageLocation.NO_LOCATION);
return ExitCode.OK;
}
else {
throw e;
}
}
finally {
Disposer.dispose(rootDisposable);
}
}
return exitCode;
}
catch (Throwable t) {
groupingCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(t),
CompilerMessageLocation.NO_LOCATION);
return INTERNAL_ERROR;
}
finally {
groupingCollector.flush();
}
}
private static void setupCommonArgumentsAndServices(
@NotNull CompilerConfiguration configuration, @NotNull CommonCompilerArguments arguments, @NotNull Services services
) {
if (arguments.noInline) {
configuration.put(CommonConfigurationKeys.DISABLE_INLINE, true);
}
CompilerJarLocator locator = services.get(CompilerJarLocator.class);
if (locator != null) {
configuration.put(CLIConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
}
setupLanguageVersionSettings(configuration, arguments);
}
private static void setupLanguageVersionSettings(
@NotNull CompilerConfiguration configuration, @NotNull CommonCompilerArguments arguments
) {
LanguageVersion languageVersion = parseVersion(configuration, arguments.languageVersion, "language");
LanguageVersion apiVersion = parseVersion(configuration, arguments.apiVersion, "API");
if (languageVersion == null) {
// If only "-api-version" is specified, language version is assumed to be the latest
languageVersion = LanguageVersion.LATEST;
}
if (apiVersion == null) {
// If only "-language-version" is specified, API version is assumed to be equal to the language version
// (API version cannot be greater than the language version)
apiVersion = languageVersion;
}
if (apiVersion.compareTo(languageVersion) > 0) {
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
CompilerMessageSeverity.ERROR,
"-api-version (" + apiVersion.getVersionString() + ") cannot be greater than " +
"-language-version (" + languageVersion.getVersionString() + ")",
CompilerMessageLocation.NO_LOCATION
);
}
List extraLanguageFeatures = new ArrayList(0);
if (arguments.multiPlatform) {
extraLanguageFeatures.add(LanguageFeature.MultiPlatformProjects);
}
if (arguments.noCheckImpl) {
extraLanguageFeatures.add(LanguageFeature.MultiPlatformDoNotCheckImpl);
}
LanguageFeature coroutinesApplicabilityLevel = chooseCoroutinesApplicabilityLevel(configuration, arguments);
if (coroutinesApplicabilityLevel != null) {
extraLanguageFeatures.add(coroutinesApplicabilityLevel);
}
configuration.put(
CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
new LanguageVersionSettingsImpl(
languageVersion,
ApiVersion.createByLanguageVersion(apiVersion),
extraLanguageFeatures,
arguments.apiVersion != null
)
);
}
@Nullable
private static LanguageFeature chooseCoroutinesApplicabilityLevel(
@NotNull CompilerConfiguration configuration, @NotNull CommonCompilerArguments arguments) {
if (!arguments.coroutinesEnable && !arguments.coroutinesError && !arguments.coroutinesWarn) {
return LanguageFeature.WarnOnCoroutines;
}
else if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) {
return LanguageFeature.ErrorOnCoroutines;
}
else if (arguments.coroutinesWarn && !arguments.coroutinesError && !arguments.coroutinesEnable) {
return LanguageFeature.WarnOnCoroutines;
}
else if (arguments.coroutinesEnable && !arguments.coroutinesWarn && !arguments.coroutinesError) {
return null;
} else {
String message = "The -Xcoroutines can only have one value";
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION
);
return null;
}
}
@Nullable
private static LanguageVersion parseVersion(
@NotNull CompilerConfiguration configuration, @Nullable String value, @NotNull String versionOf
) {
if (value == null) return null;
LanguageVersion version = LanguageVersion.fromVersionString(value);
if (version != null) {
return version;
}
List versionStrings = ArraysKt.map(LanguageVersion.values(), new Function1() {
@Override
public String invoke(LanguageVersion version) {
return version.getVersionString();
}
});
String message = "Unknown " + versionOf + " version: " + value + "\n" +
"Supported " + versionOf + " versions: " + StringsKt.join(versionStrings, ", ");
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION
);
return null;
}
protected abstract void setupPlatformSpecificArgumentsAndServices(
@NotNull CompilerConfiguration configuration, @NotNull A arguments, @NotNull Services services
);
private void reportUnknownExtraFlags(@NotNull MessageCollector collector, @NotNull A arguments) {
for (String flag : arguments.unknownExtraFlags) {
collector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Flag is not supported by this version of the compiler: " + flag,
CompilerMessageLocation.NO_LOCATION
);
}
}
private void reportUnsupportedJavaVersion(MessageCollector collector, A arguments) {
if (!SystemInfo.isJavaVersionAtLeast("1.8") && !arguments.noJavaVersionWarning) {
collector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Running the Kotlin compiler under Java 6 or 7 is unsupported and will no longer be possible in a future update.",
CompilerMessageLocation.NO_LOCATION
);
}
}
@NotNull
protected abstract ExitCode doExecute(
@NotNull A arguments,
@NotNull CompilerConfiguration configuration,
@NotNull Disposable rootDisposable
);
private void printVersionIfNeeded(@NotNull MessageCollector messageCollector, @NotNull A arguments) {
if (!arguments.version) return;
messageCollector.report(CompilerMessageSeverity.INFO,
"Kotlin Compiler version " + KotlinCompilerVersion.VERSION,
CompilerMessageLocation.NO_LOCATION);
}
/**
* 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");
try {
ExitCode exitCode = doMainNoExit(compiler, args);
if (exitCode != OK) {
System.exit(exitCode.getCode());
}
}
finally {
AppScheduledExecutorService service = (AppScheduledExecutorService) AppExecutorUtil.getAppScheduledExecutorService();
service.shutdownAppScheduledExecutorService();
}
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
@NotNull
public static ExitCode doMainNoExit(@NotNull CLICompiler compiler, @NotNull String[] args) {
try {
return compiler.exec(System.err, args);
}
catch (CompileEnvironmentException e) {
System.err.println(e.getMessage());
return INTERNAL_ERROR;
}
}
}