From 14dab749a240538ac7d197f25d2c1e8b70caa854 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 11 Dec 2014 22:32:14 +0300 Subject: [PATCH] Use reflection in Ant task to invoke compiler This will allow a more controlled management of the runtime that the compiler is linked against. Incidentally this also allows Ant task to use any of compiler arguments via because Ant task is now just a facade for the CLI compiler. The test "wrongArguments" is deleted because the full compiler usage is now printed out on a wrong , and this will become inconvenient to update with each change in compiler arguments #KT-5618 Fixed --- ant/ant.iml | 6 +- .../jet/buildtools/ant/Kotlin2JsTask.kt | 35 +++++---- .../jet/buildtools/ant/Kotlin2JvmTask.kt | 25 +++--- .../buildtools/ant/KotlinCompilerBaseTask.kt | 77 ++++++++++++------- build.xml | 3 +- .../jetbrains/jet/cli/common/CLICompiler.java | 6 ++ .../org/jetbrains/kotlin/AntTaskJvmTest.java | 13 ++-- .../ant/jvm/wrongArguments/build.log.expected | 13 ---- .../testData/ant/jvm/wrongArguments/build.xml | 9 --- .../testData/ant/jvm/wrongArguments/hello.kt | 4 - 10 files changed, 102 insertions(+), 89 deletions(-) delete mode 100644 compiler/integration-tests/testData/ant/jvm/wrongArguments/build.log.expected delete mode 100644 compiler/integration-tests/testData/ant/jvm/wrongArguments/build.xml delete mode 100644 compiler/integration-tests/testData/ant/jvm/wrongArguments/hello.kt diff --git a/ant/ant.iml b/ant/ant.iml index 50e8f687d41..4548c995447 100644 --- a/ant/ant.iml +++ b/ant/ant.iml @@ -7,11 +7,7 @@ - - - - - + \ No newline at end of file diff --git a/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JsTask.kt b/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JsTask.kt index c75dbf28907..b499b089891 100644 --- a/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JsTask.kt +++ b/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JsTask.kt @@ -17,13 +17,10 @@ package org.jetbrains.jet.buildtools.ant import org.apache.tools.ant.types.Path -import org.jetbrains.jet.cli.common.arguments.K2JSCompilerArguments -import org.jetbrains.jet.cli.js.K2JSCompiler import java.io.File -public class Kotlin2JsTask : KotlinCompilerBaseTask() { - override val arguments = K2JSCompilerArguments() - override val compiler = K2JSCompiler() +public class Kotlin2JsTask : KotlinCompilerBaseTask() { + override val compilerFqName = "org.jetbrains.jet.cli.js.K2JSCompiler" public var library: Path? = null public var outputPrefix: File? = null @@ -48,19 +45,31 @@ public class Kotlin2JsTask : KotlinCompilerBaseTask() { } override fun fillSpecificArguments() { - arguments.outputFile = output!!.canonicalPath - - arguments.noStdlib = noStdlib + args.add("-output") + args.add(output!!.canonicalPath) // TODO: write test library?.let { - arguments.libraryFiles = it.list().map { File(it).canonicalPath }.copyToArray() + args.add("-library-files") + args.add(it.list().map { File(it).canonicalPath }.join(separator = ",")) } - arguments.outputPrefix = outputPrefix?.canonicalPath - arguments.outputPostfix = outputPostfix?.canonicalPath + outputPrefix?.let { + args.add("-output-prefix") + args.add(it.canonicalPath) + } - arguments.main = main - arguments.sourceMap = sourceMap + outputPostfix?.let { + args.add("-output-postfix") + args.add(it.canonicalPath) + } + + main?.let { + args.add("-main") + args.add(it) + } + + if (noStdlib) args.add("-no-stdlib") + if (sourceMap) args.add("-source-map") } } diff --git a/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JvmTask.kt b/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JvmTask.kt index 6585934c330..cdfdd5d7dad 100644 --- a/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JvmTask.kt +++ b/ant/src/org/jetbrains/jet/buildtools/ant/Kotlin2JvmTask.kt @@ -18,14 +18,11 @@ package org.jetbrains.jet.buildtools.ant import org.apache.tools.ant.types.Path import org.apache.tools.ant.types.Reference -import org.jetbrains.jet.cli.jvm.K2JVMCompiler -import org.jetbrains.jet.cli.common.arguments.K2JVMCompilerArguments import java.io.File import java.io.File.pathSeparator -public class Kotlin2JvmTask : KotlinCompilerBaseTask() { - override val arguments = K2JVMCompilerArguments() - override val compiler = K2JVMCompiler() +public class Kotlin2JvmTask : KotlinCompilerBaseTask() { + override val compilerFqName = "org.jetbrains.jet.cli.jvm.K2JVMCompiler" public var externalAnnotations: Path? = null public var includeRuntime: Boolean = true @@ -60,21 +57,27 @@ public class Kotlin2JvmTask : KotlinCompilerBaseTask() { } override fun fillSpecificArguments() { - arguments.destination = output!!.canonicalPath + args.add("-d") + args.add(output!!.canonicalPath) val classpath = arrayListOf() compileClasspath?.let { classpath.addAll(it.list()) } - arguments.freeArgs?.forEach { + src!!.list().forEach { val file = File(it) if (file.isDirectory() || file.extension != "kt") { classpath.add(it) } } - arguments.classpath = classpath.join(pathSeparator) + args.add("-classpath") + args.add(classpath.join(pathSeparator)) - arguments.annotations = externalAnnotations?.list()?.join(pathSeparator) - arguments.noStdlib = noStdlib - arguments.includeRuntime = includeRuntime + externalAnnotations?.let { + args.add("-annotations") + args.add(it.list().join(pathSeparator)) + } + + if (noStdlib) args.add("-no-stdlib") + if (includeRuntime) args.add("-include-runtime") } } diff --git a/ant/src/org/jetbrains/jet/buildtools/ant/KotlinCompilerBaseTask.kt b/ant/src/org/jetbrains/jet/buildtools/ant/KotlinCompilerBaseTask.kt index 11cd18a0061..178457c9b8d 100644 --- a/ant/src/org/jetbrains/jet/buildtools/ant/KotlinCompilerBaseTask.kt +++ b/ant/src/org/jetbrains/jet/buildtools/ant/KotlinCompilerBaseTask.kt @@ -21,22 +21,49 @@ import org.apache.tools.ant.types.Path import org.apache.tools.ant.types.Reference import java.io.File import org.apache.tools.ant.BuildException -import org.jetbrains.jet.cli.common.ExitCode -import org.jetbrains.jet.cli.common.arguments.CommonCompilerArguments -import org.jetbrains.jet.cli.common.CLICompiler import org.apache.tools.ant.types.Commandline -import com.sampullara.cli.Args -import org.jetbrains.jet.cli.common.messages.PrintingMessageCollector -import org.jetbrains.jet.cli.common.messages.MessageRenderer -import org.jetbrains.jet.config.Services +import java.io.PrintStream +import org.apache.tools.ant.AntClassLoader +import java.lang.ref.SoftReference +import org.apache.tools.ant.Project +import java.net.JarURLConnection + +object CompilerClassLoaderHolder { + private var classLoaderRef = SoftReference(null) + + synchronized fun getOrCreateClassLoader(project: Project?): ClassLoader { + val cached = classLoaderRef.get() + if (cached != null) return cached + + val myLoader = javaClass.getClassLoader() + if (myLoader !is AntClassLoader) return myLoader + + // Find path of kotlin-ant.jar in the filesystem and find kotlin-compiler.jar in the same directory + val resourcePath = "/" + javaClass.getName().replace('.', '/') + ".class" + val jarConnection = javaClass.getResource(resourcePath).openConnection() as? JarURLConnection + ?: throw UnsupportedOperationException("Kotlin compiler Ant task should be loaded from the JAR file") + val antTaskJarPath = File(jarConnection.getJarFileURL().toURI()) + + val compilerJarPath = File(antTaskJarPath.getParent(), "kotlin-compiler.jar") + if (!compilerJarPath.exists()) { + throw IllegalStateException("kotlin-compiler.jar is not found in the directory of Kotlin Ant task") + } + + val classLoader = AntClassLoader(myLoader, project, Path(project, compilerJarPath.path), /* parentFirst = */ false) + classLoaderRef = SoftReference(classLoader) + + return classLoader + } +} /** * Base class for Kotlin compiler Ant tasks. * http://evgeny-goldin.org/javadoc/ant/tutorial-writing-tasks.html */ -public abstract class KotlinCompilerBaseTask : Task() { - protected abstract val arguments: T - protected abstract val compiler: CLICompiler +public abstract class KotlinCompilerBaseTask : Task() { + protected abstract val compilerFqName: String + + protected val args: MutableList = arrayListOf() public var src: Path? = null public var output: File? = null @@ -73,21 +100,15 @@ public abstract class KotlinCompilerBaseTask : Task private fun fillArguments() { val sourcePaths = src ?: throw BuildException("\"src\" should be specified") - arguments.freeArgs = sourcePaths.list().map { File(it).canonicalPath } + args.addAll(sourcePaths.list().map { File(it).canonicalPath }) output ?: throw BuildException("\"output\" should be specified") - arguments.suppressWarnings = nowarn - arguments.verbose = verbose - arguments.version = printVersion + if (nowarn) args.add("-nowarn") + if (verbose) args.add("-verbose") + if (printVersion) args.add("-version") - val args = additionalArguments.flatMap { it.getParts().toList() } - try { - Args.parse(arguments, args.copyToArray()) - } - catch (e: IllegalArgumentException) { - throw BuildException(e.getMessage()) - } + args.addAll(additionalArguments.flatMap { it.getParts().toList() }) fillSpecificArguments() } @@ -95,13 +116,17 @@ public abstract class KotlinCompilerBaseTask : Task final override fun execute() { fillArguments() - log("Compiling ${arguments.freeArgs} => [${output!!.canonicalPath}]"); + val compilerClass = CompilerClassLoaderHolder.getOrCreateClassLoader(project).loadClass(compilerFqName) + val compiler = compilerClass.newInstance() + val exec = compilerClass.getMethod("execFullPathsInMessages", javaClass(), javaClass>()) - val collector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN, arguments.verbose) - val exitCode = compiler.exec(collector, Services.EMPTY, arguments) + log("Compiling ${src!!.list().toList()} => [${output!!.canonicalPath}]"); - if (exitCode != ExitCode.OK) { - throw BuildException("Compilation finished with exit code $exitCode") + val exitCode = exec(compiler, System.err, args.copyToArray()) + + // TODO: support failOnError attribute of javac + if ((exitCode as Enum<*>).ordinal() != 0) { + throw BuildException("Compile failed; see the compiler error output for details.") } } } diff --git a/build.xml b/build.xml index d8e94ade7e7..89ef5929e3f 100644 --- a/build.xml +++ b/build.xml @@ -624,7 +624,6 @@ - @@ -642,7 +641,7 @@ - + diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/CLICompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/common/CLICompiler.java index e4c55dd9c50..5daa75868ce 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/common/CLICompiler.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/CLICompiler.java @@ -58,6 +58,12 @@ public abstract class CLICompiler { return exec(errStream, services, MessageRenderer.TAGS, args); } + @SuppressWarnings("UnusedDeclaration") // Used via reflection in KotlinCompilerBaseTask + @NotNull + public ExitCode execFullPathsInMessages(@NotNull PrintStream errStream, @NotNull String[] args) { + return exec(errStream, Services.EMPTY, MessageRenderer.PLAIN, args); + } + @Nullable private A parseArguments(@NotNull PrintStream errStream, @NotNull MessageRenderer messageRenderer, @NotNull String[] args) { try { diff --git a/compiler/integration-tests/src/org/jetbrains/kotlin/AntTaskJvmTest.java b/compiler/integration-tests/src/org/jetbrains/kotlin/AntTaskJvmTest.java index 3f81a32889b..357f2d9338f 100644 --- a/compiler/integration-tests/src/org/jetbrains/kotlin/AntTaskJvmTest.java +++ b/compiler/integration-tests/src/org/jetbrains/kotlin/AntTaskJvmTest.java @@ -17,11 +17,13 @@ package org.jetbrains.kotlin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.utils.UtilsPackage; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import java.io.File; +import java.util.Arrays; public class AntTaskJvmTest extends AntTaskBaseTest { private static final String JVM_OUT_FILE = "hello.jar"; @@ -44,7 +46,11 @@ public class AntTaskJvmTest extends AntTaskBaseTest { } private static String getClassPathForAnt() { - return getCompilerLib() + File.separator + "kotlin-ant.jar" + File.pathSeparator + getKotlinRuntimePath(); + return UtilsPackage.join(Arrays.asList( + getCompilerLib() + File.separator + "kotlin-ant.jar", + getCompilerLib() + File.separator + "kotlin-compiler.jar", + getKotlinRuntimePath() + ), File.pathSeparator); } private static String getIdeaSdkHome() { @@ -61,11 +67,6 @@ public class AntTaskJvmTest extends AntTaskBaseTest { doJvmAntTest(); } - @Test - public void wrongArguments() throws Exception { - doAntTest(FAILED); - } - @Test public void jvmClasspath() throws Exception { doJvmAntTest(); diff --git a/compiler/integration-tests/testData/ant/jvm/wrongArguments/build.log.expected b/compiler/integration-tests/testData/ant/jvm/wrongArguments/build.log.expected deleted file mode 100644 index c3fabbb7cd1..00000000000 --- a/compiler/integration-tests/testData/ant/jvm/wrongArguments/build.log.expected +++ /dev/null @@ -1,13 +0,0 @@ -OUT: -Buildfile: [TestData]/build.xml - -build: - -ERR: - -BUILD FAILED -[TestData]/build.xml:5: Invalid argument: -option-never-to-be-supported - -Total time: [time] - -Return code: 1 diff --git a/compiler/integration-tests/testData/ant/jvm/wrongArguments/build.xml b/compiler/integration-tests/testData/ant/jvm/wrongArguments/build.xml deleted file mode 100644 index 33b6930c19b..00000000000 --- a/compiler/integration-tests/testData/ant/jvm/wrongArguments/build.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/compiler/integration-tests/testData/ant/jvm/wrongArguments/hello.kt b/compiler/integration-tests/testData/ant/jvm/wrongArguments/hello.kt deleted file mode 100644 index 952c9795d52..00000000000 --- a/compiler/integration-tests/testData/ant/jvm/wrongArguments/hello.kt +++ /dev/null @@ -1,4 +0,0 @@ -package hello - -fun main(args : Array) { -}