blob: 167ba0f3fcc82b5fd5bc2cc74053e3cef241e6c1 [file] [log] [blame]
/*
* Copyright 2018 The Bazel Authors. All rights reserved.
*
* 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 io.bazel.kotlin.builder.toolchain
import io.bazel.kotlin.builder.utils.resolveVerified
import org.jetbrains.kotlin.preloading.ClassPreloadingUtils
import org.jetbrains.kotlin.preloading.Preloader
import java.io.File
import java.io.PrintStream
import java.io.PrintWriter
import java.lang.reflect.Method
import java.nio.file.Path
import java.nio.file.Paths
import javax.inject.Inject
import javax.inject.Singleton
class KotlinToolchain private constructor(
val kotlinHome: Path,
val classLoader: ClassLoader,
val kotlinStandardLibraries: List<String> = listOf(
"kotlin-stdlib.jar",
"kotlin-stdlib-jdk7.jar",
"kotlin-stdlib-jdk8.jar"
),
val kapt3Plugin: KotlinToolchain.CompilerPlugin = KotlinToolchain.CompilerPlugin(
kotlinHome.resolveVerified("lib", "kotlin-annotation-processing.jar").absolutePath,
"org.jetbrains.kotlin.kapt3"
)
) {
companion object {
internal val NO_ARGS = arrayOf<Any>()
private val isJdk9OrNewer = !System.getProperty("java.version").startsWith("1.")
private val javaRunfiles get() = Paths.get(System.getenv("JAVA_RUNFILES"))
private fun createClassLoader(javaHome: Path, kotlinHome: Path): ClassLoader {
val preloadJars = mutableListOf<File>().also {
it += kotlinHome.resolveVerified("lib", "kotlin-compiler.jar")
it += javaRunfiles.resolveVerified("io_bazel_rules_kotlin", "src", "main", "kotlin", "compiler_lib.jar")
if (!isJdk9OrNewer) {
it += javaHome.resolveVerified("lib", "tools.jar")
}
}
return ClassPreloadingUtils.preloadClasses(
preloadJars,
Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE,
ClassLoader.getSystemClassLoader(),
null
)
}
@JvmStatic
fun createToolchain(): KotlinToolchain {
val kotlinHome = Paths.get("external", "com_github_jetbrains_kotlin")
val javaHome = Paths.get(System.getProperty("java.home")).let { path ->
path.takeIf { !it.endsWith(Paths.get("jre")) } ?: path.parent
}
return KotlinToolchain(kotlinHome, createClassLoader(javaHome, kotlinHome))
}
}
data class CompilerPlugin(val jarPath: String, val id: String)
@Singleton
class JavacInvoker @Inject constructor(toolchain: KotlinToolchain) {
private val c = toolchain.classLoader.loadClass("com.sun.tools.javac.Main")
private val m = c.getMethod("compile", Array<String>::class.java)
private val mPw = c.getMethod("compile", Array<String>::class.java, PrintWriter::class.java)
fun compile(args: Array<String>) = m.invoke(c, args) as Int
fun compile(args: Array<String>, out: PrintWriter) = mPw.invoke(c, args, out) as Int
}
@Singleton
class JDepsInvoker @Inject constructor(toolchain: KotlinToolchain) {
private val clazz = toolchain.classLoader.loadClass("com.sun.tools.jdeps.Main")
private val method = clazz.getMethod("run", Array<String>::class.java, PrintWriter::class.java)
fun run(args: Array<String>, out: PrintWriter): Int = method.invoke(clazz, args, out) as Int
}
open class KotlinCliToolInvoker internal constructor(
toolchain: KotlinToolchain,
clazz: String
) {
private val compiler: Any
private val execMethod: Method
private val getCodeMethod: Method
init {
val compilerClass = toolchain.classLoader.loadClass(clazz)
val exitCodeClass = toolchain.classLoader.loadClass("org.jetbrains.kotlin.cli.common.ExitCode")
compiler = compilerClass.getConstructor().newInstance()
execMethod = compilerClass.getMethod("exec", PrintStream::class.java, Array<String>::class.java)
getCodeMethod = exitCodeClass.getMethod("getCode")
}
// Kotlin error codes:
// 1 is a standard compilation error
// 2 is an internal error
// 3 is the script execution error
fun compile(args: Array<String>, out: PrintStream): Int {
val exitCodeInstance = execMethod.invoke(compiler, out, args)
return getCodeMethod.invoke(exitCodeInstance, *NO_ARGS) as Int
}
}
@Singleton
class KotlincInvoker @Inject constructor(
toolchain: KotlinToolchain
) : KotlinCliToolInvoker(toolchain, "io.bazel.kotlin.compiler.BazelK2JVMCompiler")
@Singleton
class K2JSCompilerInvoker @Inject constructor(
toolchain: KotlinToolchain
) : KotlinCliToolInvoker(toolchain, "org.jetbrains.kotlin.cli.js.K2JSCompiler")
}