Project Model 2.0 initial commit: core entities

This commit is contained in:
Sergey Igushkin
2020-10-29 16:04:30 +03:00
committed by TeamCityServer
parent 6db3960d56
commit f189ebc983
14 changed files with 657 additions and 0 deletions
@@ -44,6 +44,7 @@ dependencies {
implementation(project(":kotlin-util-klib"))
implementation(project(":native:kotlin-klib-commonizer-api"))
implementation(project(":kotlin-tooling-metadata"))
implementation(project(":kotlin-project-model"))
compileOnly(project(":native:kotlin-native-utils"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(project(":kotlin-android-extensions"))
@@ -0,0 +1,28 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
publish()
standardPublicJars()
dependencies {
implementation(kotlinStdlib())
testImplementation(kotlin("test-junit"))
}
pill {
variant = org.jetbrains.kotlin.pill.PillExtension.Variant.FULL
}
kotlin.target.compilations.all {
kotlinOptions.languageVersion = "1.3"
kotlinOptions.apiVersion = "1.3"
kotlinOptions.freeCompilerArgs += listOf("-Xskip-prerelease-check")
}
tasks.named<Jar>("jar") {
callGroovy("manifestAttributes", manifest, project)
}
@@ -0,0 +1,118 @@
/*
* 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.project.model
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
import java.util.ArrayDeque
interface InternalDependencyExpansion {
fun expandInternalFragmentDependencies(consumingFragment: KotlinModuleFragment): InternalDependencyExpansionResult
}
class InternalDependencyExpansionResult(
val entries: Iterable<Entry>
) {
sealed class ExpansionOutcome(val variantMatchingResults: Iterable<KotlinVariantMatchingResult>) {
class VisibleFragments(
val fragments: Iterable<KotlinModuleFragment>,
variantMatchingResults: Iterable<KotlinVariantMatchingResult>
) : ExpansionOutcome(
variantMatchingResults
)
class Failure(variantMatchingResults: Iterable<KotlinVariantMatchingResult>) : ExpansionOutcome(variantMatchingResults)
}
class Entry(
val dependingFragment: KotlinModuleFragment,
val dependencyFragment: KotlinModuleFragment,
val outcome: ExpansionOutcome
)
}
fun InternalDependencyExpansionResult.visibleFragments(): List<KotlinModuleFragment> =
entries.flatMap { entry -> entry.outcome.visibleFragments() }
fun InternalDependencyExpansionResult.ExpansionOutcome.visibleFragments(): Iterable<KotlinModuleFragment> = when (this) {
is InternalDependencyExpansionResult.ExpansionOutcome.VisibleFragments -> fragments
else -> emptyList()
}
class DefaultInternalDependencyExpansion(
private val variantResolver: ContainingModuleVariantResolver
) : InternalDependencyExpansion {
interface ContainingModuleVariantResolver {
fun getChosenVariant(
dependingVariant: KotlinModuleVariant,
candidateVariants: Iterable<KotlinModuleVariant>
): KotlinVariantMatchingResult
}
override fun expandInternalFragmentDependencies(consumingFragment: KotlinModuleFragment): InternalDependencyExpansionResult {
val visited = mutableSetOf(consumingFragment)
val answer = mutableListOf<InternalDependencyExpansionResult.Entry>()
/**
* For each fragment **t** that we consider a source of declared dependencies that we should expand for [consumingFragment]:
* - also consider the refines-closure of **t** as sources of declared dependencies
* - expand the declared dependencies of **t** to a set of fragments **F**
* - add the expansion result to the answer
* - also consider all fragments in **F** as sources of declared dependencies
*/
val declaredDependencySourceQueue = ArrayDeque<KotlinModuleFragment>().apply { add(consumingFragment) }
while (declaredDependencySourceQueue.isNotEmpty()) {
val declaredDependencySource = declaredDependencySourceQueue.removeFirst()
declaredDependencySourceQueue.addAll(declaredDependencySource.refinesClosure.filter(visited::add))
val declaredDependenciesToExpand = declaredDependencySource.declaredContainingModuleFragmentDependencies
declaredDependenciesToExpand.forEach { declaredDependency ->
val answerEntry = expandSingleDeclaredDependency(consumingFragment, declaredDependencySource, declaredDependency)
answer += answerEntry
declaredDependencySourceQueue.addAll(answerEntry.outcome.visibleFragments())
}
}
return InternalDependencyExpansionResult(answer)
}
private fun expandSingleDeclaredDependency(
consumingFragment: KotlinModuleFragment,
declaredDependencySource: KotlinModuleFragment,
declaredDependency: KotlinModuleFragment
): InternalDependencyExpansionResult.Entry {
/**
* Go over the variants containing the fragment that we infer the expansion for (note: it's the original consuming fragment, which
* may not necessarily be the same as the fragment holding the declared dependency if the latter comes from the closure).
*
* For each such containing variant v_i, let w_i be the variant containing the declared dependency fragment. Failure to choose
* prevents us from inferring the dependency expansion and is reported as a failure to expand the dependency.
*
* Then intersect the refined fragments of each w_i and use the intersection as the result.
*/
val (consumingVariants, producingVariants) =
listOf(consumingFragment, declaredDependency).map { it.containingModule.variantsContainingFragment(it).toSet() }
val chosenVariants = consumingVariants.map { consumingVariant ->
if (consumingVariant in producingVariants)
VariantMatch(consumingVariant, consumingVariant.containingModule, consumingVariant)
else
variantResolver.getChosenVariant(consumingVariant, producingVariants)
}
val mismatchedConsumingVariants = chosenVariants.filter { it !is VariantMatch }
val outcome =
if (mismatchedConsumingVariants.isNotEmpty())
InternalDependencyExpansionResult.ExpansionOutcome.Failure(chosenVariants)
else
InternalDependencyExpansionResult.ExpansionOutcome.VisibleFragments(
chosenVariants.map { (it as VariantMatch).chosenVariant.refinesClosure }.reduce { acc, it -> acc.intersect(it) },
chosenVariants
)
return InternalDependencyExpansionResult.Entry(declaredDependencySource, declaredDependency, outcome)
}
}
@@ -0,0 +1,24 @@
/*
* 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.project.model
open class KotlinAttributeKey(
val uniqueName: String
) {
override fun equals(other: Any?): Boolean =
other is KotlinAttributeKey && uniqueName == other.uniqueName
override fun hashCode(): Int =
uniqueName.hashCode()
}
object KotlinPlatformTypeAttribute : KotlinAttributeKey("org.jetbrains.kotlin.platform.type") {
const val JVM = "jvm"
const val JS = "js"
const val NATIVE = "native"
}
object KotlinNativeTargetAttribute : KotlinAttributeKey("org.jetbrains.kotlin.native.target")
@@ -0,0 +1,30 @@
/*
* 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.project.model
sealed class ModuleSource {
class LocalBuild(val buildId: String) : ModuleSource()
class ExternalDependency(val dependencyId: String) : ModuleSource()
}
interface KotlinModule {
val moduleName: String
val moduleSource: ModuleSource
val fragments: Iterable<KotlinModuleFragment>
val variants: Iterable<KotlinModuleVariant>
get() = fragments.filterIsInstance<KotlinModuleVariant>()
}
class BasicKotlinModule(
override val moduleName: String,
override val moduleSource: ModuleSource
) : KotlinModule {
override val fragments = mutableListOf<BasicKotlinModuleFragment>()
override fun toString(): String = "module '$moduleName'"
}
@@ -0,0 +1,69 @@
/*
* 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.project.model
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
import java.io.File
interface KotlinModuleFragment {
val containingModule: KotlinModule
val fragmentName: String
val directRefinesDependencies: Iterable<KotlinModuleFragment>
val declaredContainingModuleFragmentDependencies: Iterable<KotlinModuleFragment>
val kotlinSourceRoots: Iterable<File>
}
interface KotlinModuleVariant : KotlinModuleFragment {
val variantAttributes: Map<KotlinAttributeKey, String>
var isExported: Boolean
}
val KotlinModuleFragment.fragmentAttributeSets: Map<KotlinAttributeKey, Set<String>>
get() = mutableMapOf<KotlinAttributeKey, MutableSet<String>>().apply {
containingModule.variantsContainingFragment(this@fragmentAttributeSets).forEach { variant ->
variant.variantAttributes.forEach { attribute, value ->
getOrPut(attribute) { mutableSetOf() }.add(value)
}
}
}
val KotlinModuleFragment.refinesClosure: Set<KotlinModuleFragment>
get() = mutableSetOf<KotlinModuleFragment>().apply {
fun visit(moduleFragment: KotlinModuleFragment) {
if (add(moduleFragment))
moduleFragment.directRefinesDependencies.forEach(::visit)
}
visit(this@refinesClosure)
}
open class BasicKotlinModuleFragment(
override val containingModule: KotlinModule,
override val fragmentName: String
) : KotlinModuleFragment {
override val directRefinesDependencies: MutableList<BasicKotlinModuleFragment> = mutableListOf()
override val declaredContainingModuleFragmentDependencies: MutableList<BasicKotlinModuleFragment> = mutableListOf()
override val kotlinSourceRoots: Iterable<File> = emptyList()
override fun toString(): String = "fragment $fragmentName"
}
class BasicKotlinVariant(
containingModule: KotlinModule,
fragmentName: String
) : BasicKotlinModuleFragment (
containingModule,
fragmentName
), KotlinModuleVariant {
override var isExported: Boolean = true
override val variantAttributes: MutableMap<KotlinAttributeKey, String> = mutableMapOf()
override fun toString(): String = "variant $fragmentName"
}
@@ -0,0 +1,45 @@
/*
* 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.project.model
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
interface KotlinModuleFragmentResolver {
fun getChosenFragments(dependingFragment: KotlinModuleFragment, dependencyModule: KotlinModule): KotlinChosenFragments
}
class KotlinChosenFragments(
val module: KotlinModule,
val chosenFragments: Iterable<KotlinModuleFragment>,
val variantMatchingResults: Iterable<KotlinVariantMatchingResult>
)
class DefaultKotlinModuleFragmentResolver(
private val variantResolver: KotlinModuleVariantResolver
) : KotlinModuleFragmentResolver {
override fun getChosenFragments(dependingFragment: KotlinModuleFragment, dependencyModule: KotlinModule): KotlinChosenFragments {
val dependingModule = dependingFragment.containingModule
val containingVariants = dependingModule.variantsContainingFragment(dependingFragment)
val chosenVariants = containingVariants.map { variantResolver.getChosenVariant(it, dependencyModule) }
val chosenFragments = chosenVariants.map { variantResolution ->
when (variantResolution) {
is VariantMatch -> variantResolution.chosenVariant.refinesClosure
else -> emptySet()
}
}
val result = if (chosenFragments.isEmpty())
emptyList<KotlinModuleFragment>()
else chosenFragments
// Note this emulates the existing behavior that is lenient wrt to unresolved modules, but gives imprecise results. TODO revisit
.filter { it.isNotEmpty() }
.reduce { acc, it -> acc.intersect(it) }
return KotlinChosenFragments(dependencyModule, result, chosenVariants)
}
}
@@ -0,0 +1,49 @@
/*
* 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.project.model
/*
* 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.
*/
interface KotlinModuleVariantResolver {
fun getChosenVariant(dependingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): KotlinVariantMatchingResult
}
sealed class KotlinVariantMatchingResult(
val requestingVariant: KotlinModuleVariant,
val dependencyModule: KotlinModule
) {
companion object {
fun fromMatchingVariants(
requestingVariant: KotlinModuleVariant,
dependencyModule: KotlinModule,
matchingVariants: Collection<KotlinModuleVariant>
) = when (matchingVariants.size) {
0 -> NoVariantMatch(requestingVariant, dependencyModule)
1 -> VariantMatch(requestingVariant, dependencyModule, matchingVariants.single())
else -> AmbiguousVariants(requestingVariant, dependencyModule, matchingVariants)
}
}
}
class VariantMatch(
requestingVariant: KotlinModuleVariant,
dependencyModule: KotlinModule,
val chosenVariant: KotlinModuleVariant
) : KotlinVariantMatchingResult(requestingVariant, dependencyModule)
class NoVariantMatch(
requestingVariant: KotlinModuleVariant,
dependencyModule: KotlinModule
) : KotlinVariantMatchingResult(requestingVariant, dependencyModule)
class AmbiguousVariants(
requestingVariant: KotlinModuleVariant,
dependencyModule: KotlinModule,
val matchingVariants: Iterable<KotlinModuleVariant>
) : KotlinVariantMatchingResult(requestingVariant, dependencyModule)
@@ -0,0 +1,29 @@
/*
* 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.project.model.utils
import org.jetbrains.kotlin.project.model.KotlinModule
import org.jetbrains.kotlin.project.model.KotlinModuleFragment
import org.jetbrains.kotlin.project.model.KotlinModuleVariant
import org.jetbrains.kotlin.project.model.refinesClosure
fun KotlinModule.variantsContainingFragment(fragment: KotlinModuleFragment): Iterable<KotlinModuleVariant> =
variants.filter { fragment in it.refinesClosure }
fun KotlinModule.findRefiningFragments(fragment: KotlinModuleFragment): Iterable<KotlinModuleFragment> {
val refining = mutableSetOf<KotlinModuleFragment>()
val notRefining = mutableSetOf<KotlinModuleFragment>()
fun isRefining(other: KotlinModuleFragment): Boolean = when {
other in refining -> true
other in notRefining -> false
fragment in other.directRefinesDependencies -> true.also { refining.add(other) }
fragment.directRefinesDependencies.any { isRefining(it) } -> true.also { refining.add(other) }
else -> false.also { notRefining.add(other) }
}
return fragments.filter(::isRefining)
}
@@ -0,0 +1,105 @@
/*
* 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.project.model
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class DefaultInternalDependencyExpansionTest {
/** Create a module without internal depends edges, those need to be added manually afterwards */
fun createTemplateModule(vararg purposes: String) = module("foo").apply {
listOf(*purposes).forEach { purpose ->
val common = fragment("common", purpose)
val (jvm, js, linux) = listOf("jvm", "js", "linux").map { platform ->
variant(platform, purpose).apply {
variantAttributes[KotlinPlatformTypeAttribute] = when (platform) {
"jvm" -> KotlinPlatformTypeAttribute.JVM
"js" -> KotlinPlatformTypeAttribute.JS
else -> {
variantAttributes[KotlinNativeTargetAttribute] = platform
KotlinPlatformTypeAttribute.NATIVE
}
}
}
}
val jvmAndJs = fragment("jvmAndJs", purpose).apply {
refines(common)
refinedBy(jvm)
refinedBy(js)
}
val jsAndLinux = fragment("jsAndLinux", purpose).apply {
refines(common)
refinedBy(js)
refinedBy(linux)
}
}
}
val expansion = DefaultInternalDependencyExpansion(AssociateVariants())
@Test
fun testSimpleCommonTestToCommonMainDependency() {
val module = createTemplateModule("main", "test").apply {
// Manually associate the variants:
listOf("jvm", "js", "linux").forEach {
variant(it, "test").depends(variant(it, "main"))
}
// Add just a single dependency from commonTest to commonMain
fragment("commonTest").depends(fragment("commonMain"))
}
val expectedFragments = mapOf(
"commonTest" to setOf("commonMain"),
"jvmAndJsTest" to setOf("jvmAndJsMain", "commonMain"),
"jsAndLinuxTest" to setOf("jsAndLinuxMain", "commonMain"),
"jvmTest" to setOf("jvmMain", "jvmAndJsMain", "commonMain"),
"jsTest" to setOf("jsMain", "jsAndLinuxMain", "jvmAndJsMain", "commonMain"),
"linuxTest" to setOf("linuxMain", "jsAndLinuxMain", "commonMain")
)
module.fragments.forEach { fragment ->
val result =
expansion.expandInternalFragmentDependencies(fragment).visibleFragments().map { it.fragmentName }.toSet()
val expected = expectedFragments[fragment.fragmentName].orEmpty()
assertEquals(expected, result)
}
}
@Test
fun testDeclaredDependenciesFromExpansion() {
val module = createTemplateModule("main", "test", "integration").apply {
/** Manually associate the variants: */
listOf("jvm", "js", "linux").forEach {
variant(it, "test").depends(variant(it, "main"))
variant(it, "integration").depends(variant(it, "test"))
variant(it, "integration").depends(variant(it, "main"))
}
/**
* Add a dependency from `commonIntegration` to `commonTest` and from (a more specific fragment)
* `jvmAndJsTest` to `jvmAndJsMain`.
*/
fragment("commonIntegration").depends(fragment("commonTest"))
fragment("jvmAndJsTest").depends(fragment("jvmAndJsMain"))
/**
* Expect that the fragment `jvmAndJsIntegration` receives the dependency on `jvmAndJsMain` through
* the declared dependency added to the fragment `jvmAndJsTest`, which is only visible in expansion.
*/
val result = expansion.expandInternalFragmentDependencies(fragment("jvmAndJsIntegration"))
val expected = setOf("commonMain", "jvmAndJsMain", "commonTest", "jvmAndJsTest")
assertEquals(expected, result.visibleFragments().map { it.fragmentName }.toSet())
assertTrue {
result.entries.any {
it.dependingFragment == fragment("jvmAndJsTest") &&
it.dependencyFragment == fragment("jvmAndJsMain") &&
it.outcome.visibleFragments().map { it.fragmentName }.toSet() == setOf("commonMain", "jvmAndJsMain")
}
}
}
}
}
@@ -0,0 +1,54 @@
/*
* 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.project.model
import org.junit.Assume
import org.junit.Assume.assumeTrue
import org.junit.Test
import kotlin.test.assertEquals
class DefaultKotlinModuleFragmentResolverTest {
val moduleFoo = simpleModule("foo")
val moduleBar = simpleModule("bar")
val fragmentResolver = DefaultKotlinModuleFragmentResolver(MatchVariantsByExactAttributes())
@Test
fun testFragmentVisibility() {
val expectedVisibleFragments = mapOf(
"common" to setOf("commonMain"),
"jvmAndJs" to setOf("commonMain", "jvmAndJsMain"),
"jsAndLinux" to setOf("commonMain", "jsAndLinuxMain"),
"jvm" to setOf("commonMain", "jvmAndJsMain", "jvmMain"),
"js" to setOf("commonMain", "jvmAndJsMain", "jsAndLinuxMain", "jsMain"),
"linux" to setOf("commonMain", "jsAndLinuxMain", "linuxMain")
)
moduleBar.fragments.forEach { consumingFragment ->
val result = fragmentResolver.getChosenFragments(consumingFragment, moduleFoo)
val expected = expectedVisibleFragments.getValue(consumingFragment.fragmentName.removeSuffix("Main").removeSuffix("Test"))
assertEquals(expected, result.chosenFragments.map { it.fragmentName }.toSet())
}
}
@Test
fun testVisibilityWithMismatchedVariant() {
// TODO this behavior replicates 1.3.x MPP where a mismatched variant gets ignored and only matched variants are intersected.
// This helps with non-published local native targets.
// Consider making it more strict when we have a solution to the original problem.
val dependingModule = simpleModule("baz").apply {
variant("linuxMain").variantAttributes.replace(KotlinNativeTargetAttribute, "notLinux")
}
assumeTrue(MatchVariantsByExactAttributes().getChosenVariant(dependingModule.variant("linuxMain"), moduleFoo) is NoVariantMatch)
val (commonMainResult, jsAndLinuxResult) = listOf("commonMain", "jsAndLinuxMain").map {
fragmentResolver.getChosenFragments(dependingModule.fragment(it), moduleFoo).chosenFragments.map { it.fragmentName }.toSet()
}
assertEquals(setOf("commonMain", "jvmAndJsMain"), commonMainResult)
assertEquals(setOf("commonMain", "jvmAndJsMain", "jsAndLinuxMain", "jsMain"), jsAndLinuxResult)
}
}
@@ -0,0 +1,28 @@
/*
* 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.project.model
class MatchVariantsByExactAttributes : KotlinModuleVariantResolver {
override fun getChosenVariant(dependingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): KotlinVariantMatchingResult {
val candidates = dependencyModule.variants
return candidates.filter { candidate ->
candidate.isExported && candidate.variantAttributes.all { (attributeKey, candidateValue) ->
attributeKey !in dependingVariant.variantAttributes.keys ||
candidateValue == dependingVariant.variantAttributes.getValue(attributeKey)
}
}.let { KotlinVariantMatchingResult.fromMatchingVariants(dependingVariant, dependencyModule, it) }
}
}
class AssociateVariants : DefaultInternalDependencyExpansion.ContainingModuleVariantResolver {
override fun getChosenVariant(
dependingVariant: KotlinModuleVariant,
candidateVariants: Iterable<KotlinModuleVariant>
): KotlinVariantMatchingResult {
val result = candidateVariants.filter { it in dependingVariant.declaredContainingModuleFragmentDependencies }
return KotlinVariantMatchingResult.fromMatchingVariants(dependingVariant, dependingVariant.containingModule, result)
}
}
@@ -0,0 +1,75 @@
/*
* 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.project.model
fun module(name: String) = BasicKotlinModule(name, ModuleSource.LocalBuild("current"))
fun BasicKotlinModule.fragment(vararg nameParts: String): BasicKotlinModuleFragment =
fragment(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() })
fun BasicKotlinModule.fragment(name: String): BasicKotlinModuleFragment =
fragments.firstOrNull { it.fragmentName == name } ?: BasicKotlinModuleFragment(this, name).also { fragments.add(it) }
fun BasicKotlinModule.variant(vararg nameParts: String): BasicKotlinVariant =
variant(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() })
fun BasicKotlinModule.variant(name: String): BasicKotlinVariant =
fragments.firstOrNull { it.fragmentName == name }
?.let { it as? BasicKotlinVariant ?: error("$name is not a variant") }
?: BasicKotlinVariant(this, name).also { fragments.add(it) }
fun BasicKotlinModuleFragment.depends(fragment: BasicKotlinModuleFragment) {
require(fragment.containingModule == containingModule)
declaredContainingModuleFragmentDependencies.add(fragment)
}
fun BasicKotlinModuleFragment.refinedBy(fragment: BasicKotlinModuleFragment) {
fragment.refines(this)
}
fun BasicKotlinModuleFragment.refines(fragment: BasicKotlinModuleFragment) {
require(fragment.containingModule == containingModule)
directRefinesDependencies.add(fragment)
}
// ---
fun simpleModule(name: String) = module(name).apply {
listOf("main", "test").forEach { purpose ->
val common = fragment("common", purpose)
if (purpose == "test")
common.depends(fragment("common", "main"))
val (jvm, js, linux) = listOf("jvm", "js", "linux").map { platform ->
variant(platform, purpose).apply {
variantAttributes[KotlinPlatformTypeAttribute] = when (platform) {
"jvm" -> KotlinPlatformTypeAttribute.JVM
"js" -> KotlinPlatformTypeAttribute.JS
else -> {
variantAttributes[KotlinNativeTargetAttribute] = platform
KotlinPlatformTypeAttribute.NATIVE
}
}
if (purpose == "test") {
isExported = false
depends(variant(platform, "main"))
}
}
}
val jvmAndJs = fragment("jvmAndJs", purpose).apply {
refines(common)
refinedBy(jvm)
refinedBy(js)
}
val jsAndLinux = fragment("jsAndLinux", purpose).apply {
refines(common)
refinedBy(js)
refinedBy(linux)
}
}
}
+2
View File
@@ -243,6 +243,7 @@ include ":benchmarks",
":generators",
":generators:test-generator",
":tools:kotlinp",
":kotlin-project-model",
":kotlin-gradle-plugin-api",
":kotlin-gradle-plugin-dsl-codegen",
":kotlin-gradle-plugin-npm-versions-codegen",
@@ -510,6 +511,7 @@ project(':noarg-ide-plugin').projectDir = "$rootDir/plugins/noarg/noarg-ide" as
project(':kotlin-sam-with-receiver-compiler-plugin').projectDir = "$rootDir/plugins/sam-with-receiver/sam-with-receiver-cli" as File
project(':sam-with-receiver-ide-plugin').projectDir = "$rootDir/plugins/sam-with-receiver/sam-with-receiver-ide" as File
project(':tools:kotlinp').projectDir = "$rootDir/libraries/tools/kotlinp" as File
project(':kotlin-project-model').projectDir = "$rootDir/libraries/tools/kotlin-project-model" as File
project(':kotlin-gradle-plugin-api').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-api" as File
project(':kotlin-gradle-plugin-dsl-codegen').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-dsl-codegen" as File
project(':kotlin-gradle-plugin-npm-versions-codegen').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-npm-versions-codegen" as File