Implement compiler classpath config property in gradle...

instead of compiler jar
And add stdlib/reflect/script_runtime to the classpath on detection
This commit is contained in:
Ilya Chernikov
2017-09-13 19:58:39 +02:00
parent 776f1c8d19
commit 51fc3da2f5
8 changed files with 106 additions and 60 deletions
@@ -2,7 +2,6 @@ package org.jetbrains.kotlin.compilerRunner
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
import org.jetbrains.kotlin.gradle.tasks.findToolsJar
@@ -13,22 +12,22 @@ import java.io.File
import java.net.URL
internal open class GradleCompilerEnvironment(
val compilerJar: File,
val compilerClasspath: List<File>,
messageCollector: GradleMessageCollector,
outputItemsCollector: OutputItemsCollector,
val compilerArgs: CommonCompilerArguments
) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) {
val toolsJar: File? by lazy { findToolsJar() }
val compilerClasspath: List<File>
get() = listOf(compilerJar, toolsJar).filterNotNull()
val compilerFullClasspath: List<File>
get() = (compilerClasspath + toolsJar).filterNotNull()
val compilerClasspathURLs: List<URL>
get() = compilerClasspath.map { it.toURI().toURL() }
get() = compilerFullClasspath.map { it.toURI().toURL() }
}
internal class GradleIncrementalCompilerEnvironment(
compilerJar: File,
compilerClasspath: List<File>,
val changedFiles: ChangedFiles,
val reporter: ICReporter,
val workingDir: File,
@@ -38,4 +37,4 @@ internal class GradleIncrementalCompilerEnvironment(
val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null,
val artifactFile: File? = null
) : GradleCompilerEnvironment(compilerJar, messageCollector, outputItemsCollector, compilerArgs)
) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs)
@@ -139,15 +139,14 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
environment: GradleCompilerEnvironment
): ExitCode {
if (compilerArgs.version) {
project.logger.lifecycle("Kotlin version " + loadCompilerVersion(environment.compilerJar) +
project.logger.lifecycle("Kotlin version " + loadCompilerVersion(environment.compilerClasspath) +
" (JRE " + System.getProperty("java.runtime.version") + ")")
compilerArgs.version = false
}
val argsArray = ArgumentUtils.convertArgumentsToStringList(compilerArgs).toTypedArray()
with (project.logger) {
kotlinDebug { "Kotlin compiler class: $compilerClassName" }
kotlinDebug { "Kotlin compiler classpath: ${environment.compilerClasspath.map { it.canonicalPath }.joinToString()}" }
kotlinDebug { "Kotlin compiler classpath: ${environment.compilerFullClasspath.map { it.canonicalPath }.joinToString()}" }
kotlinDebug { "Kotlin compiler args: ${argsArray.joinToString(" ")}" }
}
@@ -284,7 +283,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
compilerClassName: String,
environment: GradleCompilerEnvironment
): ExitCode {
return runToolInSeparateProcess(argsArray, compilerClassName, environment.compilerClasspath, log, loggingMessageCollector)
return runToolInSeparateProcess(argsArray, compilerClassName, environment.compilerFullClasspath, log, loggingMessageCollector)
}
private fun compileInProcess(
@@ -317,7 +316,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
private fun logFinish(strategy: String) = log.logFinish(strategy)
override fun getDaemonConnection(environment: GradleCompilerEnvironment): CompileServiceSession? {
val compilerId = CompilerId.makeCompilerId(environment.compilerClasspath)
val compilerId = CompilerId.makeCompilerId(environment.compilerFullClasspath)
val clientIsAliveFlagFile = getOrCreateClientFlagFile(project)
val sessionIsAliveFlagFile = getOrCreateSessionFlagFile(project)
return newDaemonConnection(compilerId, clientIsAliveFlagFile, sessionIsAliveFlagFile, environment)
@@ -32,26 +32,40 @@ import java.io.File
import java.util.zip.ZipFile
import kotlin.concurrent.thread
internal fun loadCompilerVersion(compilerJar: File): String {
var result = "<unknown>"
internal fun loadCompilerVersion(compilerClasspath: List<File>): String {
var result: String? = null
fun checkVersion(bytes: ByteArray) {
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor {
if (name == KotlinCompilerVersion::VERSION.name && value is String) {
result = value
}
return super.visitField(access, name, desc, signature, value)
}
}, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES or ClassReader.SKIP_DEBUG)
}
try {
ZipFile(compilerJar).use { file ->
val fileName = KotlinCompilerVersion::class.java.name.replace('.', '/') + ".class"
val bytes = file.getInputStream(file.getEntry(fileName)).use { inputStream -> inputStream.readBytes() }
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor {
if (name == KotlinCompilerVersion::VERSION.name && value is String) {
result = value
}
return super.visitField(access, name, desc, signature, value)
val versionClassFileName = KotlinCompilerVersion::class.java.name.replace('.', '/') + ".class"
for (cpFile in compilerClasspath) {
if (cpFile.isFile && cpFile.extension.toLowerCase() == "jar") {
ZipFile(cpFile).use { jar ->
val bytes = jar.getInputStream(jar.getEntry(versionClassFileName)).use { it.readBytes() }
checkVersion(bytes)
}
}, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES or ClassReader.SKIP_DEBUG)
}
else if (cpFile.isDirectory) {
File(cpFile, versionClassFileName).takeIf { it.isFile }?.let {
checkVersion(it.readBytes())
}
}
if (result != null) break
}
}
catch (e: Throwable) {}
return result
return result ?: "<unknown>"
}
internal fun runToolInSeparateProcess(
@@ -63,7 +63,7 @@ open class KaptTask : ConventionTask() {
val messageCollector = GradleMessageCollector(logger)
val outputItemCollector = OutputItemsCollectorImpl()
val environment = GradleCompilerEnvironment(kotlinCompileTask.compilerJar, messageCollector, outputItemCollector, args)
val environment = GradleCompilerEnvironment(kotlinCompileTask.computedCompilerClasspath, messageCollector, outputItemCollector, args)
if (environment.toolsJar == null) {
throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work")
}
@@ -32,8 +32,8 @@ internal open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompil
override fun getSourceRoots(): SourceRoots =
SourceRoots.KotlinOnly.create(getSource())
override fun findKotlinCompilerJar(project: Project): File? =
findKotlinMetadataCompilerJar(project)
override fun findKotlinCompilerClasspath(project: Project): List<File> =
findKotlinMetadataCompilerClasspath(project)
override fun callCompiler(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
val classpathList = classpath.files.toMutableList()
@@ -49,7 +49,7 @@ internal open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompil
val messageCollector = GradleMessageCollector(logger)
val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = GradleCompilerRunner(project)
val environment = GradleCompilerEnvironment(compilerJar, messageCollector, outputItemCollector, args)
val environment = GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
val exitCode = compilerRunner.runMetadataCompiler(sourceRoots.kotlinSourceFiles, args, environment)
throwGradleExceptionIfError(exitCode)
}
@@ -37,7 +37,7 @@ open class KotlinJsDce : AbstractKotlinCompileTool<K2JSDceArguments>(), KotlinJs
override val keep: MutableList<String> = mutableListOf()
override fun findKotlinCompilerJar(project: Project): File? = findKotlinJsDceJar(project)
override fun findKotlinCompilerClasspath(project: Project): List<File> = findKotlinJsDceClasspath(project)
override fun compile() {}
@@ -58,7 +58,7 @@ open class KotlinJsDce : AbstractKotlinCompileTool<K2JSDceArguments>(), KotlinJs
val log = GradleKotlinLogger(project.logger)
val allArgs = argsArray + outputDirArgs + inputFiles
val exitCode = runToolInSeparateProcess(allArgs, K2JSDce::class.java.name, listOf(compilerJar),
val exitCode = runToolInSeparateProcess(allArgs, K2JSDce::class.java.name, computedCompilerClasspath,
log, createLoggingMessageCollector(log))
throwGradleExceptionIfError(exitCode)
}
@@ -55,12 +55,19 @@ const val USING_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin incremental comp
const val USING_EXPERIMENTAL_JS_INCREMENTAL_COMPILATION_MESSAGE = "Using experimental Kotlin/JS incremental compilation"
abstract class AbstractKotlinCompileTool<T : CommonToolArguments>() : AbstractCompile() {
// TODO: deprecate and remove
var compilerJarFile: File? = null
internal val compilerJar: File
get() = compilerJarFile
?: findKotlinCompilerJar(project)
?: throw IllegalStateException("Could not find Kotlin Compiler jar. Please specify $name.compilerJarFile")
protected abstract fun findKotlinCompilerJar(project: Project): File?
var compilerClasspath: List<File>? = null
internal val computedCompilerClasspath: List<File>
get() = compilerClasspath?.takeIf { it.isNotEmpty() }
?: compilerJarFile?.let {
// a hack to remove compiler jar from the cp, will be dropped when compilerJarFile will be removed
listOf(it) + findKotlinCompilerClasspath(project).filter { it.name.startsWith("kotlin-compiler") }
}
?: findKotlinCompilerClasspath(project).takeIf { it.isNotEmpty() }
?: throw IllegalStateException("Could not find Kotlin Compiler classpath. Please specify $name.compilerClasspath")
protected abstract fun findKotlinCompilerClasspath(project: Project): List<File>
}
abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKotlinCompileTool<T>(), CompilerArgumentAware {
@@ -255,8 +262,8 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
incremental = true
}
override fun findKotlinCompilerJar(project: Project): File? =
findKotlinJvmCompilerJar(project)
override fun findKotlinCompilerClasspath(project: Project): List<File> =
findKotlinJvmCompilerClasspath(project)
override fun createCompilerArgs(): K2JVMCompilerArguments =
K2JVMCompilerArguments()
@@ -306,10 +313,10 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
val reporter = GradleICReporter(project.rootProject.projectDir)
val environment = when {
!incremental -> GradleCompilerEnvironment(compilerJar, messageCollector, outputItemCollector, args)
!incremental -> GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
else -> {
logger.warn(USING_INCREMENTAL_COMPILATION_MESSAGE)
GradleIncrementalCompilerEnvironment(compilerJar, changedFiles, reporter, taskBuildDirectory,
GradleIncrementalCompilerEnvironment(computedCompilerClasspath, changedFiles, reporter, taskBuildDirectory,
messageCollector, outputItemCollector, args, kaptAnnotationsFileUpdater,
artifactDifferenceRegistryProvider,
artifactFile)
@@ -403,8 +410,8 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
val outputFile: String
get() = kotlinOptions.outputFile ?: defaultOutputFile.canonicalPath
override fun findKotlinCompilerJar(project: Project): File? =
findKotlinJsCompilerJar(project)
override fun findKotlinCompilerClasspath(project: Project): List<File> =
findKotlinJsCompilerClasspath(project)
override fun createCompilerArgs(): K2JSCompilerArguments =
K2JSCompilerArguments()
@@ -463,11 +470,11 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
incremental -> {
logger.warn(USING_EXPERIMENTAL_JS_INCREMENTAL_COMPILATION_MESSAGE)
GradleIncrementalCompilerEnvironment(
compilerJar, changedFiles, reporter, taskBuildDirectory,
computedCompilerClasspath, changedFiles, reporter, taskBuildDirectory,
messageCollector, outputItemCollector, args)
}
else -> {
GradleCompilerEnvironment(compilerJar, messageCollector, outputItemCollector, args)
GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
}
}
@@ -32,21 +32,48 @@ private val K2JVM_COMPILER_CLASS = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler"
private val K2JS_COMPILER_CLASS = "org.jetbrains.kotlin.cli.js.K2JSCompiler"
private val K2JS_DCE_CLASS = "org.jetbrains.kotlin.cli.js.dce.K2JSDce"
private val K2METADATA_COMPILER_CLASS = "org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler"
private val KOTLIN_STDLIB_EXPECTED_CLASS = "kotlin.collections.ArraysKt"
private val KOTLIN_SCRIPT_RUNTIME_EXPECTED_CLASS = "kotlin.script.templates.AnnotationsKt"
private val KOTLIN_REFLECT_EXPECTED_CLASS = "kotlin.reflect.full.KClasses"
private val KOTLIN_MODULE_GROUP = "org.jetbrains.kotlin"
private val KOTLIN_GRADLE_PLUGIN = "kotlin-gradle-plugin"
private val KOTLIN_COMPILER_EMBEDDABLE = "kotlin-compiler-embeddable"
private val KOTLIN_STDLIB = "kotlin-stdlib"
private val KOTLIN_SCRIPT_RUNTIME = "kotlin-script-runtime"
private val KOTLIN_REFLECT = "kotlin-reflect"
internal fun findKotlinJvmCompilerJar(project: Project): File? =
findKotlinCompilerJar(project, K2JVM_COMPILER_CLASS)
internal fun findKotlinJvmCompilerClasspath(project: Project): List<File> =
findKotlinModuleJar(project, K2JVM_COMPILER_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let {
if (it.isEmpty()) it
else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project)
}
internal fun findKotlinJsCompilerJar(project: Project): File? =
findKotlinCompilerJar(project, K2JS_COMPILER_CLASS)
internal fun findKotlinJsCompilerClasspath(project: Project): List<File> =
findKotlinModuleJar(project, K2JS_COMPILER_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let {
if (it.isEmpty()) it
else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project)
}
internal fun findKotlinMetadataCompilerJar(project: Project): File? =
findKotlinCompilerJar(project, K2METADATA_COMPILER_CLASS)
internal fun findKotlinMetadataCompilerClasspath(project: Project): List<File> =
findKotlinModuleJar(project, K2METADATA_COMPILER_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let {
if (it.isEmpty()) it
else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project)
}
internal fun findKotlinJsDceJar(project: Project): File? =
findKotlinCompilerJar(project, K2JS_DCE_CLASS)
internal fun findKotlinJsDceClasspath(project: Project): List<File> =
findKotlinModuleJar(project, K2JS_DCE_CLASS, KOTLIN_COMPILER_EMBEDDABLE).let {
if (it.isEmpty()) it
else it + findKotlinStdlibClasspath(project) + findKotlinScriptRuntimeClasspath(project) + findKotlinReflectClasspath(project)
}
internal fun findKotlinStdlibClasspath(project: Project): List<File> =
findKotlinModuleJar(project, KOTLIN_STDLIB_EXPECTED_CLASS, KOTLIN_STDLIB)
internal fun findKotlinScriptRuntimeClasspath(project: Project): List<File> =
findKotlinModuleJar(project, KOTLIN_SCRIPT_RUNTIME_EXPECTED_CLASS, KOTLIN_SCRIPT_RUNTIME)
internal fun findKotlinReflectClasspath(project: Project): List<File> =
findKotlinModuleJar(project, KOTLIN_REFLECT_EXPECTED_CLASS, KOTLIN_REFLECT)
internal fun findToolsJar(): File? =
Class.forName("com.sun.tools.javac.util.Context")?.let(::findJarByClass)
@@ -61,13 +88,13 @@ private fun findJarByClass(klass: Class<*>): File? {
return File(fileName)
}
private fun findKotlinCompilerJar(project: Project, compilerClassName: String): File? {
private fun findKotlinModuleJar(project: Project, expectedClassName: String, moduleId: String): List<File> {
val pluginVersion = pluginVersionFromAppliedPlugin(project)
val filesToCheck = sequenceOf(pluginVersion?.let(::getCompilerFromClassLoader)) +
Sequence { findPotentialCompilerJars(project).iterator() } //call the body only when queried
val entryToFind = compilerClassName.replace(".", "/") + ".class"
return filesToCheck.filterNotNull().firstOrNull { it.hasEntry(entryToFind) }
Sequence { findPotentialModuleJars(project, moduleId).iterator() } //call the body only when queried
val entryToFind = expectedClassName.replace(".", "/") + ".class"
return filesToCheck.filterNotNull().firstOrNull { it.hasEntry(entryToFind) }?.let { listOf(it) } ?: emptyList()
}
private fun pluginVersionFromAppliedPlugin(project: Project): String? =
@@ -81,7 +108,7 @@ private fun getCompilerFromClassLoader(pluginVersion: String): File? {
?.takeIf(File::exists)
}
private fun findPotentialCompilerJars(project: Project): Iterable<File> {
private fun findPotentialModuleJars(project: Project, moduleId: String): Iterable<File> {
val projects = generateSequence(project) { it.parent }
val classpathConfigurations = projects
.map { it.buildscript.configurations.findByName(ScriptHandler.CLASSPATH_CONFIGURATION) }
@@ -90,7 +117,7 @@ private fun findPotentialCompilerJars(project: Project): Iterable<File> {
val allFiles = HashSet<File>()
for (configuration in classpathConfigurations) {
val compilerEmbeddable = findCompilerEmbeddable(configuration)
val compilerEmbeddable = findKotlinModuleDependency(configuration, moduleId)
if (compilerEmbeddable != null) {
return compilerEmbeddable.moduleArtifacts.map { it.file }
@@ -103,13 +130,13 @@ private fun findPotentialCompilerJars(project: Project): Iterable<File> {
return allFiles
}
private fun findCompilerEmbeddable(configuration: Configuration): ResolvedDependency? {
private fun findKotlinModuleDependency(configuration: Configuration, moduleId: String): ResolvedDependency? {
fun Iterable<ResolvedDependency>.findDependency(group: String, name: String): ResolvedDependency? =
find { it.moduleGroup == group && it.moduleName == name }
val firstLevelModuleDependencies = configuration.resolvedConfiguration.firstLevelModuleDependencies
val gradlePlugin = firstLevelModuleDependencies.findDependency(KOTLIN_MODULE_GROUP, KOTLIN_GRADLE_PLUGIN)
return gradlePlugin?.children?.findDependency(KOTLIN_MODULE_GROUP, KOTLIN_COMPILER_EMBEDDABLE)
return gradlePlugin?.children?.findDependency(KOTLIN_MODULE_GROUP, moduleId)
}
private fun File.hasEntry(entryToFind: String): Boolean {