Add (and disable) metadata compilation for single backend source sets

KotlinProjectStructureMetadata is created by inspecting source sets
with metadata compilation. Single backend common source sets are
currently not supported for metadata compilation and therefore
did not register one. Foreseeing to support this case with KT-42468,
metadata compilation is now created and registered, but just disabled.
This change will include android variants in
KotlinProjectStructureMetadata even when not officially published.
With those variants, the IDE can infer visibility from some module's
"jvmAndAndroidMain" source set to another modules "jvmAndAndroidMain"

KotlinMetadataTargetConfigurator: rename getPublishedCommonSourceSets to getCommonSourceSetsForMetadataCompilation

KotlinMetadataTargetConfigurator: isMetadataCompilationSupported and add comment to getPublishedCommonSourceSets

Re-use androidPluginIds in KotlinPlugin

KotlinAndroidTarget: Create KotlinComponents for non-published variants and mark them with publishable=false

JvmAndAndroidIntermediateSourceSetTest: add KotlinProjectStructureMetadata jvmAndAndroidMain exists in jvm variants test

AbstractAndroidProjectHandler: Add java sources only in post processing

KotlinAndroidTarget: Inline creation of usageContexts

KotlinAndroidTarget.doCreateComponents: filter non-publishable variants when publishLibraryVariantsGroupedByFlavor is enabled

#KT-42383 fixed
This commit is contained in:
sebastian.sellmair
2020-11-04 11:40:34 +01:00
committed by Sebastian Sellmair
parent 03e63f1103
commit 96352b9c8c
29 changed files with 264 additions and 86 deletions

View File

@@ -101,4 +101,4 @@
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
</codeStyleSettings>
</code_scheme>
</component>
</component>

View File

@@ -453,4 +453,4 @@
<option name="ignoreNonEmtpyLoops" value="false" />
</inspection_tool>
</profile>
</component>
</component>

View File

@@ -0,0 +1,128 @@
/*
* 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.
*/
@file:Suppress("invisible_reference", "invisible_member", "FunctionName", "DuplicatedCode")
package org.jetbrains.kotlin.gradle
import com.android.build.gradle.LibraryExtension
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.buildKotlinProjectStructureMetadata
import kotlin.test.*
class JvmAndAndroidIntermediateSourceSetTest {
private lateinit var project: ProjectInternal
private lateinit var kotlin: KotlinMultiplatformExtension
private lateinit var jvmAndAndroidMain: KotlinSourceSet
@BeforeTest
fun setup() {
project = ProjectBuilder.builder().build() as ProjectInternal
project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableGranularSourceSetsMetadata", "true")
project.plugins.apply("kotlin-multiplatform")
project.plugins.apply("android-library")
/* Arbitrary minimal Android setup */
val android = project.extensions.getByName("android") as LibraryExtension
android.compileSdkVersion(30)
/* Kotlin Setup */
kotlin = project.multiplatformExtension
kotlin.jvm()
kotlin.android()
jvmAndAndroidMain = kotlin.sourceSets.create("jvmAndAndroidMain")
kotlin.sourceSets.run {
jvmAndAndroidMain.dependsOn(getByName("commonMain"))
getByName("jvmMain") {
it.dependsOn(jvmAndAndroidMain)
}
getByName("androidMain") {
it.dependsOn(jvmAndAndroidMain)
}
}
}
@Test
fun `metadata compilation is created and disabled`() {
/* evaluate */
project.evaluate()
/* Check if compilation is created correctly */
val jvmAndAndroidMainMetadataCompilations = kotlin.targets.flatMap { it.compilations }
.filterIsInstance<KotlinMetadataCompilation<*>>()
.filter { it.name == jvmAndAndroidMain.name }
assertEquals(
1, jvmAndAndroidMainMetadataCompilations.size,
"Expected exactly one metadata compilation created for jvmAndAndroidMain source set"
)
val compilation = jvmAndAndroidMainMetadataCompilations.single()
assertFalse(
compilation.compileKotlinTaskProvider.get().enabled,
"Expected compilation task to be disabled, because not supported yet"
)
}
@Test
fun `KotlinProjectStructureMetadata jvmAndAndroidMain exists in jvm variants`() {
project.evaluate()
val metadata = assertNotNull(buildKotlinProjectStructureMetadata(project))
assertTrue("jvmAndAndroidMain" in metadata.sourceSetNamesByVariantName["jvmApiElements"].orEmpty())
assertTrue("jvmAndAndroidMain" in metadata.sourceSetNamesByVariantName["jvmRuntimeElements"].orEmpty())
}
@Test
fun `KotlinProjectStructureMetadata jvmAndAndroidMain exists in android variants`() {
project.evaluate()
val metadata = assertNotNull(buildKotlinProjectStructureMetadata(project))
assertTrue("jvmAndAndroidMain" in metadata.sourceSetNamesByVariantName["debugApiElements"].orEmpty())
assertTrue("jvmAndAndroidMain" in metadata.sourceSetNamesByVariantName["debugRuntimeElements"].orEmpty())
assertTrue("jvmAndAndroidMain" in metadata.sourceSetNamesByVariantName["releaseApiElements"].orEmpty())
assertTrue("jvmAndAndroidMain" in metadata.sourceSetNamesByVariantName["releaseRuntimeElements"].orEmpty())
}
@Test
fun `Android Kotlin Components are marked as not publishable when variant is not published`() {
val target = kotlin.targets.getByName("android") as KotlinAndroidTarget
target.publishLibraryVariants = emptyList()
project.evaluate()
val kotlinComponents = target.kotlinComponents
assertTrue(kotlinComponents.isNotEmpty(), "Expected at least one KotlinComponent to be present")
kotlinComponents.forEach { component ->
assertFalse(component.publishable, "Expected component to not publishable, because no publication is configured")
}
}
@Test
fun `Android Kotlin Components are marked as publishable when variant is published`() {
val target = kotlin.targets.getByName("android") as KotlinAndroidTarget
target.publishLibraryVariants = listOf("release")
project.evaluate()
val kotlinComponents = target.kotlinComponents
assertTrue(kotlinComponents.isNotEmpty(), "Expected at least one KotlinComponent to be present")
kotlinComponents.forEach { component ->
val isReleaseComponent = "release" in component.name.toLowerCase()
if (isReleaseComponent) {
assertTrue(component.publishable, "Expected release component to be marked as publishable")
} else {
assertFalse(component.publishable, "Expected non-release component to be marked as not publishable")
}
}
}
}

View File

@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// TODO KT-34102
@file:Suppress("invisible_reference", "invisible_member")
@file:Suppress("invisible_reference", "invisible_member", "FunctionName")
package org.jetbrains.kotlin.gradle
import com.android.build.gradle.LibraryExtension

View File

@@ -1,4 +1,3 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
@@ -28,6 +27,8 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.logging.kotlinWarn
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
import org.jetbrains.kotlin.gradle.utils.androidPluginIds
import java.util.concurrent.atomic.AtomicBoolean
abstract class KotlinPlatformPluginBase(protected val platformName: String) : Plugin<Project> {
companion object {
@@ -182,6 +183,7 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin
private fun getKotlinSourceSetsSafe(project: Project): NamedDomainObjectCollection<out Named> {
// Access through reflection, because another project's KotlinProjectExtension might be loaded by a different class loader:
val kotlinExt = project.extensions.getByName("kotlin")
@Suppress("UNCHECKED_CAST")
val sourceSets = kotlinExt.javaClass.getMethod("getSourceSets").invoke(kotlinExt) as NamedDomainObjectCollection<out Named>
return sourceSets
@@ -210,8 +212,24 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin
internal fun <T> Project.whenEvaluated(fn: Project.() -> T) {
if (state.executed) {
fn()
} else {
afterEvaluate { it.fn() }
return
}
/* Make sure that all afterEvaluate blocks from the AndroidPlugin get scheduled first */
val isDispatched = AtomicBoolean(false)
androidPluginIds.forEach { androidPluginId ->
pluginManager.withPlugin(androidPluginId) {
if (!isDispatched.getAndSet(true)) {
afterEvaluate { fn() }
}
}
}
afterEvaluate {
/* If no Android plugin was loaded, then the action was not dispatched and we can freely execute it now */
if (!isDispatched.getAndSet(true)) {
fn()
}
}
}
@@ -263,4 +281,4 @@ private fun warnAboutKotlin12xMppDeprecation(project: Project) {
if (project.findProperty(KOTLIN_12X_MPP_DEPRECATION_SUPPRESS_FLAG) != "true") {
SingleWarningPerBuild.show(project, KOTLIN_12X_MPP_DEPRECATION_WARNING)
}
}
}

View File

@@ -796,11 +796,6 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
val androidPluginIds = listOf(
"android", "com.android.application", "android-library", "com.android.library",
"com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp"
)
val plugin by lazy {
androidPluginIds.asSequence()
.mapNotNull { project.plugins.findPlugin(it) as? BasePlugin }
@@ -904,6 +899,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
// Trivial mapping of Android variants to Android source set names is impossible here,
// because some variants have their dedicated source sets with mismatching names,
// because some variants have their dedicated source sets with mismatching names,
// e.g. variant 'fooBarDebugAndroidTest' <-> source set 'androidTestFooBarDebug'
// In single-platform projects, the Kotlin compilations already reference the Android plugin's configurations by the names,
@@ -974,6 +970,8 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
// Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet)
compilation.androidVariant.forEachKotlinSourceSet { kotlinSourceSet -> compilation.source(kotlinSourceSet) }
}
private fun postprocessVariant(
@@ -983,7 +981,6 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
androidExt: BaseExtension,
androidPlugin: BasePlugin
) {
val javaTask = variantData.getJavaTaskProvider()
getTestedVariantData(variantData)?.let { testedVariant ->
val testedVariantName = getVariantName(testedVariant)
@@ -991,34 +988,24 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
compilation.associateWith(testedCompilation)
}
val javaTask = variantData.getJavaTaskProvider()
val kotlinTask = compilation.compileKotlinTaskProvider
processAndroidKotlinAndJavaSources(
compilation,
addKotlinSources = { kotlinSourceSet -> compilation.source(kotlinSourceSet) },
addJavaSources = { sources -> compilation.compileKotlinTaskProvider.configure { it.source(sources) } }
)
compilation.androidVariant.forEachJavaSourceDir { sources -> kotlinTask.configure { it.source(sources) } }
wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask)
}
}
private fun getAllJavaSources(variantData: BaseVariant): Iterable<File> =
variantData.getSourceFolders(SourceKind.JAVA).map { it.dir }
internal fun processAndroidKotlinAndJavaSources(
compilation: KotlinJvmAndroidCompilation,
addKotlinSources: (KotlinSourceSet) -> Unit,
addJavaSources: (Iterable<File>) -> Unit
) {
val variantData = compilation.androidVariant
for (provider in variantData.sourceSets) {
val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue
addKotlinSources(kotlinSourceSet)
}
addJavaSources(getAllJavaSources(variantData))
internal inline fun BaseVariant.forEachKotlinSourceSet(action: (KotlinSourceSet) -> Unit) {
sourceSets
.mapNotNull { provider -> provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet }
.forEach(action)
}
internal inline fun BaseVariant.forEachJavaSourceDir(action: (File) -> Unit) {
getSourceFolders(SourceKind.JAVA).map { it.dir }.forEach(action)
}
internal fun configureJavaTask(
kotlinTaskProvider: TaskProvider<out KotlinCompile>,
javaTaskProvider: TaskProvider<out AbstractCompile>

View File

@@ -486,4 +486,4 @@ fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
attributes.attribute(KotlinNativeTarget.konanTargetAttribute, target.konanTarget.name)
}
return this
}
}

View File

@@ -51,12 +51,8 @@ internal fun Project.runOnceAfterEvaluated(name: String, task: TaskProvider<*>,
}
internal fun Project.runOnceAfterEvaluated(runOnce: RunOnceAfterEvaluated, task: TaskProvider<*>) {
if (state.executed) {
runOnce.onEvaluated()
} else {
afterEvaluate { runOnce.onEvaluated() }
}
whenEvaluated { runOnce.onEvaluated() }
task.configure {
runOnce.onConfigure()
}
}
}

View File

@@ -114,11 +114,8 @@ class SubpluginEnvironment(
internal fun addCompilationSourcesToExternalCompileTask(compilation: KotlinCompilation<*>, task: TaskProvider<out AbstractCompile>) {
if (compilation is KotlinJvmAndroidCompilation) {
processAndroidKotlinAndJavaSources(
compilation,
addKotlinSources = { sourceSet -> task.configure { it.source(sourceSet.kotlin) } },
addJavaSources = { sources -> task.configure { it.source(sources) }}
)
compilation.androidVariant.forEachKotlinSourceSet { sourceSet -> task.configure { it.source(sourceSet.kotlin) } }
compilation.androidVariant.forEachJavaSourceDir { sources -> task.configure { it.source(sources) } }
} else {
task.configure { taskInstance ->
compilation.allKotlinSourceSets.forEach { sourceSet -> taskInstance.source(sourceSet.kotlin) }
@@ -174,4 +171,4 @@ internal fun findJavaTaskForKotlinCompilation(compilation: KotlinCompilation<*>)
is KotlinWithJavaCompilation -> compilation.compileJavaTaskProvider
is KotlinJvmCompilation -> compilation.compileJavaTaskProvider // may be null for Kotlin-only JVM target in MPP
else -> null
}
}

View File

@@ -38,4 +38,4 @@ class KotlinCommonCompilation(
val friendSourceSets = getVisibleSourceSetsFromAssociateCompilations(target.project, defaultSourceSet)
project.files(friendSourceSets.mapNotNull { target.compilations.findByName(it.name)?.output?.classesDirs })
})
}
}

View File

@@ -139,4 +139,4 @@ private fun Project.resolvableConfigurationFromCompilationByScope(
}
return project.configurations.getByName(configurationName)
}
}

View File

@@ -363,4 +363,4 @@ internal object CompilationSourceSetUtil {
private val invalidModuleNameCharactersRegex = """[\\/\r\n\t]""".toRegex()
private fun filterModuleName(moduleName: String): String =
moduleName.replace(invalidModuleNameCharactersRegex, "_")
moduleName.replace(invalidModuleNameCharactersRegex, "_")

View File

@@ -238,4 +238,4 @@ abstract class KotlinOnlyTarget<T : KotlinCompilation<*>>(
override var disambiguationClassifier: String? = null
internal set
}
}

View File

@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.GranularMetadataTransformation
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.*
import java.io.File
import java.lang.reflect.Constructor
@@ -84,7 +85,7 @@ class DefaultKotlinSourceSet(
// Fail-fast approach: check on each new added edge and report a circular dependency at once when the edge is added.
checkForCircularDependencies()
project.afterEvaluate { defaultSourceSetLanguageSettingsChecker.runAllChecks(this, other) }
project.whenEvaluated { defaultSourceSetLanguageSettingsChecker.runAllChecks(this@DefaultKotlinSourceSet, other) }
}
private val dependsOnSourceSetsImpl = mutableSetOf<KotlinSourceSet>()
@@ -247,4 +248,4 @@ private fun <T> Class<T>.constructorOrNull(vararg parameterTypes: Class<*>): Con
getConstructor(*parameterTypes)
} catch (e: NoSuchMethodException) {
null
}
}

View File

@@ -43,7 +43,9 @@ open class KotlinAndroidTarget(
* all library variants will be published, but not test or application variants. */
var publishLibraryVariants: List<String>? = listOf()
// Workaround for Groovy GString items in a list:
set(value) { field = value?.map(Any::toString) }
set(value) {
field = value?.map(Any::toString)
}
/** Add Android library variant names to [publishLibraryVariants]. */
fun publishLibraryVariants(vararg names: String) {
@@ -91,11 +93,16 @@ open class KotlinAndroidTarget(
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
}
private fun isVariantPublished(variant: BaseVariant): Boolean {
return publishLibraryVariants?.contains(getVariantName(variant)) ?: true
}
private fun AbstractAndroidProjectHandler.doCreateComponents(): Set<KotlinTargetComponent> {
val publishableVariants = mutableListOf<BaseVariant>()
.apply { project.forEachVariant { add(it) } }
.toList() // Defensive copy against unlikely modification by the lambda that captures the list above in forEachVariant { }
.filter { getLibraryOutputTask(it) != null && publishLibraryVariants?.contains(getVariantName(it)) ?: true }
.filter { getLibraryOutputTask(it) != null }
val publishableVariantGroups = publishableVariants.groupBy { variant ->
val flavorNames = getFlavorNames(variant)
@@ -118,12 +125,12 @@ open class KotlinAndroidTarget(
val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor }
val usageContexts = createAndroidUsageContexts(androidVariant, compilation, artifactClassifier)
createKotlinVariant(
lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()),
compilation,
usageContexts
createAndroidUsageContexts(androidVariant, compilation, artifactClassifier)
).apply {
publishable = isVariantPublished(androidVariant)
sourcesArtifacts = setOf(
sourcesJarArtifact(
compilation, compilation.disambiguateName(""),
@@ -148,10 +155,10 @@ open class KotlinAndroidTarget(
if (publishLibraryVariantsGroupedByFlavor) {
JointAndroidKotlinTargetComponent(
this@KotlinAndroidTarget,
nestedVariants,
flavorGroupNameParts,
nestedVariants.flatMap { it.sourcesArtifacts }.toSet()
target = this@KotlinAndroidTarget,
nestedVariants = nestedVariants.filter { it.publishable }.toSet(),
flavorNames = flavorGroupNameParts,
sourcesArtifacts = nestedVariants.filter { it.publishable }.flatMap { it.sourcesArtifacts }.toSet()
)
} else {
nestedVariants.single()
@@ -195,4 +202,4 @@ open class KotlinAndroidTarget(
)
}
}
}
}

View File

@@ -70,4 +70,4 @@ class ParcelizeSubplugin : KotlinCompilerPluginSupportPlugin {
)
}
}
}
}

View File

@@ -88,4 +88,4 @@ class KotlinJsDcePlugin : Plugin<Project> {
private const val DCE_TASK_PREFIX = "runDce"
private const val DEFAULT_OUT_DIR = "kotlin-js-min"
}
}
}

View File

@@ -98,4 +98,4 @@ internal fun jsPluginDeprecationMessage(id: String): String =
The `$id` Gradle plugin has been deprecated.
Please use `org.jetbrains.kotlin.js` plugin instead `kotlin2js` and `org.jetbrains.kotlin.frontend`
For usage details, see https://kotlinlang.org/docs/reference/js-project-setup.html
""".trimIndent()
""".trimIndent()

View File

@@ -128,4 +128,4 @@ class KotlinJsSingleTargetPreset(
override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator(
kotlinPluginVersion
)
}
}

View File

@@ -256,4 +256,4 @@ abstract class KotlinJsIrSubTarget(
const val PREPARE_JS_LIBRARY_TASK_NAME = "prepare"
}
}
}

View File

@@ -94,4 +94,4 @@ class KotlinJsIrSingleTargetPreset(
override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<KotlinJsIrCompilation, KotlinJsIrTarget> =
KotlinJsIrTargetConfigurator(kotlinPluginVersion)
}
}

View File

@@ -127,4 +127,4 @@ internal class KotlinProjectNpmResolver(
*/
internal fun <T : Task, R : Any> TaskCollection<T>.implementing(kclass: KClass<R>): TaskCollection<T> =
@Suppress("UNCHECKED_CAST")
withType(kclass.java as Class<T>)
withType(kclass.java as Class<T>)

View File

@@ -166,4 +166,4 @@ abstract class KotlinJsSubTarget(
companion object {
const val RUN_TASK_NAME = "run"
}
}
}

View File

@@ -76,4 +76,4 @@ class KotlinJvmTargetConfigurator(kotlinPluginVersion: String) :
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(tasksProvider, compilation, kotlinPluginVersion)
}
}
}

View File

@@ -77,4 +77,4 @@ import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
val compileJavaTaskProvider: TaskProvider<out JavaCompile>
get() = target.project.tasks.withType(JavaCompile::class.java).named(javaSourceSet.compileJavaTaskName)
}
}

View File

@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets
import org.jetbrains.kotlin.gradle.plugin.sources.*
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.tasks.*
@@ -163,7 +164,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
private fun setupDependencyTransformationForCommonSourceSets(target: KotlinMetadataTarget) {
target.project.whenEvaluated {
val publishedCommonSourceSets: Set<KotlinSourceSet> = getPublishedCommonSourceSets(project)
val publishedCommonSourceSets: Set<KotlinSourceSet> = getCommonSourceSetsForMetadataCompilation(project)
kotlinExtension.sourceSets.all {
setupDependencyTransformationForSourceSet(target.project, it, it in publishedCommonSourceSets)
@@ -177,12 +178,17 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
) = target.project.whenEvaluated {
// Do this after all targets are configured by the user build script
val publishedCommonSourceSets: Set<KotlinSourceSet> = getPublishedCommonSourceSets(project)
val publishedCommonSourceSets: Set<KotlinSourceSet> = getCommonSourceSetsForMetadataCompilation(project)
val hostSpecificSourceSets: Set<KotlinSourceSet> = getHostSpecificSourceSets(project).toSet()
val sourceSetsWithMetadataCompilations: Map<KotlinSourceSet, AbstractKotlinCompilation<*>> =
publishedCommonSourceSets.associate { sourceSet ->
sourceSet to createMetadataCompilation(target, sourceSet, allMetadataJar, sourceSet in hostSpecificSourceSets)
val sourceSetsWithMetadataCompilations: Map<KotlinSourceSet, AbstractKotlinCompilation<*>> = publishedCommonSourceSets
.associateWith { sourceSet ->
createMetadataCompilation(target, sourceSet, allMetadataJar, sourceSet in hostSpecificSourceSets)
}
.onEach { (sourceSet, compilation) ->
if (!isMetadataCompilationSupported(target.project, sourceSet)) {
compilation.compileKotlinTaskProvider.configure { it.enabled = false }
}
}
if (project.isCompatibilityMetadataVariantEnabled) {
@@ -195,6 +201,30 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
}
}
private fun isMetadataCompilationSupported(project: Project, sourceSet: KotlinSourceSet): Boolean {
val platforms = compilationsBySourceSets(project)[sourceSet].orEmpty()
.filter { it.target !is KotlinMetadataTarget }
.map { it.target.platformType }.distinct()
/*
Android and jvm do share the JVM backend which is not supported for metadata compilation
See [HMPP: Bad IDEA dependencies for JVM and Android intermediate source set](https://youtrack.jetbrains.com/issue/KT-42383)
See [HMPP: JVM and Android intermediate source set publication](https://youtrack.jetbrains.com/issue/KT-42468)
*/
if (platforms.all { it == KotlinPlatformType.jvm || it == KotlinPlatformType.androidJvm }) {
return false
}
/* Metadata compilation for a single platform is only supported native and common source sets */
if (platforms.size == 1) {
val platform = platforms.single()
return platform == KotlinPlatformType.native || platform == KotlinPlatformType.common
}
/* Source sets sharing code between multiple backends are supported */
return true
}
private fun configureProjectStructureMetadataGeneration(project: Project, allMetadataJar: TaskProvider<out Jar>) {
val generateMetadata = project.createGenerateProjectStructureMetadataTask()
@@ -256,7 +286,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
val compilationName = sourceSet.name
val platformCompilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(sourceSet)
val platformCompilations = compilationsBySourceSets(project).getValue(sourceSet)
val isNativeSourceSet = platformCompilations.all { compilation -> compilation.target is KotlinNativeTarget }
@@ -271,7 +301,6 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
return compilationFactory.create(compilationName).apply {
target.compilations.add(this@apply)
addExactSourceSetsEagerly(setOf(sourceSet))
configureMetadataDependenciesForCompilation(this@apply)
if (!isHostSpecific) {
@@ -513,14 +542,18 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
}
}
internal fun getPublishedCommonSourceSets(project: Project): Set<KotlinSourceSet> {
/**
* @return All common source sets that can potentially be published. Right now, not all combinations of platforms actually
* support metadata compilation (see [KotlinMetadataTargetConfigurator.isMetadataCompilationSupported].
* Those compilations will be created but the corresponding tasks will be disabled.
*/
internal fun getCommonSourceSetsForMetadataCompilation(project: Project): Set<KotlinSourceSet> {
val compilationsBySourceSet: Map<KotlinSourceSet, Set<KotlinCompilation<*>>> =
CompilationSourceSetUtil.compilationsBySourceSets(project)
compilationsBySourceSets(project)
val sourceSetsUsedInMultipleTargets = compilationsBySourceSet.filterValues { compilations ->
compilations.map { it.target.platformType }.distinct().run {
size > 1 && toSet() != setOf(KotlinPlatformType.androidJvm, KotlinPlatformType.jvm) ||
singleOrNull() == KotlinPlatformType.native && compilations.map { it.target }.distinct().size > 1
size > 1 || singleOrNull() == KotlinPlatformType.native && compilations.map { it.target }.distinct().size > 1
// TODO: platform-shared source sets other than Kotlin/Native ones are not yet supported; support will be needed for JVM, JS
}
}
@@ -565,4 +598,4 @@ internal fun Project.filesWithUnpackedArchives(from: FileCollection, extensions:
internal fun Project.getMetadataCompilationForSourceSet(sourceSet: KotlinSourceSet): AbstractKotlinCompilation<*>? {
return multiplatformExtension.metadata().compilations.findByName(sourceSet.name)
}
}

View File

@@ -166,4 +166,4 @@ open class KotlinNativeTargetWithHostTests @Inject constructor(project: Project,
KotlinNativeTargetWithTests<KotlinNativeHostTestRun>(project, konanTarget)
open class KotlinNativeTargetWithSimulatorTests @Inject constructor(project: Project, konanTarget: KonanTarget) :
KotlinNativeTargetWithTests<KotlinNativeSimulatorTestRun>(project, konanTarget)
KotlinNativeTargetWithTests<KotlinNativeSimulatorTestRun>(project, konanTarget)

View File

@@ -593,4 +593,4 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
gemListRetCode == 0 && gemListOutput.contains("cocoapods-generate")
}
}
}
}

View File

@@ -0,0 +1,11 @@
/*
* 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 org.jetbrains.kotlin.gradle.utils
internal val androidPluginIds = listOf(
"android", "com.android.application", "android-library", "com.android.library",
"com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp"
)