Build PM2.0 in o.j.k.multiplatform (+ dependency resolution API)

This commit is contained in:
Sergey Igushkin
2020-11-16 19:15:55 +03:00
committed by TeamCityServer
parent 994f940a31
commit 6aee396e4b
6 changed files with 494 additions and 20 deletions
@@ -0,0 +1,218 @@
/*
* 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.plugin.mpp
import org.gradle.api.Named
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.matchesModule
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations
import org.jetbrains.kotlin.project.model.*
class ProjectStructureMetadataModuleBuilder {
private val modulesCache = mutableMapOf<String, KotlinModule>()
private fun buildModuleFromProjectStructureMetadata(
moduleName: String,
moduleOrigin: ModuleOrigin,
metadata: KotlinProjectStructureMetadata
): KotlinModule =
BasicKotlinModule(moduleName, moduleOrigin).apply {
metadata.sourceSetNamesByVariantName.keys.forEach { variantName ->
fragments.add(BasicKotlinModuleVariant(this@apply, variantName))
}
metadata.sourceSetNamesByVariantName.forEach { (variantName, sourceSets) ->
val variant = fragmentByName(variantName)
sourceSets.forEach { sourceSetName ->
if (fragments.none { it.fragmentName == sourceSetName })
fragments.add(BasicKotlinModuleFragment(this@apply, sourceSetName))
val fragment = fragmentByName(sourceSetName)
variant.directRefinesDependencies.add(fragment)
}
}
metadata.sourceSetsDependsOnRelation.forEach { (depending, dependencies) ->
val dependingFragment = fragmentByName(depending)
dependencies.forEach { dependency ->
if (fragments.none { it.fragmentName == dependency })
fragments.add(BasicKotlinModuleFragment(this@apply, dependency))
val fragment = fragmentByName(dependency)
dependingFragment.directRefinesDependencies.add(fragment)
}
}
}
fun getModule(name: String, moduleOrigin: ModuleOrigin, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
return modulesCache.getOrPut(name) {
buildModuleFromProjectStructureMetadata(
name,
moduleOrigin,
projectStructureMetadata
)
}
}
}
@Suppress("unused")
class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExplicit: Boolean) {
fun buildModuleFromProject(project: Project): KotlinModule? {
val extension = project.multiplatformExtensionOrNull
?: project.kotlinExtension
?: return null
val targets = when (extension) {
is KotlinMultiplatformExtension -> extension.targets.filter { it.name != "metadata" }
is KotlinSingleTargetExtension -> listOf(extension.target)
else -> return null
}
return BasicKotlinModule(project.path, LocalBuild(project.currentBuildId().name)).apply {
val variantToCompilation = mutableMapOf<KotlinModuleFragment, KotlinCompilation<*>>()
targets.forEach { target ->
val publishedVariantsByCompilation = (target as? AbstractKotlinTarget)?.kotlinComponents.orEmpty()
.flatMap { component -> (component as? KotlinVariant)?.usages.orEmpty() }
.groupBy { it.compilation }
target.compilations.forEach { compilation ->
val names =
publishedVariantsByCompilation[compilation].orEmpty()
.filter { it.includeIntoProjectStructureMetadata }.map { it.name }
names.forEach { variantName ->
val variant = BasicKotlinModuleVariant(this@apply, variantName)
variantToCompilation[variant] = compilation
fragments.add(variant)
// TODO The attributes from the compile dependencies configuration might differ from exposed attributes
val compileDependenciesConfiguration =
project.configurations.getByName(compilation.compileDependencyConfigurationName)
compileDependenciesConfiguration.attributes.keySet().forEach { key ->
variant.variantAttributes[KotlinAttributeKey(key.name)] =
attributeString(compileDependenciesConfiguration.attributes, key)
}
variant.isExported = compilation.isMain()
}
}
}
extension.sourceSets.forEach { sourceSet ->
val existingVariant = fragments.filterIsInstance<BasicKotlinModuleVariant>().find { it.fragmentName == sourceSet.name }
val fragment = existingVariant ?: BasicKotlinModuleFragment(this@apply, sourceSet.name).also { fragments.add(it) }
fragment.kotlinSourceRoots = sourceSet.kotlin.sourceDirectories.toList()
// FIXME: Kotlin/Native implementation-effective-api dependencies are missing here. Introduce dependency scopes
project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies.forEach {
val moduleDependency = it.toModuleDependency(project)
fragment.declaredModuleDependencies.add(moduleDependency)
}
}
// Once all fragments are created, add dependencies between them
fragments.forEach { fragment ->
val sourceSet = extension.sourceSets.findByName(fragment.fragmentName)
?: variantToCompilation.getValue(fragment).defaultSourceSet
sourceSet.dependsOn.forEach { dependency ->
val dependencyFragment = fragmentByName(dependency.name)
fragment.directRefinesDependencies.add(dependencyFragment)
}
}
targets.forEach { target ->
target.compilations.forEach { compilation ->
val fragment = fragmentByName(compilation.defaultSourceSetName)
compilation.associateWith.forEach { associate ->
val dependencyFragment = fragmentByName(associate.defaultSourceSetName)
fragment.declaredContainingModuleFragmentDependencies.add(dependencyFragment)
}
}
}
if (addInferredSourceSetVisibilityAsExplicit) {
extension.sourceSets.forEach { sourceSet ->
val fragment = fragmentByName(sourceSet.name)
getVisibleSourceSetsFromAssociateCompilations(project, sourceSet).forEach { dependency ->
val dependencyFragment = fragmentByName(dependency.name)
fragment.declaredContainingModuleFragmentDependencies.add(dependencyFragment)
}
}
}
}
}
private fun <T : Any> attributeString(container: AttributeContainer, attributeKey: Attribute<T>): String {
val value = container.getAttribute(attributeKey)
return when (value) {
is Named -> value.name
else -> value.toString()
}
}
}
internal fun Dependency.toModuleDependency(
project: Project
) = when (val dependency = this) {
is ProjectDependency ->
LocalModuleDependency(LocalBuild(project.currentBuildId().name), dependency.dependencyProject.path)
else ->
ExternalModuleDependency(ExternalOrigin(listOfNotNull(dependency.group, dependency.name)))
}
private fun BasicKotlinModule.fragmentByName(name: String) =
fragments.single { it.fragmentName == name }
class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver {
private val resolvedVariantProvider = ResolvedMppVariantsProvider.get(project)
override fun getChosenVariant(requestingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): VariantResolution {
// TODO maybe improve this behavior? Currently it contradicts dependency resolution in that it may return a chosen variant for an
// unrequested dependency. This workaround is needed for synthetic modules which were not produced from module metadata, so maybe
// those modules should be marked somehow
dependencyModule.variants.singleOrNull()
?.let { return VariantResolution.fromMatchingVariants(requestingVariant, dependencyModule, listOf(it)) }
val module = requestingVariant.containingModule
// This implementation can only resolve variants for the current project's KotlinModule
require(module.moduleName == project.path)
require(module.moduleOrigin.let { it is LocalBuild && it.buildId == project.currentBuildId().name })
val targets =
project.multiplatformExtensionOrNull?.targets ?: listOf((project.kotlinExtension as KotlinSingleTargetExtension).target)
val compilation =
targets.filterIsInstance<AbstractKotlinTarget>()
.flatMap { it.kotlinComponents.filterIsInstance<KotlinVariant>() }
.flatMap { it.usages }
.firstOrNull { it.name == requestingVariant.fragmentName }
?.compilation
?: return VariantResolution.NotRequested(requestingVariant, dependencyModule)
val compileClasspath = project.configurations.getByName(compilation.compileDependencyConfigurationName)
// TODO optimize O(n) search, store the mapping
val component = compileClasspath.incoming.resolutionResult.allComponents.find { it.id.matchesModule(dependencyModule) }
?: return VariantResolution.NotRequested(requestingVariant, dependencyModule)
val dependencyModuleId = ModuleIds.fromComponent(project, component)
// FIXME check composite builds, it's likely that resolvedVariantProvider fails on them?
val variantName = resolvedVariantProvider.getResolvedVariantName(dependencyModuleId, compileClasspath)
val resultVariant = dependencyModule.variants.singleOrNull { it.fragmentName == variantName }
return if (resultVariant == null)
VariantResolution.NoVariantMatch(requestingVariant, dependencyModule)
else
VariantResolution.VariantMatch(requestingVariant, dependencyModule, resultVariant)
}
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
import java.io.File
import java.io.InputStream
import java.util.*
@@ -224,23 +225,12 @@ internal class GranularMetadataTransformation(
parentResolutionsForModule: Iterable<MetadataDependencyResolution>,
parent: ResolvedComponentResult?
): MetadataDependencyResolution {
val resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project)
val metadataArtifact = resolvedMppVariantsProvider.getPlatformArtifactByPlatformModule(
ModuleIds.fromComponent(project, module),
configurationToResolve
val mppDependencyMetadataExtractor = getMetadataExtractor(
project,
module,
configurationToResolve,
resolveViaAvailableAt = false // we will process this dependency later in the queue
)
val moduleId = module.id
val mppDependencyMetadataExtractor: MppDependencyMetadataExtractor? = when {
moduleId is ProjectComponentIdentifier -> when {
moduleId.build.isCurrentBuild ->
ProjectMppDependencyMetadataExtractor(project, module, project.project(moduleId.projectPath))
metadataArtifact != null ->
IncludedBuildMetadataExtractor(project, module, metadataArtifact)
else -> null
}
metadataArtifact != null -> JarArtifactMppDependencyMetadataExtractor(project, module, metadataArtifact)
else -> null
}
val resolvedToProject: Project? = (mppDependencyMetadataExtractor as? ProjectMppDependencyMetadataExtractor)?.dependencyProject
@@ -468,6 +458,46 @@ private open class JarArtifactMppDependencyMetadataExtractor(
}
}
private fun getMetadataExtractor(
project: Project,
module: ResolvedComponentResult,
configuration: Configuration,
resolveViaAvailableAt: Boolean
): MppDependencyMetadataExtractor? {
val resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project)
val moduleIdentifier = ModuleIds.fromComponent(project, module)
val metadataArtifact = resolvedMppVariantsProvider.getResolvedArtifactByPlatformModule(
moduleIdentifier,
configuration
) ?: if (resolveViaAvailableAt) resolvedMppVariantsProvider.getHostSpecificMetadataArtifactByRootModule(
moduleIdentifier,
configuration
) else null
val moduleId = module.id
return when {
moduleId is ProjectComponentIdentifier -> when {
moduleId.build.isCurrentBuild ->
ProjectMppDependencyMetadataExtractor(project, module, project.project(moduleId.projectPath))
metadataArtifact != null ->
IncludedBuildMetadataExtractor(project, module, metadataArtifact)
else -> null
}
metadataArtifact != null -> JarArtifactMppDependencyMetadataExtractor(project, module, metadataArtifact)
else -> null
}
}
internal fun getProjectStructureMetadata(
project: Project,
module: ResolvedComponentResult,
configuration: Configuration
): KotlinProjectStructureMetadata? {
// FIXME test dependencies won't work
val extractor = getMetadataExtractor(project, module, configuration, resolveViaAvailableAt = true)
return extractor?.getProjectStructureMetadata()
}
// This class is needed to encapsulate how we extract the files and point to them in a way that doesn't capture the Gradle project state
internal abstract class ExtractableMetadataFiles {
abstract fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map<String, FileCollection>
@@ -45,13 +45,14 @@ internal class ResolvedMppVariantsProvider private constructor(private val proje
* module of a multiplatform project, not one of its platform-specific modules, as seen in the given [configuration].
* If the [configuration] requests platform artifacts and not the common code metadata, then this function will resolve its
* dependencies to metadata separately. */
fun getMetadataArtifactByRootModule(rootModuleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): File? {
fun getHostSpecificMetadataArtifactByRootModule(rootModuleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): File? {
val rootModuleEntry = getEntryForModule(rootModuleIdentifier)
val platformModuleEntry = rootModuleEntry.run {
if (configuration !in chosenPlatformModuleByConfiguration) {
resolveConfigurationAndSaveVariants(configuration, artifactResolutionMode = ArtifactResolutionMode.METADATA)
}
// At this point the map should contain the result if calculation above succeeded. If not, put null to avoid recalculation.
chosenPlatformModuleByConfiguration.getOrPut(configuration) { null }
}
@@ -66,7 +67,7 @@ internal class ResolvedMppVariantsProvider private constructor(private val proje
}
/** Gets the artifact of a particular MPP platform-specific [moduleIdentifier] as resolved in the [configuration]. */
fun getPlatformArtifactByPlatformModule(moduleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): File? =
fun getResolvedArtifactByPlatformModule(moduleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): File? =
getEntryForModule(moduleIdentifier).run {
if (configuration !in resolvedArtifactByConfiguration) {
resolveConfigurationAndSaveVariants(configuration, artifactResolutionMode = ArtifactResolutionMode.NORMAL)
@@ -170,7 +171,7 @@ internal class ResolvedMppVariantsProvider private constructor(private val proje
moduleEntry.resolvedMetadataArtifactByConfiguration[configuration] = null
}
// We found the host-specific artifact for a platform variant of some MPP:
// We found a requested artifact of the MPP; it is one of: platform artifact, root metadata, host-specific metadata
artifact != null -> {
val resolvedArtifactMap = when (artifactResolutionMode) {
ArtifactResolutionMode.NORMAL -> moduleEntry.resolvedArtifactByConfiguration
@@ -111,7 +111,7 @@ internal class SourceSetVisibilityProvider(
someVariantByHostSpecificSourceSet.entries.mapNotNull { (sourceSetName, variantName) ->
val configuration = firstConfigurationByVariant.getValue(variantName)
resolvedVariantsProvider.getMetadataArtifactByRootModule(mppModuleIdentifier, configuration)
resolvedVariantsProvider.getHostSpecificMetadataArtifactByRootModule(mppModuleIdentifier, configuration)
?.let { sourceSetName to it }
}.toMap()
}
@@ -0,0 +1,194 @@
/*
* 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.plugin.mpp.pm20
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.component.ComponentIdentifier
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.GradleModuleVariantResolver
import org.jetbrains.kotlin.gradle.plugin.mpp.ProjectStructureMetadataModuleBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.GradleProjectModuleBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
import org.jetbrains.kotlin.project.model.*
import java.util.ArrayDeque
class GradleModuleDependencyResolver(
private val project: Project,
private val projectStructureMetadataModuleBuilder: ProjectStructureMetadataModuleBuilder,
private val projectModuleBuilder: GradleProjectModuleBuilder
) : ModuleDependencyResolver {
// FIXME this wont work for tests, as their dependencies are currently not in the all compile dependencies configuration
private val configurationToResolve: Configuration
get() = project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)
override fun resolveDependency(moduleDependency: ModuleDependency): KotlinModule? {
val allComponents = configurationToResolve.incoming.resolutionResult.allComponents
// TODO: optimize O(n) search, store the resolved components with dependency keys
val component = allComponents.find { it.id.matchesModuleDependency(moduleDependency) }
val id = component?.id
return when {
id is ProjectComponentIdentifier && id.build.isCurrentBuild ->
projectModuleBuilder.buildModuleFromProject(project.project(id.projectPath))
id is ModuleComponentIdentifier -> {
val metadata = getProjectStructureMetadata(project, component, configurationToResolve) ?: return null
projectStructureMetadataModuleBuilder.getModule(id.displayName, id.toModuleOrigin(), metadata)
}
else -> null
}
}
}
class GradleProjectDependencyDiscovery(
private val project: Project,
private val variantDependencyDiscovery: VariantDependencyDiscovery,
private val fragmentDependenciesDiscovery: FragmentDependenciesDiscovery
) : DependencyDiscovery {
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency> {
require(fragment.containingModule.representsProject(project))
return when (fragment) {
is KotlinModuleVariant -> variantDependencyDiscovery.discoverDependencies(fragment)
else -> fragmentDependenciesDiscovery.discoverDependencies(fragment)
}
}
}
class FragmentDependenciesDiscovery(
private val project: Project,
private val expansion: InternalDependencyExpansion,
private val moduleResolver: ModuleDependencyResolver,
private val fragmentResolver: KotlinModuleFragmentResolver
) : DependencyDiscovery {
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency> {
require(fragment.containingModule.representsProject(project))
val requested = expansion.expandInternalFragmentDependencies(fragment).visibleFragments().plus(fragment.refinesClosure)
.flatMap { it.declaredModuleDependencies }
.toSet()
// FIXME: test dependencies won't work here
val allComponents =
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME).incoming.resolutionResult.allComponents
val componentByModuleDependency = allComponents.associateBy { it.toModuleDependency() }
val resolvedComponentQueue = ArrayDeque<ResolvedComponentResult>().apply {
addAll(componentByModuleDependency.filterKeys { it in requested }.values)
}
val visited = mutableSetOf<ResolvedComponentResult>()
while (resolvedComponentQueue.isNotEmpty()) {
val component = resolvedComponentQueue.removeFirst()
visited.add(component)
val module = moduleResolver.resolveDependency(component.toModuleDependency())
?: buildStubModule(component, component.variants.singleOrNull()?.displayName ?: "default")
val visibleFragments = fragmentResolver.getChosenFragments(fragment, module)
val newTransitiveDeps =
visibleFragments.chosenFragments.flatMap { it.declaredModuleDependencies }.mapNotNull { dependency ->
componentByModuleDependency[dependency].takeIf { component -> component !in visited }
}
resolvedComponentQueue.addAll(newTransitiveDeps)
}
return visited.map { it.toModuleDependency() }
}
// refactor extract to a separate class
// TODO think about multi-variant stub modules for non-Kotlin modules which got more than one chosen variant
private fun buildStubModule(resolvedComponentResult: ResolvedComponentResult, singleVariantName: String): KotlinModule {
val moduleDependency = resolvedComponentResult.toModuleDependency()
val moduleName = when (val id = resolvedComponentResult.id) {
is ProjectComponentIdentifier -> id.projectPath
else -> id.displayName
}
return BasicKotlinModule(moduleName, moduleDependency.moduleOrigin).apply {
BasicKotlinModuleVariant(this@apply, singleVariantName).apply {
fragments.add(this)
this.declaredModuleDependencies.addAll(
resolvedComponentResult.dependencies
.filterIsInstance<ResolvedDependencyResult>()
.map { it.selected.toModuleDependency() }
)
}
}
}
}
class VariantDependencyDiscovery(
val project: Project
) : DependencyDiscovery {
private val kotlinExtension
get() = project.kotlinExtension
private fun resolvedComponentResults(fragment: KotlinModuleFragment): Iterable<ResolvedComponentResult> {
val compilation = kotlinExtension.targets.asSequence()
.flatMap { it.compilations.asSequence() }
.find { it.defaultSourceSetName == fragment.fragmentName }
requireNotNull(compilation)
val configuration = project.configurations.getByName(compilation.compileDependencyConfigurationName)
return configuration.incoming.resolutionResult.allComponents
}
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency> {
require(fragment is KotlinModuleVariant)
require(fragment.containingModule.representsProject(project))
val components = resolvedComponentResults(fragment)
return components.mapNotNull { component ->
when (val id = component.id) {
is ProjectComponentIdentifier -> LocalModuleDependency(LocalBuild(id.build.name), id.projectPath)
is ModuleComponentIdentifier -> ExternalModuleDependency(id.toModuleOrigin())
else -> null // TODO check that no other options are possible, throw errors
}
}
}
}
private fun ModuleComponentIdentifier.toModuleOrigin(): ExternalOrigin =
ExternalOrigin(listOf(moduleIdentifier.group, moduleIdentifier.name))
internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean {
return when (val moduleSource = module.moduleOrigin) {
is LocalBuild -> {
val projectId = this as? ProjectComponentIdentifier
projectId?.build?.name == moduleSource.buildId && projectId.projectPath == module.moduleName
}
is ExternalOrigin -> {
val moduleId = this as? ModuleComponentIdentifier
moduleId?.toModuleOrigin() == moduleSource
}
}
}
internal fun ResolvedComponentResult.toModuleDependency(): ModuleDependency = when (val id = id) {
is ProjectComponentIdentifier -> LocalModuleDependency(LocalBuild(id.build.name), id.projectPath)
is ModuleComponentIdentifier -> ExternalModuleDependency(id.toModuleOrigin())
else -> ExternalModuleDependency(ExternalOrigin(listOf(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty())))
}
internal fun ComponentIdentifier.matchesModuleDependency(moduleDependency: ModuleDependency) =
when (moduleDependency) {
is LocalModuleDependency -> {
val projectId = this as? ProjectComponentIdentifier
projectId?.build?.name == moduleDependency.moduleOrigin.buildId && projectId.projectPath == moduleDependency.moduleName
}
is ExternalModuleDependency -> {
val moduleId = this as? ModuleComponentIdentifier
moduleId?.toModuleOrigin() == moduleDependency.moduleOrigin
}
}
@@ -0,0 +1,31 @@
/*
* 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.plugin.mpp.pm20
import org.gradle.api.Project
import org.gradle.api.artifacts.component.BuildIdentifier
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.build.BuildState
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.project.model.KotlinModule
import org.jetbrains.kotlin.project.model.LocalBuild
val KotlinProjectExtension.targets: Iterable<KotlinTarget>
get() = when (this) {
is KotlinSingleTargetExtension -> listOf(this.target)
is KotlinMultiplatformExtension -> targets
else -> error("Unexpected 'kotlin' extension $this")
}
fun KotlinModule.representsProject(project: Project): Boolean =
moduleOrigin.let { it is LocalBuild && it.buildId == project.currentBuildId().name } && moduleName == project.path
// FIXME internal API?
fun Project.currentBuildId(): BuildIdentifier =
(project as ProjectInternal).services.get(BuildState::class.java).buildIdentifier