From 162dc3aa0c6c768ee080039f92422803dd671fab Mon Sep 17 00:00:00 2001 From: Ivan Gavrilovic Date: Wed, 9 Sep 2020 20:08:41 +0100 Subject: [PATCH] KT-41295: Friend paths and compiler options compatible with conf caching This change fixes serialization of friend paths and it uses a file collection to record all friend paths. Also, when computing the tested classpath for android projects, we now avoid resolving the file collection until necessary. Fixes: KT-41295 Test: ConfigurationCacheForAndroidIT --- .../gradle/ConfigurationCacheForAndroidIT.kt | 6 ++ .../app/src/androidTest/kotlin/Main.kt | 10 +++ .../app/src/test/kotlin/Main.kt | 10 +++ .../internal/CompilerArgumentsContributor.kt | 2 +- .../android/Android25ProjectHandler.kt | 66 +++++++++++++++---- .../gradle/tasks/KotlinCompileCommon.kt | 2 +- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 27 ++++---- 7 files changed, 95 insertions(+), 28 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/androidTest/kotlin/Main.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/test/kotlin/Main.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt index b01bbe5b8ff..cccfd33da97 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt @@ -41,6 +41,12 @@ class ConfigurationCacheForAndroidIT : AbstractConfigurationCacheIT() { testConfigurationCacheOf(":Lib:compileFlavor1DebugKotlin", ":Android:compileFlavor1DebugKotlin") } + @Test + fun testKotlinAndroidProjectTests() = with(Project("AndroidIncrementalMultiModule")) { + applyAndroid40Alpha4KotlinVersionWorkaround() + testConfigurationCacheOf(":app:compileDebugAndroidTestKotlin", ":app:compileDebugUnitTestKotlin") + } + /** * Android Gradle plugin 4.0-alpha4 depends on the EAP versions of some o.j.k modules. * Force the current Kotlin version, so the EAP versions are not queried from the diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/androidTest/kotlin/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/androidTest/kotlin/Main.kt new file mode 100644 index 00000000000..da7228576f7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/androidTest/kotlin/Main.kt @@ -0,0 +1,10 @@ +import com.example.AppDummy + +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +fun main(args: Array) { + println(AppDummy()) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/test/kotlin/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/test/kotlin/Main.kt new file mode 100644 index 00000000000..da7228576f7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidIncrementalMultiModule/app/src/test/kotlin/Main.kt @@ -0,0 +1,10 @@ +import com.example.AppDummy + +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +fun main(args: Array) { + println(AppDummy()) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt index 450f8d52493..d41009c43a2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt @@ -99,7 +99,7 @@ internal open class KotlinJvmCompilerArgumentsContributor( args.moduleName = moduleName logger.kotlinDebug { "args.moduleName = ${args.moduleName}" } - args.friendPaths = friendPaths + args.friendPaths = friendPaths.files.map { it.absolutePath }.toTypedArray() logger.kotlinDebug { "args.friendPaths = ${args.friendPaths?.joinToString() ?: "[]"}" } if (DefaultsOnly in flags) return diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/Android25ProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/Android25ProjectHandler.kt index 99c396795bd..db83e01f39a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/Android25ProjectHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/Android25ProjectHandler.kt @@ -11,9 +11,11 @@ import com.android.build.gradle.* import com.android.build.gradle.api.* import com.android.build.gradle.tasks.MergeResources import org.gradle.api.Project +import org.gradle.api.artifacts.ArtifactCollection import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.attributes.Attribute import org.gradle.api.file.FileCollection +import org.gradle.api.specs.Spec import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.AbstractArchiveTask @@ -26,6 +28,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.thisTaskProvider import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import java.io.File +import java.io.ObjectInputStream +import java.io.ObjectOutputStream +import java.io.Serializable import java.util.concurrent.Callable class Android25ProjectHandler( @@ -65,20 +70,16 @@ class Android25ProjectHandler( } // Find the classpath entries that come from the tested variant and register them as the friend paths, lazily + val originalArtifactCollection = variantData.getCompileClasspathArtifacts(preJavaClasspathKey) + val testedVariantDataIsNotNull = getTestedVariantData(variantData) != null + val projectPath = project.path compilation.testedVariantArtifacts.set( - project.files( - project.provider { - variantData.getCompileClasspathArtifacts(preJavaClasspathKey) - .filter { - it.id.componentIdentifier is TestedComponentIdentifier || - // If tests depend on the main classes transitively, through a test dependency on another module which - // depends on this module, then there's no artifact with a TestedComponentIdentifier, so consider the artifact of the - // current module a friend path, too: - getTestedVariantData(variantData) != null && - (it.id.componentIdentifier as? ProjectComponentIdentifier)?.projectPath == project.path - } - .map { it.file } - } + originalArtifactCollection.artifactFiles.filter( + AndroidTestedVariantArtifactsFilter( + originalArtifactCollection, + testedVariantDataIsNotNull, + projectPath + ) ) ) @@ -213,4 +214,43 @@ private fun MergeResources.computeResourceSetList0(): List? { } finally { computeResourceSetListMethod.isAccessible = oldIsAccessible } +} + +/** Filter for the AGP test variant classpath artifacts. */ +class AndroidTestedVariantArtifactsFilter( + private val artifactCollection: ArtifactCollection, + private val testedVariantDataIsNotNull: Boolean, + private val projectPath: String +) : Serializable, Spec { + + /** Make transient as it should be derived from the [artifactCollection] property which may change in configuration cached runs. */ + @Transient + private var filteredFiles = initFilteredFiles() + + private fun initFilteredFiles(): Lazy> { + return lazy { + artifactCollection.filter { + it.id.componentIdentifier is TestedComponentIdentifier || + // If tests depend on the main classes transitively, through a test dependency on another module which + // depends on this module, then there's no artifact with a TestedComponentIdentifier, so consider the artifact of the + // current module a friend path, too: + testedVariantDataIsNotNull && + (it.id.componentIdentifier as? ProjectComponentIdentifier)?.projectPath == projectPath + } + .mapTo(mutableSetOf()) { it.file } + } + } + + private fun writeObject(objectOutputStream: ObjectOutputStream) { + objectOutputStream.defaultWriteObject() + } + + private fun readObject(objectInputStream: ObjectInputStream) { + objectInputStream.defaultReadObject() + filteredFiles = initFilteredFiles() + } + + override fun isSatisfiedBy(element: File): Boolean { + return element in filteredFiles.value + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt index fb4e0155293..ebcf8c02833 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt @@ -70,7 +70,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile() : AbstractKo } @get:Internal // takes part in the compiler arguments - val friendPaths: Array by project.provider { - taskData.compilation.run { - if (this !is AbstractKotlinCompilation<*>) return@run emptyArray() - associateWithTransitiveClosure - .flatMap { it.output.classesDirs } - .plus(friendArtifacts) - .map { it.absolutePath }.toTypedArray() + val friendPaths: FileCollection = project.files( + project.provider { + taskData.compilation.run { + if (this !is AbstractKotlinCompilation<*>) return@run project.files() + mutableListOf().also { allCollections -> + associateWithTransitiveClosure.forEach { allCollections.add(it.output.classesDirs) } + allCollections.add(friendArtifacts) + } + } } - } + ) private val kotlinLogger by lazy { GradleKotlinLogger(logger) } @@ -415,7 +417,7 @@ class KotlinJvmCompilerArgumentsProvider (taskProvider: KotlinCompile) : KotlinCompileArgumentsProvider(taskProvider) { val moduleName: String - val friendPaths: Array + val friendPaths: FileCollection val compileClasspath: Iterable val destinationDir: File internal val kotlinOptions: List @@ -643,10 +645,9 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko internal val friendDependencies: List get() { val filter = libraryFilter - return friendPaths.filter { - val file = File(it) - file.exists() && filter(file) - } + return friendPaths.files.filter { + it.exists() && filter(it) + }.map { it.absolutePath } } @Suppress("unused")