Provide compiler classpath as task input.

This ensures that compiler classpath is what is expected
by Kotlin Plugin and removes possibility of leaking wrong jars
from Gradle wrapper classpath.

For 'kotlin.useFallbackCompilerSearch' old behaviour is still present,
but this option should be marked as deprecated and removed in one
of the Kotlin releases.
This commit is contained in:
Yahor Berdnikau
2021-02-11 14:02:32 +01:00
parent 0eaea655d0
commit 069941cdaf
5 changed files with 89 additions and 25 deletions
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.compilerRunner.*
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import kotlin.test.assertTrue
// todo: test client file creation/deletion // todo: test client file creation/deletion
// todo: test daemon start (does not start every build) // todo: test daemon start (does not start every build)
@@ -91,4 +92,43 @@ class KotlinDaemonIT : BaseGradleIT() {
} }
} }
} }
@Test
fun testGradleBuildClasspathShouldNotBeLeakedIntoDaemonClasspath() {
val testProject = Project("kotlinProject")
testProject.setupWorkingDir()
testProject.build("assemble") {
assertGradleClasspathNotLeaked()
}
}
@Test
fun testGradleBuildClasspathShouldNotBeLeakedIntoDaemonClasspathOnUsingCustomCompilerJar() {
val testProject = Project(
projectName = "customCompilerFile",
)
testProject.setupWorkingDir()
// copy compiler embeddable into project dir using custom name
val classpath = System.getProperty("java.class.path").split(File.pathSeparator)
val kotlinEmbeddableJar = File(classpath.find { it.contains("kotlin-compiler-embeddable") }!!)
val compilerJar = File(testProject.projectDir, "compiler.jar")
kotlinEmbeddableJar.copyTo(compilerJar)
testProject.build("build") {
assertGradleClasspathNotLeaked()
}
}
private fun CompiledProject.assertGradleClasspathNotLeaked() {
assertContains("Kotlin compiler classpath:")
val daemonClasspath = output.lineSequence().find {
it.contains("Kotlin compiler classpath:")
}!!
assertTrue("Daemon classpath contains embeddable daemon jar leaked from Gradle dist classpath: $daemonClasspath") {
!daemonClasspath.contains(".gradle/wrapper/dists")
}
}
} }
@@ -21,10 +21,8 @@ import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.FeaturePreviews import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.build.event.BuildEventsListenerRegistry
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
@@ -36,10 +34,10 @@ import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_KLIB_COMMONIZER_EMBEDDABLE import org.jetbrains.kotlin.gradle.tasks.KOTLIN_KLIB_COMMONIZER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources
@@ -68,9 +66,8 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
whenBuildEvaluated(project) whenBuildEvaluated(project)
} }
project.configurations.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME).defaultDependencies { addKotlinCompilerConfiguration(project)
it.add(project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:$kotlinPluginVersion"))
}
project.configurations.maybeCreate(PLUGIN_CLASSPATH_CONFIGURATION_NAME) project.configurations.maybeCreate(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
project.configurations.maybeCreate(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME).apply { project.configurations.maybeCreate(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME).apply {
isTransitive = false isTransitive = false
@@ -106,6 +103,25 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
project.addNpmDependencyExtension() project.addNpmDependencyExtension()
} }
private fun addKotlinCompilerConfiguration(project: Project) {
project
.configurations
.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME)
.defaultDependencies {
it.add(
project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:$kotlinPluginVersion")
)
}
project
.tasks
.withType(AbstractKotlinCompile::class.java)
.configureEach { task ->
task.defaultCompilerClasspath.setFrom(
project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME)
)
}
}
open fun whenBuildEvaluated(project: Project) { open fun whenBuildEvaluated(project: Project) {
} }
@@ -64,6 +64,7 @@ class KotlinJsDcePlugin : Plugin<Project> {
val dceTask = project.registerTask<KotlinJsDce>(dceTaskName) { val dceTask = project.registerTask<KotlinJsDce>(dceTaskName) {
it.dependsOn(kotlinTask) it.dependsOn(kotlinTask)
it.defaultCompilerClasspath.setFrom(project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME))
} }
project.tasks.named("build").dependsOn(dceTask) project.tasks.named("build").dependsOn(dceTask)
@@ -10,6 +10,7 @@ import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce
import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
@@ -287,6 +288,7 @@ open class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
it.classpath = project.configurations.getByName(compilation.runtimeDependencyConfigurationName) it.classpath = project.configurations.getByName(compilation.runtimeDependencyConfigurationName)
it.destinationDir = it.dceOptions.outputDirectory?.let { File(it) } it.destinationDir = it.dceOptions.outputDirectory?.let { File(it) }
?: compilation.npmProject.dir.resolve(if (dev) DCE_DEV_DIR else DCE_DIR) ?: compilation.npmProject.dir.resolve(if (dev) DCE_DEV_DIR else DCE_DIR)
it.defaultCompilerClasspath.setFrom(project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME))
it.source(kotlinTask.map { it.outputFile }) it.source(kotlinTask.map { it.outputFile })
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
@@ -98,28 +99,32 @@ abstract class AbstractKotlinCompileTool<T : CommonToolArguments>
override val metrics: BuildMetricsReporter = override val metrics: BuildMetricsReporter =
BuildMetricsReporterImpl() BuildMetricsReporterImpl()
/**
* By default should be set by plugin from [COMPILER_CLASSPATH_CONFIGURATION_NAME] configuration.
*
* Empty classpath will fail the build.
*/
@get:Classpath
internal val defaultCompilerClasspath: ConfigurableFileCollection =
project.objects.fileCollection()
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
internal val computedCompilerClasspath: List<File> by lazy { internal val computedCompilerClasspath: List<File> by lazy {
compilerClasspath?.takeIf { it.isNotEmpty() } require(!defaultCompilerClasspath.isEmpty) {
?: compilerJarFile?.let { "Default Kotlin compiler classpath is empty! Task: ${this::class.qualifiedName}"
// 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") }
} when {
?: if (!useFallbackCompilerSearch) { !compilerClasspath.isNullOrEmpty() -> compilerClasspath!!
try { compilerJarFile != null -> listOf(compilerJarFile!!) +
project.configurations.getByName(COMPILER_CLASSPATH_CONFIGURATION_NAME).resolve().toList() defaultCompilerClasspath
} catch (e: Exception) { .filterNot {
logger.error( it.nameWithoutExtension.startsWith("kotlin-compiler")
"Could not resolve compiler classpath. " + }
"Check if Kotlin Gradle plugin repository is configured in $project." useFallbackCompilerSearch -> findKotlinCompilerClasspath(project)
) else -> defaultCompilerClasspath.toList()
throw e }
}
} else {
findKotlinCompilerClasspath(project)
}
?: throw IllegalStateException("Could not find Kotlin Compiler classpath")
} }