| /* |
| * 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") |
| } |
| |
| |