Fix eager Kotlin/JVM task configuration on Gradle 7.3+
^KT-54836 Fixed
This commit is contained in:
committed by
Space Team
parent
f8e6c59600
commit
2e5981eb94
+24
@@ -25,6 +25,30 @@ class ConfigurationAvoidanceIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmGradlePluginTests
|
||||
@GradleTestVersions(
|
||||
maxVersion = TestVersions.Gradle.G_7_5,
|
||||
additionalVersions = [TestVersions.Gradle.G_7_3, TestVersions.Gradle.G_7_4]
|
||||
)
|
||||
@DisplayName("KGP/Jvm does not eagerly configure any tasks")
|
||||
@GradleTest
|
||||
fun testJvmConfigurationAvoidance(gradleVersion: GradleVersion) {
|
||||
project("simpleProject", gradleVersion) {
|
||||
buildGradle.appendText(
|
||||
"""
|
||||
|
|
||||
|tasks.configureEach {
|
||||
| if (name != "help" && name != "clean") {
|
||||
| throw new GradleException("Configuration avoidance failure for ${'$'}name!")
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
build("--dry-run")
|
||||
}
|
||||
}
|
||||
|
||||
@AndroidGradlePluginTests
|
||||
@DisplayName("Android unrelated tasks are not configured")
|
||||
@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_42)
|
||||
|
||||
+47
-1
@@ -9,6 +9,7 @@ import org.junit.jupiter.api.DisplayName
|
||||
import kotlin.io.path.appendText
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.createFile
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
@JvmGradlePluginTests
|
||||
@DisplayName("KGP simple tests")
|
||||
@@ -202,7 +203,7 @@ class SimpleKotlinGradleIT : KGPBaseTest() {
|
||||
|
||||
@GradleTest
|
||||
@DisplayName("Should be compatible with project isolation")
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_1, maxVersion = TestVersions.Gradle.G_7_1)
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_1)
|
||||
fun testProjectIsolation(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "instantExecution",
|
||||
@@ -273,4 +274,49 @@ class SimpleKotlinGradleIT : KGPBaseTest() {
|
||||
build("assemble")
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Changing compile task destination directory does not break test compilation")
|
||||
@GradleTest
|
||||
internal fun customDestinationDir(gradleVersion: GradleVersion) {
|
||||
project("simpleProject", gradleVersion) {
|
||||
//language=Groovy
|
||||
buildGradle.appendText(
|
||||
"""
|
||||
|
|
||||
|tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile.class).configureEach {
|
||||
| if (it.name == "compileKotlin") {
|
||||
| it.destinationDirectory.set(project.layout.buildDirectory.dir("banana"))
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
build("build") {
|
||||
assertFileInProjectExists("build/banana/demo/KotlinGreetingJoiner.class")
|
||||
assertFileInProjectExists("build/libs/simpleProject.jar")
|
||||
ZipFile(projectPath.resolve("build/libs/simpleProject.jar").toFile()).use { jar ->
|
||||
assert(jar.entries().asSequence().count { it.name == "demo/KotlinGreetingJoiner.class" } == 1) {
|
||||
"The jar should contain one entry `demo/KotlinGreetingJoiner.class` with no duplicates\n" +
|
||||
jar.entries().asSequence().map { it.name }.joinToString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Default jar content should not contain duplicates")
|
||||
@GradleTest
|
||||
internal fun defaultJarContent(gradleVersion: GradleVersion) {
|
||||
project("simpleProject", gradleVersion) {
|
||||
build("build") {
|
||||
assertFileInProjectExists("build/libs/simpleProject.jar")
|
||||
ZipFile(projectPath.resolve("build/libs/simpleProject.jar").toFile()).use { jar ->
|
||||
assert(jar.entries().asSequence().count { it.name == "demo/KotlinGreetingJoiner.class" } == 1) {
|
||||
"The jar should contain one entry `demo/KotlinGreetingJoiner.class` with no duplicates\n" +
|
||||
jar.entries().asSequence().map { it.name }.joinToString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ dependencies {
|
||||
|
||||
task integrationTestJar(type: Jar) {
|
||||
from sourceSets.integrationTest.output
|
||||
from sourceSets.integrationTest.kotlin.classesDirectory
|
||||
classifier = 'inttests'
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -18,6 +18,6 @@ dependencies {
|
||||
testImplementation 'org.testng:testng:6.8'
|
||||
}
|
||||
|
||||
test {
|
||||
tasks.named("test").configure {
|
||||
useTestNG()
|
||||
}
|
||||
|
||||
+7
-5
@@ -6,15 +6,17 @@ import java.util.ArrayList
|
||||
|
||||
class KotlinGreetingJoiner(val greeter : Greeter) {
|
||||
|
||||
val names = ArrayList<String?>()
|
||||
private val names = ArrayList<String?>()
|
||||
|
||||
fun addName(name : String?): Unit{
|
||||
names.add(name)
|
||||
}
|
||||
|
||||
fun getJoinedGreeting() : String? {
|
||||
val joiner = Joiner.on(" and ").skipNulls();
|
||||
return "${greeter.getGreeting()} ${joiner.join(names)}"
|
||||
}
|
||||
fun getJoinedGreeting() : String? {
|
||||
val joiner = Joiner.on(" and ").skipNulls();
|
||||
return "${greeter.getGreeting()} ${joiner.join(names)}"
|
||||
}
|
||||
|
||||
internal fun getNames(): List<String?> = names.toList()
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -1,10 +1,6 @@
|
||||
package demo
|
||||
|
||||
import com.google.common.primitives.Ints
|
||||
import com.google.common.base.Joiner
|
||||
import org.testng.Assert.*
|
||||
import org.testng.annotations.AfterMethod
|
||||
import org.testng.annotations.BeforeMethod
|
||||
import org.testng.annotations.Test as test
|
||||
|
||||
class TestSource() {
|
||||
@@ -16,6 +12,7 @@ class TestSource() {
|
||||
example.addName("Hermione")
|
||||
|
||||
assertEquals(example.getJoinedGreeting(), "Hi Harry and Ron and Hermione")
|
||||
assertEquals(example.getNames().size, 4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -164,6 +164,9 @@ internal val KotlinCompilationInfo.tcsOrNull: KotlinCompilationInfo.TCS?
|
||||
is KotlinCompilationInfo.TCS -> this
|
||||
}
|
||||
|
||||
internal val KotlinCompilationInfo.tcs: KotlinCompilationInfo.TCS
|
||||
get() = this as KotlinCompilationInfo.TCS
|
||||
|
||||
internal val KotlinCompilationInfo.kpmOrNull: KotlinCompilationInfo.KPM?
|
||||
get() = when (this) {
|
||||
is KotlinCompilationInfo.KPM -> this
|
||||
|
||||
+44
-1
@@ -7,11 +7,14 @@ package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
|
||||
@@ -50,7 +53,47 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
|
||||
|
||||
private fun prepareKotlinCompileTask(): TaskProvider<out T> =
|
||||
doRegisterTask(project, compilationInfo.compileKotlinTaskName).also { task ->
|
||||
compilationInfo.classesDirs.from(task.flatMap { it.destinationDirectory })
|
||||
// Workaround for case when 'compilationInfo.classesDirs' is actually Java 'SourceSet.output.classesDir'
|
||||
// Gradle from 7.3 started to realize 'FileCollection's registered for 'BuildOutputCleanupRegistry'
|
||||
// at the end of the configuration phase. One of this 'FileCollection' is 'SourceSetOutput'
|
||||
// which causes 'KotlinCompile' tasks to be eagerly realized.
|
||||
// Related commit into Gradle: https://github.com/gradle/gradle/commit/758adc5b0c5a5d27e2797a9fa8fd2690530c5de9
|
||||
// Proper workaround is peeked from here:
|
||||
// https://github.com/gradle/gradle/blob/16cbc3a678771b08418d086eb67e9934c9282dfb/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/JvmPluginsHelper.java#L142-L148
|
||||
if (compilationInfo.tcsOrNull?.compilation is KotlinWithJavaCompilation<*, *>) {
|
||||
val kotlinSourceDirectorySet = compilationInfo.tcs.compilation.defaultSourceSet.kotlin
|
||||
kotlinSourceDirectorySet.destinationDirectory.value(defaultKotlinDestinationDir)
|
||||
task.configure {
|
||||
if (it is Kotlin2JsCompile) {
|
||||
it.defaultDestinationDirectory.convention(kotlinSourceDirectorySet.destinationDirectory)
|
||||
} else {
|
||||
it.destinationDirectory.convention(kotlinSourceDirectorySet.destinationDirectory)
|
||||
}
|
||||
}
|
||||
compilationInfo.classesDirs.from(kotlinSourceDirectorySet.destinationDirectory).builtBy(task)
|
||||
if (compilationInfo.tcs.compilation.platformType == KotlinPlatformType.js) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
kotlinSourceDirectorySet.compiledBy(
|
||||
task as TaskProvider<Kotlin2JsCompile>,
|
||||
Kotlin2JsCompile::defaultDestinationDirectory
|
||||
)
|
||||
(kotlinSourceDirectorySet.classesDirectory as Property<Directory>).set(task.flatMap { it.destinationDirectory })
|
||||
} else {
|
||||
kotlinSourceDirectorySet.compiledBy(task, AbstractKotlinCompile<*>::destinationDirectory)
|
||||
}
|
||||
if (compilationInfo.isMain) {
|
||||
project.tasks.named(
|
||||
compilationInfo.tcs.compilation.target.artifactsTaskName,
|
||||
Jar::class.java
|
||||
) {
|
||||
it.from(kotlinSourceDirectorySet.classesDirectory)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
compilationInfo.classesDirs.from(
|
||||
task.flatMap { it.destinationDirectory }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
|
||||
+10
@@ -34,6 +34,16 @@ internal object DefaultKotlinCompilationAssociator : KotlinCompilationAssociator
|
||||
*/
|
||||
project.dependencies.add(auxiliary.compileOnlyConfigurationName, project.files({ main.output.classesDirs }))
|
||||
project.dependencies.add(auxiliary.runtimeOnlyConfigurationName, project.files({ main.output.allOutputs }))
|
||||
// Adding classes that could be produced into non-default destination for JVM target
|
||||
// Check KotlinSourceSetProcessor for details
|
||||
project.dependencies.add(
|
||||
auxiliary.implementationConfigurationName,
|
||||
project.objects.fileCollection().from(
|
||||
{
|
||||
main.defaultSourceSet.kotlin.classesDirectory.orNull?.asFile
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
auxiliary.compileDependencyConfigurationName.addAllDependenciesFromOtherConfigurations(
|
||||
project,
|
||||
|
||||
+13
-1
@@ -10,6 +10,8 @@ import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool
|
||||
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||
|
||||
internal interface KotlinCompilationFriendPathsResolver {
|
||||
@@ -23,7 +25,17 @@ internal class DefaultKotlinCompilationFriendPathsResolver(
|
||||
|
||||
override fun resolveFriendPaths(compilation: InternalKotlinCompilation<*>): Iterable<FileCollection> {
|
||||
return mutableListOf<FileCollection>().apply {
|
||||
compilation.associateWithClosure.forEach { add(it.output.classesDirs) }
|
||||
compilation.associateWithClosure.forEach {
|
||||
add(it.output.classesDirs)
|
||||
// Adding classes that could be produced to non-default destination for JVM target
|
||||
// Check KotlinSourceSetProcessor for details
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
add(
|
||||
compilation.project.files(
|
||||
(it.compileTaskProvider as TaskProvider<KotlinCompileTool>).flatMap { task -> task.destinationDirectory }
|
||||
)
|
||||
)
|
||||
}
|
||||
add(friendArtifactResolver.resolveFriendArtifacts(compilation))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ open class GradleKpmCompilationTaskConfigurator(
|
||||
val result = project.tasks.named(compilationData.compileKotlinTaskName, KotlinCompile::class.java) {
|
||||
it.kotlinPluginData = project.compilerPluginProviderForPlatformCompilation(variant, compilationData)
|
||||
}
|
||||
compilationData.output.classesDirs.from(result.map { it.destinationDirectory })
|
||||
compilationData.output.classesDirs.from(result.flatMap { it.destinationDirectory })
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user