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:
+1
-5
@@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +624,6 @@
|
||||
</src>
|
||||
<compilerarg value="-Xlint:all"/>
|
||||
<classpath>
|
||||
<fileset dir="${kotlin-home}/lib" includes="kotlin-compiler.jar"/>
|
||||
<file file="${bootstrap.runtime}"/>
|
||||
<fileset dir="${dependencies.dir}/ant-1.7/lib" includes="ant.jar"/>
|
||||
</classpath>
|
||||
@@ -642,7 +641,7 @@
|
||||
<attribute name="Implementation-Title" value="${manifest.impl.title.kotlin.compiler.ant.task}"/>
|
||||
<attribute name="Implementation-Version" value="${build.number}"/>
|
||||
|
||||
<attribute name="Class-Path" value="kotlin-compiler.jar"/>
|
||||
<attribute name="Class-Path" value="kotlin-runtime.jar"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
@@ -58,6 +58,12 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
@@ -1,9 +0,0 @@
|
||||
<project name="Ant Task Test" default="build">
|
||||
<taskdef resource="org/jetbrains/jet/buildtools/ant/antlib.xml" classpath="${kotlin.lib}/kotlin-ant.jar"/>
|
||||
|
||||
<target name="build">
|
||||
<kotlinc src="${test.data}/hello.kt" output="${temp}/hello.jar">
|
||||
<compilerarg value="-option-never-to-be-supported"/>
|
||||
</kotlinc>
|
||||
</target>
|
||||
</project>
|
||||
@@ -1,4 +0,0 @@
|
||||
package hello
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
}
|
||||
Reference in New Issue
Block a user