Include the production artifacts in friend paths (KT-35942)
In some setups, the friendArtifacts that we had in addition to direct associate compilations' output classes were insufficient. For example: 1. When an Android module's tests depend on another module which transitively depends on the module containing tests: then the AGP API doesn't tell us that there are artifacts from the tested variant, but instead there's just the module's artifact. 2. Similar case but without Android, and if the classes are read from the module's artifact rather than the production output classes dir, then they are definitely not on the friend paths (which include the production classes dirs and not the the production artifact). In both cases, the friendArtifacts mechanism allows us to add those potential sources of the friend classes to the friend paths. Issue #KT-35942 Fixed
This commit is contained in:
+23
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.gradle.plugin.MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJE
|
||||
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
|
||||
import org.jetbrains.kotlin.gradle.tasks.USING_JVM_INCREMENTAL_COMPILATION_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
@@ -1003,4 +1004,26 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
assertNotContains(ScriptingGradleSubplugin.MISCONFIGURATION_MESSAGE_SUFFIX)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKtKt35942InternalsFromMainInTestViaTransitiveDeps() = with(Project("kt-35942-jvm")) {
|
||||
build(":lib1:compileTestKotlin") {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":lib1:compileKotlin", ":lib2:jar")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKtKt35942InternalsFromMainInTestViaTransitiveDepsAndroid() = with(Project("kt-35942-android")) {
|
||||
build(
|
||||
":lib1:compileDebugUnitTestKotlin",
|
||||
options = defaultBuildOptions().copy(
|
||||
androidGradlePluginVersion = AGPVersion.v3_2_0,
|
||||
androidHome = KotlinTestUtils.findAndroidSdk(),
|
||||
),
|
||||
) {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":lib1:compileDebugKotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:$android_tools_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
|
||||
|
||||
testImplementation project(':lib2')
|
||||
testImplementation 'junit:junit:4.13'
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<manifest package="com.example.lib1"/>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.lib1
|
||||
|
||||
internal object InternalClass {
|
||||
fun hello() {}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.lib1
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
class UnitTest {
|
||||
@Test
|
||||
fun `call internal class`() {
|
||||
InternalClass.hello()
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':lib1')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<manifest package="com.example.lib2" />
|
||||
+1
@@ -0,0 +1 @@
|
||||
include ':lib1', ':lib2'
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
|
||||
|
||||
testImplementation project(':lib2')
|
||||
testImplementation 'junit:junit:4.13'
|
||||
}
|
||||
|
||||
kotlin.target.compilations.test {
|
||||
compileDependencyFiles = compileDependencyFiles - kotlin.target.compilations.main.output.classesDirs
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.lib1
|
||||
|
||||
internal object InternalClass {
|
||||
fun hello() {}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.lib1
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
class UnitTest {
|
||||
@Test
|
||||
fun `call internal class`() {
|
||||
InternalClass.hello()
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
|
||||
dependencies {
|
||||
api project(':lib1')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
include ':lib1', ':lib2'
|
||||
+16
-4
@@ -16,6 +16,7 @@ import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.TaskState
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateTask
|
||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@@ -198,12 +200,22 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
|
||||
|
||||
override fun toString(): String = "compilation '$compilationName' ($target)"
|
||||
|
||||
/** If a compilation is aware of its associate compilations' outputs being added to the classpath in a transformed or packaged way,
|
||||
/**
|
||||
* If a compilation is aware of its associate compilations' outputs being added to the classpath in a transformed or packaged way,
|
||||
* it should point to those friend artifact files via this property.
|
||||
* This is a workaround for Android variants that are compiled against
|
||||
* JARs of each other, which is not exposed in the API in any other way than in the consumer's classpath. */
|
||||
*/
|
||||
internal open val friendArtifacts: FileCollection
|
||||
get() = target.project.files()
|
||||
get() = with(target.project) {
|
||||
if (associateWithTransitiveClosure.any { it.name == KotlinCompilation.MAIN_COMPILATION_NAME }) {
|
||||
// In case the main artifact is transitively added to the test classpath via a test dependency on another module
|
||||
// that depends on this module's production part, include the main artifact in the friend artifacts, lazily:
|
||||
files(
|
||||
provider {
|
||||
listOfNotNull(tasks.withType(AbstractArchiveTask::class.java).findByName(target.artifactsTaskName)?.archiveFile)
|
||||
}
|
||||
)
|
||||
} else files()
|
||||
}
|
||||
|
||||
override val moduleName: String
|
||||
get() = KotlinCompilationsModuleGroups.getModuleLeaderCompilation(this).takeIf { it != this }?.ownModuleName ?: ownModuleName
|
||||
|
||||
+18
-6
@@ -12,6 +12,7 @@ import com.android.build.gradle.api.*
|
||||
import com.android.build.gradle.tasks.MergeResources
|
||||
import com.android.builder.model.SourceProvider
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
@@ -57,12 +58,23 @@ class Android25ProjectHandler(
|
||||
kotlinClasspath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
|
||||
}
|
||||
|
||||
// Find the classpath entries that comes from the tested variant and register it as the friend path, lazily
|
||||
compilation.testedVariantArtifacts.set(project.files(project.provider {
|
||||
variantData.getCompileClasspathArtifacts(preJavaClasspathKey)
|
||||
.filter { it.id.componentIdentifier is TestedComponentIdentifier }
|
||||
.map { it.file }
|
||||
}))
|
||||
// Find the classpath entries that come from the tested variant and register them as the friend paths, lazily
|
||||
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 }
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
kotlinTask.javaOutputDir = javaTask.destinationDir
|
||||
|
||||
|
||||
Reference in New Issue
Block a user