diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt
index 7fa8d603a8a..ec7bdb2dc38 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt
@@ -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")
+ }
+ }
}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/build.gradle
new file mode 100644
index 00000000000..442b6108a9a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/build.gradle
@@ -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()
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle
new file mode 100644
index 00000000000..62aefd33f7f
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle
@@ -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'
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/main/AndroidManifest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..77fb5f4db23
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/main/java/com/example/lib1/InternalClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/main/java/com/example/lib1/InternalClass.kt
new file mode 100644
index 00000000000..754389cb9c1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/main/java/com/example/lib1/InternalClass.kt
@@ -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() {}
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/test/java/com/example/lib1/UnitTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/test/java/com/example/lib1/UnitTest.kt
new file mode 100644
index 00000000000..ec6c3d18d96
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/src/test/java/com/example/lib1/UnitTest.kt
@@ -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()
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle
new file mode 100644
index 00000000000..61eb714045d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle
@@ -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"
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/src/main/AndroidManifest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..8aab7020fbc
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/settings.gradle
new file mode 100644
index 00000000000..4252cfc9cf3
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/settings.gradle
@@ -0,0 +1 @@
+include ':lib1', ':lib2'
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/build.gradle
new file mode 100644
index 00000000000..93992385ef6
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/build.gradle
@@ -0,0 +1,17 @@
+buildscript {
+ repositories {
+ mavenLocal()
+ jcenter()
+
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ mavenLocal()
+ jcenter()
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/build.gradle
new file mode 100644
index 00000000000..e4ab3ad2761
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/build.gradle
@@ -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
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/src/main/kotlin/com/example/lib1/InternalClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/src/main/kotlin/com/example/lib1/InternalClass.kt
new file mode 100644
index 00000000000..754389cb9c1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/src/main/kotlin/com/example/lib1/InternalClass.kt
@@ -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() {}
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/src/test/kotlin/com/example/lib1/UnitTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/src/test/kotlin/com/example/lib1/UnitTest.kt
new file mode 100644
index 00000000000..ec6c3d18d96
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib1/src/test/kotlin/com/example/lib1/UnitTest.kt
@@ -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()
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib2/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib2/build.gradle
new file mode 100644
index 00000000000..94bd53ad35a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/lib2/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'org.jetbrains.kotlin.jvm'
+
+dependencies {
+ api project(':lib1')
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/settings.gradle
new file mode 100644
index 00000000000..4252cfc9cf3
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-jvm/settings.gradle
@@ -0,0 +1 @@
+include ':lib1', ':lib2'
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt
index 3f8609330ba..609c60cbd1d 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt
@@ -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(
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
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 b014b3d8381..a121f25c198 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
@@ -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