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 <compilerarg> 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 <compilerarg>, and this will become inconvenient to
update with each change in compiler arguments

 #KT-5618 Fixed
This commit is contained in:
Alexander Udalov
2014-12-11 22:32:14 +03:00
parent 3bde619ca3
commit 14dab749a2
10 changed files with 102 additions and 89 deletions
+1 -5
View File
@@ -7,11 +7,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="cli" />
<orderEntry type="library" name="idea-full" level="project" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="backend" />
<orderEntry type="module" module-name="util" />
<orderEntry type="library" name="ant-1.7" level="project" />
<orderEntry type="library" name="kotlin-runtime" level="project" />
</component>
</module>
@@ -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<K2JSCompilerArguments>() {
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<K2JSCompilerArguments>() {
}
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")
}
}
@@ -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<K2JVMCompilerArguments>() {
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<K2JVMCompilerArguments>() {
}
override fun fillSpecificArguments() {
arguments.destination = output!!.canonicalPath
args.add("-d")
args.add(output!!.canonicalPath)
val classpath = arrayListOf<String>()
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")
}
}
@@ -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<ClassLoader?>(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<T : CommonCompilerArguments> : Task() {
protected abstract val arguments: T
protected abstract val compiler: CLICompiler<T>
public abstract class KotlinCompilerBaseTask : Task() {
protected abstract val compilerFqName: String
protected val args: MutableList<String> = arrayListOf()
public var src: Path? = null
public var output: File? = null
@@ -73,21 +100,15 @@ public abstract class KotlinCompilerBaseTask<T : CommonCompilerArguments> : 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<T : CommonCompilerArguments> : 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<PrintStream>(), javaClass<Array<String>>())
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.")
}
}
}