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)
+2 -1
View File
@@ -18,6 +18,7 @@ where possible options include:
-output-postfix <path> Path to file which will be added to the end of output file
-language-version <version> Provide source compatibility with specified language version
-api-version <version> Allow to use declarations only from the specified version of bundled libraries
-kotlin-home <path> Path to Kotlin compiler home directory, used for runtime libraries discovery
-P plugin:<pluginId>:<optionName>=<value>
Pass an option to a plugin
-help (-h) Print a synopsis of standard options
@@ -25,4 +26,4 @@ where possible options include:
-version Display compiler version
-verbose Enable verbose logging output
-nowarn Generate no warnings
OK
OK
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/simple2js.kt
-kotlin-home
$TESTDATA_DIR$
-output
$TEMP_DIR$/out.js
+2
View File
@@ -0,0 +1,2 @@
warning: unable to find kotlin-stdlib-js.jar in the Kotlin home directory. Pass either '-no-stdlib' to prevent adding it to the classpath, or the correct '-kotlin-home'
OK
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/simple2js.kt
-kotlin-home
non-existing-path
-output
$TEMP_DIR$/out.js
+2
View File
@@ -0,0 +1,2 @@
error: Kotlin home does not exist or is not a directory: non-existing-path
COMPILATION_ERROR
+1 -1
View File
@@ -10,12 +10,12 @@ where possible options include:
-script Evaluate the script file
-script-templates <fully qualified class name[,]>
Script definition template classes
-kotlin-home <path> Path to Kotlin compiler home directory, used for runtime libraries discovery
-module-name Module name
-jvm-target <version> Target version of the generated JVM bytecode (1.6 or 1.8), default is 1.6
-java-parameters Generate metadata for Java 1.8 reflection on method parameters
-language-version <version> Provide source compatibility with specified language version
-api-version <version> Allow to use declarations only from the specified version of bundled libraries
-kotlin-home <path> Path to Kotlin compiler home directory, used for runtime libraries discovery
-P plugin:<pluginId>:<optionName>=<value>
Pass an option to a plugin
-help (-h) Print a synopsis of standard options
@@ -0,0 +1,3 @@
$TESTDATA_DIR$/simple.kt
-kotlin-home
$TESTDATA_DIR$
+10
View File
@@ -0,0 +1,10 @@
warning: unable to find kotlin-stdlib.jar in the Kotlin home directory. Pass either '-no-stdlib' to prevent adding it to the classpath, or the correct '-kotlin-home'
warning: unable to find kotlin-script-runtime.jar in the Kotlin home directory. Pass either '-no-stdlib' to prevent adding it to the classpath, or the correct '-kotlin-home'
warning: unable to find kotlin-reflect.jar in the Kotlin home directory. Pass either '-no-reflect' or '-no-stdlib' to prevent adding it to the classpath, or the correct '-kotlin-home'
compiler/testData/cli/jvm/simple.kt:1:16: error: unresolved reference: Array
fun main(args: Array<String>) = println("hello world")
^
compiler/testData/cli/jvm/simple.kt:1:33: error: unresolved reference: println
fun main(args: Array<String>) = println("hello world")
^
COMPILATION_ERROR
@@ -3,6 +3,7 @@ Buildfile: [TestData]/build.xml
build:
[kotlin2js] Compiling [[TestData]/root1] => [[Temp]/out.js]
[kotlin2js] logging: using Kotlin home directory [KotlinProjectHome]/dist/kotlinc
[kotlin2js] logging: compiling source files: [TestData]/root1/foo.kt
BUILD SUCCESSFUL
@@ -224,6 +224,12 @@ public class CliTestGenerated extends AbstractCliTest {
doJvmTest(fileName);
}
@TestMetadata("kotlinHomeWithoutStdlib.args")
public void testKotlinHomeWithoutStdlib() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/kotlinHomeWithoutStdlib.args");
doJvmTest(fileName);
}
@TestMetadata("kotlinPackage.args")
public void testKotlinPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/kotlinPackage.args");
@@ -467,6 +473,12 @@ public class CliTestGenerated extends AbstractCliTest {
doJsTest(fileName);
}
@TestMetadata("kotlinHomeWithoutStdlib.args")
public void testKotlinHomeWithoutStdlib() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/kotlinHomeWithoutStdlib.args");
doJsTest(fileName);
}
@TestMetadata("kotlinPackage.args")
public void testKotlinPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/kotlinPackage.args");
@@ -485,6 +497,12 @@ public class CliTestGenerated extends AbstractCliTest {
doJsTest(fileName);
}
@TestMetadata("nonExistingKotlinHome.args")
public void testNonExistingKotlinHome() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/nonExistingKotlinHome.args");
doJsTest(fileName);
}
@TestMetadata("nonExistingSourcePath.args")
public void testNonExistingSourcePath() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/nonExistingSourcePath.args");