Support -Xmodule-path and -Xadd-modules command line arguments
#KT-18598 In Progress #KT-18599 Fixed
This commit is contained in:
+11
@@ -88,6 +88,17 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
|
|||||||
|
|
||||||
// Advanced options
|
// Advanced options
|
||||||
|
|
||||||
|
@Argument(value = "-Xmodule-path", valueDescription = "<path>", description = "Paths where to find Java 9+ modules")
|
||||||
|
public String javaModulePath;
|
||||||
|
|
||||||
|
@Argument(
|
||||||
|
value = "-Xadd-modules",
|
||||||
|
valueDescription = "<module[,]>",
|
||||||
|
description = "Root modules to resolve in addition to the initial modules,\n" +
|
||||||
|
"or all modules on the module path if <module> is ALL-MODULE-PATH"
|
||||||
|
)
|
||||||
|
public String[] additionalJavaModules;
|
||||||
|
|
||||||
@Argument(value = "-Xno-call-assertions", description = "Don't generate not-null assertion after each invocation of method returning not-null")
|
@Argument(value = "-Xno-call-assertions", description = "Don't generate not-null assertion after each invocation of method returning not-null")
|
||||||
public boolean noCallAssertions;
|
public boolean noCallAssertions;
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
|
|||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||||
@@ -106,6 +107,9 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
|
|
||||||
val classpath = getClasspath(paths, arguments)
|
val classpath = getClasspath(paths, arguments)
|
||||||
configuration.addJvmClasspathRoots(classpath)
|
configuration.addJvmClasspathRoots(classpath)
|
||||||
|
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
|
||||||
|
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
|
||||||
|
}
|
||||||
|
|
||||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME)
|
configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME)
|
||||||
|
|
||||||
@@ -299,6 +303,10 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
configuration.put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, components)
|
configuration.put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, components)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arguments.additionalJavaModules?.let { additionalJavaModules ->
|
||||||
|
configuration.addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, additionalJavaModules.toList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createArguments(): K2JVMCompilerArguments = K2JVMCompilerArguments().apply {
|
override fun createArguments(): K2JVMCompilerArguments = K2JVMCompilerArguments().apply {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageUtil
|
|||||||
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||||
import org.jetbrains.kotlin.config.ContentRoot
|
import org.jetbrains.kotlin.config.ContentRoot
|
||||||
@@ -41,6 +42,7 @@ import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
|||||||
internal class ClasspathRootsResolver(
|
internal class ClasspathRootsResolver(
|
||||||
private val psiManager: PsiManager,
|
private val psiManager: PsiManager,
|
||||||
private val messageCollector: MessageCollector?,
|
private val messageCollector: MessageCollector?,
|
||||||
|
private val additionalModules: List<String>,
|
||||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?
|
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?
|
||||||
) {
|
) {
|
||||||
private val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
|
private val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
|
||||||
@@ -75,6 +77,15 @@ internal class ClasspathRootsResolver(
|
|||||||
is JvmClasspathRoot -> {
|
is JvmClasspathRoot -> {
|
||||||
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
||||||
}
|
}
|
||||||
|
is JvmModulePathRoot -> {
|
||||||
|
// TODO: sanitize the automatic module name exactly as in javac
|
||||||
|
// TODO: read Automatic-Module-Name manifest entry
|
||||||
|
val module = modularBinaryRoot(root, automaticModuleName = { contentRoot.file.name })
|
||||||
|
if (module != null) {
|
||||||
|
// TODO: report something in case of several modules with the same name?
|
||||||
|
modules += module
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> error("Unknown root type: $contentRoot")
|
else -> error("Unknown root type: $contentRoot")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,6 +108,17 @@ internal class ClasspathRootsResolver(
|
|||||||
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), root, moduleInfoFile, isBinary = false)
|
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), root, moduleInfoFile, isBinary = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun modularBinaryRoot(root: VirtualFile, automaticModuleName: () -> String): JavaModule? {
|
||||||
|
val moduleInfoFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE)
|
||||||
|
return if (moduleInfoFile != null) {
|
||||||
|
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
|
||||||
|
JavaModule.Explicit(moduleInfo, root, moduleInfoFile, isBinary = true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JavaModule.Automatic(automaticModuleName(), root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
|
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
|
||||||
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filterNot(JavaModule::isBinary)
|
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filterNot(JavaModule::isBinary)
|
||||||
if (sourceModules.size > 1) {
|
if (sourceModules.size > 1) {
|
||||||
@@ -113,12 +135,17 @@ internal class ClasspathRootsResolver(
|
|||||||
|
|
||||||
if (javaModuleFinder.allObservableModules.none()) return
|
if (javaModuleFinder.allObservableModules.none()) return
|
||||||
|
|
||||||
val rootModules =
|
val addAllModulePathToRoots = "ALL-MODULE-PATH" in additionalModules
|
||||||
if (sourceModules.isNotEmpty()) {
|
if (addAllModulePathToRoots && sourceModules.isNotEmpty()) {
|
||||||
// TODO: support an option similar to --add-modules
|
report(ERROR, "-Xadd-modules=ALL-MODULE-PATH can only be used when compiling the unnamed module")
|
||||||
listOf(sourceModules.single().name)
|
return
|
||||||
}
|
}
|
||||||
else computeDefaultRootModules() // TODO: + everything from module path
|
|
||||||
|
val rootModules = when {
|
||||||
|
sourceModules.isNotEmpty() -> listOf(sourceModules.single().name) + additionalModules
|
||||||
|
addAllModulePathToRoots -> modules.map(JavaModule::name)
|
||||||
|
else -> computeDefaultRootModules() + modules.map(JavaModule::name)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: if at least one automatic module is added, add all automatic modules as per java.lang.module javadoc
|
// TODO: if at least one automatic module is added, add all automatic modules as per java.lang.module javadoc
|
||||||
val allDependencies = javaModuleGraph.getAllDependencies(rootModules).also { loadedModules ->
|
val allDependencies = javaModuleGraph.getAllDependencies(rootModules).also { loadedModules ->
|
||||||
|
|||||||
@@ -194,7 +194,11 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
|
|
||||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
|
|
||||||
classpathRootsResolver = ClasspathRootsResolver(PsiManager.getInstance(project), messageCollector, this::contentRootToVirtualFile)
|
classpathRootsResolver = ClasspathRootsResolver(
|
||||||
|
PsiManager.getInstance(project), messageCollector,
|
||||||
|
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||||
|
this::contentRootToVirtualFile
|
||||||
|
)
|
||||||
|
|
||||||
val (initialRoots, javaModules) =
|
val (initialRoots, javaModules) =
|
||||||
classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS))
|
classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS))
|
||||||
@@ -304,7 +308,8 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
|
|
||||||
private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? {
|
private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? {
|
||||||
return when (root) {
|
return when (root) {
|
||||||
is JvmClasspathRoot -> if (root.file.isFile) findJarRoot(root) else findLocalFile(root)
|
is JvmClasspathRoot -> if (root.file.isFile) findJarRoot(root.file) else findLocalFile(root)
|
||||||
|
is JvmModulePathRoot -> if (root.file.isFile) findJarRoot(root.file) else findLocalFile(root)
|
||||||
is JavaSourceRoot -> findLocalFile(root)
|
is JavaSourceRoot -> findLocalFile(root)
|
||||||
else -> throw IllegalStateException("Unexpected root: $root")
|
else -> throw IllegalStateException("Unexpected root: $root")
|
||||||
}
|
}
|
||||||
@@ -320,8 +325,8 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findJarRoot(root: JvmClasspathRoot): VirtualFile? =
|
private fun findJarRoot(file: File): VirtualFile? =
|
||||||
applicationEnvironment.jarFileSystem.findFileByPath("${root.file}${URLUtil.JAR_SEPARATOR}")
|
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||||
|
|
||||||
private fun getSourceRootsCheckingForDuplicates(): Collection<String> {
|
private fun getSourceRootsCheckingForDuplicates(): Collection<String> {
|
||||||
val uniqueSourceRoots = linkedSetOf<String>()
|
val uniqueSourceRoots = linkedSetOf<String>()
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ data class JvmClasspathRoot(override val file: File) : JvmContentRoot
|
|||||||
|
|
||||||
data class JavaSourceRoot(override val file: File, val packagePrefix: String?) : JvmContentRoot
|
data class JavaSourceRoot(override val file: File, val packagePrefix: String?) : JvmContentRoot
|
||||||
|
|
||||||
|
data class JvmModulePathRoot(override val file: File) : JvmContentRoot
|
||||||
|
|
||||||
fun CompilerConfiguration.addJvmClasspathRoot(file: File) {
|
fun CompilerConfiguration.addJvmClasspathRoot(file: File) {
|
||||||
add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
|
add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,4 +117,6 @@ public class JVMConfigurationKeys {
|
|||||||
public static final CompilerConfigurationKey<Boolean> USE_JAVAC =
|
public static final CompilerConfigurationKey<Boolean> USE_JAVAC =
|
||||||
CompilerConfigurationKey.create("use javac");
|
CompilerConfigurationKey.create("use javac");
|
||||||
|
|
||||||
|
public static final CompilerConfigurationKey<List<String>> ADDITIONAL_JAVA_MODULES =
|
||||||
|
CompilerConfigurationKey.create("additional Java modules");
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -1,5 +1,8 @@
|
|||||||
Usage: kotlinc-jvm <options> <source files>
|
Usage: kotlinc-jvm <options> <source files>
|
||||||
where advanced options include:
|
where advanced options include:
|
||||||
|
-Xmodule-path=<path> Paths where to find Java 9+ modules
|
||||||
|
-Xadd-modules=<module[,]> Root modules to resolve in addition to the initial modules,
|
||||||
|
or all modules on the module path if <module> is ALL-MODULE-PATH
|
||||||
-Xno-call-assertions Don't generate not-null assertion after each invocation of method returning not-null
|
-Xno-call-assertions Don't generate not-null assertion after each invocation of method returning not-null
|
||||||
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
|
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
|
||||||
-Xno-optimize Disable optimizations
|
-Xno-optimize Disable optimizations
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
error: -Xadd-modules=ALL-MODULE-PATH can only be used when compiling the unnamed module
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class Foo
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
module main {
|
||||||
|
exports foo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
public class A {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleA {
|
||||||
|
exports a;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package b;
|
||||||
|
|
||||||
|
public class B {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleB {
|
||||||
|
exports b;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package c;
|
||||||
|
|
||||||
|
public class C {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleC {
|
||||||
|
exports c;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module moduleD {
|
||||||
|
requires moduleA;
|
||||||
|
requires moduleB;
|
||||||
|
requires moduleC;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import a.*
|
||||||
|
import b.B
|
||||||
|
import c.C
|
||||||
|
|
||||||
|
fun usage() {
|
||||||
|
A()
|
||||||
|
B()
|
||||||
|
C()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
compiler/testData/javaModules/jdkModulesFromNamed/main/test.kt:11:24: error: unresolved reference: httpserver
|
||||||
|
val s: com.sun.net.httpserver.HttpServer? = null
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module main {
|
||||||
|
requires java.naming;
|
||||||
|
requires jdk.net;
|
||||||
|
requires oracle.desktop;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
// Module java.naming
|
||||||
|
val b: javax.naming.Binding? = null
|
||||||
|
println(b)
|
||||||
|
|
||||||
|
// Module jdk.net
|
||||||
|
val j: jdk.net.Sockets? = null
|
||||||
|
println(j)
|
||||||
|
|
||||||
|
// Module jdk.httpserver (this module doesn't depend on it)
|
||||||
|
val s: com.sun.net.httpserver.HttpServer? = null
|
||||||
|
println(s)
|
||||||
|
|
||||||
|
// Module oracle.desktop
|
||||||
|
val a: com.oracle.awt.AWTUtils? = null
|
||||||
|
println(a)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
// Module java.naming
|
||||||
|
val b: javax.naming.Binding? = null
|
||||||
|
println(b)
|
||||||
|
|
||||||
|
// Module jdk.net
|
||||||
|
val j: jdk.net.Sockets? = null
|
||||||
|
println(j)
|
||||||
|
|
||||||
|
// Module jdk.httpserver
|
||||||
|
val s: com.sun.net.httpserver.HttpServer? = null
|
||||||
|
println(s)
|
||||||
|
|
||||||
|
// Module oracle.desktop
|
||||||
|
val a: com.oracle.awt.AWTUtils? = null
|
||||||
|
println(a)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package foo;
|
||||||
|
|
||||||
|
public class Foo {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleA {
|
||||||
|
exports foo;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleB {
|
||||||
|
requires moduleA;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import foo.Foo
|
||||||
|
|
||||||
|
fun usage() {
|
||||||
|
Foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import a.impl.AImpl;
|
||||||
|
|
||||||
|
public class A {
|
||||||
|
public static AImpl getInstance() {
|
||||||
|
return new AImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package a.impl;
|
||||||
|
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
public class AImpl extends A {}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleA {
|
||||||
|
exports a;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:8:9: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'
|
||||||
|
val a3: AImpl = A.getInstance()
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleB {
|
||||||
|
requires moduleA;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
import a.impl.*
|
||||||
|
|
||||||
|
val a1: A = A()
|
||||||
|
val a2: A = A.getInstance()
|
||||||
|
val a3: AImpl = A.getInstance()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package foo;
|
||||||
|
|
||||||
|
public class Foo {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module moduleA {
|
||||||
|
exports foo;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
public class Usage {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new foo.Foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import foo.Foo
|
||||||
|
|
||||||
|
fun usage() {
|
||||||
|
Foo()
|
||||||
|
}
|
||||||
@@ -912,6 +912,27 @@ public class KotlinTestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean compileJavaFilesExternallyWithJava9(@NotNull Collection<File> files, @NotNull List<String> options) {
|
||||||
|
File jdk9 = getJdk9HomeIfPossible();
|
||||||
|
assert jdk9 != null : "Environment variable JDK_19 is not set";
|
||||||
|
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add(new File(jdk9, "bin/javac").getPath());
|
||||||
|
command.addAll(options);
|
||||||
|
for (File file : files) {
|
||||||
|
command.add(file.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = new ProcessBuilder().command(command).inheritIO().start();
|
||||||
|
process.waitFor();
|
||||||
|
return process.exitValue() == 0;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw ExceptionUtilsKt.rethrow(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String errorsToString(@NotNull DiagnosticCollector<JavaFileObject> diagnosticCollector, boolean humanReadable) {
|
private static String errorsToString(@NotNull DiagnosticCollector<JavaFileObject> diagnosticCollector, boolean humanReadable) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|||||||
+13
-4
@@ -39,6 +39,8 @@ abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
|
|||||||
return File(testDataDirectory, "${getTestName(true)}.$extension")
|
return File(testDataDirectory, "${getTestName(true)}.$extension")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class JavaCompilationError : AssertionError("Java files are not compiled successfully")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles all sources (.java and .kt) under the directory named [libraryName] to [destination].
|
* Compiles all sources (.java and .kt) under the directory named [libraryName] to [destination].
|
||||||
* [destination] should be either a path to the directory under [tmpdir], or a path to the resulting .jar file (also under [tmpdir]).
|
* [destination] should be either a path to the directory under [tmpdir], or a path to the resulting .jar file (also under [tmpdir]).
|
||||||
@@ -50,10 +52,15 @@ abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
|
|||||||
libraryName: String,
|
libraryName: String,
|
||||||
destination: File = File(tmpdir, "$libraryName.jar"),
|
destination: File = File(tmpdir, "$libraryName.jar"),
|
||||||
additionalOptions: List<String> = emptyList(),
|
additionalOptions: List<String> = emptyList(),
|
||||||
|
compileJava: (sourceDir: File, javaFiles: List<File>, outputDir: File) -> Boolean = { _, javaFiles, outputDir ->
|
||||||
|
KotlinTestUtils.compileJavaFiles(javaFiles, listOf("-d", outputDir.path))
|
||||||
|
},
|
||||||
|
checkKotlinOutput: (String) -> Unit = { actual -> assertEquals(normalizeOutput("" to ExitCode.OK), actual) },
|
||||||
vararg extraClassPath: File
|
vararg extraClassPath: File
|
||||||
): File {
|
): File {
|
||||||
val javaFiles = FileUtil.findFilesByMask(JAVA_FILES, File(testDataDirectory, libraryName))
|
val sourceDir = File(testDataDirectory, libraryName)
|
||||||
val kotlinFiles = FileUtil.findFilesByMask(KOTLIN_FILES, File(testDataDirectory, libraryName))
|
val javaFiles = FileUtil.findFilesByMask(JAVA_FILES, sourceDir)
|
||||||
|
val kotlinFiles = FileUtil.findFilesByMask(KOTLIN_FILES, sourceDir)
|
||||||
assert(javaFiles.isNotEmpty() || kotlinFiles.isNotEmpty()) { "There should be either .kt or .java files in the directory" }
|
assert(javaFiles.isNotEmpty() || kotlinFiles.isNotEmpty()) { "There should be either .kt or .java files in the directory" }
|
||||||
|
|
||||||
val isJar = destination.name.endsWith(".jar")
|
val isJar = destination.name.endsWith(".jar")
|
||||||
@@ -61,12 +68,14 @@ abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
|
|||||||
val outputDir = if (isJar) File(tmpdir, "output-$libraryName") else destination
|
val outputDir = if (isJar) File(tmpdir, "output-$libraryName") else destination
|
||||||
if (kotlinFiles.isNotEmpty()) {
|
if (kotlinFiles.isNotEmpty()) {
|
||||||
val output = compileKotlin(libraryName, outputDir, extraClassPath.toList(), K2JVMCompiler(), additionalOptions, expectedFileName = null)
|
val output = compileKotlin(libraryName, outputDir, extraClassPath.toList(), K2JVMCompiler(), additionalOptions, expectedFileName = null)
|
||||||
assertEquals(normalizeOutput("" to ExitCode.OK), normalizeOutput(output))
|
checkKotlinOutput(normalizeOutput(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (javaFiles.isNotEmpty()) {
|
if (javaFiles.isNotEmpty()) {
|
||||||
outputDir.mkdirs()
|
outputDir.mkdirs()
|
||||||
KotlinTestUtils.compileJavaFiles(javaFiles, listOf("-d", outputDir.path))
|
if (!compileJava(sourceDir, javaFiles, outputDir)) {
|
||||||
|
throw JavaCompilationError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJar) {
|
if (isJar) {
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.jvm.compiler
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class Java9ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
||||||
|
override val testDataPath: String
|
||||||
|
get() = "compiler/testData/javaModules/"
|
||||||
|
|
||||||
|
private fun module(
|
||||||
|
name: String,
|
||||||
|
modulePath: List<File> = emptyList(),
|
||||||
|
addModules: List<String> = emptyList()
|
||||||
|
): File {
|
||||||
|
val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: return File("<test-skipped>")
|
||||||
|
|
||||||
|
val paths = modulePath.joinToString(separator = File.pathSeparator) { it.path }
|
||||||
|
|
||||||
|
val kotlinOptions = mutableListOf(
|
||||||
|
"-jdk-home", jdk9Home.path,
|
||||||
|
"-Xmodule-path=$paths"
|
||||||
|
)
|
||||||
|
if (addModules.isNotEmpty()) {
|
||||||
|
kotlinOptions += "-Xadd-modules=${addModules.joinToString()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return compileLibrary(
|
||||||
|
name,
|
||||||
|
additionalOptions = kotlinOptions,
|
||||||
|
compileJava = { _, javaFiles, outputDir ->
|
||||||
|
val javaOptions = mutableListOf(
|
||||||
|
"-d", outputDir.path,
|
||||||
|
"--module-path", paths
|
||||||
|
)
|
||||||
|
if (addModules.isNotEmpty()) {
|
||||||
|
javaOptions += "--add-modules"
|
||||||
|
javaOptions += addModules.joinToString()
|
||||||
|
}
|
||||||
|
KotlinTestUtils.compileJavaFilesExternallyWithJava9(javaFiles, javaOptions)
|
||||||
|
},
|
||||||
|
checkKotlinOutput = { actual ->
|
||||||
|
KotlinTestUtils.assertEqualsToFile(File(testDataDirectory, "$name.txt"), actual)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSimple() {
|
||||||
|
val a = module("moduleA")
|
||||||
|
module("moduleB", listOf(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSimpleUseNonExportedPackage() {
|
||||||
|
val a = module("moduleA")
|
||||||
|
module("moduleB", listOf(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testDependOnManyModules() {
|
||||||
|
val a = module("moduleA")
|
||||||
|
val b = module("moduleB")
|
||||||
|
val c = module("moduleC")
|
||||||
|
module("moduleD", listOf(a, b, c))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testUnnamedDependsOnNamed() {
|
||||||
|
val a = module("moduleA")
|
||||||
|
module("moduleB", listOf(a), listOf("moduleA"))
|
||||||
|
|
||||||
|
// Also check that -Xadd-modules=ALL-MODULE-PATH has the same effect as -Xadd-module=moduleA, i.e. adds moduleA to the roots
|
||||||
|
module("moduleB", listOf(a), listOf("ALL-MODULE-PATH"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAllModulePathAndNamedModule() {
|
||||||
|
try {
|
||||||
|
module("main", addModules = listOf("ALL-MODULE-PATH"))
|
||||||
|
}
|
||||||
|
catch (e: JavaCompilationError) {
|
||||||
|
// Java compilation should fail, it's expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testJdkModulesFromNamed() {
|
||||||
|
module("main")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testJdkModulesFromUnnamed() {
|
||||||
|
module("main")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user