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