[KPM] Implement initial kpm/idea dependency resolution
^KT-51386 Verification Pending
This commit is contained in:
committed by
Space
parent
8b84ed4978
commit
1a5fc84080
@@ -6,6 +6,6 @@ plugins {
|
||||
dependencies {
|
||||
api(project(":native:kotlin-native-utils"))
|
||||
api(project(":kotlin-project-model"))
|
||||
|
||||
implementation(project(":kotlin-tooling-core"))
|
||||
compileOnly("com.android.tools.build:gradle:3.4.0")
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -13,7 +13,9 @@ import org.gradle.api.file.SourceDirectorySet
|
||||
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleFragment
|
||||
import org.jetbrains.kotlin.project.model.withRefinesClosure
|
||||
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
|
||||
import org.jetbrains.kotlin.tooling.core.closure
|
||||
import org.jetbrains.kotlin.tooling.core.withClosure
|
||||
|
||||
interface KotlinGradleFragment : KotlinModuleFragment, HasKotlinDependencies, KotlinFragmentDependencyConfigurations, Named {
|
||||
override val kotlinSourceRoots: SourceDirectorySet
|
||||
@@ -58,5 +60,14 @@ interface KotlinGradleFragment : KotlinModuleFragment, HasKotlinDependencies, Ko
|
||||
listOf(transitiveApiConfiguration.name, transitiveImplementationConfiguration.name)
|
||||
}
|
||||
|
||||
val KotlinGradleFragment.withRefinesClosure: Set<KotlinGradleFragment>
|
||||
get() = this.withClosure { it.directRefinesDependencies }
|
||||
|
||||
val KotlinGradleFragment.refinesClosure: Set<KotlinGradleFragment>
|
||||
get() = (this as KotlinModuleFragment).withRefinesClosure.mapTo(mutableSetOf()) { it as KotlinGradleFragment }
|
||||
get() = this.closure { it.directRefinesDependencies }
|
||||
|
||||
val KotlinGradleFragment.path: String
|
||||
get() = "${project.path}/${containingModule.name}/$fragmentName"
|
||||
|
||||
val KotlinGradleFragment.containingVariants: Set<KotlinGradleVariant>
|
||||
get() = containingModule.variantsContainingFragment(this).map { it as KotlinGradleVariant }.toSet()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
`java-test-fixtures`
|
||||
}
|
||||
|
||||
object BackwardsCompatibilityTestConfiguration {
|
||||
@@ -18,9 +19,14 @@ dependencies {
|
||||
testImplementation(gradleKotlinDsl())
|
||||
testImplementation(project(":kotlin-gradle-plugin"))
|
||||
testImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
|
||||
testImplementation("org.reflections:reflections:0.10.2") {
|
||||
because("Tests on the object graph are performed. This library will find implementations of interfaces at runtime")
|
||||
}
|
||||
|
||||
testFixturesImplementation(gradleApi())
|
||||
testFixturesImplementation(gradleKotlinDsl())
|
||||
testFixturesImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
}
|
||||
|
||||
publish()
|
||||
@@ -39,7 +45,13 @@ run {
|
||||
val version = if (isSnapshotTest) properties["defaultSnapshotVersion"].toString()
|
||||
else BackwardsCompatibilityTestConfiguration.minimalBackwardsCompatibleVersion
|
||||
|
||||
val minimalBackwardsCompatibleVersionTestClasspath by configurations.creating
|
||||
val minimalBackwardsCompatibleVersionTestClasspath by configurations.creating {
|
||||
isCanBeResolved = true
|
||||
isCanBeConsumed = false
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minimalBackwardsCompatibleVersionTestClasspath("org.jetbrains.kotlin:kotlin-gradle-plugin-idea:$version")
|
||||
@@ -51,9 +63,17 @@ run {
|
||||
if (isSnapshotTest) logger.quiet("Running test against snapshot: $version")
|
||||
else logger.quiet("Running test against $version")
|
||||
|
||||
val resolvedClasspath = minimalBackwardsCompatibleVersionTestClasspath.files
|
||||
if (resolvedClasspath.none { "kotlin-gradle-plugin-idea-$version.jar" in it.path }) {
|
||||
throw IllegalStateException(buildString {
|
||||
appendLine("Bad backwardsCompatibilityClasspath: $resolvedClasspath")
|
||||
appendLine("Dependencies:${minimalBackwardsCompatibleVersionTestClasspath.allDependencies.joinToString()}")
|
||||
})
|
||||
}
|
||||
|
||||
systemProperty(
|
||||
"backwardsCompatibilityClasspath",
|
||||
minimalBackwardsCompatibleVersionTestClasspath.files.joinToString(";") { it.absolutePath }
|
||||
resolvedClasspath.joinToString(";") { it.absolutePath }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.DOCUMENTATION_BINARY_TYPE
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.SOURCES_BINARY_TYPE
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
|
||||
sealed interface IdeaKotlinDependency : Serializable {
|
||||
val external: KotlinExternalModelContainer
|
||||
|
||||
companion object {
|
||||
const val CLASSPATH_BINARY_TYPE = "org.jetbrains.binary.type.classpath"
|
||||
const val SOURCES_BINARY_TYPE = "org.jetbrains.binary.type.sources"
|
||||
const val DOCUMENTATION_BINARY_TYPE = "org.jetbrains.binary.type.documentation"
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinSourceDependency : IdeaKotlinDependency {
|
||||
val buildId: String
|
||||
val projectPath: String
|
||||
val projectName: String
|
||||
val kotlinModuleName: String
|
||||
val kotlinModuleClassifier: String?
|
||||
val kotlinFragmentName: String
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinBinaryCoordinates : Serializable {
|
||||
val group: String
|
||||
val module: String
|
||||
val version: String
|
||||
val kotlinModuleName: String?
|
||||
val kotlinFragmentName: String?
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinBinaryDependency : IdeaKotlinDependency {
|
||||
val coordinates: IdeaKotlinBinaryCoordinates?
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinUnresolvedBinaryDependency : IdeaKotlinBinaryDependency {
|
||||
val cause: String?
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinResolvedBinaryDependency : IdeaKotlinBinaryDependency {
|
||||
val binaryType: String
|
||||
val binaryFile: File
|
||||
}
|
||||
|
||||
val IdeaKotlinResolvedBinaryDependency.isSourcesType get() = binaryType == SOURCES_BINARY_TYPE
|
||||
val IdeaKotlinResolvedBinaryDependency.isDocumentationType get() = binaryType == DOCUMENTATION_BINARY_TYPE
|
||||
val IdeaKotlinResolvedBinaryDependency.isClasspathType get() = binaryType == CLASSPATH_BINARY_TYPE
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class IdeaKotlinSourceDependencyImpl(
|
||||
override val buildId: String,
|
||||
override val projectPath: String,
|
||||
override val projectName: String,
|
||||
override val kotlinModuleName: String,
|
||||
override val kotlinModuleClassifier: String?,
|
||||
override val kotlinFragmentName: String,
|
||||
override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty
|
||||
) : IdeaKotlinSourceDependency {
|
||||
|
||||
override fun toString(): String {
|
||||
return "project://$buildId:$projectPath:$kotlinModuleName:$kotlinFragmentName"
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class IdeaKotlinBinaryCoordinatesImpl(
|
||||
override val group: String,
|
||||
override val module: String,
|
||||
override val version: String,
|
||||
override val kotlinModuleName: String? = null,
|
||||
override val kotlinFragmentName: String? = null
|
||||
) : IdeaKotlinBinaryCoordinates {
|
||||
|
||||
override fun toString(): String {
|
||||
return "$group:$module:$version" +
|
||||
(if (kotlinModuleName != null) ":$kotlinModuleName" else "") +
|
||||
(if (kotlinFragmentName != null) ":$kotlinFragmentName" else "")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
override val coordinates: IdeaKotlinBinaryCoordinates?,
|
||||
override val binaryType: String,
|
||||
override val binaryFile: File,
|
||||
override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty
|
||||
) : IdeaKotlinResolvedBinaryDependency {
|
||||
|
||||
override fun toString(): String {
|
||||
return "${binaryType.split(".").last()}://$coordinates/${binaryFile.name}"
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class IdeaKotlinUnresolvedBinaryDependencyImpl(
|
||||
override val cause: String?,
|
||||
override val coordinates: IdeaKotlinBinaryCoordinates?,
|
||||
override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty
|
||||
) : IdeaKotlinUnresolvedBinaryDependency {
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -11,8 +11,9 @@ import java.io.Serializable
|
||||
interface IdeaKotlinFragment : Serializable {
|
||||
val name: String
|
||||
val moduleIdentifier: IdeaKotlinModuleIdentifier
|
||||
val platforms: Set<IdeaKotlinPlatform>
|
||||
val languageSettings: IdeaKotlinLanguageSettings?
|
||||
val dependencies: List<IdeaKotlinFragmentDependency>
|
||||
val dependencies: List<IdeaKotlinDependency>
|
||||
val directRefinesDependencies: List<IdeaKotlinFragment>
|
||||
val sourceDirectories: List<IdeaKotlinSourceDirectory>
|
||||
val resourceDirectories: List<IdeaKotlinResourceDirectory>
|
||||
@@ -23,8 +24,9 @@ interface IdeaKotlinFragment : Serializable {
|
||||
data class IdeaKotlinFragmentImpl(
|
||||
override val name: String,
|
||||
override val moduleIdentifier: IdeaKotlinModuleIdentifier,
|
||||
override val platforms: Set<IdeaKotlinPlatform>,
|
||||
override val languageSettings: IdeaKotlinLanguageSettings?,
|
||||
override val dependencies: List<IdeaKotlinFragmentDependency>,
|
||||
override val dependencies: List<IdeaKotlinDependency>,
|
||||
override val directRefinesDependencies: List<IdeaKotlinFragment>,
|
||||
override val sourceDirectories: List<IdeaKotlinSourceDirectory>,
|
||||
override val resourceDirectories: List<IdeaKotlinResourceDirectory>,
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
interface IdeaKotlinFragmentDependency : Serializable
|
||||
|
||||
@Suppress("unused")
|
||||
@InternalKotlinGradlePluginApi
|
||||
class IdeaKotlinFragmentDependencyImpl: IdeaKotlinFragmentDependency {
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
+2
@@ -8,12 +8,14 @@ package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
import java.io.Serializable
|
||||
|
||||
interface IdeaKotlinModule : Serializable {
|
||||
val name: String
|
||||
val moduleIdentifier: IdeaKotlinModuleIdentifier
|
||||
val fragments: List<IdeaKotlinFragment>
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class IdeaKotlinModuleImpl(
|
||||
override val name: String,
|
||||
override val moduleIdentifier: IdeaKotlinModuleIdentifier,
|
||||
override val fragments: List<IdeaKotlinFragment>
|
||||
) : IdeaKotlinModule {
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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("unused")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
sealed interface IdeaKotlinPlatform : Serializable {
|
||||
val platformType: String
|
||||
val platformDetails: IdeaKotlinPlatformDetails?
|
||||
|
||||
companion object {
|
||||
val unknown: IdeaKotlinPlatform = IdeaKotlinPlatformImpl("unknown", null)
|
||||
|
||||
const val wasmPlatformType = "wasm"
|
||||
const val nativePlatformType = "native"
|
||||
const val jvmPlatformType = "jvm"
|
||||
const val jsPlatformType = "js"
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinPlatformDetails : Serializable
|
||||
|
||||
sealed interface IdeaKotlinWasmPlatformDetails : IdeaKotlinPlatformDetails
|
||||
|
||||
sealed interface IdeaKotlinNativePlatformDetails : IdeaKotlinPlatformDetails {
|
||||
val konanTarget: String
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinJvmPlatformDetails : IdeaKotlinPlatformDetails {
|
||||
val jvmTarget: String
|
||||
}
|
||||
|
||||
sealed interface IdeaKotlinJsPlatformDetails : IdeaKotlinPlatformDetails {
|
||||
val isIr: Boolean
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
fun IdeaKotlinPlatform.Companion.wasm(): IdeaKotlinPlatform {
|
||||
return IdeaKotlinPlatformImpl(wasmPlatformType, IdeaKotlinWasmPlatformDetailsImpl)
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
fun IdeaKotlinPlatform.Companion.native(konanTarget: String): IdeaKotlinPlatform {
|
||||
return IdeaKotlinPlatformImpl(nativePlatformType, IdeaKotlinNativePlatformDetailsImpl(konanTarget))
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
fun IdeaKotlinPlatform.Companion.jvm(jvmTarget: String): IdeaKotlinPlatform {
|
||||
return IdeaKotlinPlatformImpl(jvmPlatformType, IdeaKotlinJvmPlatformDetailsImpl(jvmTarget))
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
fun IdeaKotlinPlatform.Companion.js(isIr: Boolean): IdeaKotlinPlatform {
|
||||
return IdeaKotlinPlatformImpl(jsPlatformType, IdeaKotlinJsPlatformDetailsImpl(isIr))
|
||||
}
|
||||
|
||||
val IdeaKotlinPlatform.isWasm get() = platformType == IdeaKotlinPlatform.wasmPlatformType
|
||||
val IdeaKotlinPlatform.isNative get() = platformType == IdeaKotlinPlatform.nativePlatformType
|
||||
val IdeaKotlinPlatform.isJvm get() = platformType == IdeaKotlinPlatform.jvmPlatformType
|
||||
val IdeaKotlinPlatform.isJs get() = platformType == IdeaKotlinPlatform.jsPlatformType
|
||||
|
||||
val IdeaKotlinPlatform.nativeOrNull get() = (platformDetails as? IdeaKotlinNativePlatformDetails)
|
||||
val IdeaKotlinPlatform.jvmOrNull get() = (platformDetails as? IdeaKotlinJvmPlatformDetails)
|
||||
val IdeaKotlinPlatform.jsOrNull get() = (platformDetails as? IdeaKotlinJsPlatformDetails)
|
||||
|
||||
private data class IdeaKotlinPlatformImpl(
|
||||
override val platformType: String, override val platformDetails: IdeaKotlinPlatformDetails?
|
||||
) : IdeaKotlinPlatform {
|
||||
|
||||
companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
private object IdeaKotlinWasmPlatformDetailsImpl : IdeaKotlinWasmPlatformDetails {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
|
||||
private data class IdeaKotlinJvmPlatformDetailsImpl(override val jvmTarget: String) : IdeaKotlinJvmPlatformDetails {
|
||||
private companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
private data class IdeaKotlinNativePlatformDetailsImpl(override val konanTarget: String) : IdeaKotlinNativePlatformDetails {
|
||||
private companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
private data class IdeaKotlinJsPlatformDetailsImpl(override val isIr: Boolean) : IdeaKotlinJsPlatformDetails {
|
||||
private companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
+2
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
import java.io.Serializable
|
||||
|
||||
interface IdeaKotlinVariant : IdeaKotlinFragment, Serializable {
|
||||
val platform: IdeaKotlinPlatform
|
||||
val variantAttributes: Map<String, String>
|
||||
val compilationOutputs: IdeaKotlinCompilationOutput
|
||||
}
|
||||
@@ -15,6 +16,7 @@ interface IdeaKotlinVariant : IdeaKotlinFragment, Serializable {
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class IdeaKotlinVariantImpl(
|
||||
internal val fragment: IdeaKotlinFragment,
|
||||
override val platform: IdeaKotlinPlatform,
|
||||
override val variantAttributes: Map<String, String>,
|
||||
override val compilationOutputs: IdeaKotlinCompilationOutput,
|
||||
) : IdeaKotlinVariant, IdeaKotlinFragment by fragment {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.verification.DependencyVerificationMode
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.configurationcache.extensions.serviceOf
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
@@ -20,5 +21,8 @@ fun Project.buildIdeaKotlinProjectModel(): IdeaKotlinProjectModel {
|
||||
fun createKpmProject(): Pair<ProjectInternal, KotlinPm20ProjectExtension> {
|
||||
val project = ProjectBuilder.builder().build() as ProjectInternal
|
||||
project.plugins.apply(KotlinPm20PluginWrapper::class.java)
|
||||
project.gradle.startParameter.dependencyVerificationMode = DependencyVerificationMode.OFF
|
||||
project.repositories.mavenLocal()
|
||||
project.repositories.maven { it.setUrl("https://cache-redirector.jetbrains.com/maven-central") }
|
||||
return project to project.extensions.getByType(KotlinPm20ProjectExtension::class.java)
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm
|
||||
|
||||
import deserialize
|
||||
import serialize
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.deserialize
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.serialize
|
||||
import java.io.Serializable
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
+2
-5
@@ -10,23 +10,20 @@ package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
import buildIdeaKotlinProjectModel
|
||||
import createKpmProject
|
||||
import createProxyInstance
|
||||
import deserialize
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.configurationcache.extensions.serviceOf
|
||||
import org.gradle.kotlin.dsl.create
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||
import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelKey
|
||||
import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelSerializer.Companion.serializable
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.external
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.deserialize
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.serialize
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPm20PluginWrapper
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinLinuxX64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm
|
||||
import serialize
|
||||
import unwrapProxyInstance
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea.testFixtures
|
||||
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinBinaryCoordinates
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinBinaryDependency
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinResolvedBinaryDependency
|
||||
import java.io.File
|
||||
|
||||
fun buildIdeaKotlinDependencyMatchers(any: Any?): List<IdeaKotlinDependencyMatcher> {
|
||||
return when (any) {
|
||||
null -> emptyList()
|
||||
is IdeaKotlinDependencyMatcher -> listOf(any)
|
||||
is String -> listOf(IdeaKotlinDependencyMatcher.Coordinates(parseIdeaKotlinBinaryCoordinates(any)))
|
||||
is Regex -> listOf(IdeaKotlinDependencyMatcher.CoordinatesRegex(any))
|
||||
is File -> listOf(IdeaKotlinDependencyMatcher.BinaryFile(any))
|
||||
is Iterable<*> -> any.flatMap { child -> buildIdeaKotlinDependencyMatchers(child) }
|
||||
else -> error("Can't build ${IdeaKotlinDependencyMatcher::class.simpleName} from $any")
|
||||
}
|
||||
}
|
||||
|
||||
interface IdeaKotlinDependencyMatcher {
|
||||
val description: String
|
||||
fun matches(dependency: IdeaKotlinDependency): Boolean
|
||||
|
||||
class Coordinates(
|
||||
private val coordinates: IdeaKotlinBinaryCoordinates
|
||||
) : IdeaKotlinDependencyMatcher {
|
||||
override val description: String = coordinates.toString()
|
||||
|
||||
override fun matches(dependency: IdeaKotlinDependency): Boolean {
|
||||
return dependency is IdeaKotlinBinaryDependency && coordinates == dependency.coordinates
|
||||
}
|
||||
}
|
||||
|
||||
class CoordinatesRegex(
|
||||
private val regex: Regex
|
||||
) : IdeaKotlinDependencyMatcher {
|
||||
override val description: String = regex.pattern
|
||||
|
||||
override fun matches(dependency: IdeaKotlinDependency): Boolean {
|
||||
return dependency is IdeaKotlinBinaryDependency && regex.matches(dependency.coordinates.toString())
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryFile(
|
||||
private val binaryFile: File
|
||||
) : IdeaKotlinDependencyMatcher {
|
||||
override val description: String = binaryFile.path
|
||||
|
||||
override fun matches(dependency: IdeaKotlinDependency): Boolean {
|
||||
return dependency is IdeaKotlinResolvedBinaryDependency && dependency.binaryFile == binaryFile
|
||||
}
|
||||
}
|
||||
|
||||
class InDirectory(
|
||||
private val parentFile: File
|
||||
) : IdeaKotlinDependencyMatcher {
|
||||
constructor(parentFilePath: String) : this(File(parentFilePath))
|
||||
|
||||
override val description: String = "$parentFile/**"
|
||||
|
||||
override fun matches(dependency: IdeaKotlinDependency): Boolean {
|
||||
return dependency is IdeaKotlinResolvedBinaryDependency &&
|
||||
dependency.binaryFile.absoluteFile.normalize().canonicalPath.startsWith(
|
||||
parentFile.absoluteFile.normalize().canonicalPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea.testFixtures
|
||||
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.*
|
||||
import kotlin.test.fail
|
||||
|
||||
fun IdeaKotlinProjectModel.assertIsNotEmpty(): IdeaKotlinProjectModel = apply {
|
||||
if (this.modules.isEmpty()) fail("Expected at least one module in model")
|
||||
}
|
||||
|
||||
fun IdeaKotlinProjectModel.assertContainsModule(name: String): IdeaKotlinModule {
|
||||
return modules.find { it.name == name }
|
||||
?: fail("Missing module with name '$name'. Found: ${modules.map { it.name }}")
|
||||
}
|
||||
|
||||
fun IdeaKotlinModule.assertContainsFragment(name: String): IdeaKotlinFragment {
|
||||
return fragments.find { it.name == name }
|
||||
?: fail("Missing fragment with name '$name'. Found: ${fragments.map { it.name }}")
|
||||
}
|
||||
|
||||
fun IdeaKotlinFragment.assertResolvedBinaryDependencies(
|
||||
binaryType: String,
|
||||
matchers: Set<IdeaKotlinDependencyMatcher>
|
||||
): Set<IdeaKotlinResolvedBinaryDependency> {
|
||||
val resolvedBinaryDependencies = dependencies
|
||||
.mapNotNull { dependency ->
|
||||
when (dependency) {
|
||||
is IdeaKotlinResolvedBinaryDependencyImpl -> dependency
|
||||
is IdeaKotlinUnresolvedBinaryDependencyImpl -> fail("Unexpected unresolved dependency: $dependency")
|
||||
is IdeaKotlinSourceDependencyImpl -> null
|
||||
}
|
||||
}
|
||||
.filter { it.binaryType == binaryType }
|
||||
.toSet()
|
||||
|
||||
val unexpectedResolvedBinaryDependencies = resolvedBinaryDependencies
|
||||
.filter { dependency -> matchers.none { matcher -> matcher.matches(dependency) } }
|
||||
|
||||
val missingDependencies = matchers.filter { matcher ->
|
||||
resolvedBinaryDependencies.none { dependency -> matcher.matches(dependency) }
|
||||
}
|
||||
|
||||
if (unexpectedResolvedBinaryDependencies.isEmpty() && missingDependencies.isEmpty()) {
|
||||
return resolvedBinaryDependencies
|
||||
}
|
||||
|
||||
fail(
|
||||
buildString {
|
||||
if (unexpectedResolvedBinaryDependencies.isNotEmpty()) {
|
||||
appendLine("${name}: Unexpected dependencies found:")
|
||||
unexpectedResolvedBinaryDependencies.forEach { unexpectedDependency ->
|
||||
appendLine(unexpectedDependency)
|
||||
}
|
||||
|
||||
appendLine()
|
||||
appendLine("Unexpected dependency coordinates:")
|
||||
unexpectedResolvedBinaryDependencies.forEach { unexpectedDependency ->
|
||||
appendLine("\"${unexpectedDependency.coordinates}\",")
|
||||
}
|
||||
}
|
||||
|
||||
if (missingDependencies.isNotEmpty()) {
|
||||
appendLine()
|
||||
appendLine("Missing dependencies:")
|
||||
missingDependencies.forEach { missingDependency ->
|
||||
appendLine(missingDependency.description)
|
||||
}
|
||||
}
|
||||
|
||||
appendLine()
|
||||
appendLine("Resolved Dependency Coordinates:")
|
||||
resolvedBinaryDependencies.mapNotNull { it.coordinates }.forEach { coordinates ->
|
||||
appendLine("\"$coordinates\",")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@JvmName("assertResolvedBinaryDependenciesByAnyMatcher")
|
||||
fun IdeaKotlinFragment.assertResolvedBinaryDependencies(
|
||||
binaryType: String, matchers: Set<Any?>,
|
||||
) = assertResolvedBinaryDependencies(binaryType, matchers.flatMap { buildIdeaKotlinDependencyMatchers(it) }.toSet())
|
||||
|
||||
fun IdeaKotlinFragment.assertResolvedBinaryDependencies(
|
||||
binaryType: String, vararg matchers: Any?
|
||||
) = assertResolvedBinaryDependencies(binaryType, matchers.toSet())
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea.testFixtures
|
||||
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinBinaryCoordinates
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinBinaryCoordinatesImpl
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinBinaryDependency
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinFragment
|
||||
|
||||
fun parseIdeaKotlinBinaryCoordinates(coordinates: String): IdeaKotlinBinaryCoordinates {
|
||||
val parts = coordinates.split(":")
|
||||
if (parts.size == 3) {
|
||||
return IdeaKotlinBinaryCoordinatesImpl(parts[0], parts[1], parts[2])
|
||||
}
|
||||
|
||||
if (parts.size == 5) {
|
||||
return IdeaKotlinBinaryCoordinatesImpl(parts[0], parts[1], parts[2], parts[3], parts[4])
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("Cannot parse $coordinates into ${IdeaKotlinBinaryCoordinates::class.java.simpleName}")
|
||||
}
|
||||
|
||||
fun Iterable<IdeaKotlinBinaryCoordinates>.parsableString() =
|
||||
joinToString("," + System.lineSeparator(), "", "") { "\"$it\"" }
|
||||
|
||||
@Suppress("unused") // Debugging API
|
||||
fun IdeaKotlinFragment.parsableDependencyCoordinatesString(): String {
|
||||
return dependencies.filterIsInstance<IdeaKotlinBinaryDependency>()
|
||||
.mapNotNull { it.coordinates }.toSet()
|
||||
.parsableString()
|
||||
}
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea.testFixtures
|
||||
|
||||
import org.gradle.internal.io.ClassLoaderObjectInputStream
|
||||
import java.io.*
|
||||
|
||||
@@ -6,20 +8,20 @@ import java.io.*
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
internal fun Any.serialize(): ByteArray {
|
||||
fun Any.serialize(): ByteArray {
|
||||
return ByteArrayOutputStream().use { byteArrayOutputStream ->
|
||||
ObjectOutputStream(byteArrayOutputStream).writeObject(this)
|
||||
byteArrayOutputStream.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified T : Serializable> ByteArray.deserialize(): T {
|
||||
inline fun <reified T : Serializable> ByteArray.deserialize(): T {
|
||||
val inputStream = ByteArrayInputStream(this)
|
||||
val objectInputStream = ObjectInputStream(inputStream)
|
||||
return objectInputStream.use { it.readObject() } as T
|
||||
}
|
||||
|
||||
internal fun ByteArray.deserialize(classLoader: ClassLoader): Any {
|
||||
fun ByteArray.deserialize(classLoader: ClassLoader): Any {
|
||||
val inputStream = ByteArrayInputStream(this)
|
||||
val objectInputStream = ClassLoaderObjectInputStream(inputStream, classLoader)
|
||||
return objectInputStream.use { it.readObject() }
|
||||
+3
-1
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.project
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
|
||||
fun KotlinPm20ProjectExtension.android() {
|
||||
fun KotlinPm20ProjectExtension.androidPrototype() {
|
||||
project.extensions.findByType<AppExtension>()?.applicationVariants?.all { androidVariant ->
|
||||
main { createKotlinAndroidVariant(androidVariant) }
|
||||
test { createKotlinAndroidVariant(androidVariant.unitTestVariant ?: return@test) }
|
||||
@@ -27,4 +27,6 @@ fun KotlinPm20ProjectExtension.android() {
|
||||
test { createKotlinAndroidVariant(androidVariant.unitTestVariant ?: return@test) }
|
||||
instrumentedTest { createKotlinAndroidVariant(androidVariant.testVariant ?: return@instrumentedTest) }
|
||||
}
|
||||
|
||||
setupIdeaKotlinFragmentDependencyResolver()
|
||||
}
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.android
|
||||
|
||||
import com.android.build.gradle.internal.publishing.AndroidArtifacts
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.external
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.project
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder.FragmentConstraint
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinResolvedBinaryDependencyImpl
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.configureIdeaKotlinSpecialPlatformDependencyResolution
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.containingVariants
|
||||
|
||||
@OptIn(ExternalVariantApi::class)
|
||||
val isAndroidFragment = FragmentConstraint { fragment ->
|
||||
fragment.containingVariants.all { variant -> androidDslKey in variant.external }
|
||||
}
|
||||
|
||||
@OptIn(ExternalVariantApi::class)
|
||||
val isAndroidAndJvmSharedFragment = FragmentConstraint constraint@{ fragment ->
|
||||
val variants = fragment.containingVariants
|
||||
if (variants.any { it.platformType != KotlinPlatformType.jvm }) return@constraint false
|
||||
variants.any { androidDslKey in it.external } && variants.any { androidDslKey !in it.external }
|
||||
}
|
||||
|
||||
@OptIn(ExternalVariantApi::class, InternalKotlinGradlePluginApi::class)
|
||||
internal fun KotlinPm20ProjectExtension.setupIdeaKotlinFragmentDependencyResolver() {
|
||||
configureIdeaKotlinSpecialPlatformDependencyResolution {
|
||||
|
||||
/*
|
||||
Handle android + jvm use cases:
|
||||
We do not yet support jvm based metadata compilations, therefore we do not
|
||||
expect any reasonable results coming from metadata resolution.
|
||||
We default to a 'KotlinPlatformType.jvm' resolution
|
||||
*/
|
||||
withConstraint(isAndroidAndJvmSharedFragment) {
|
||||
withPlatformResolutionAttributes {
|
||||
namedAttribute(Usage.USAGE_ATTRIBUTE, Usage.JAVA_API)
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.jvm)
|
||||
}
|
||||
|
||||
artifactView(CLASSPATH_BINARY_TYPE)
|
||||
}
|
||||
|
||||
withConstraint(isAndroidFragment) {
|
||||
withPlatformResolutionAttributes {
|
||||
namedAttribute(Usage.USAGE_ATTRIBUTE, Usage.JAVA_API)
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
|
||||
}
|
||||
|
||||
artifactView(CLASSPATH_BINARY_TYPE) {
|
||||
attributes {
|
||||
attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.CLASSES_JAR.type)
|
||||
}
|
||||
}
|
||||
|
||||
artifactView("manifest") {
|
||||
attributes {
|
||||
attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.MANIFEST.type)
|
||||
}
|
||||
}
|
||||
|
||||
artifactView("resources") {
|
||||
attributes {
|
||||
attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.ANDROID_RES.type)
|
||||
}
|
||||
}
|
||||
|
||||
artifactView("android-symbol") {
|
||||
attributes {
|
||||
attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.COMPILE_SYMBOL_LIST.type)
|
||||
}
|
||||
}
|
||||
|
||||
additionalDependencies {
|
||||
project.getAndroidRuntimeJars().map { androidRuntimeJar ->
|
||||
IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
binaryType = CLASSPATH_BINARY_TYPE,
|
||||
binaryFile = androidRuntimeJar,
|
||||
coordinates = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -18,6 +18,7 @@ import org.gradle.kotlin.dsl.named
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.createExternalJvmVariant
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.external
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
|
||||
|
||||
fun KotlinGradleModule.createKotlinAndroidVariant(androidVariant: BaseVariant) {
|
||||
@@ -45,7 +46,11 @@ fun KotlinGradleModule.createKotlinAndroidVariant(androidVariant: BaseVariant) {
|
||||
val kotlinVariant = createExternalJvmVariant(
|
||||
"android${androidVariant.buildType.name.capitalize()}", KotlinJvmVariantConfig(
|
||||
/* Only swap out configuration that is used. Default setup shall still be applied */
|
||||
compileDependencies = DefaultKotlinCompileDependenciesDefinition
|
||||
compileDependencies = (DefaultKotlinCompileDependenciesDefinition +
|
||||
FragmentAttributes<KotlinGradleFragment> {
|
||||
namedAttribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, TargetJvmEnvironment.ANDROID)
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
|
||||
})
|
||||
.withConfigurationProvider { androidVariant.compileConfiguration },
|
||||
|
||||
/* Only swap out configuration that is used. Default setup shall still be applied */
|
||||
@@ -84,4 +89,5 @@ fun KotlinGradleModule.createKotlinAndroidVariant(androidVariant: BaseVariant) {
|
||||
androidDsl.androidManifest = project.file("AndroidManifest.xml")
|
||||
androidDsl.compileSdk = 23
|
||||
androidCommon.external[androidDslKey] = androidDsl
|
||||
kotlinVariant.external[androidDslKey] = androidDsl
|
||||
}
|
||||
|
||||
+1
-2
@@ -5,11 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.android
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragmentInternal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
|
||||
|
||||
val KotlinGradleModule.androidCommon: KotlinGradleFragmentInternal
|
||||
get() = (fragments.findByName("android") ?: fragments.create("android") { fragment ->
|
||||
get() = (fragments.findByName("androidCommon") ?: fragments.create("androidCommon") { fragment ->
|
||||
fragment.refines(common)
|
||||
}) as KotlinGradleFragmentInternal
|
||||
|
||||
@@ -95,6 +95,8 @@ dependencies {
|
||||
because("Functional tests are using APIs from Android. Latest Version is used to avoid NoClassDefFoundError")
|
||||
}
|
||||
"functionalTestImplementation"(gradleKotlinDsl())
|
||||
"functionalTestImplementation"(project(":kotlin-gradle-plugin-kpm-android"))
|
||||
"functionalTestImplementation"(testFixtures(project(":kotlin-gradle-plugin-idea")))
|
||||
}
|
||||
|
||||
testImplementation(commonDependency("org.jetbrains.teamcity:serviceMessages"))
|
||||
|
||||
+5
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_DEFAULT_DEPENDENCY
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
|
||||
@@ -101,3 +102,7 @@ fun Project.enableCInteropCommonization(enabled: Boolean = true) {
|
||||
fun Project.enableHierarchicalStructureByDefault(enabled: Boolean = true) {
|
||||
propertiesExtension.set(KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT, enabled.toString())
|
||||
}
|
||||
|
||||
fun Project.enableDefaultStdlibDependency(enabled: Boolean = true) {
|
||||
project.propertiesExtension.set(KOTLIN_STDLIB_DEFAULT_DEPENDENCY, enabled.toString())
|
||||
}
|
||||
|
||||
+10
-10
@@ -6,27 +6,27 @@
|
||||
package org.jetbrains.kotlin.gradle.kpm
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.verification.DependencyVerificationMode
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.gradle.addBuildEventsListenerRegistryMock
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModel
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
abstract class AbstractKpmExtensionTest {
|
||||
protected val project: ProjectInternal = ProjectBuilder.builder().build() as ProjectInternal
|
||||
protected lateinit var kotlin: KotlinPm20ProjectExtension
|
||||
private set
|
||||
|
||||
@BeforeTest
|
||||
open fun setup() {
|
||||
kotlin = project.applyKpmPlugin()
|
||||
protected val project: ProjectInternal = (ProjectBuilder.builder().build() as ProjectInternal).also { project ->
|
||||
project.gradle.startParameter.dependencyVerificationMode = DependencyVerificationMode.OFF
|
||||
}
|
||||
|
||||
protected val kotlin: KotlinPm20ProjectExtension by lazy { project.applyKpmPlugin() }
|
||||
}
|
||||
|
||||
fun Project.applyKpmPlugin(): KotlinPm20ProjectExtension {
|
||||
fun Project.applyKpmPlugin(configure: KotlinPm20ProjectExtension.() -> Unit = {}): KotlinPm20ProjectExtension {
|
||||
addBuildEventsListenerRegistryMock(project)
|
||||
plugins.apply("org.jetbrains.kotlin.multiplatform.pm20")
|
||||
return extensions.getByName("kotlin") as KotlinPm20ProjectExtension
|
||||
return (extensions.getByName("kotlin") as KotlinPm20ProjectExtension).also(configure)
|
||||
}
|
||||
|
||||
fun KotlinPm20ProjectExtension.buildIdeaKotlinProjectModel(): IdeaKotlinProjectModel {
|
||||
return ideaKotlinProjectModelBuilder.buildIdeaKotlinProjectModel()
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.verification.DependencyVerificationMode
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.commonizer.KonanDistribution
|
||||
import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||
import org.jetbrains.kotlin.gradle.enableDefaultStdlibDependency
|
||||
|
||||
abstract class AbstractLightweightIdeaDependencyResolutionTest {
|
||||
|
||||
fun buildProject(builder: ProjectBuilder.() -> Unit = {}): ProjectInternal {
|
||||
return (ProjectBuilder.builder().also(builder).build()).also { project ->
|
||||
project.gradle.startParameter.dependencyVerificationMode = DependencyVerificationMode.OFF
|
||||
project.enableDefaultStdlibDependency(false)
|
||||
project.repositories.mavenLocal()
|
||||
project.repositories.maven { it.setUrl("https://cache-redirector.jetbrains.com/maven-central") }
|
||||
} as ProjectInternal
|
||||
}
|
||||
|
||||
val Project.konanDistribution get() = KonanDistribution(project.konanHome)
|
||||
}
|
||||
+6
-10
@@ -9,6 +9,9 @@ package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.gradle.kotlin.dsl.create
|
||||
import org.jetbrains.kotlin.gradle.kpm.AbstractKpmExtensionTest
|
||||
import org.jetbrains.kotlin.gradle.kpm.buildIdeaKotlinProjectModel
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.deserialize
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.serialize
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinLinuxX64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinMacosX64Variant
|
||||
@@ -26,7 +29,7 @@ class IdeaKotlinProjectModelSerializableTest : AbstractKpmExtensionTest() {
|
||||
@Test
|
||||
fun `test - serialize and deserialize - empty project`() {
|
||||
project.evaluate()
|
||||
assertSerializeAndDeserializeEquals(kotlin.toIdeaKotlinProjectModel())
|
||||
assertSerializeAndDeserializeEquals(kotlin.buildIdeaKotlinProjectModel())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -47,18 +50,11 @@ class IdeaKotlinProjectModelSerializableTest : AbstractKpmExtensionTest() {
|
||||
}
|
||||
|
||||
project.evaluate()
|
||||
assertSerializeAndDeserializeEquals(kotlin.toIdeaKotlinProjectModel())
|
||||
assertSerializeAndDeserializeEquals(kotlin.buildIdeaKotlinProjectModel())
|
||||
}
|
||||
|
||||
private fun assertSerializeAndDeserializeEquals(model: IdeaKotlinProjectModel) {
|
||||
val byteStream = ByteArrayOutputStream()
|
||||
ObjectOutputStream(byteStream).use { stream -> stream.writeObject(model) }
|
||||
val serializedModel = byteStream.toByteArray()
|
||||
|
||||
val deserializedModel = ObjectInputStream(ByteArrayInputStream(serializedModel)).use { stream -> stream.readObject() }
|
||||
if (deserializedModel !is IdeaKotlinProjectModelImpl) {
|
||||
fail("Expected 'deserializedModel' to implement ${IdeaKotlinProjectModelImpl::class.java}. Found $deserializedModel")
|
||||
}
|
||||
val deserializedModel = model.serialize().deserialize<IdeaKotlinProjectModel>()
|
||||
|
||||
assertEquals(
|
||||
model.toString(), deserializedModel.toString(),
|
||||
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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("FunctionName", "DuplicatedCode")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import com.android.build.gradle.LibraryPlugin
|
||||
import org.jetbrains.kotlin.commonizer.platformLibsDir
|
||||
import org.jetbrains.kotlin.commonizer.stdlib
|
||||
import org.jetbrains.kotlin.gradle.android.androidPrototype
|
||||
import org.jetbrains.kotlin.gradle.kpm.applyKpmPlugin
|
||||
import org.jetbrains.kotlin.gradle.kpm.buildIdeaKotlinProjectModel
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosArm64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinJvmVariant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.junit.Test
|
||||
|
||||
class MviKotlinIdeaDependencyResolutionTest : AbstractLightweightIdeaDependencyResolutionTest() {
|
||||
|
||||
@Test
|
||||
fun `test - simple ios and jvm project`() {
|
||||
val project = buildProject()
|
||||
|
||||
val kotlin = project.applyKpmPlugin {
|
||||
mainAndTest {
|
||||
fragments.create("jvm", KotlinJvmVariant::class.java)
|
||||
val iosX64Variant = fragments.create("iosX64", KotlinIosX64Variant::class.java)
|
||||
val iosArm64Variant = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
|
||||
val iosCommon = fragments.create("iosCommon")
|
||||
|
||||
iosCommon.refines(common)
|
||||
iosX64Variant.refines(iosCommon)
|
||||
iosArm64Variant.refines(iosCommon)
|
||||
|
||||
dependencies {
|
||||
implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta01")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.buildIdeaKotlinProjectModel().assertIsNotEmpty().modules.forEach { module ->
|
||||
module.assertContainsFragment("common").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta01:main:commonMain",
|
||||
"com.arkivanov.essenty:lifecycle:0.2.2:main:commonMain",
|
||||
"com.arkivanov.essenty:instance-keeper:0.2.2:main:commonMain",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10"
|
||||
)
|
||||
|
||||
module.assertContainsFragment("jvm").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"com.arkivanov.mvikotlin:mvikotlin-jvm:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:lifecycle-jvm:0.2.2",
|
||||
"com.arkivanov.essenty:instance-keeper-jvm:0.2.2",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
)
|
||||
|
||||
module.assertContainsFragment("iosCommon").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
project.konanDistribution.stdlib,
|
||||
"com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta01:main:commonMain",
|
||||
"com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta01:main:jsNativeMain",
|
||||
"com.arkivanov.essenty:lifecycle:0.2.2:main:commonMain",
|
||||
"com.arkivanov.essenty:instance-keeper:0.2.2:main:commonMain",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
|
||||
/*
|
||||
The following dependencies are actually marked as 'implementation' on mvikotlin.
|
||||
We still resolve those dependencies, because this source set is shared native and
|
||||
the shared native compiler expects those dependencies as well.
|
||||
|
||||
Including those dependencies for IDE analysis is up for discussion.
|
||||
*/
|
||||
"com.arkivanov.mvikotlin:utils-internal:3.0.0-beta01:main:commonMain",
|
||||
"com.arkivanov.mvikotlin:utils-internal:3.0.0-beta01:main:darwinMain",
|
||||
"com.arkivanov.mvikotlin:utils-internal:3.0.0-beta01:main:nativeMain",
|
||||
"com.arkivanov.mvikotlin:rx:3.0.0-beta01:main:commonMain",
|
||||
"com.arkivanov.mvikotlin:rx-internal:3.0.0-beta01:main:commonMain",
|
||||
"com.arkivanov.mvikotlin:rx-internal:3.0.0-beta01:main:darwinMain",
|
||||
"com.arkivanov.mvikotlin:rx-internal:3.0.0-beta01:main:nativeMain",
|
||||
"com.arkivanov.essenty:utils-internal:0.2.2:main:commonMain",
|
||||
"com.arkivanov.essenty:utils-internal:0.2.2:main:nativeMain",
|
||||
)
|
||||
|
||||
module.assertContainsFragment("iosX64").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
project.konanDistribution.stdlib,
|
||||
IdeaKotlinDependencyMatcher.InDirectory(project.konanDistribution.platformLibsDir.resolve(KonanTarget.IOS_X64.name)),
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
"com.arkivanov.mvikotlin:mvikotlin-iosx64:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:lifecycle-iosx64:0.2.2",
|
||||
"com.arkivanov.essenty:instance-keeper-iosx64:0.2.2",
|
||||
|
||||
/* Internals are listed here as well. See comment above */
|
||||
"com.arkivanov.mvikotlin:rx-internal-iosx64:3.0.0-beta01",
|
||||
"com.arkivanov.mvikotlin:utils-internal-iosx64:3.0.0-beta01",
|
||||
"com.arkivanov.mvikotlin:rx-iosx64:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:utils-internal-iosx64:0.2.2",
|
||||
)
|
||||
|
||||
module.assertContainsFragment("iosArm64").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
project.konanDistribution.stdlib,
|
||||
IdeaKotlinDependencyMatcher.InDirectory(project.konanDistribution.platformLibsDir.resolve(KonanTarget.IOS_ARM64.name)),
|
||||
"com.arkivanov.mvikotlin:mvikotlin-iosarm64:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:lifecycle-iosarm64:0.2.2",
|
||||
"com.arkivanov.essenty:instance-keeper-iosarm64:0.2.2",
|
||||
|
||||
/* Internals are listed here as well. See comment above */
|
||||
"com.arkivanov.mvikotlin:rx-internal-iosarm64:3.0.0-beta01",
|
||||
"com.arkivanov.mvikotlin:utils-internal-iosarm64:3.0.0-beta01",
|
||||
"com.arkivanov.mvikotlin:rx-iosarm64:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:utils-internal-iosarm64:0.2.2",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - android and jvm`() {
|
||||
val project = buildProject()
|
||||
|
||||
/* Setup Android */
|
||||
project.plugins.apply(LibraryPlugin::class.java)
|
||||
val android = project.extensions.getByType(LibraryExtension::class.java)
|
||||
android.compileSdkVersion(30)
|
||||
|
||||
|
||||
val kotlin = project.applyKpmPlugin {
|
||||
androidPrototype()
|
||||
jvm {}
|
||||
mainAndTest {
|
||||
dependencies {
|
||||
implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta01")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Android requires project to evaluate */
|
||||
project.evaluate()
|
||||
|
||||
kotlin.buildIdeaKotlinProjectModel().assertIsNotEmpty().assertContainsModule("main").let { module ->
|
||||
listOf("common", "jvm").forEach { fragmentName ->
|
||||
module.assertContainsFragment(fragmentName).assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"com.arkivanov.mvikotlin:mvikotlin-jvm:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:lifecycle-jvm:0.2.2",
|
||||
"com.arkivanov.essenty:instance-keeper-jvm:0.2.2",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
)
|
||||
}
|
||||
|
||||
listOf("androidCommon", "androidRelease").forEach { fragmentName ->
|
||||
module.assertContainsFragment(fragmentName).assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"com.arkivanov.mvikotlin:mvikotlin-android:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:lifecycle-android:0.2.2",
|
||||
"com.arkivanov.essenty:instance-keeper-android:0.2.2",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
android.bootClasspath
|
||||
)
|
||||
}
|
||||
|
||||
module.assertContainsFragment("androidDebug").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"com.arkivanov.mvikotlin:mvikotlin-android-debug:3.0.0-beta01",
|
||||
"com.arkivanov.essenty:lifecycle-android-debug:0.2.2",
|
||||
"com.arkivanov.essenty:instance-keeper-android-debug:0.2.2",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10",
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
android.bootClasspath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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("FunctionName", "DuplicatedCode")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.enableDefaultStdlibDependency
|
||||
import org.jetbrains.kotlin.gradle.kpm.applyKpmPlugin
|
||||
import org.jetbrains.kotlin.gradle.kpm.buildIdeaKotlinProjectModel
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.IdeaKotlinDependencyMatcher
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.assertContainsFragment
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.assertIsNotEmpty
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.assertResolvedBinaryDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosArm64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinJvmVariant
|
||||
import org.junit.Test
|
||||
|
||||
class StdlibKotlinIdeaDependencyResolutionTest : AbstractLightweightIdeaDependencyResolutionTest() {
|
||||
|
||||
@Test
|
||||
fun `test - simple ios and jvm project`() {
|
||||
val project = buildProject()
|
||||
|
||||
val kotlin = project.applyKpmPlugin {
|
||||
mainAndTest {
|
||||
fragments.create("jvm", KotlinJvmVariant::class.java)
|
||||
val iosX64Variant = fragments.create("iosX64", KotlinIosX64Variant::class.java)
|
||||
val iosArm64Variant = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
|
||||
val iosCommon = fragments.create("iosCommon")
|
||||
|
||||
iosCommon.refines(common)
|
||||
iosX64Variant.refines(iosCommon)
|
||||
iosArm64Variant.refines(iosCommon)
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-common", "1.6.10"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.buildIdeaKotlinProjectModel().assertIsNotEmpty().modules.forEach { module ->
|
||||
module.assertContainsFragment("common").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10"
|
||||
)
|
||||
|
||||
/* stdlib-common does not automatically add platform dependencies */
|
||||
module.assertContainsFragment("jvm").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
)
|
||||
|
||||
/* native fragments only references dependencies from the native distribution */
|
||||
listOf("iosCommon", "iosX64", "iosArm64").forEach { fragmentName ->
|
||||
module.assertContainsFragment(fragmentName).assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE, IdeaKotlinDependencyMatcher.InDirectory(project.konanDistribution.root)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing common misconfiguration of depending directly to the regular stdlib in common
|
||||
*/
|
||||
@Test
|
||||
fun `test - simple ios and jvm project - dependency to 'stdlib' in common`() {
|
||||
val project = buildProject()
|
||||
|
||||
val kotlin = project.applyKpmPlugin {
|
||||
mainAndTest {
|
||||
fragments.create("jvm", KotlinJvmVariant::class.java)
|
||||
val iosX64Variant = fragments.create("iosX64", KotlinIosX64Variant::class.java)
|
||||
val iosArm64Variant = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
|
||||
val iosCommon = fragments.create("iosCommon")
|
||||
|
||||
iosCommon.refines(common)
|
||||
iosX64Variant.refines(iosCommon)
|
||||
iosArm64Variant.refines(iosCommon)
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib", "1.6.10"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.buildIdeaKotlinProjectModel().assertIsNotEmpty().modules.forEach { module ->
|
||||
module.assertContainsFragment("common").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10",
|
||||
/*
|
||||
Actually not a desired dependency, however a general filtering mechanism cannot be implemented,
|
||||
since this variant does not contain attributes that would enable such filter.
|
||||
|
||||
A special filter for the stdlib is not implemented yet.
|
||||
*/
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
)
|
||||
|
||||
module.assertContainsFragment("jvm").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
)
|
||||
|
||||
/* native fragments only references dependencie from the native distribution */
|
||||
listOf("iosCommon", "iosX64", "iosArm64").forEach { fragmentName ->
|
||||
module.assertContainsFragment(fragmentName).assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
IdeaKotlinDependencyMatcher.InDirectory(project.konanDistribution.root),
|
||||
|
||||
/* Actually not correct as well, since those are jvm dependencies. Filtering is not easily possible here, as well */
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10",
|
||||
"org.jetbrains:annotations:13.0",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test simple ios and jvm project - with default stdlib dependency`() {
|
||||
val project = buildProject()
|
||||
project.enableDefaultStdlibDependency(true)
|
||||
|
||||
val kotlin = project.applyKpmPlugin {
|
||||
mainAndTest {
|
||||
fragments.create("jvm", KotlinJvmVariant::class.java)
|
||||
val iosX64Variant = fragments.create("iosX64", KotlinIosX64Variant::class.java)
|
||||
val iosArm64Variant = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
|
||||
val iosCommon = fragments.create("iosCommon")
|
||||
|
||||
iosCommon.refines(common)
|
||||
iosX64Variant.refines(iosCommon)
|
||||
iosArm64Variant.refines(iosCommon)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.buildIdeaKotlinProjectModel().assertIsNotEmpty().modules.forEach { module ->
|
||||
module.assertContainsFragment("common").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-common:${project.getKotlinPluginVersion()}"
|
||||
)
|
||||
|
||||
module.assertContainsFragment("jvm").assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
"org.jetbrains.kotlin:kotlin-stdlib:${project.getKotlinPluginVersion()}",
|
||||
Regex("""org\.jetbrains:annotations:.*"""),
|
||||
)
|
||||
|
||||
listOf("iosCommon", "iosX64", "iosArm64").forEach { fragmentName ->
|
||||
module.assertContainsFragment(fragmentName).assertResolvedBinaryDependencies(
|
||||
CLASSPATH_BINARY_TYPE,
|
||||
IdeaKotlinDependencyMatcher.InDirectory(project.konanDistribution.root),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.kpm.external
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.kpm.KotlinMutableExternalModelContainer
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
|
||||
|
||||
@RequiresOptIn("API is intended to build external Kotlin Targets.")
|
||||
@@ -17,6 +18,10 @@ annotation class ExternalVariantApi
|
||||
val KotlinTopLevelExtension.project: Project
|
||||
get() = this.project
|
||||
|
||||
@ExternalVariantApi
|
||||
val KotlinPm20ProjectExtension.ideaKotlinProjectModelBuilder: IdeaKotlinProjectModelBuilder
|
||||
get() = this.ideaKotlinProjectModelBuilder
|
||||
|
||||
@ExternalVariantApi
|
||||
fun KotlinGradleModule.createExternalJvmVariant(
|
||||
name: String,
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.ObjectOutputStream
|
||||
|
||||
internal fun Project.locateOrRegisterBuildIdeaKotlinProjectModelTask(): TaskProvider<BuildIdeaKotlinProjectModelTask> {
|
||||
return locateOrRegisterTask(BuildIdeaKotlinProjectModelTask.defaultTaskName)
|
||||
}
|
||||
|
||||
internal open class BuildIdeaKotlinProjectModelTask : DefaultTask() {
|
||||
@OutputDirectory
|
||||
val outputDirectory = project.buildDir.resolve("ideaKotlinProjectModel")
|
||||
|
||||
private val builder = project.pm20Extension.ideaKotlinProjectModelBuilder
|
||||
|
||||
init {
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected fun buildIdeaKotlinProjectModel() {
|
||||
outputDirectory.mkdirs()
|
||||
|
||||
val model = builder.buildIdeaKotlinProjectModel()
|
||||
val textFile = outputDirectory.resolve("model.txt")
|
||||
textFile.writeText(model.toString())
|
||||
|
||||
val binaryFile = outputDirectory.resolve("model.bin")
|
||||
binaryFile.writeBytes(ByteArrayOutputStream().use { byteArrayOutputStream ->
|
||||
ObjectOutputStream(byteArrayOutputStream).use { objectOutputStream -> objectOutputStream.writeObject(model) }
|
||||
byteArrayOutputStream.toByteArray()
|
||||
})
|
||||
|
||||
val jsonFile = outputDirectory.resolve("model.json")
|
||||
jsonFile.writeText(GsonBuilder().setLenient().setPrettyPrinting().create().toJson(model))
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val defaultTaskName = "buildIdeaKotlinProjectModel"
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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:OptIn(ExternalVariantApi::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinPlatformDependencyResolver.ArtifactResolution
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentAttributes
|
||||
|
||||
@DslMarker
|
||||
@ExternalVariantApi
|
||||
annotation class ExternalVariantPlatformDependencyResolutionDsl
|
||||
|
||||
@ExternalVariantApi
|
||||
fun KotlinPm20ProjectExtension.configureIdeaKotlinSpecialPlatformDependencyResolution(
|
||||
configure: IdeaKotlinPlatformDependencyResolutionDslHandle.() -> Unit
|
||||
) {
|
||||
IdeaKotlinPlatformDependencyResolutionDslHandle(ideaKotlinProjectModelBuilder).also(configure).setup()
|
||||
}
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
class IdeaKotlinPlatformDependencyResolutionDslHandle internal constructor(
|
||||
private val toolingModelBuilder: IdeaKotlinProjectModelBuilder,
|
||||
private val constraint: FragmentConstraint = FragmentConstraint.unconstrained,
|
||||
private val parent: IdeaKotlinPlatformDependencyResolutionDslHandle? = null
|
||||
) {
|
||||
private val artifactViewDslHandles = mutableListOf<ArtifactViewDslHandle>()
|
||||
private val children = mutableListOf<IdeaKotlinPlatformDependencyResolutionDslHandle>()
|
||||
private val additionalDependencies = mutableListOf<IdeaKotlinDependencyResolver>()
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
var platformResolutionAttributes: FragmentAttributes<KotlinGradleFragment>? = null
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
class ArtifactViewDslHandle(
|
||||
@property:ExternalVariantPlatformDependencyResolutionDsl
|
||||
var binaryType: String
|
||||
) {
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
var attributes: FragmentAttributes<KotlinGradleFragment> = FragmentAttributes { }
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
fun attributes(setAttributes: KotlinGradleFragmentConfigurationAttributesContext<KotlinGradleFragment>.() -> Unit) {
|
||||
attributes += FragmentAttributes(setAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
fun withConstraint(
|
||||
constraint: FragmentConstraint,
|
||||
configure: IdeaKotlinPlatformDependencyResolutionDslHandle.() -> Unit
|
||||
) {
|
||||
children += IdeaKotlinPlatformDependencyResolutionDslHandle(
|
||||
toolingModelBuilder, constraint, this,
|
||||
).apply(configure)
|
||||
}
|
||||
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
fun withPlatformResolutionAttributes(
|
||||
setAttributes: KotlinGradleFragmentConfigurationAttributesContext<KotlinGradleFragment>.() -> Unit
|
||||
) {
|
||||
val additionalAttributes = FragmentAttributes(setAttributes)
|
||||
this.platformResolutionAttributes = platformResolutionAttributes?.plus(additionalAttributes) ?: additionalAttributes
|
||||
}
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
fun artifactView(binaryType: String, configure: ArtifactViewDslHandle.() -> Unit = {}) {
|
||||
artifactViewDslHandles += ArtifactViewDslHandle(binaryType).apply(configure)
|
||||
}
|
||||
|
||||
@ExternalVariantPlatformDependencyResolutionDsl
|
||||
fun additionalDependencies(dependencyProvider: (KotlinGradleFragment) -> List<IdeaKotlinDependency>) {
|
||||
this.additionalDependencies += IdeaKotlinDependencyResolver { fragment -> dependencyProvider(fragment).toSet() }
|
||||
}
|
||||
|
||||
internal fun setup() {
|
||||
children.forEach { child -> child.setup() }
|
||||
|
||||
val constraint = buildConstraint()
|
||||
val platformResolutionAttributes = buildPlatformResolutionAttributes()
|
||||
|
||||
/* Setup artifact views */
|
||||
artifactViewDslHandles.toList().forEach { artifactViewDslHandle ->
|
||||
toolingModelBuilder.registerDependencyResolver(
|
||||
IdeaKotlinPlatformDependencyResolver(
|
||||
binaryType = artifactViewDslHandle.binaryType,
|
||||
artifactResolution = ArtifactResolution.Variant(artifactViewDslHandle.attributes)
|
||||
),
|
||||
constraint = FragmentConstraint.isVariant and constraint,
|
||||
phase = DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = DependencyResolutionLevel.Overwrite
|
||||
)
|
||||
|
||||
if (platformResolutionAttributes != null) {
|
||||
toolingModelBuilder.registerDependencyResolver(
|
||||
IdeaKotlinPlatformDependencyResolver(
|
||||
binaryType = artifactViewDslHandle.binaryType,
|
||||
artifactResolution = ArtifactResolution.PlatformFragment(
|
||||
artifactViewAttributes = artifactViewDslHandle.attributes,
|
||||
platformResolutionAttributes = platformResolutionAttributes
|
||||
)
|
||||
),
|
||||
constraint = !FragmentConstraint.isVariant and constraint,
|
||||
phase = DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = DependencyResolutionLevel.Overwrite
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup plain additional resolvers */
|
||||
additionalDependencies.toList().forEach { additionalDependencyResolver ->
|
||||
toolingModelBuilder.registerDependencyResolver(
|
||||
resolver = additionalDependencyResolver,
|
||||
constraint = constraint,
|
||||
phase = DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = DependencyResolutionLevel.Overwrite
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildConstraint(): FragmentConstraint {
|
||||
val parentConstraint = parent?.buildConstraint() ?: return constraint
|
||||
return parentConstraint and this.constraint
|
||||
}
|
||||
|
||||
private fun buildPlatformResolutionAttributes(): FragmentAttributes<KotlinGradleFragment>? {
|
||||
val parentAttributes = parent?.buildPlatformResolutionAttributes() ?: return this.platformResolutionAttributes
|
||||
return parentAttributes + (this.platformResolutionAttributes ?: return parentAttributes)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
|
||||
fun interface IdeaKotlinDependencyEffect {
|
||||
operator fun invoke(fragment: KotlinGradleFragment, dependencies: Set<IdeaKotlinDependency>)
|
||||
}
|
||||
|
||||
fun IdeaKotlinDependencyResolver.withEffect(effect: IdeaKotlinDependencyEffect) = IdeaKotlinDependencyResolver { fragment ->
|
||||
this@withEffect.resolve(fragment).also { dependencies -> effect(fragment, dependencies) }
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.path
|
||||
|
||||
internal object IdeaKotlinDependencyLogger : IdeaKotlinDependencyEffect {
|
||||
override fun invoke(
|
||||
fragment: KotlinGradleFragment, dependencies: Set<IdeaKotlinDependency>
|
||||
) {
|
||||
val fragmentPathRegex = fragment.project.properties["idea.kotlin.log.dependencies"]?.toString() ?: return
|
||||
if (!fragment.path.matches(Regex(fragmentPathRegex))) return
|
||||
|
||||
val message = buildString {
|
||||
appendLine("Resolved dependencies for ${fragment.path}")
|
||||
dependencies.forEach { dependency -> appendLine("> $dependency") }
|
||||
appendLine()
|
||||
}
|
||||
|
||||
fragment.project.logger.quiet(message)
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
|
||||
fun interface IdeaKotlinDependencyResolver {
|
||||
fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency>
|
||||
|
||||
object Empty : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> = emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
fun IdeaKotlinDependencyResolver(
|
||||
resolvers: Iterable<IdeaKotlinDependencyResolver>
|
||||
): IdeaKotlinDependencyResolver {
|
||||
val resolversList = resolvers.toList()
|
||||
if (resolversList.isEmpty()) return IdeaKotlinDependencyResolver.Empty
|
||||
return CompositeIdeaKotlinDependencyResolver(resolversList)
|
||||
}
|
||||
|
||||
fun IdeaKotlinDependencyResolver(
|
||||
vararg resolvers: IdeaKotlinDependencyResolver
|
||||
): IdeaKotlinDependencyResolver = IdeaKotlinDependencyResolver(resolvers.toList())
|
||||
|
||||
operator fun IdeaKotlinDependencyResolver.plus(
|
||||
other: IdeaKotlinDependencyResolver
|
||||
): IdeaKotlinDependencyResolver {
|
||||
if (this is CompositeIdeaKotlinDependencyResolver && other is CompositeIdeaKotlinDependencyResolver) {
|
||||
return CompositeIdeaKotlinDependencyResolver(this.children + other.children)
|
||||
}
|
||||
|
||||
if (this is CompositeIdeaKotlinDependencyResolver) {
|
||||
return CompositeIdeaKotlinDependencyResolver(this.children + other)
|
||||
}
|
||||
|
||||
if (other is CompositeIdeaKotlinDependencyResolver) {
|
||||
return CompositeIdeaKotlinDependencyResolver(listOf(this) + other.children)
|
||||
}
|
||||
|
||||
return CompositeIdeaKotlinDependencyResolver(listOf(this, other))
|
||||
}
|
||||
|
||||
private class CompositeIdeaKotlinDependencyResolver(
|
||||
val children: List<IdeaKotlinDependencyResolver>
|
||||
) : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
return children.flatMap { child -> child.resolve(fragment) }.toSet()
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
|
||||
fun interface IdeaKotlinDependencyTransformer {
|
||||
fun transform(
|
||||
fragment: KotlinGradleFragment, dependencies: Set<IdeaKotlinDependency>
|
||||
): Set<IdeaKotlinDependency>
|
||||
}
|
||||
|
||||
fun IdeaKotlinDependencyResolver.withTransformer(transformer: IdeaKotlinDependencyTransformer) = IdeaKotlinDependencyResolver { fragment ->
|
||||
transformer.transform(fragment, this@withTransformer.resolve(fragment))
|
||||
}
|
||||
|
||||
fun IdeaKotlinDependencyTransformer(
|
||||
transformers: List<IdeaKotlinDependencyTransformer>
|
||||
): IdeaKotlinDependencyTransformer = CompositeIdeaKotlinDependencyTransformer(transformers)
|
||||
|
||||
fun IdeaKotlinDependencyTransformer(
|
||||
vararg transformers: IdeaKotlinDependencyTransformer
|
||||
): IdeaKotlinDependencyTransformer = IdeaKotlinDependencyTransformer(transformers.toList())
|
||||
|
||||
operator fun IdeaKotlinDependencyTransformer.plus(other: IdeaKotlinDependencyTransformer):
|
||||
IdeaKotlinDependencyTransformer {
|
||||
if (this is CompositeIdeaKotlinDependencyTransformer && other is CompositeIdeaKotlinDependencyTransformer) {
|
||||
return CompositeIdeaKotlinDependencyTransformer(this.transformers + other.transformers)
|
||||
}
|
||||
|
||||
if (this is CompositeIdeaKotlinDependencyTransformer) {
|
||||
return CompositeIdeaKotlinDependencyTransformer(this.transformers + other)
|
||||
}
|
||||
|
||||
if (other is CompositeIdeaKotlinDependencyTransformer) {
|
||||
return CompositeIdeaKotlinDependencyTransformer(listOf(this) + other.transformers)
|
||||
}
|
||||
|
||||
return CompositeIdeaKotlinDependencyTransformer(listOf(this, other))
|
||||
}
|
||||
|
||||
private class CompositeIdeaKotlinDependencyTransformer(
|
||||
val transformers: List<IdeaKotlinDependencyTransformer>
|
||||
) : IdeaKotlinDependencyTransformer {
|
||||
override fun transform(
|
||||
fragment: KotlinGradleFragment, dependencies: Set<IdeaKotlinDependency>
|
||||
): Set<IdeaKotlinDependency> {
|
||||
return transformers.fold(dependencies) { currentDependencies, transformer -> transformer.transform(fragment, currentDependencies) }
|
||||
}
|
||||
}
|
||||
+26
-23
@@ -9,39 +9,42 @@ import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragmentInternal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleVariant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.variantsContainingFragment
|
||||
|
||||
private typealias IdeaKotlinFragmentBuilderCache = MutableMap<KotlinGradleFragment, IdeaKotlinFragment>
|
||||
internal class IdeaKotlinFragmentBuildingContext(
|
||||
private val parent: IdeaKotlinProjectModelBuildingContext
|
||||
) : IdeaKotlinProjectModelBuildingContext by parent {
|
||||
private val fragmentCache = mutableMapOf<KotlinGradleFragment, IdeaKotlinFragment>()
|
||||
fun getOrPut(source: KotlinGradleFragment, builder: () -> IdeaKotlinFragment) = fragmentCache.getOrPut(source, builder)
|
||||
}
|
||||
|
||||
internal fun KotlinGradleFragment.toIdeaKotlinFragment(
|
||||
cache: IdeaKotlinFragmentBuilderCache = mutableMapOf()
|
||||
): IdeaKotlinFragment {
|
||||
return cache.getOrPut(this) {
|
||||
if (this is KotlinGradleVariant) buildIdeaKotlinVariant(cache)
|
||||
else buildIdeaKotlinFragment(cache)
|
||||
internal fun IdeaKotlinFragmentBuildingContext.toIdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment {
|
||||
return getOrPut(fragment) {
|
||||
if (fragment is KotlinGradleVariant) buildIdeaKotlinVariant(fragment)
|
||||
else buildIdeaKotlinFragment(fragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinGradleFragment.buildIdeaKotlinFragment(cache: IdeaKotlinFragmentBuilderCache): IdeaKotlinFragment {
|
||||
private fun IdeaKotlinFragmentBuildingContext.buildIdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment {
|
||||
return IdeaKotlinFragmentImpl(
|
||||
name = name,
|
||||
moduleIdentifier = containingModule.moduleIdentifier.toIdeaKotlinModuleIdentifier(),
|
||||
languageSettings = languageSettings.toIdeaKotlinLanguageSettings(),
|
||||
dependencies = emptyList(),
|
||||
directRefinesDependencies = directRefinesDependencies.map { refinesFragment ->
|
||||
refinesFragment.toIdeaKotlinFragment(cache)
|
||||
},
|
||||
sourceDirectories = kotlinSourceRoots.sourceDirectories.files.toList().map { file ->
|
||||
IdeaKotlinSourceDirectoryImpl(file)
|
||||
},
|
||||
name = fragment.name,
|
||||
moduleIdentifier = fragment.containingModule.moduleIdentifier.toIdeaKotlinModuleIdentifier(),
|
||||
platforms = fragment.containingModule.variantsContainingFragment(fragment)
|
||||
.map { variant -> buildIdeaKotlinPlatform(variant) }.toSet(),
|
||||
languageSettings = fragment.languageSettings.toIdeaKotlinLanguageSettings(),
|
||||
dependencies = dependencyResolver.resolve(fragment).toList(),
|
||||
directRefinesDependencies = fragment.directRefinesDependencies.map { refinesFragment -> toIdeaKotlinFragment(refinesFragment) },
|
||||
sourceDirectories = fragment.kotlinSourceRoots.sourceDirectories.files.toList().map { file -> IdeaKotlinSourceDirectoryImpl(file) },
|
||||
resourceDirectories = emptyList(),
|
||||
external = (this as? KotlinGradleFragmentInternal)?.external ?: KotlinExternalModelContainer.Empty
|
||||
external = (fragment as? KotlinGradleFragmentInternal)?.external ?: KotlinExternalModelContainer.Empty
|
||||
)
|
||||
}
|
||||
|
||||
private fun KotlinGradleVariant.buildIdeaKotlinVariant(cache: IdeaKotlinFragmentBuilderCache): IdeaKotlinVariant {
|
||||
private fun IdeaKotlinFragmentBuildingContext.buildIdeaKotlinVariant(variant: KotlinGradleVariant): IdeaKotlinVariant {
|
||||
return IdeaKotlinVariantImpl(
|
||||
fragment = buildIdeaKotlinFragment(cache),
|
||||
variantAttributes = variantAttributes.mapKeys { (key, _) -> key.uniqueName },
|
||||
compilationOutputs = compilationOutputs.toIdeaKotlinCompilationOutputs()
|
||||
fragment = buildIdeaKotlinFragment(variant),
|
||||
platform = buildIdeaKotlinPlatform(variant),
|
||||
variantAttributes = variant.variantAttributes.mapKeys { (key, _) -> key.uniqueName },
|
||||
compilationOutputs = variant.compilationOutputs.toIdeaKotlinCompilationOutputs()
|
||||
)
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.JarMetadataProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentGranularMetadataResolverFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule.Companion.moduleName
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toModuleDependency
|
||||
import org.jetbrains.kotlin.gradle.utils.withTemporaryDirectory
|
||||
|
||||
internal class IdeaKotlinMetadataBinaryDependencyResolver(
|
||||
private val fragmentGranularMetadataResolverFactory: FragmentGranularMetadataResolverFactory
|
||||
) : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
return fragmentGranularMetadataResolverFactory.getOrCreate(fragment).resolutions
|
||||
.filterIsInstance<ChooseVisibleSourceSets>()
|
||||
.flatMap { resolution -> resolve(fragment, resolution) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun resolve(fragment: KotlinGradleFragment, resolution: ChooseVisibleSourceSets): Iterable<IdeaKotlinDependency> {
|
||||
val gradleModuleIdentifier = resolution.dependency.id as? ModuleComponentIdentifier ?: return emptySet()
|
||||
val kotlinModuleIdentifier = resolution.dependency.toModuleDependency().moduleIdentifier
|
||||
|
||||
/* Project to project metadata dependencies shall be resolved as source dependencies, somewhere else */
|
||||
val metadataProvider = when (resolution.metadataProvider) {
|
||||
is ProjectMetadataProvider -> return emptySet()
|
||||
is JarMetadataProvider -> resolution.metadataProvider
|
||||
}
|
||||
return resolution.allVisibleSourceSetNames.mapNotNull { visibleFragmentName ->
|
||||
val binaryFile = withTemporaryDirectory("metadataBinaryDependencyResolver") { temporaryDirectory ->
|
||||
val sourceBinaryFile = metadataProvider.getSourceSetCompiledMetadata(
|
||||
sourceSetName = visibleFragmentName,
|
||||
outputDirectory = temporaryDirectory,
|
||||
materializeFile = true
|
||||
)
|
||||
|
||||
if (!sourceBinaryFile.isFile)
|
||||
return@mapNotNull null
|
||||
|
||||
fragment.project.rootDir
|
||||
.resolve(".gradle").resolve("kotlin").resolve("transformedKotlinMetadata")
|
||||
.resolve(gradleModuleIdentifier.group)
|
||||
.resolve(gradleModuleIdentifier.module)
|
||||
.resolve(gradleModuleIdentifier.version)
|
||||
.resolve(kotlinModuleIdentifier.moduleName)
|
||||
.resolve(visibleFragmentName)
|
||||
.resolve(sourceBinaryFile.name)
|
||||
.apply { if (!isFile) sourceBinaryFile.copyTo(this) }
|
||||
}
|
||||
|
||||
IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
binaryFile = binaryFile,
|
||||
coordinates = IdeaKotlinBinaryCoordinatesImpl(
|
||||
group = gradleModuleIdentifier.group,
|
||||
module = gradleModuleIdentifier.module,
|
||||
version = gradleModuleIdentifier.version,
|
||||
kotlinModuleName = kotlinModuleIdentifier.moduleName,
|
||||
kotlinFragmentName = visibleFragmentName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.path
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
|
||||
internal object IdeaKotlinMissingFileDependencyLogger : IdeaKotlinDependencyEffect {
|
||||
override fun invoke(fragment: KotlinGradleFragment, dependencies: Set<IdeaKotlinDependency>) {
|
||||
dependencies.filterIsInstance<IdeaKotlinResolvedBinaryDependency>()
|
||||
.filter { !it.binaryFile.exists() }
|
||||
.ifNotEmpty { fragment.project.logger.warn(buildWarningMessage(fragment, this)) }
|
||||
}
|
||||
|
||||
private fun buildWarningMessage(fragment: KotlinGradleFragment, dependencies: List<IdeaKotlinResolvedBinaryDependency>) = buildString {
|
||||
append("[WARNING] ${fragment.path}: Missing dependency files")
|
||||
dependencies.forEach { dependency ->
|
||||
appendLine(dependency.binaryFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -5,13 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
|
||||
|
||||
internal fun KotlinGradleModule.toIdeaKotlinModule(): IdeaKotlinModule {
|
||||
val fragmentsCache = mutableMapOf<KotlinGradleFragment, IdeaKotlinFragment>()
|
||||
internal fun IdeaKotlinProjectModelBuildingContext.toIdeaKotlinModule(module: KotlinGradleModule): IdeaKotlinModule {
|
||||
val fragmentBuildingContext = IdeaKotlinFragmentBuildingContext(this)
|
||||
return IdeaKotlinModuleImpl(
|
||||
moduleIdentifier = moduleIdentifier.toIdeaKotlinModuleIdentifier(),
|
||||
fragments = fragments.toList().map { fragment -> fragment.toIdeaKotlinFragment(fragmentsCache) }
|
||||
name = module.name,
|
||||
moduleIdentifier = module.moduleIdentifier.toIdeaKotlinModuleIdentifier(),
|
||||
fragments = module.fragments.toList().map { fragment -> fragmentBuildingContext.toIdeaKotlinFragment(fragment) }
|
||||
)
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.commonizer.KonanDistribution
|
||||
import org.jetbrains.kotlin.commonizer.platformLibsDir
|
||||
import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeVariantInternal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.containingVariants
|
||||
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
|
||||
import org.jetbrains.kotlin.library.resolveSingleFileKlib
|
||||
import org.jetbrains.kotlin.library.shortName
|
||||
import org.jetbrains.kotlin.library.uniqueName
|
||||
import java.io.File
|
||||
|
||||
internal class IdeaKotlinNativePlatformDependencyResolver : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
val konanTargets = fragment.containingVariants
|
||||
.map { it as? KotlinNativeVariantInternal ?: return emptySet() }
|
||||
.map { it.konanTarget }
|
||||
.toSet()
|
||||
|
||||
/* Fragments with multiple konan targets will receive commonized klibs */
|
||||
val konanTarget = konanTargets.singleOrNull() ?: return emptySet()
|
||||
|
||||
return fragment.project.konanDistribution.platformLibsDir.resolve(konanTarget.name)
|
||||
.listLibraryFiles()
|
||||
.mapNotNull { libraryFile -> fragment.project.resolveKlib(libraryFile) }
|
||||
.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.resolveKlib(file: File): IdeaKotlinResolvedBinaryDependency? {
|
||||
try {
|
||||
val kotlinLibrary = resolveSingleFileKlib(
|
||||
org.jetbrains.kotlin.konan.file.File(file.absolutePath),
|
||||
strategy = ToolingSingleFileKlibResolveStrategy
|
||||
)
|
||||
|
||||
return IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
binaryFile = file,
|
||||
coordinates = IdeaKotlinBinaryCoordinatesImpl(
|
||||
group = "org.jetbrains.kotlin.native",
|
||||
module = kotlinLibrary.shortName ?: kotlinLibrary.uniqueName,
|
||||
version = project.getKotlinPluginVersion()
|
||||
)
|
||||
)
|
||||
} catch (t: Throwable) {
|
||||
logger.error("Failed resolving library ${file.path}", t)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private val Project.konanDistribution: KonanDistribution
|
||||
get() = KonanDistribution(project.file(konanHome))
|
||||
|
||||
private fun File.listLibraryFiles(): Set<File> = listFiles().orEmpty()
|
||||
.filter { it.isDirectory || it.extension == "klib" }
|
||||
.toSet()
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.KonanDistribution
|
||||
import org.jetbrains.kotlin.commonizer.stdlib
|
||||
import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
|
||||
internal object IdeaKotlinNativeStdlibDependencyResolver : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
return setOf(
|
||||
IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
binaryFile = KonanDistribution(fragment.project.konanHome).stdlib,
|
||||
coordinates = IdeaKotlinBinaryCoordinatesImpl(
|
||||
"org.jetbrains.kotlin", "stdlib-native", fragment.project.getKotlinPluginVersion()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.KeepOriginalDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentGranularMetadataResolverFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.resolvableMetadataConfigurationName
|
||||
|
||||
internal class IdeaKotlinOriginalMetadataDependencyResolver(
|
||||
private val fragmentGranularMetadataResolverFactory: FragmentGranularMetadataResolverFactory
|
||||
) : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
val dependencyIdentifiers = fragmentGranularMetadataResolverFactory.getOrCreate(fragment).resolutions
|
||||
.filterIsInstance<KeepOriginalDependency>()
|
||||
.mapNotNull { resolution -> resolution.dependency.id as? ModuleComponentIdentifier }
|
||||
.toSet()
|
||||
|
||||
val allModuleCompileDependenciesConfiguration = fragment.project.configurations
|
||||
.getByName(fragment.containingModule.resolvableMetadataConfigurationName)
|
||||
|
||||
return allModuleCompileDependenciesConfiguration.incoming.artifactView { view ->
|
||||
view.componentFilter { id -> id in dependencyIdentifiers }
|
||||
view.isLenient = true
|
||||
}.artifacts
|
||||
.map { artifact ->
|
||||
val artifactId = artifact.variant.owner as ModuleComponentIdentifier
|
||||
IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
binaryFile = artifact.file,
|
||||
coordinates = IdeaKotlinBinaryCoordinatesImpl(artifactId.group, artifactId.module, artifactId.version)
|
||||
)
|
||||
}.toSet()
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
|
||||
@Suppress("unused")
|
||||
/* Receiver acts as scope, or key to that function */
|
||||
internal fun IdeaKotlinFragmentBuildingContext.buildIdeaKotlinPlatform(variant: KotlinGradleVariant): IdeaKotlinPlatform {
|
||||
when (variant) {
|
||||
is KotlinJvmVariant -> return IdeaKotlinPlatform.jvm(variant.compilationData.kotlinOptions.jvmTarget ?: JvmTarget.DEFAULT.name)
|
||||
is KotlinNativeVariantInternal -> return IdeaKotlinPlatform.native(variant.konanTarget.name)
|
||||
is LegacyMappedVariant -> when (val compilation = variant.compilation) {
|
||||
is KotlinJvmCompilation -> return IdeaKotlinPlatform.jvm(compilation.kotlinOptions.jvmTarget ?: JvmTarget.DEFAULT.name)
|
||||
is KotlinJvmAndroidCompilation -> return IdeaKotlinPlatform.jvm(compilation.kotlinOptions.jvmTarget ?: JvmTarget.DEFAULT.name)
|
||||
is KotlinNativeCompilation -> return IdeaKotlinPlatform.native(compilation.konanTarget.name)
|
||||
is KotlinJsIrCompilation -> return IdeaKotlinPlatform.js(isIr = true)
|
||||
is KotlinJsCompilation -> return IdeaKotlinPlatform.js(isIr = false)
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback calculation based on 'platformType' alone */
|
||||
/* This is a last line of defence, not expected to be actually executed */
|
||||
assert(false) { "Unable to build 'IdeaKotlinPlatform' from variant ${variant.path}" }
|
||||
return when (variant.platformType) {
|
||||
KotlinPlatformType.common -> throw IllegalArgumentException("Unexpected platformType 'common' for variant ${variant.name}")
|
||||
KotlinPlatformType.jvm -> IdeaKotlinPlatform.jvm(JvmTarget.DEFAULT.name)
|
||||
KotlinPlatformType.androidJvm -> IdeaKotlinPlatform.jvm(JvmTarget.DEFAULT.name)
|
||||
KotlinPlatformType.js -> IdeaKotlinPlatform.js(false)
|
||||
KotlinPlatformType.native -> IdeaKotlinPlatform.native(HostManager.host.name)
|
||||
KotlinPlatformType.wasm -> IdeaKotlinPlatform.wasm()
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.gradle.api.artifacts.ArtifactView
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.component.ComponentIdentifier
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.component.ModuleComponentSelector
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.internal.resolve.ModuleVersionResolveException
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinPlatformDependencyResolver.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentAttributes
|
||||
|
||||
class IdeaKotlinPlatformDependencyResolver(
|
||||
private val binaryType: String = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
private val artifactResolution: ArtifactResolution = ArtifactResolution.Variant()
|
||||
) : IdeaKotlinDependencyResolver {
|
||||
|
||||
sealed class ArtifactResolution {
|
||||
data class Variant(
|
||||
internal val artifactViewAttributes: FragmentAttributes<KotlinGradleFragment> = FragmentAttributes { }
|
||||
) : ArtifactResolution()
|
||||
|
||||
data class PlatformFragment(
|
||||
internal val platformResolutionAttributes: FragmentAttributes<KotlinGradleFragment>,
|
||||
internal val artifactViewAttributes: FragmentAttributes<KotlinGradleFragment> = FragmentAttributes { },
|
||||
) : ArtifactResolution()
|
||||
}
|
||||
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinBinaryDependency> {
|
||||
val artifacts = artifactResolution.createArtifactView(fragment)?.artifacts ?: return emptySet()
|
||||
|
||||
val unresolvedDependencies = artifacts.failures
|
||||
.onEach { reason -> fragment.project.logger.error("Failed to resolve dependency", reason) }
|
||||
.map { reason ->
|
||||
val selector = (reason as? ModuleVersionResolveException)?.selector as? ModuleComponentSelector
|
||||
/* Can't figure out the dependency here :( */
|
||||
?: return@map IdeaKotlinUnresolvedBinaryDependencyImpl(
|
||||
coordinates = null, cause = reason.message?.takeIf { it.isNotBlank() }
|
||||
)
|
||||
|
||||
IdeaKotlinUnresolvedBinaryDependencyImpl(
|
||||
coordinates = IdeaKotlinBinaryCoordinatesImpl(selector.group, selector.module, selector.version),
|
||||
cause = reason.message?.takeIf { it.isNotBlank() }
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
val resolvedDependencies = artifacts.artifacts.mapNotNull { artifact ->
|
||||
IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
coordinates = artifact.variant.owner.ideaKotlinBinaryCoordinates,
|
||||
binaryType = binaryType,
|
||||
binaryFile = artifact.file
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
return resolvedDependencies + unresolvedDependencies
|
||||
}
|
||||
}
|
||||
|
||||
private val ComponentIdentifier.ideaKotlinBinaryCoordinates: IdeaKotlinBinaryCoordinates?
|
||||
get() = when (this) {
|
||||
is ModuleComponentIdentifier -> IdeaKotlinBinaryCoordinatesImpl(group, module, version)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun ArtifactResolution.createArtifactView(fragment: KotlinGradleFragment): ArtifactView? {
|
||||
return when (this) {
|
||||
is ArtifactResolution.Variant -> createVariantArtifactView(fragment)
|
||||
is ArtifactResolution.PlatformFragment -> createPlatformFragmentArtifactView(fragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArtifactResolution.Variant.createVariantArtifactView(fragment: KotlinGradleFragment): ArtifactView? {
|
||||
if (fragment !is KotlinGradleVariant) return null
|
||||
return fragment.compileDependenciesConfiguration.incoming.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.componentFilter { id -> id !is ProjectComponentIdentifier }
|
||||
view.attributes.apply(artifactViewAttributes, fragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArtifactResolution.PlatformFragment.createPlatformFragmentArtifactView(fragment: KotlinGradleFragment): ArtifactView {
|
||||
val fragmentCompileDependencies = fragment.project.configurations.detachedConfiguration()
|
||||
|
||||
fragmentCompileDependencies.dependencies.addAll(
|
||||
fragment.transitiveApiConfiguration.allDependencies.matching { it !is ProjectDependency }
|
||||
)
|
||||
|
||||
fragmentCompileDependencies.dependencies.addAll(
|
||||
fragment.transitiveImplementationConfiguration.allDependencies.matching { it !is ProjectDependency }
|
||||
)
|
||||
|
||||
fragmentCompileDependencies.attributes.apply(platformResolutionAttributes, fragment)
|
||||
|
||||
/* Ensure consistent dependency resolution result within the whole module */
|
||||
val allModuleCompileDependencies = fragment.project.configurations.getByName(
|
||||
fragment.containingModule.resolvableMetadataConfigurationName
|
||||
)
|
||||
fragmentCompileDependencies.shouldResolveConsistentlyWith(allModuleCompileDependencies)
|
||||
|
||||
|
||||
return fragmentCompileDependencies.incoming.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.componentFilter { id -> id !is ProjectComponentIdentifier }
|
||||
view.attributes.apply(artifactViewAttributes, fragment)
|
||||
}
|
||||
}
|
||||
+91
-13
@@ -1,26 +1,104 @@
|
||||
@file:OptIn(ExternalVariantApi::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilder
|
||||
import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder.FragmentConstraint
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.native
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.containingVariants
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleVariant
|
||||
import java.io.File
|
||||
|
||||
class IdeaKotlinProjectModelBuilder : ToolingModelBuilder {
|
||||
override fun canBuild(modelName: String): Boolean = modelName == IdeaKotlinProjectModel::class.java.name
|
||||
override fun buildAll(modelName: String, project: Project): IdeaKotlinProjectModel {
|
||||
return project.pm20Extension.toIdeaKotlinProjectModel()
|
||||
internal interface IdeaKotlinProjectModelBuildingContext {
|
||||
val dependencyResolver: IdeaKotlinDependencyResolver
|
||||
|
||||
companion object Empty : IdeaKotlinProjectModelBuildingContext {
|
||||
override val dependencyResolver: IdeaKotlinDependencyResolver = IdeaKotlinDependencyResolver.Empty
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinPm20ProjectExtension.toIdeaKotlinProjectModel(): IdeaKotlinProjectModel {
|
||||
interface IdeaKotlinProjectModelBuilder {
|
||||
|
||||
enum class DependencyResolutionPhase {
|
||||
PreDependencyResolution,
|
||||
SourceDependencyResolution,
|
||||
BinaryDependencyResolution,
|
||||
PostDependencyResolution
|
||||
}
|
||||
|
||||
enum class DependencyResolutionLevel {
|
||||
Default, Overwrite
|
||||
}
|
||||
|
||||
enum class DependencyTransformationPhase {
|
||||
PreDependencyTransformationPhase,
|
||||
FreeDependencyTransformationPhase,
|
||||
DependencyFilteringPhase,
|
||||
PostDependencyTransformationPhase
|
||||
}
|
||||
|
||||
fun interface FragmentConstraint {
|
||||
operator fun invoke(fragment: KotlinGradleFragment): Boolean
|
||||
|
||||
companion object {
|
||||
val unconstrained = FragmentConstraint { true }
|
||||
val isVariant = FragmentConstraint { fragment -> fragment is KotlinModuleVariant }
|
||||
val isNative = FragmentConstraint { fragment -> fragment.containingVariants.run { any() && all { it.platformType == native } } }
|
||||
}
|
||||
}
|
||||
|
||||
@ExternalVariantApi
|
||||
fun registerDependencyResolver(
|
||||
resolver: IdeaKotlinDependencyResolver,
|
||||
constraint: FragmentConstraint,
|
||||
phase: DependencyResolutionPhase,
|
||||
level: DependencyResolutionLevel = DependencyResolutionLevel.Default,
|
||||
)
|
||||
|
||||
@ExternalVariantApi
|
||||
fun registerDependencyTransformer(
|
||||
transformer: IdeaKotlinDependencyTransformer,
|
||||
constraint: FragmentConstraint,
|
||||
phase: DependencyTransformationPhase
|
||||
)
|
||||
|
||||
@ExternalVariantApi
|
||||
fun registerDependencyEffect(
|
||||
effect: IdeaKotlinDependencyEffect,
|
||||
constraint: FragmentConstraint
|
||||
)
|
||||
|
||||
fun buildIdeaKotlinProjectModel(): IdeaKotlinProjectModel
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
internal fun IdeaKotlinProjectModelBuildingContext.toIdeaKotlinProjectModel(extension: KotlinPm20ProjectExtension): IdeaKotlinProjectModel {
|
||||
return IdeaKotlinProjectModelImpl(
|
||||
gradlePluginVersion = project.getKotlinPluginVersion(),
|
||||
coreLibrariesVersion = coreLibrariesVersion,
|
||||
explicitApiModeCliOption = explicitApi?.cliOption,
|
||||
kotlinNativeHome = File(project.konanHome).absoluteFile,
|
||||
modules = modules.map { module -> module.toIdeaKotlinModule() }
|
||||
gradlePluginVersion = extension.project.getKotlinPluginVersion(),
|
||||
coreLibrariesVersion = extension.coreLibrariesVersion,
|
||||
explicitApiModeCliOption = extension.explicitApi?.cliOption,
|
||||
kotlinNativeHome = File(extension.project.konanHome).absoluteFile,
|
||||
modules = extension.modules.map { module -> toIdeaKotlinModule(module) }
|
||||
)
|
||||
}
|
||||
|
||||
infix fun FragmentConstraint.or(
|
||||
other: FragmentConstraint
|
||||
) = FragmentConstraint { fragment ->
|
||||
this@or(fragment) || other(fragment)
|
||||
}
|
||||
|
||||
infix fun FragmentConstraint.and(
|
||||
other: FragmentConstraint
|
||||
): FragmentConstraint = FragmentConstraint { fragment ->
|
||||
this@and(fragment) && other(fragment)
|
||||
}
|
||||
|
||||
operator fun FragmentConstraint.not() = FragmentConstraint { fragment ->
|
||||
this@not(fragment).not()
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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:OptIn(ExternalVariantApi::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentGranularMetadataResolverFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.utils.UnsafeApi
|
||||
|
||||
@OptIn(UnsafeApi::class)
|
||||
internal fun IdeaKotlinProjectModelBuilder.Companion.default(
|
||||
extension: KotlinPm20ProjectExtension
|
||||
) = IdeaKotlinProjectModelBuilderImpl(extension).apply {
|
||||
val fragmentMetadataResolverFactory = FragmentGranularMetadataResolverFactory()
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinSourceDependencyResolver(fragmentMetadataResolverFactory),
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.SourceDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinMetadataBinaryDependencyResolver(fragmentMetadataResolverFactory),
|
||||
constraint = !IdeaKotlinProjectModelBuilder.FragmentConstraint.isVariant,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinOriginalMetadataDependencyResolver(fragmentMetadataResolverFactory),
|
||||
constraint = !IdeaKotlinProjectModelBuilder.FragmentConstraint.isVariant,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinPlatformDependencyResolver(),
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.isVariant,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinNativeStdlibDependencyResolver,
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.isNative,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinNativePlatformDependencyResolver(),
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.isNative,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeaKotlinSourcesAndDocumentationResolver(),
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.PostDependencyResolution,
|
||||
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyTransformer(
|
||||
transformer = IdeaKotlinSinglePlatformStdlibCommonFilter,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyTransformationPhase.DependencyFilteringPhase,
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained
|
||||
)
|
||||
|
||||
registerDependencyTransformer(
|
||||
transformer = IdeaKotlinUnusedSourcesAndDocumentationFilter,
|
||||
phase = IdeaKotlinProjectModelBuilder.DependencyTransformationPhase.DependencyFilteringPhase,
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained
|
||||
)
|
||||
|
||||
registerDependencyEffect(
|
||||
effect = IdeaKotlinDependencyLogger,
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained
|
||||
)
|
||||
|
||||
registerDependencyEffect(
|
||||
effect = IdeaKotlinMissingFileDependencyLogger,
|
||||
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained
|
||||
)
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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:OptIn(ExternalVariantApi::class)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kpm.idea
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilder
|
||||
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.utils.UnsafeApi
|
||||
|
||||
internal class IdeaKotlinProjectModelBuilderImpl @UnsafeApi("Use factory methods instead") constructor(
|
||||
private val extension: KotlinPm20ProjectExtension,
|
||||
) : ToolingModelBuilder, IdeaKotlinProjectModelBuilder {
|
||||
|
||||
private data class RegisteredDependencyResolver(
|
||||
val resolver: IdeaKotlinDependencyResolver,
|
||||
val constraint: FragmentConstraint,
|
||||
val phase: DependencyResolutionPhase,
|
||||
val level: DependencyResolutionLevel,
|
||||
)
|
||||
|
||||
private data class RegisteredDependencyTransformer(
|
||||
val transformer: IdeaKotlinDependencyTransformer,
|
||||
val constraint: FragmentConstraint,
|
||||
val phase: DependencyTransformationPhase
|
||||
)
|
||||
|
||||
private data class RegisteredDependencyEffect(
|
||||
val effect: IdeaKotlinDependencyEffect,
|
||||
val constraint: FragmentConstraint,
|
||||
)
|
||||
|
||||
private val registeredDependencyResolvers = mutableListOf<RegisteredDependencyResolver>()
|
||||
private val registeredDependencyTransformers = mutableListOf<RegisteredDependencyTransformer>()
|
||||
private val registeredDependencyEffects = mutableListOf<RegisteredDependencyEffect>()
|
||||
|
||||
override fun registerDependencyResolver(
|
||||
resolver: IdeaKotlinDependencyResolver,
|
||||
constraint: FragmentConstraint,
|
||||
phase: DependencyResolutionPhase,
|
||||
level: DependencyResolutionLevel
|
||||
) {
|
||||
registeredDependencyResolvers.add(
|
||||
RegisteredDependencyResolver(resolver, constraint, phase, level)
|
||||
)
|
||||
}
|
||||
|
||||
override fun registerDependencyTransformer(
|
||||
transformer: IdeaKotlinDependencyTransformer,
|
||||
constraint: FragmentConstraint,
|
||||
phase: DependencyTransformationPhase
|
||||
) {
|
||||
registeredDependencyTransformers.add(
|
||||
RegisteredDependencyTransformer(transformer, constraint, phase)
|
||||
)
|
||||
}
|
||||
|
||||
override fun registerDependencyEffect(
|
||||
effect: IdeaKotlinDependencyEffect,
|
||||
constraint: FragmentConstraint
|
||||
) {
|
||||
registeredDependencyEffects.add(
|
||||
RegisteredDependencyEffect(effect, constraint)
|
||||
)
|
||||
}
|
||||
|
||||
override fun buildIdeaKotlinProjectModel(): IdeaKotlinProjectModel {
|
||||
return Context().toIdeaKotlinProjectModel(extension)
|
||||
}
|
||||
|
||||
override fun canBuild(modelName: String): Boolean =
|
||||
modelName == IdeaKotlinProjectModel::class.java.name
|
||||
|
||||
override fun buildAll(modelName: String, project: Project): IdeaKotlinProjectModel {
|
||||
check(project === extension.project) { "Expected project ${extension.project.path}, found ${project.path}" }
|
||||
return buildIdeaKotlinProjectModel()
|
||||
}
|
||||
|
||||
private inner class Context : IdeaKotlinProjectModelBuildingContext {
|
||||
override val dependencyResolver = createDependencyResolver()
|
||||
}
|
||||
|
||||
private fun createDependencyResolver(): IdeaKotlinDependencyResolver {
|
||||
return IdeaKotlinDependencyResolver(DependencyResolutionPhase.values().map { phase ->
|
||||
createDependencyResolver(phase)
|
||||
}).withTransformer(createDependencyTransformer())
|
||||
.withEffect(createDependencyEffect())
|
||||
}
|
||||
|
||||
private fun createDependencyResolver(phase: DependencyResolutionPhase) = IdeaKotlinDependencyResolver resolve@{ fragment ->
|
||||
val applicableResolvers = registeredDependencyResolvers
|
||||
.filter { it.phase == phase }
|
||||
.filter { it.constraint(fragment) }
|
||||
.groupBy { it.level }
|
||||
|
||||
/* Find resolvers in the highest resolution level and only consider those */
|
||||
DependencyResolutionLevel.values().reversed().forEach { level ->
|
||||
val resolvers = applicableResolvers[level].orEmpty().map { it.resolver }
|
||||
if (resolvers.isNotEmpty()) {
|
||||
return@resolve IdeaKotlinDependencyResolver(resolvers).resolve(fragment)
|
||||
}
|
||||
}
|
||||
|
||||
/* No resolvers found */
|
||||
emptySet()
|
||||
}
|
||||
|
||||
private fun createDependencyTransformer(): IdeaKotlinDependencyTransformer {
|
||||
return IdeaKotlinDependencyTransformer(DependencyTransformationPhase.values().map { phase ->
|
||||
createDependencyTransformer(phase)
|
||||
})
|
||||
}
|
||||
|
||||
private fun createDependencyTransformer(phase: DependencyTransformationPhase): IdeaKotlinDependencyTransformer {
|
||||
return IdeaKotlinDependencyTransformer { fragment, dependencies ->
|
||||
IdeaKotlinDependencyTransformer(
|
||||
registeredDependencyTransformers
|
||||
.filter { it.phase == phase }
|
||||
.filter { it.constraint(fragment) }
|
||||
.map { it.transformer }
|
||||
).transform(fragment, dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDependencyEffect(): IdeaKotlinDependencyEffect = IdeaKotlinDependencyEffect { fragment, dependencies ->
|
||||
registeredDependencyEffects
|
||||
.filter { it.constraint(fragment) }
|
||||
.forEach { it.effect(fragment, dependencies) }
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.containingVariants
|
||||
|
||||
internal object IdeaKotlinSinglePlatformStdlibCommonFilter : IdeaKotlinDependencyTransformer {
|
||||
private const val stdlibCoordinatesGroup = "org.jetbrains.kotlin"
|
||||
private val stdlibCoordinatesModules = setOf("kotlin-stdlib-common", "kotlin-test-common", "kotlin-test-annotations-common")
|
||||
|
||||
override fun transform(
|
||||
fragment: KotlinGradleFragment,
|
||||
dependencies: Set<IdeaKotlinDependency>
|
||||
): Set<IdeaKotlinDependency> {
|
||||
val platforms = fragment.containingVariants.map { it.platformType }.toSet()
|
||||
if (platforms.size != 1) return dependencies
|
||||
|
||||
return dependencies.filter { dependency ->
|
||||
val binaryDependency = dependency as? IdeaKotlinBinaryDependency ?: return@filter true
|
||||
val coordinates = binaryDependency.coordinates ?: return@filter true
|
||||
coordinates.group != stdlibCoordinatesGroup || coordinates.module !in stdlibCoordinatesModules
|
||||
}.toSet()
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentGranularMetadataResolverFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule.Companion.moduleName
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toModuleDependency
|
||||
|
||||
internal class IdeaKotlinSourceDependencyResolver(
|
||||
private val fragmentGranularMetadataResolverFactory: FragmentGranularMetadataResolverFactory
|
||||
) : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
return fragmentGranularMetadataResolverFactory.getOrCreate(fragment).resolutions
|
||||
.filterIsInstance<ChooseVisibleSourceSets>()
|
||||
.flatMap { resolution -> resolve(resolution) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun resolve(resolution: ChooseVisibleSourceSets): Iterable<IdeaKotlinDependency> {
|
||||
val gradleProjectIdentifier = resolution.dependency.id as? ProjectComponentIdentifier ?: return emptyList()
|
||||
val kotlinModuleIdentifier = resolution.dependency.toModuleDependency().moduleIdentifier
|
||||
return resolution.allVisibleSourceSetNames.map { visibleFragmentName ->
|
||||
IdeaKotlinSourceDependencyImpl(
|
||||
buildId = gradleProjectIdentifier.build.name,
|
||||
projectPath = gradleProjectIdentifier.projectPath,
|
||||
projectName = gradleProjectIdentifier.projectName,
|
||||
kotlinModuleName = kotlinModuleIdentifier.moduleName,
|
||||
kotlinModuleClassifier = kotlinModuleIdentifier.moduleClassifier,
|
||||
kotlinFragmentName = visibleFragmentName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.result.ArtifactResolutionResult
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult
|
||||
import org.gradle.api.component.Artifact
|
||||
import org.gradle.jvm.JvmLibrary
|
||||
import org.gradle.language.base.artifact.SourcesArtifact
|
||||
import org.gradle.language.java.artifact.JavadocArtifact
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleVariant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.resolvableMetadataConfigurationName
|
||||
|
||||
internal class IdeaKotlinSourcesAndDocumentationResolver : IdeaKotlinDependencyResolver {
|
||||
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
|
||||
if (fragment is KotlinGradleVariant) {
|
||||
return resolve(fragment.project, fragment.compileDependenciesConfiguration)
|
||||
}
|
||||
|
||||
val metadataDependencies = fragment.project.configurations.getByName(fragment.containingModule.resolvableMetadataConfigurationName)
|
||||
return resolve(fragment.project, metadataDependencies)
|
||||
}
|
||||
|
||||
private fun resolve(project: Project, configuration: Configuration): Set<IdeaKotlinDependency> {
|
||||
val resolutionResult = project.dependencies.createArtifactResolutionQuery()
|
||||
.forComponents(configuration.incoming.resolutionResult.allComponents.map { it.id })
|
||||
.withArtifacts(JvmLibrary::class.java, SourcesArtifact::class.java, JavadocArtifact::class.java)
|
||||
.execute()
|
||||
|
||||
return resolve(resolutionResult, SourcesArtifact::class.java, IdeaKotlinDependency.SOURCES_BINARY_TYPE) +
|
||||
resolve(resolutionResult, JavadocArtifact::class.java, IdeaKotlinDependency.DOCUMENTATION_BINARY_TYPE)
|
||||
}
|
||||
|
||||
fun resolve(
|
||||
resolutionResult: ArtifactResolutionResult, artifactType: Class<out Artifact>, binaryType: String
|
||||
): Set<IdeaKotlinDependency> {
|
||||
return resolutionResult.resolvedComponents.flatMap { resolved ->
|
||||
resolved.getArtifacts(artifactType)
|
||||
.filterIsInstance<ResolvedArtifactResult>()
|
||||
.mapNotNull { artifact ->
|
||||
val id = artifact.id.componentIdentifier as? ModuleComponentIdentifier ?: return@mapNotNull null
|
||||
IdeaKotlinResolvedBinaryDependencyImpl(
|
||||
coordinates = IdeaKotlinBinaryCoordinatesImpl(group = id.group, module = id.module, version = id.version),
|
||||
binaryType = binaryType,
|
||||
binaryFile = artifact.file
|
||||
)
|
||||
}
|
||||
}.toSet()
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.kpm.idea
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
|
||||
|
||||
internal object IdeaKotlinUnusedSourcesAndDocumentationFilter : IdeaKotlinDependencyTransformer {
|
||||
override fun transform(
|
||||
fragment: KotlinGradleFragment, dependencies: Set<IdeaKotlinDependency>
|
||||
): Set<IdeaKotlinDependency> {
|
||||
val sourcesAndDocumentationDependencies = dependencies
|
||||
.filterIsInstance<IdeaKotlinResolvedBinaryDependency>()
|
||||
.filter { dependency -> dependency.isSourcesType || dependency.isDocumentationType }
|
||||
.toSet()
|
||||
|
||||
val classpathCoordinates = dependencies.filterIsInstance<IdeaKotlinResolvedBinaryDependency>()
|
||||
.filter { dependency -> dependency.isClasspathType }
|
||||
.mapNotNull { it.coordinates }
|
||||
.toSet()
|
||||
|
||||
val unusedSourcesAndDocumentationDependencies = sourcesAndDocumentationDependencies
|
||||
.filter { it.coordinates !in classpathCoordinates }
|
||||
.toSet()
|
||||
|
||||
return dependencies - unusedSourcesAndDocumentationDependencies
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_DEPENDENCY_PROPAGATION
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_DEFAULT_DEPENDENCY
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dukat.ExternalsOutputFormat
|
||||
@@ -437,7 +438,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
get() = booleanProperty(NOWARN_2JS_FLAG) ?: false
|
||||
|
||||
val stdlibDefaultDependency: Boolean
|
||||
get() = booleanProperty("kotlin.stdlib.default.dependency") ?: true
|
||||
get() = booleanProperty(KOTLIN_STDLIB_DEFAULT_DEPENDENCY) ?: true
|
||||
|
||||
val kotlinTestInferJvmVariant: Boolean
|
||||
get() = booleanProperty("kotlin.test.infer.jvm.variant") ?: true
|
||||
@@ -500,6 +501,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
}
|
||||
|
||||
object PropertyNames {
|
||||
const val KOTLIN_STDLIB_DEFAULT_DEPENDENCY = "kotlin.stdlib.default.dependency"
|
||||
const val KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA = "kotlin.mpp.enableGranularSourceSetsMetadata"
|
||||
const val KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION = "kotlin.mpp.enableCInteropCommonization"
|
||||
const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT = "kotlin.internal.mpp.hierarchicalStructureByDefault"
|
||||
@@ -529,6 +531,6 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
?: PropertiesProvider(project) // Fallback if multiple class loaders are involved
|
||||
}
|
||||
|
||||
internal val Project.kotlinPropertiesProvider get() = PropertiesProvider(this)
|
||||
internal val Project.kotlinPropertiesProvider get() = invoke(this)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.refinesClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.withRefinesClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
||||
@@ -200,7 +201,7 @@ internal fun buildProjectStructureMetadata(module: KotlinGradleModule): KotlinPr
|
||||
}.toMap()
|
||||
|
||||
val kotlinFragmentsPerKotlinVariant =
|
||||
module.variants.associate { variant -> variant.name to variant.refinesClosure.map { it.name }.toSet() }
|
||||
module.variants.associate { variant -> variant.name to variant.withRefinesClosure.map { it.name }.toSet() }
|
||||
val fragmentRefinesRelation =
|
||||
module.fragments.associate { it.name to it.directRefinesDependencies.map { it.fragmentName }.toSet() }
|
||||
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ internal class FragmentGranularMetadataResolver(
|
||||
error("unexpected failure in dependency graph resolution for $requestingFragment in $project")
|
||||
|
||||
dependencyGraph as GradleDependencyGraph // refactor the type hierarchy to avoid this downcast? FIXME?
|
||||
val fragmentsToInclude = requestingFragment.refinesClosure
|
||||
val fragmentsToInclude = requestingFragment.withRefinesClosure
|
||||
val requestedDependencies = dependencyGraph.root.dependenciesByFragment.filterKeys { it in fragmentsToInclude }.values.flatten()
|
||||
|
||||
val visited = mutableSetOf<GradleDependencyGraphNode>()
|
||||
|
||||
+8
-6
@@ -15,14 +15,13 @@ import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.default
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.locateOrRegisterBuildIdeaKotlinProjectModelTask
|
||||
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class KotlinPm20GradlePlugin @Inject constructor(
|
||||
@@ -40,7 +39,7 @@ abstract class KotlinPm20GradlePlugin @Inject constructor(
|
||||
registerDefaultVariantFactories(project)
|
||||
setupFragmentsMetadataForKpmModules(project)
|
||||
setupKpmModulesPublication(project)
|
||||
setupToolingModelBuilder()
|
||||
setupToolingModelBuilder(project)
|
||||
}
|
||||
|
||||
private fun createDefaultModules(project: Project) {
|
||||
@@ -82,8 +81,9 @@ abstract class KotlinPm20GradlePlugin @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolingModelBuilder() {
|
||||
toolingModelBuilderRegistry.register(IdeaKotlinProjectModelBuilder())
|
||||
private fun setupToolingModelBuilder(project: Project) {
|
||||
toolingModelBuilderRegistry.register(project.pm20Extension.ideaKotlinProjectModelBuilder)
|
||||
project.locateOrRegisterBuildIdeaKotlinProjectModelTask()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ open class KotlinPm20ProjectExtension(project: Project) : KotlinTopLevelExtensio
|
||||
|
||||
internal val kpmModelContainer = DefaultKpmGradleProjectModelContainer.create(project)
|
||||
|
||||
internal val ideaKotlinProjectModelBuilder by lazy { IdeaKotlinProjectModelBuilder.default(this) }
|
||||
|
||||
val modules: NamedDomainObjectContainer<KotlinGradleModule>
|
||||
get() = project.kpmModules
|
||||
|
||||
|
||||
+6
@@ -49,6 +49,12 @@ fun <T : KotlinGradleFragment> FragmentAttributes(
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : KotlinGradleFragment> AttributeContainer.apply(
|
||||
attributes: KotlinGradleFragmentConfigurationAttributes<T>, fragment: T
|
||||
) {
|
||||
attributes.setAttributes(this, fragment)
|
||||
}
|
||||
|
||||
operator fun <T : KotlinGradleFragment> FragmentAttributes<T>.plus(other: FragmentAttributes<T>): FragmentAttributes<T> {
|
||||
if (this === KotlinGradleFragmentConfigurationAttributes.None) return other
|
||||
if (other === KotlinGradleFragmentConfigurationAttributes.None) return this
|
||||
|
||||
+4
-4
@@ -59,16 +59,16 @@ internal abstract class AbstractKotlinFragmentMetadataCompilationData<T : Kotlin
|
||||
|
||||
override var compileDependencyFiles: FileCollection by project.newProperty {
|
||||
createMetadataDependencyTransformationClasspath(
|
||||
project,
|
||||
resolvableMetadataConfiguration(fragment.containingModule),
|
||||
lazy {
|
||||
project = project,
|
||||
fromFiles = resolvableMetadataConfiguration(fragment.containingModule),
|
||||
parentCompiledMetadataFiles = lazy {
|
||||
fragment.refinesClosure.minus(fragment).map {
|
||||
val compilation = metadataCompilationRegistry.getForFragmentOrNull(it)
|
||||
?: return@map project.files()
|
||||
compilation.output.classesDirs
|
||||
}
|
||||
},
|
||||
resolvedMetadataFiles
|
||||
metadataResolutionProviders = resolvedMetadataFiles
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
@@ -109,7 +110,9 @@ private fun createResolvableMetadataConfigurationForModule(module: KotlinGradleM
|
||||
project.configurations.create(module.resolvableMetadataConfigurationName).apply {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
|
||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
||||
module.fragments.all { fragment ->
|
||||
project.addExtendsFromRelation(name, fragment.apiConfigurationName)
|
||||
project.addExtendsFromRelation(name, fragment.implementationConfigurationName)
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ abstract class KotlinCompileCommon @Inject constructor(
|
||||
is AbstractKotlinFragmentMetadataCompilationData -> {
|
||||
val fragment = compilation.fragment
|
||||
project.files(
|
||||
fragment.refinesClosure.minus(fragment).map {
|
||||
fragment.refinesClosure.map {
|
||||
val compilation = compilation.metadataCompilationRegistry.getForFragmentOrNull(it)
|
||||
?: return@map project.files()
|
||||
compilation.output.classesDirs
|
||||
|
||||
+10
-1
@@ -59,4 +59,13 @@ internal fun File.isParentOf(childCandidate: File, strict: Boolean = false): Boo
|
||||
internal fun File.canonicalPathWithoutExtension(): String =
|
||||
canonicalPath.substringBeforeLast(".")
|
||||
|
||||
internal fun File.listFilesOrEmpty() = (if (exists()) listFiles() else null).orEmpty()
|
||||
internal fun File.listFilesOrEmpty() = (if (exists()) listFiles() else null).orEmpty()
|
||||
|
||||
internal inline fun <T> withTemporaryDirectory(prefix: String, action: (directory: File) -> T): T {
|
||||
val directory = Files.createTempDirectory(prefix).toFile()
|
||||
return try {
|
||||
action(directory)
|
||||
} finally {
|
||||
directory.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
||||
import java.io.File
|
||||
|
||||
public data class KonanDistribution(val root: File)
|
||||
public data class KonanDistribution(val root: File) {
|
||||
public constructor(rootPath: String) : this(File(rootPath))
|
||||
}
|
||||
|
||||
public val KonanDistribution.konanCommonLibraries: File
|
||||
get() = root.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
|
||||
|
||||
Reference in New Issue
Block a user