CLI: improve error message if libraries are not found in Kotlin home

Also support the '-kotlin-home' argument in kotlinc-js

 #KT-18859 Fixed
This commit is contained in:
Alexander Udalov
2017-07-05 21:47:27 +03:00
parent eb673d6ed3
commit 1e6850f198
16 changed files with 158 additions and 42 deletions
@@ -44,6 +44,13 @@ public abstract class CommonCompilerArguments extends CommonToolArguments {
)
public String apiVersion;
@Argument(
value = "-kotlin-home",
valueDescription = "<path>",
description = "Path to Kotlin compiler home directory, used for runtime libraries discovery"
)
public String kotlinHome;
@Argument(value = "-P", valueDescription = PLUGIN_OPTION_FORMAT, description = "Pass an option to a plugin")
public String[] pluginOptions;
@@ -67,13 +67,6 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
)
public String[] scriptTemplates;
@Argument(
value = "-kotlin-home",
valueDescription = "<path>",
description = "Path to Kotlin compiler home directory, used for runtime libraries discovery"
)
public String kotlinHome;
@Argument(value = "-module-name", description = "Module name")
public String moduleName;
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.common;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;
import kotlin.collections.ArraysKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
@@ -32,8 +33,12 @@ 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.KotlinPaths;
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir;
import org.jetbrains.kotlin.utils.PathUtil;
import org.jetbrains.kotlin.utils.StringsKt;
import java.io.File;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.List;
@@ -68,6 +73,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
try {
setupCommonArgumentsAndServices(configuration, arguments, services);
setupPlatformSpecificArgumentsAndServices(configuration, arguments, services);
KotlinPaths paths = computeKotlinPaths(groupingCollector, arguments);
if (groupingCollector.hasErrors()) {
return ExitCode.COMPILATION_ERROR;
}
@@ -93,7 +99,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
Disposable rootDisposable = Disposer.newDisposable();
try {
setIdeaIoUseFallback();
ExitCode code = doExecute(arguments, configuration, rootDisposable);
ExitCode code = doExecute(arguments, configuration, rootDisposable, paths);
exitCode = groupingCollector.hasErrors() ? COMPILATION_ERROR : code;
}
catch (CompilationCanceledException e) {
@@ -200,6 +206,51 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
));
}
@Nullable
private static KotlinPaths computeKotlinPaths(@NotNull MessageCollector messageCollector, @NotNull CommonCompilerArguments arguments) {
KotlinPaths paths;
if (arguments.kotlinHome != null) {
File kotlinHome = new File(arguments.kotlinHome);
if (kotlinHome.isDirectory()) {
paths = new KotlinPathsFromHomeDir(kotlinHome);
}
else {
messageCollector.report(ERROR, "Kotlin home does not exist or is not a directory: " + kotlinHome, null);
paths = null;
}
}
else {
paths = PathUtil.getKotlinPathsForCompiler();
}
if (paths != null) {
messageCollector.report(LOGGING, "Using Kotlin home directory " + paths.getHomePath(), null);
}
return paths;
}
@Nullable
public static File getLibraryFromHome(
@Nullable KotlinPaths paths,
@NotNull Function1<KotlinPaths, File> getLibrary,
@NotNull String libraryName,
@NotNull MessageCollector messageCollector,
@NotNull String noLibraryArgument
) {
if (paths != null) {
File stdlibJar = getLibrary.invoke(paths);
if (stdlibJar.exists()) {
return stdlibJar;
}
}
messageCollector.report(STRONG_WARNING, "Unable to find " + libraryName + " in the Kotlin home directory. " +
"Pass either " + noLibraryArgument + " to prevent adding it to the classpath, " +
"or the correct '-kotlin-home'", null);
return null;
}
@Nullable
private static LanguageFeature.State chooseCoroutinesApplicabilityLevel(
@NotNull CompilerConfiguration configuration,
@@ -245,6 +296,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
protected abstract ExitCode doExecute(
@NotNull A arguments,
@NotNull CompilerConfiguration configuration,
@NotNull Disposable rootDisposable
@NotNull Disposable rootDisposable,
@Nullable KotlinPaths paths
);
}
@@ -23,11 +23,11 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import kotlin.collections.ArraysKt;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
import org.jetbrains.kotlin.cli.common.CLICompiler;
@@ -60,6 +60,7 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.serialization.js.ModuleKind;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
import org.jetbrains.kotlin.utils.KotlinPaths;
import org.jetbrains.kotlin.utils.PathUtil;
import org.jetbrains.kotlin.utils.StringsKt;
@@ -101,7 +102,10 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
@NotNull
@Override
protected ExitCode doExecute(
@NotNull K2JSCompilerArguments arguments, @NotNull CompilerConfiguration configuration, @NotNull Disposable rootDisposable
@NotNull K2JSCompilerArguments arguments,
@NotNull CompilerConfiguration configuration,
@NotNull Disposable rootDisposable,
@Nullable KotlinPaths paths
) {
MessageCollector messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY);
@@ -113,6 +117,8 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
return COMPILATION_ERROR;
}
configuration.put(JSConfigurationKeys.LIBRARIES, configureLibraries(arguments, paths, messageCollector));
ContentRootsKt.addKotlinSourceRoots(configuration, arguments.freeArgs);
KotlinCoreEnvironment environmentForJS =
KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JS_CONFIG_FILES);
@@ -317,18 +323,6 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
configuration.put(JSConfigurationKeys.META_INFO, true);
}
List<String> libraries = new SmartList<>();
if (!arguments.noStdlib) {
libraries.add(0, PathUtil.getKotlinPathsForCompiler().getJsStdLibJarPath().getAbsolutePath());
}
if (arguments.libraries != null) {
ContainerUtil.addAll(libraries, ArraysKt.filterNot(arguments.libraries.split(File.pathSeparator), String::isEmpty));
}
configuration.put(JSConfigurationKeys.LIBRARIES, libraries);
if (arguments.typedArrays) {
configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, true);
}
@@ -367,6 +361,27 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
}
}
@NotNull
private static List<String> configureLibraries(
@NotNull K2JSCompilerArguments arguments,
@Nullable KotlinPaths paths,
@NotNull MessageCollector messageCollector
) {
List<String> libraries = new SmartList<>();
if (!arguments.noStdlib) {
File stdlibJar = getLibraryFromHome(
paths, KotlinPaths::getJsStdLibJarPath, PathUtil.JS_LIB_JAR_NAME, messageCollector, "'-no-stdlib'");
if (stdlibJar != null) {
libraries.add(stdlibJar.getAbsolutePath());
}
}
if (arguments.libraries != null) {
libraries.addAll(ArraysKt.filterNot(arguments.libraries.split(File.pathSeparator), String::isEmpty));
}
return libraries;
}
@NotNull
private static String calculateSourceMapSourceRoot(
@NotNull MessageCollector messageCollector,
@@ -47,7 +47,6 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.util.PerformanceCounter
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.lang.management.ManagementFactory
@@ -56,17 +55,15 @@ import java.util.*
import java.util.concurrent.TimeUnit
class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
override fun doExecute(arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration, rootDisposable: Disposable): ExitCode {
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val paths = if (arguments.kotlinHome != null)
KotlinPathsFromHomeDir(File(arguments.kotlinHome))
else
PathUtil.kotlinPathsForCompiler
messageCollector.report(LOGGING, "Using Kotlin home directory ${paths.homePath}")
override fun doExecute(
arguments: K2JVMCompilerArguments,
configuration: CompilerConfiguration,
rootDisposable: Disposable,
paths: KotlinPaths?
): ExitCode {
PerformanceCounter.setTimeCounterEnabled(arguments.reportPerf)
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
setupJdkClasspathRoots(arguments, configuration, messageCollector).let {
if (it != OK) return it
}
@@ -189,7 +186,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
val scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size)
return KotlinToJVMBytecodeCompiler.compileAndExecuteScript(environment, scriptArgs)
}
else {
@@ -372,7 +368,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
}
private fun configureContentRoots(paths: KotlinPaths, arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration) {
private fun configureContentRoots(paths: KotlinPaths?, arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration) {
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
for (path in arguments.classpath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(File(path)))
}
@@ -382,7 +379,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
val isModularJava = configuration.get(JVMConfigurationKeys.JDK_HOME).let { it != null && CoreJrtFileSystem.isModularJdk(it) }
fun addRoot(moduleName: String, file: File) {
fun addRoot(moduleName: String, libraryName: String, getLibrary: (KotlinPaths) -> File, noLibraryArgument: String) {
val file = getLibraryFromHome(paths, getLibrary, libraryName, messageCollector, noLibraryArgument) ?: return
if (isModularJava) {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file))
configuration.add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, moduleName)
@@ -393,13 +391,13 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
if (!arguments.noStdlib) {
addRoot("kotlin.stdlib", paths.stdlibPath)
addRoot("kotlin.script.runtime", paths.scriptRuntimePath)
addRoot("kotlin.stdlib", PathUtil.KOTLIN_JAVA_STDLIB_JAR, KotlinPaths::getStdlibPath, "'-no-stdlib'")
addRoot("kotlin.script.runtime", PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR, KotlinPaths::getScriptRuntimePath, "'-no-stdlib'")
}
// "-no-stdlib" implies "-no-reflect": otherwise we would be able to transitively read stdlib classes through kotlin-reflect,
// which is likely not what user wants since s/he manually provided "-no-stdlib"
if (!arguments.noReflect && !arguments.noStdlib) {
addRoot("kotlin.reflect", paths.reflectPath)
addRoot("kotlin.reflect", PathUtil.KOTLIN_JAVA_REFLECT_JAR, KotlinPaths::getReflectPath, "'-no-reflect' or '-no-stdlib'")
}
}
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.config.addKotlinSourceRoot
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.utils.KotlinPaths
import java.io.File
class K2MetadataCompiler : CLICompiler<K2MetadataCompilerArguments>() {
@@ -45,7 +46,10 @@ class K2MetadataCompiler : CLICompiler<K2MetadataCompilerArguments>() {
}
override fun doExecute(
arguments: K2MetadataCompilerArguments, configuration: CompilerConfiguration, rootDisposable: Disposable
arguments: K2MetadataCompilerArguments,
configuration: CompilerConfiguration,
rootDisposable: Disposable,
paths: KotlinPaths?
): ExitCode {
val collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)