Support published MPP consumption with PM2.0 projects
This commit is contained in:
+3
@@ -35,6 +35,9 @@ internal fun Project.createKotlinExtension(extensionClass: KClass<out KotlinTopL
|
||||
internal val Project.topLevelExtension: KotlinTopLevelExtension
|
||||
get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinTopLevelExtension
|
||||
|
||||
internal val Project.topLevelExtensionOrNull: KotlinTopLevelExtension?
|
||||
get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinTopLevelExtension?
|
||||
|
||||
internal val Project.kotlinExtensionOrNull: KotlinProjectExtension?
|
||||
get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME) as? KotlinProjectExtension
|
||||
|
||||
|
||||
+3
-2
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.isMain
|
||||
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.*
|
||||
@@ -283,14 +284,14 @@ internal class Kotlin2JsSourceSetProcessor(
|
||||
val baseName = if (kotlinCompilation.isMain()) {
|
||||
project.name
|
||||
} else {
|
||||
"${project.name}_${kotlinCompilation.name}"
|
||||
"${project.name}_${kotlinCompilation.compilationPurpose}"
|
||||
}
|
||||
kotlinTaskInstance.kotlinOptions.freeCompilerArgs += "$MODULE_NAME=${project.klibModuleName(baseName)}"
|
||||
}
|
||||
}
|
||||
|
||||
val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
|
||||
if (kotlinCompilation is KotlinCompilation<*>) {
|
||||
if (kotlinCompilation is KotlinCompilation<*>) { // FIXME support compiler plugins with PM20
|
||||
subpluginEnvironment.addSubpluginOptions(project, kotlinCompilation)
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -86,9 +86,6 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
|
||||
kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
|
||||
|
||||
project.createKotlinExtension(projectExtensionClass).apply {
|
||||
if (this is KotlinPm20ProjectExtension)
|
||||
this.project = project // refactor this code to remove the specific plugin class?
|
||||
|
||||
coreLibrariesVersion = kotlinPluginVersion
|
||||
|
||||
fun kotlinSourceSetContainer(factory: NamedDomainObjectFactory<KotlinSourceSet>) =
|
||||
|
||||
+3
-7
@@ -11,10 +11,6 @@ import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
import javax.xml.transform.OutputKeys
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
|
||||
open class GenerateProjectStructureMetadata : DefaultTask() {
|
||||
@get:Internal
|
||||
@@ -25,14 +21,14 @@ open class GenerateProjectStructureMetadata : DefaultTask() {
|
||||
get() = lazyKotlinProjectStructureMetadata.value
|
||||
|
||||
@get:OutputFile
|
||||
val resultXmlFile: File
|
||||
val resultFile: File
|
||||
get() = project.buildDir.resolve("kotlinProjectStructureMetadata/$MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME")
|
||||
|
||||
@TaskAction
|
||||
fun generateMetadataXml() {
|
||||
resultXmlFile.parentFile.mkdirs()
|
||||
resultFile.parentFile.mkdirs()
|
||||
val resultString = kotlinProjectStructureMetadata.toJson()
|
||||
resultXmlFile.writeText(resultString)
|
||||
resultFile.writeText(resultString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+83
-59
@@ -7,13 +7,15 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ModuleDependency
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.attributes.AttributeContainer
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
@@ -28,45 +30,56 @@ class ProjectStructureMetadataModuleBuilder {
|
||||
private val modulesCache = mutableMapOf<KotlinModuleIdentifier, KotlinModule>()
|
||||
|
||||
private fun buildModuleFromProjectStructureMetadata(
|
||||
moduleIdentifier: KotlinModuleIdentifier,
|
||||
component: ResolvedComponentResult,
|
||||
metadata: KotlinProjectStructureMetadata
|
||||
): KotlinModule =
|
||||
BasicKotlinModule(moduleIdentifier).apply {
|
||||
metadata.sourceSetNamesByVariantName.keys.forEach { variantName ->
|
||||
fragments.add(BasicKotlinModuleVariant(this@apply, variantName))
|
||||
}
|
||||
fun fragment(sourceSetName: String): BasicKotlinModuleFragment {
|
||||
if (fragments.none { it.fragmentName == sourceSetName })
|
||||
fragments.add(BasicKotlinModuleFragment(this@apply, sourceSetName))
|
||||
return fragmentByName(sourceSetName)
|
||||
}
|
||||
metadata.sourceSetNamesByVariantName.forEach { (variantName, sourceSets) ->
|
||||
val variant = fragmentByName(variantName)
|
||||
sourceSets.forEach { sourceSetName ->
|
||||
variant.directRefinesDependencies.add(fragment(sourceSetName))
|
||||
): KotlinModule {
|
||||
return ExternalImportedKotlinModule(
|
||||
BasicKotlinModule(component.toModuleIdentifier()).apply {
|
||||
metadata.sourceSetNamesByVariantName.keys.forEach { variantName ->
|
||||
fragments.add(BasicKotlinModuleVariant(this@apply, variantName))
|
||||
}
|
||||
}
|
||||
metadata.sourceSetModuleDependencies.forEach { (sourceSetName, dependencies) ->
|
||||
val fragment = fragment(sourceSetName)
|
||||
dependencies.forEach { dependency ->
|
||||
fragment.declaredModuleDependencies.add(
|
||||
KotlinModuleDependency(MavenModuleIdentifier(dependency.groupId.orEmpty(), dependency.moduleId, null /* TODO */))
|
||||
)
|
||||
fun fragment(sourceSetName: String): BasicKotlinModuleFragment {
|
||||
if (fragments.none { it.fragmentName == sourceSetName })
|
||||
fragments.add(BasicKotlinModuleFragment(this@apply, sourceSetName))
|
||||
return fragmentByName(sourceSetName)
|
||||
}
|
||||
metadata.sourceSetNamesByVariantName.forEach { (variantName, sourceSets) ->
|
||||
val variant = fragmentByName(variantName)
|
||||
sourceSets.forEach { sourceSetName ->
|
||||
variant.directRefinesDependencies.add(fragment(sourceSetName))
|
||||
}
|
||||
}
|
||||
metadata.sourceSetModuleDependencies.forEach { (sourceSetName, dependencies) ->
|
||||
val fragment = fragment(sourceSetName)
|
||||
dependencies.forEach { dependency ->
|
||||
fragment.declaredModuleDependencies.add(
|
||||
KotlinModuleDependency(
|
||||
MavenModuleIdentifier(
|
||||
dependency.groupId.orEmpty(),
|
||||
dependency.moduleId,
|
||||
null /* TODO */
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metadata.sourceSetsDependsOnRelation.forEach { (depending, dependencies) ->
|
||||
val dependingFragment = fragment(depending)
|
||||
dependencies.forEach { dependency ->
|
||||
dependingFragment.directRefinesDependencies.add(fragment(dependency))
|
||||
metadata.sourceSetsDependsOnRelation.forEach { (depending, dependencies) ->
|
||||
val dependingFragment = fragment(depending)
|
||||
dependencies.forEach { dependency ->
|
||||
dependingFragment.directRefinesDependencies.add(fragment(dependency))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
metadata
|
||||
)
|
||||
}
|
||||
|
||||
fun getModule(moduleIdentifier: KotlinModuleIdentifier, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
|
||||
return modulesCache.getOrPut(moduleIdentifier) {
|
||||
fun getModule(component: ResolvedComponentResult, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
|
||||
val moduleId = component.toModuleIdentifier()
|
||||
return modulesCache.getOrPut(moduleId) {
|
||||
buildModuleFromProjectStructureMetadata(
|
||||
moduleIdentifier,
|
||||
component,
|
||||
projectStructureMetadata
|
||||
)
|
||||
}
|
||||
@@ -107,7 +120,12 @@ private fun detectModules(targets: Iterable<KotlinTarget>, sourceSets: Iterable<
|
||||
|
||||
@Suppress("unused")
|
||||
class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExplicit: Boolean) {
|
||||
private fun getModulesFromPm20Project(project: Project) = project.pm20Extension.modules.toList()
|
||||
|
||||
fun buildModulesFromProject(project: Project): List<KotlinModule> {
|
||||
if (project.topLevelExtension is KotlinPm20ProjectExtension)
|
||||
return getModulesFromPm20Project(project)
|
||||
|
||||
val extension = project.multiplatformExtensionOrNull
|
||||
?: project.kotlinExtension
|
||||
?: return emptyList()
|
||||
@@ -217,15 +235,6 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun <T : Any> attributeString(container: AttributeContainer, attributeKey: Attribute<T>): String {
|
||||
val value = container.getAttribute(attributeKey)
|
||||
return when (value) {
|
||||
is Named -> value.name
|
||||
else -> value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Dependency.toModuleDependency(
|
||||
project: Project
|
||||
): KotlinModuleDependency {
|
||||
@@ -266,21 +275,7 @@ class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver
|
||||
// This implementation can only resolve variants for the current project's KotlinModule
|
||||
require(module.representsProject(project))
|
||||
|
||||
val targets =
|
||||
project.multiplatformExtensionOrNull?.targets ?: listOf((project.kotlinExtension as KotlinSingleTargetExtension).target)
|
||||
|
||||
val compilation =
|
||||
targets.filterIsInstance<AbstractKotlinTarget>()
|
||||
.flatMap { it.kotlinComponents.filterIsInstance<KotlinVariant>() }
|
||||
.flatMap { it.usages }
|
||||
.firstOrNull { it.name == requestingVariant.fragmentName }
|
||||
?.compilation
|
||||
?: targets.asSequence().flatMap { it.compilations.asSequence() }.single {
|
||||
it.defaultSourceSetName == requestingVariant.fragmentName
|
||||
} // TODO: generalize the mapping PM2.0 <-> MPP
|
||||
?: return VariantResolution.Unknown(requestingVariant, dependencyModule)
|
||||
|
||||
val compileClasspath = project.configurations.getByName(compilation.compileDependencyConfigurationName)
|
||||
val compileClasspath = getCompileDependenciesConfigurationForVariant(project, requestingVariant)
|
||||
|
||||
// TODO optimize O(n) search, store the mapping
|
||||
val component = compileClasspath.incoming.resolutionResult.allComponents.find { it.id.matchesModule(dependencyModule) }
|
||||
@@ -288,7 +283,9 @@ class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver
|
||||
|
||||
val dependencyModuleId = ModuleIds.fromComponent(project, component)
|
||||
// FIXME check composite builds, it's likely that resolvedVariantProvider fails on them?
|
||||
val variantName = resolvedVariantProvider.getResolvedVariantName(dependencyModuleId, compileClasspath)
|
||||
val variantName =
|
||||
resolvedVariantProvider.getResolvedVariantName(dependencyModuleId, compileClasspath)
|
||||
?.let(::kotlinVariantNameFromPublishedVariantName)
|
||||
|
||||
val resultVariant = dependencyModule.variants.singleOrNull { it.fragmentName == variantName }
|
||||
|
||||
@@ -297,4 +294,31 @@ class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver
|
||||
else
|
||||
VariantResolution.VariantMatch(requestingVariant, dependencyModule, resultVariant)
|
||||
}
|
||||
|
||||
private fun getCompileDependenciesConfigurationForVariant(project: Project, requestingVariant: KotlinModuleVariant): Configuration =
|
||||
when (project.topLevelExtension) {
|
||||
is KotlinMultiplatformExtension, is KotlinSingleTargetExtension -> {
|
||||
val targets =
|
||||
project.multiplatformExtensionOrNull?.targets ?: listOf((project.kotlinExtension as KotlinSingleTargetExtension).target)
|
||||
|
||||
val compilation =
|
||||
targets.filterIsInstance<AbstractKotlinTarget>()
|
||||
.flatMap { it.kotlinComponents.filterIsInstance<KotlinVariant>() }
|
||||
.flatMap { it.usages }
|
||||
.firstOrNull { it.name == requestingVariant.fragmentName }
|
||||
?.compilation
|
||||
|
||||
?: targets.asSequence().flatMap { it.compilations.asSequence() }.single {
|
||||
it.defaultSourceSetName == requestingVariant.fragmentName
|
||||
} // TODO: generalize the mapping PM2.0 <-> MPP
|
||||
|
||||
?: error("could not find a compilation that produces the variant $requestingVariant in $project")
|
||||
|
||||
project.configurations.getByName(compilation.compileDependencyConfigurationName)
|
||||
}
|
||||
is KotlinPm20ProjectExtension -> {
|
||||
project.configurations.getByName((requestingVariant as KotlinGradleVariant).compileDependencyConfigurationName)
|
||||
}
|
||||
else -> error("could not find the compile dependencies configuration for variant $requestingVariant")
|
||||
}
|
||||
}
|
||||
+69
-34
@@ -13,8 +13,12 @@ import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toModuleIdentifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||
@@ -207,7 +211,7 @@ internal class GranularMetadataTransformation(
|
||||
resolveViaAvailableAt = false // we will process the available-at module as a dependency later in the queue
|
||||
)
|
||||
|
||||
val resolvedToProject: Project? = (mppDependencyMetadataExtractor as? ProjectMppDependencyMetadataExtractor)?.dependencyProject
|
||||
val resolvedToProject: Project? = module.toProjectOrNull(project)
|
||||
|
||||
val projectStructureMetadata = mppDependencyMetadataExtractor?.getProjectStructureMetadata()
|
||||
?: return MetadataDependencyResolution.KeepOriginalDependency(module, resolvedToProject)
|
||||
@@ -254,25 +258,33 @@ internal class GranularMetadataTransformation(
|
||||
transitiveDependenciesToVisit, mppDependencyMetadataExtractor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ChooseVisibleSourceSetsImpl(
|
||||
dependency: ResolvedComponentResult,
|
||||
projectDependency: Project?,
|
||||
projectStructureMetadata: KotlinProjectStructureMetadata,
|
||||
allVisibleSourceSetNames: Set<String>,
|
||||
visibleSourceSetNamesExcludingDependsOn: Set<String>,
|
||||
visibleTransitiveDependencies: Set<ResolvedDependencyResult>,
|
||||
private val metadataExtractor: MppDependencyMetadataExtractor
|
||||
) : MetadataDependencyResolution.ChooseVisibleSourceSets(
|
||||
dependency,
|
||||
projectDependency,
|
||||
projectStructureMetadata,
|
||||
allVisibleSourceSetNames,
|
||||
visibleSourceSetNamesExcludingDependsOn,
|
||||
visibleTransitiveDependencies
|
||||
) {
|
||||
override fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles =
|
||||
metadataExtractor.getExtractableMetadataFiles(visibleSourceSetNamesExcludingDependsOn, baseDir)
|
||||
internal class ChooseVisibleSourceSetsImpl(
|
||||
dependency: ResolvedComponentResult,
|
||||
projectDependency: Project?,
|
||||
projectStructureMetadata: KotlinProjectStructureMetadata,
|
||||
allVisibleSourceSetNames: Set<String>,
|
||||
visibleSourceSetNamesExcludingDependsOn: Set<String>,
|
||||
visibleTransitiveDependencies: Set<ResolvedDependencyResult>,
|
||||
private val metadataExtractor: MppDependencyMetadataExtractor
|
||||
) : MetadataDependencyResolution.ChooseVisibleSourceSets(
|
||||
dependency,
|
||||
projectDependency,
|
||||
projectStructureMetadata,
|
||||
allVisibleSourceSetNames,
|
||||
visibleSourceSetNamesExcludingDependsOn,
|
||||
visibleTransitiveDependencies
|
||||
) {
|
||||
override fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles =
|
||||
metadataExtractor.getExtractableMetadataFiles(visibleSourceSetNamesExcludingDependsOn, baseDir)
|
||||
}
|
||||
|
||||
internal fun ResolvedComponentResult.toProjectOrNull(currentProject: Project): Project? {
|
||||
val identifier = id
|
||||
return when {
|
||||
identifier is ProjectComponentIdentifier && identifier.build.isCurrentBuild -> currentProject.project(identifier.projectPath)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +357,7 @@ internal fun requestedDependencies(
|
||||
}
|
||||
|
||||
|
||||
private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) {
|
||||
internal abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) {
|
||||
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
|
||||
|
||||
abstract fun getExtractableMetadataFiles(
|
||||
@@ -366,9 +378,19 @@ private class ProjectMppDependencyMetadataExtractor(
|
||||
visibleSourceSetNames: Set<String>,
|
||||
baseDir: File
|
||||
): ExtractableMetadataFiles {
|
||||
val result = dependencyProject.multiplatformExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations
|
||||
.filter { it.name in visibleSourceSetNames }
|
||||
.associate { it.defaultSourceSet.name to it.output.classesDirs }
|
||||
val result = when (val projectExtension = dependencyProject.topLevelExtension) {
|
||||
is KotlinMultiplatformExtension -> projectExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations
|
||||
.filter { it.name in visibleSourceSetNames }.associate { it.defaultSourceSet.name to it.output.classesDirs }
|
||||
is KotlinPm20ProjectExtension -> {
|
||||
val moduleId = dependency.toModuleIdentifier()
|
||||
val module = projectExtension.modules.single { it.moduleIdentifier == moduleId }
|
||||
val metadataCompilationRegistry = projectExtension.metadataCompilationRegistryByModuleId.getValue(moduleId)
|
||||
visibleSourceSetNames.associateWith {
|
||||
metadataCompilationRegistry.byFragment(module.fragments.getByName(it)).output.classesDirs
|
||||
}
|
||||
}
|
||||
else -> error("unexpected top-level Kotlin extension $projectExtension")
|
||||
}
|
||||
|
||||
return object : ExtractableMetadataFiles() {
|
||||
override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map<String, FileCollection> = result
|
||||
@@ -502,32 +524,45 @@ private open class JarArtifactMppDependencyMetadataExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMetadataExtractor(
|
||||
internal fun getMetadataExtractor(
|
||||
project: Project,
|
||||
module: ResolvedComponentResult,
|
||||
resolvedComponentResult: ResolvedComponentResult,
|
||||
configuration: Configuration,
|
||||
resolveViaAvailableAt: Boolean
|
||||
): MppDependencyMetadataExtractor? {
|
||||
val resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project)
|
||||
val moduleIdentifier = ModuleIds.fromComponent(project, module)
|
||||
val moduleIdentifier = ModuleIds.fromComponent(project, resolvedComponentResult)
|
||||
// TODO check how this code works with multi-capability resolutions
|
||||
|
||||
var resolvedViaAvailableAt = false
|
||||
|
||||
val metadataArtifact = resolvedMppVariantsProvider.getResolvedArtifactByPlatformModule(
|
||||
moduleIdentifier,
|
||||
configuration
|
||||
) ?: if (resolveViaAvailableAt) resolvedMppVariantsProvider.getHostSpecificMetadataArtifactByRootModule(
|
||||
moduleIdentifier,
|
||||
configuration
|
||||
) else null
|
||||
) ?: if (resolveViaAvailableAt) {
|
||||
resolvedMppVariantsProvider.getHostSpecificMetadataArtifactByRootModule(
|
||||
moduleIdentifier,
|
||||
configuration
|
||||
)?.also {
|
||||
resolvedViaAvailableAt = true
|
||||
}
|
||||
} else null
|
||||
|
||||
val moduleId = module.id
|
||||
val actualComponent = if (resolvedViaAvailableAt) {
|
||||
resolvedComponentResult.dependencies.filterIsInstance<ResolvedDependencyResult>().singleOrNull()?.selected
|
||||
?: resolvedComponentResult
|
||||
} else resolvedComponentResult
|
||||
|
||||
val moduleId = actualComponent.id
|
||||
return when {
|
||||
moduleId is ProjectComponentIdentifier -> when {
|
||||
moduleId.build.isCurrentBuild ->
|
||||
ProjectMppDependencyMetadataExtractor(project, module, project.project(moduleId.projectPath))
|
||||
ProjectMppDependencyMetadataExtractor(project, actualComponent, project.project(moduleId.projectPath))
|
||||
metadataArtifact != null ->
|
||||
IncludedBuildMetadataExtractor(project, module, metadataArtifact)
|
||||
IncludedBuildMetadataExtractor(project, actualComponent, metadataArtifact)
|
||||
else -> null
|
||||
}
|
||||
metadataArtifact != null -> JarArtifactMppDependencyMetadataExtractor(project, module, metadataArtifact)
|
||||
metadataArtifact != null -> JarArtifactMppDependencyMetadataExtractor(project, actualComponent, metadataArtifact)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+77
-4
@@ -10,10 +10,16 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.refinesClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||
@@ -27,10 +33,40 @@ import org.w3c.dom.NodeList
|
||||
import java.io.StringWriter
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
data class ModuleDependencyIdentifier(
|
||||
val groupId: String?,
|
||||
val moduleId: String
|
||||
)
|
||||
// FIXME support module classifiers for PM2.0 or drop this class in favor of KotlinModuleIdentifier
|
||||
open class ModuleDependencyIdentifier(
|
||||
open val groupId: String?,
|
||||
open val moduleId: String
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ModuleDependencyIdentifier) return false
|
||||
|
||||
if (groupId != other.groupId) return false
|
||||
if (moduleId != other.moduleId) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = groupId?.hashCode() ?: 0
|
||||
result = 31 * result + moduleId.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
operator fun component1(): String? = groupId
|
||||
operator fun component2(): String = moduleId
|
||||
}
|
||||
|
||||
class ChangingModuleDependencyIdentifier(
|
||||
val groupIdProvider: () -> String?,
|
||||
val moduleIdProvider: () -> String
|
||||
) : ModuleDependencyIdentifier(groupIdProvider(), moduleIdProvider()) {
|
||||
override val groupId: String?
|
||||
get() = groupIdProvider()
|
||||
override val moduleId: String
|
||||
get() = moduleIdProvider()
|
||||
}
|
||||
|
||||
sealed class SourceSetMetadataLayout(
|
||||
@get:Input
|
||||
@@ -96,6 +132,12 @@ data class KotlinProjectStructureMetadata(
|
||||
}
|
||||
|
||||
internal fun buildKotlinProjectStructureMetadata(project: Project): KotlinProjectStructureMetadata? {
|
||||
val topLevelExtensionOrNull = project.topLevelExtensionOrNull
|
||||
if (topLevelExtensionOrNull is KotlinPm20ProjectExtension) {
|
||||
// FIXME: get module by ID, don't just take the main module
|
||||
return buildProjectStructureMetadata(topLevelExtensionOrNull.main)
|
||||
}
|
||||
|
||||
val sourceSetsWithMetadataCompilations =
|
||||
project.multiplatformExtensionOrNull?.targets?.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME)?.compilations?.associate {
|
||||
it.defaultSourceSet to it
|
||||
@@ -142,6 +184,37 @@ internal fun buildKotlinProjectStructureMetadata(project: Project): KotlinProjec
|
||||
)
|
||||
}
|
||||
|
||||
internal fun buildProjectStructureMetadata(module: KotlinGradleModule): KotlinProjectStructureMetadata {
|
||||
val kotlinVariantToGradleVariantNames = module.variants.associate { it.name to it.gradleVariantNames }
|
||||
|
||||
fun <T> expandVariantKeys(map: Map<String, T>) =
|
||||
map.entries.flatMap { (key, value) ->
|
||||
kotlinVariantToGradleVariantNames[key].orEmpty().plus(key).map { it to value }
|
||||
}.toMap()
|
||||
|
||||
val kotlinFragmentsPerKotlinVariant =
|
||||
module.variants.associate { variant -> variant.name to variant.refinesClosure.map { it.name }.toSet() }
|
||||
val fragmentRefinesRelation =
|
||||
module.fragments.associate { it.name to it.directRefinesDependencies.map { it.name }.toSet() }
|
||||
|
||||
// FIXME: support native implementation-as-api-dependencies
|
||||
val fragmentDependencies =
|
||||
module.fragments.associate { fragment ->
|
||||
fragment.name to fragment.declaredModuleDependencies.map {
|
||||
ModuleIds.lossyFromModuleIdentifier(module.project, it.moduleIdentifier)
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
return KotlinProjectStructureMetadata(
|
||||
expandVariantKeys(kotlinFragmentsPerKotlinVariant),
|
||||
fragmentRefinesRelation,
|
||||
module.fragments.associate { it.name to SourceSetMetadataLayout.KLIB },
|
||||
fragmentDependencies,
|
||||
emptySet(),
|
||||
isPublishedAsRoot = true
|
||||
)
|
||||
}
|
||||
|
||||
internal fun <Serializer> KotlinProjectStructureMetadata.serialize(
|
||||
serializer: Serializer,
|
||||
node: Serializer.(name: String, Serializer.() -> Unit) -> Unit,
|
||||
|
||||
+6
-2
@@ -84,9 +84,13 @@ abstract class KotlinSoftwareComponent(
|
||||
return _usages
|
||||
}
|
||||
|
||||
|
||||
val sourcesArtifacts: Set<PublishArtifact> by lazy {
|
||||
val sourcesJarTask = sourcesJarTask(project, lazy { project.kotlinExtension.sourceSets.toSet() }, null, name.toLowerCase())
|
||||
val sourcesJarTask = sourcesJarTask(
|
||||
project,
|
||||
lazy { project.kotlinExtension.sourceSets.associate { it.name to it.kotlin } },
|
||||
null,
|
||||
name.toLowerCase()
|
||||
)
|
||||
val sourcesJarArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, sourcesJarTask) { sourcesJarArtifact ->
|
||||
sourcesJarArtifact.classifier = "sources"
|
||||
}
|
||||
|
||||
+63
@@ -10,8 +10,18 @@ import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.component.*
|
||||
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
|
||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||
import org.jetbrains.kotlin.project.model.LocalModuleIdentifier
|
||||
import org.jetbrains.kotlin.project.model.MavenModuleIdentifier
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
internal object ModuleIds {
|
||||
fun fromDependency(dependency: Dependency) = when (dependency) {
|
||||
@@ -60,4 +70,57 @@ internal object ModuleIds {
|
||||
|
||||
private fun idOfRootModuleByProjectPath(thisProject: Project, projectPath: String): ModuleDependencyIdentifier =
|
||||
idOfRootModule(thisProject.project(projectPath))
|
||||
|
||||
// FIXME use capabilities to point to auxiliary modules
|
||||
fun lossyFromModuleIdentifier(thisProject: Project, moduleIdentifier: KotlinModuleIdentifier): ModuleDependencyIdentifier {
|
||||
when (moduleIdentifier) {
|
||||
is LocalModuleIdentifier -> {
|
||||
check(moduleIdentifier.buildId == thisProject.currentBuildId().name)
|
||||
val dependencyProject = thisProject.project(moduleIdentifier.projectId)
|
||||
val getRootPublication: () -> MavenPublication? = when (val topLevelExtension = dependencyProject.topLevelExtension) {
|
||||
is KotlinMultiplatformExtension -> {
|
||||
{ topLevelExtension.rootSoftwareComponent.publicationDelegate }
|
||||
}
|
||||
is KotlinPm20ProjectExtension -> {
|
||||
{ topLevelExtension.rootPublication }
|
||||
}
|
||||
else -> error("unexpected top-level extension $topLevelExtension")
|
||||
}
|
||||
val coordinatesProvider = MultiplatformRootPublicationCoordinatesProvider(dependencyProject, getRootPublication)
|
||||
return ChangingModuleDependencyIdentifier({ coordinatesProvider.group }, { coordinatesProvider.name })
|
||||
}
|
||||
is MavenModuleIdentifier -> {
|
||||
return ModuleDependencyIdentifier(moduleIdentifier.group, moduleIdentifier.name)
|
||||
}
|
||||
else -> error("unexpected module identifier $moduleIdentifier")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface PublishedModuleCoordinatesProvider {
|
||||
val group: String
|
||||
val name: String
|
||||
val version: String
|
||||
val capabilities: Iterable<String>
|
||||
}
|
||||
|
||||
class MultiplatformRootPublicationCoordinatesProvider(
|
||||
project: Project,
|
||||
val getPublication: () -> MavenPublication?
|
||||
) : PublishedModuleCoordinatesProvider {
|
||||
|
||||
override val group: String by project.provider {
|
||||
getPublication()?.groupId ?: project.group.toString()
|
||||
}
|
||||
|
||||
override val name: String by project.provider {
|
||||
getPublication()?.artifactId ?: project.group.toString()
|
||||
}
|
||||
|
||||
override val version: String by project.provider {
|
||||
getPublication()?.artifactId ?: project.version.toString()
|
||||
}
|
||||
|
||||
override val capabilities: Iterable<String>
|
||||
get() = emptyList()
|
||||
}
|
||||
+1
-1
@@ -123,7 +123,7 @@ internal class SourceSetVisibilityProvider(
|
||||
}
|
||||
}
|
||||
|
||||
private fun kotlinVariantNameFromPublishedVariantName(resolvedToVariantName: String): String =
|
||||
internal fun kotlinVariantNameFromPublishedVariantName(resolvedToVariantName: String): String =
|
||||
originalVariantNameFromPublished(resolvedToVariantName) ?: resolvedToVariantName
|
||||
|
||||
private fun Project.resolvableConfigurationFromCompilationByScope(
|
||||
|
||||
+12
-2
@@ -11,10 +11,12 @@ import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.TransformKotlinGranularMetadataForFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||
import java.io.File
|
||||
@@ -100,9 +102,9 @@ open class TransformKotlinGranularMetadata
|
||||
.associateWith { it.getExtractableMetadataFiles(outputsDir) }
|
||||
|
||||
@get:Internal
|
||||
internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
||||
internal val filesByResolution: Map<MetadataDependencyResolution, FileCollection>
|
||||
get() = extractableFilesByResolution.mapValues { (_, value) ->
|
||||
project.files(value.getMetadataFilesPerSourceSet(false).values)
|
||||
project.files(value.getMetadataFilesPerSourceSet(false).values).builtBy(this)
|
||||
}
|
||||
|
||||
private val extractableFiles by project.provider { extractableFilesByResolution.values }
|
||||
@@ -117,3 +119,11 @@ open class TransformKotlinGranularMetadata
|
||||
extractableFiles.forEach { it.getMetadataFilesPerSourceSet(doProcessFiles = true) }
|
||||
}
|
||||
}
|
||||
|
||||
internal class SourceSetResolvedMetadataProvider(
|
||||
taskProvider: TaskProvider<out TransformKotlinGranularMetadata>
|
||||
) : ResolvedMetadataFilesProvider {
|
||||
override val buildDependencies: Iterable<TaskProvider<*>> = listOf(taskProvider)
|
||||
override val metadataResolutions: Iterable<MetadataDependencyResolution> by taskProvider.map { it.metadataDependencyResolutions }
|
||||
override val metadataFilesByResolution: Map<MetadataDependencyResolution, FileCollection> by taskProvider.map { it.filesByResolution }
|
||||
}
|
||||
|
||||
+6
@@ -236,6 +236,12 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
|
||||
} else files()
|
||||
}
|
||||
|
||||
override val friendPaths: Iterable<FileCollection>
|
||||
get() = mutableListOf<FileCollection>().also { allCollections ->
|
||||
associateWithTransitiveClosure.forEach { allCollections.add(it.output.classesDirs) }
|
||||
allCollections.add(friendArtifacts)
|
||||
}
|
||||
|
||||
override val moduleName: String
|
||||
get() = KotlinCompilationsModuleGroups.getModuleLeaderCompilation(this).takeIf { it != this }?.ownModuleName() ?: ownModuleName
|
||||
|
||||
|
||||
+28
-11
@@ -10,22 +10,33 @@ import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
import org.jetbrains.kotlin.project.model.*
|
||||
|
||||
class GradleKotlinDependencyGraphResolver(
|
||||
private val project: Project,
|
||||
private val moduleResolver: ModuleDependencyResolver,
|
||||
private val fragmentsResolver: ModuleFragmentsResolver
|
||||
) : KotlinDependencyGraphResolver {
|
||||
internal fun resolvableMetadataConfiguration(
|
||||
module: KotlinGradleModule
|
||||
) = module.project.configurations.getByName(module.resolvableMetadataConfigurationName)
|
||||
|
||||
private val configurationToResolve: Configuration
|
||||
get() = resolvableMetadataConfiguration(
|
||||
internal fun configurationToResolveMetadataDependencies(project: Project, requestingModule: KotlinModule): Configuration =
|
||||
when (project.topLevelExtension) {
|
||||
is KotlinPm20ProjectExtension -> resolvableMetadataConfiguration(requestingModule as KotlinGradleModule)
|
||||
else -> resolvableMetadataConfiguration(
|
||||
project,
|
||||
project.kotlinExtension.sourceSets, // take dependencies from all source sets; TODO introduce consistency scopes?
|
||||
KotlinDependencyScope.compileScopes
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class GradleKotlinDependencyGraphResolver(
|
||||
private val project: Project,
|
||||
private val moduleResolver: ModuleDependencyResolver
|
||||
) : KotlinDependencyGraphResolver {
|
||||
|
||||
private fun configurationToResolve(requestingModule: KotlinModule): Configuration =
|
||||
configurationToResolveMetadataDependencies(project, requestingModule)
|
||||
|
||||
override fun resolveDependencyGraph(requestingModule: KotlinModule): DependencyGraphResolution {
|
||||
if (!requestingModule.representsProject(project))
|
||||
@@ -40,11 +51,17 @@ class GradleKotlinDependencyGraphResolver(
|
||||
return nodeByModuleId.getOrPut(id) {
|
||||
val module = if (isRoot)
|
||||
requestingModule
|
||||
else moduleResolver.resolveDependency(component.toModuleDependency())
|
||||
else moduleResolver.resolveDependency(requestingModule, component.toModuleDependency())
|
||||
.takeIf { component !in excludeLegacyMetadataModulesFromResult }
|
||||
?: buildStubModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
||||
?: buildSyntheticModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
||||
|
||||
val resolvedComponentDependencies = component.dependencies
|
||||
val componentContainingTransitiveDependencies =
|
||||
(module as? ExternalImportedKotlinModule)
|
||||
?.takeIf { it.hasLegacyMetadataModule }
|
||||
?.let { (component.dependencies.singleOrNull() as? ResolvedDependencyResult)?.selected }
|
||||
?: component
|
||||
|
||||
val resolvedComponentDependencies = componentContainingTransitiveDependencies.dependencies
|
||||
.filterIsInstance<ResolvedDependencyResult>()
|
||||
.flatMap { dependency -> dependency.requested.toModuleIdentifiers().map { id -> id to dependency.selected } }
|
||||
.toMap()
|
||||
@@ -61,7 +78,7 @@ class GradleKotlinDependencyGraphResolver(
|
||||
|
||||
return DependencyGraphResolution.DependencyGraph(
|
||||
requestingModule,
|
||||
nodeFromComponent(configurationToResolve.incoming.resolutionResult.root, isRoot = true)
|
||||
nodeFromComponent(configurationToResolve(requestingModule).incoming.resolutionResult.root, isRoot = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
+27
-20
@@ -13,8 +13,7 @@ import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.gradle.api.capabilities.Capability
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ProjectStructureMetadataModuleBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.GradleProjectModuleBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
@@ -27,15 +26,11 @@ class GradleModuleDependencyResolver(
|
||||
private val projectModuleBuilder: GradleProjectModuleBuilder
|
||||
) : ModuleDependencyResolver {
|
||||
|
||||
private val configurationToResolve: Configuration
|
||||
get() = resolvableMetadataConfiguration(
|
||||
project,
|
||||
project.kotlinExtension.sourceSets, // take dependencies from all source sets; TODO introduce consistency scopes?
|
||||
listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE, KotlinDependencyScope.COMPILE_ONLY_SCOPE)
|
||||
)
|
||||
private fun configurationToResolve(requestingModule: KotlinModule): Configuration =
|
||||
configurationToResolveMetadataDependencies(project, requestingModule)
|
||||
|
||||
override fun resolveDependency(moduleDependency: KotlinModuleDependency): KotlinModule? {
|
||||
val allComponents = configurationToResolve.incoming.resolutionResult.allComponents
|
||||
override fun resolveDependency(requestingModule: KotlinModule, moduleDependency: KotlinModuleDependency): KotlinModule? {
|
||||
val allComponents = configurationToResolve(requestingModule).incoming.resolutionResult.allComponents
|
||||
// TODO: optimize O(n) search, store the resolved components with dependency keys?
|
||||
val component = allComponents.find { it.id.matchesModuleDependency(moduleDependency) }
|
||||
val id = component?.id
|
||||
@@ -48,8 +43,9 @@ class GradleModuleDependencyResolver(
|
||||
projectModuleBuilder.buildModulesFromProject(project.project(id.projectPath))
|
||||
.find { it.moduleIdentifier.moduleClassifier == classifier }
|
||||
id is ModuleComponentIdentifier -> {
|
||||
val metadata = getProjectStructureMetadata(project, component, configurationToResolve) ?: return null
|
||||
projectStructureMetadataModuleBuilder.getModule(id.toModuleIdentifier(classifier), metadata)
|
||||
val metadata = getProjectStructureMetadata(project, component, configurationToResolve(requestingModule)) ?: return null
|
||||
val result = projectStructureMetadataModuleBuilder.getModule(component, metadata)
|
||||
result
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
@@ -110,9 +106,9 @@ class FragmentDependenciesDiscovery(
|
||||
val component = resolvedComponentQueue.removeFirst()
|
||||
visited.add(component)
|
||||
|
||||
val module = moduleResolver.resolveDependency(component.toModuleDependency())
|
||||
val module = moduleResolver.resolveDependency(fragment.containingModule, component.toModuleDependency())
|
||||
.takeIf { component !in excludeLegacyMetadataModulesFromResult }
|
||||
?: buildStubModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
||||
?: buildSyntheticModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
||||
|
||||
val transitiveDepsToVisit = when (val fragmentsResolution = fragmentsResolver.getChosenFragments(fragment, module)) {
|
||||
is FragmentResolution.ChosenFragments ->
|
||||
@@ -126,9 +122,7 @@ class FragmentDependenciesDiscovery(
|
||||
|
||||
// With legacy publication scheme, the root MPP module may have a single 'dependency' (available-at) to the metadata module
|
||||
val isMetadataModulePublishedSeparately =
|
||||
getProjectStructureMetadata(project, component, configurationToResolve)
|
||||
?.let { !it.isPublishedAsRoot }
|
||||
?: false
|
||||
(module is ExternalImportedKotlinModule) && module.hasLegacyMetadataModule
|
||||
val singleDependencyComponentOrNull = (component.dependencies.singleOrNull() as? ResolvedDependencyResult)?.selected
|
||||
if (isMetadataModulePublishedSeparately && singleDependencyComponentOrNull != null) {
|
||||
newTransitiveDeps.add(singleDependencyComponentOrNull)
|
||||
@@ -144,9 +138,9 @@ class FragmentDependenciesDiscovery(
|
||||
|
||||
// refactor extract to a separate class/interface
|
||||
// TODO think about multi-variant stub modules for non-Kotlin modules which got more than one chosen variant
|
||||
internal fun buildStubModule(resolvedComponentResult: ResolvedComponentResult, singleVariantName: String): KotlinModule {
|
||||
internal fun buildSyntheticModule(resolvedComponentResult: ResolvedComponentResult, singleVariantName: String): ExternalSyntheticKotlinModule {
|
||||
val moduleDependency = resolvedComponentResult.toModuleDependency()
|
||||
return BasicKotlinModule(moduleDependency.moduleIdentifier).apply {
|
||||
return ExternalSyntheticKotlinModule(BasicKotlinModule(moduleDependency.moduleIdentifier).apply {
|
||||
BasicKotlinModuleVariant(this@apply, singleVariantName).apply {
|
||||
fragments.add(this)
|
||||
this.declaredModuleDependencies.addAll(
|
||||
@@ -155,7 +149,20 @@ internal fun buildStubModule(resolvedComponentResult: ResolvedComponentResult, s
|
||||
.map { it.selected.toModuleDependency() }
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
internal class ExternalSyntheticKotlinModule(private val moduleData: BasicKotlinModule) : KotlinModule by moduleData {
|
||||
override fun toString(): String = "synthetic $moduleData"
|
||||
}
|
||||
|
||||
internal class ExternalImportedKotlinModule(
|
||||
private val moduleData: BasicKotlinModule,
|
||||
val projectStructureMetadata: KotlinProjectStructureMetadata
|
||||
) : KotlinModule by moduleData {
|
||||
val hasLegacyMetadataModule = !projectStructureMetadata.isPublishedAsRoot
|
||||
|
||||
override fun toString(): String = "imported $moduleData"
|
||||
}
|
||||
|
||||
class VariantDependencyDiscovery(
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ChooseVisibleSourceSetsImpl
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.getMetadataExtractor
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.project.model.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
||||
import java.util.ArrayDeque
|
||||
|
||||
internal class FragmentGranularMetadataResolver(
|
||||
val requestingFragment: KotlinGradleFragment,
|
||||
val refinesParentResolvers: Lazy<Iterable<FragmentGranularMetadataResolver>>
|
||||
) {
|
||||
val resolutions: Iterable<MetadataDependencyResolution> by lazy {
|
||||
doResolveMetadataDependencies()
|
||||
}
|
||||
|
||||
private val project: Project
|
||||
get() = requestingFragment.containingModule.project
|
||||
|
||||
private val parentResultsByModuleIdentifier: Map<KotlinModuleIdentifier, List<MetadataDependencyResolution>> by lazy {
|
||||
refinesParentResolvers.value.flatMap { it.resolutions }.groupBy { it.dependency.toModuleIdentifier() }
|
||||
}
|
||||
|
||||
private val metadataModuleBuilder = ProjectStructureMetadataModuleBuilder()
|
||||
private val projectModuleBuilder = GradleProjectModuleBuilder(true)
|
||||
private val moduleResolver = GradleModuleDependencyResolver(project, metadataModuleBuilder, projectModuleBuilder)
|
||||
private val variantResolver = GradleModuleVariantResolver(project)
|
||||
private val fragmentResolver = DefaultModuleFragmentsResolver(variantResolver)
|
||||
private val dependencyGraphResolver = GradleKotlinDependencyGraphResolver(project, moduleResolver)
|
||||
|
||||
private fun doResolveMetadataDependencies(): Iterable<MetadataDependencyResolution> {
|
||||
val configurationToResolve = configurationToResolveMetadataDependencies(project, requestingFragment.containingModule)
|
||||
val resolvedComponentsByModuleId =
|
||||
configurationToResolve.incoming.resolutionResult.allComponents.associateBy { it.toModuleIdentifier() }
|
||||
val resolvedDependenciesByModuleId =
|
||||
configurationToResolve.incoming.resolutionResult.allDependencies.filterIsInstance<ResolvedDependencyResult>()
|
||||
.flatMap { dependency -> dependency.requested.toModuleIdentifiers().map { id -> id to dependency } }.toMap()
|
||||
|
||||
val dependencyGraph = dependencyGraphResolver.resolveDependencyGraph(requestingFragment.containingModule)
|
||||
|
||||
if (dependencyGraph is DependencyGraphResolution.Unknown)
|
||||
error("unexpected failure in dependency graph resolution for $requestingFragment in $project")
|
||||
|
||||
dependencyGraph as DependencyGraphResolution.DependencyGraph
|
||||
val fragmentsToInclude = requestingFragment.refinesClosure
|
||||
val requestedDependencies = dependencyGraph.root.dependenciesByFragment.filterKeys { it in fragmentsToInclude }.values.flatten()
|
||||
|
||||
val visited = mutableSetOf<DependencyGraphNode>()
|
||||
val fragmentResolutionQueue = ArrayDeque<DependencyGraphNode>().apply {
|
||||
addAll(requestedDependencies)
|
||||
}
|
||||
|
||||
val results = mutableSetOf<MetadataDependencyResolution>()
|
||||
|
||||
while (fragmentResolutionQueue.isNotEmpty()) {
|
||||
val dependencyNode = fragmentResolutionQueue.removeFirst()
|
||||
visited.add(dependencyNode)
|
||||
|
||||
val dependencyModule = dependencyNode.module
|
||||
|
||||
val fragmentVisibility = fragmentResolver.getChosenFragments(requestingFragment, dependencyModule)
|
||||
val visibleFragments = (fragmentVisibility as? FragmentResolution.ChosenFragments)?.visibleFragments?.toList().orEmpty()
|
||||
val variantResolutions =
|
||||
(fragmentVisibility as? FragmentResolution.ChosenFragments)?.variantResolutions?.associateBy { it.requestingVariant }
|
||||
|
||||
val visibleTransitiveDependencies =
|
||||
dependencyNode.dependenciesByFragment.filterKeys { it in visibleFragments }.values.flattenTo(mutableSetOf())
|
||||
|
||||
//FIXME host-specific fragments
|
||||
|
||||
fragmentResolutionQueue.addAll(visibleTransitiveDependencies.filter { it !in visited })
|
||||
|
||||
val resolvedComponentResult = resolvedComponentsByModuleId.getValue(dependencyModule.moduleIdentifier)
|
||||
val isResolvedAsProject = resolvedComponentResult.toProjectOrNull(project)
|
||||
val result = when (dependencyModule) {
|
||||
is ExternalSyntheticKotlinModule -> {
|
||||
MetadataDependencyResolution.KeepOriginalDependency(resolvedComponentResult, isResolvedAsProject)
|
||||
}
|
||||
else -> run {
|
||||
val projectStructureMetadata = (dependencyModule as? ExternalImportedKotlinModule)?.projectStructureMetadata
|
||||
?: checkNotNull(getProjectStructureMetadata(project, resolvedComponentResult, configurationToResolve))
|
||||
|
||||
// FIXME host-specific metadata!!!
|
||||
|
||||
val metadataSourceComponent = when {
|
||||
dependencyModule is ExternalImportedKotlinModule && dependencyModule.hasLegacyMetadataModule ->
|
||||
resolvedComponentResult.dependencies.filterIsInstance<ResolvedDependencyResult>().singleOrNull()?.selected
|
||||
?: resolvedComponentResult
|
||||
else -> resolvedComponentResult
|
||||
}
|
||||
|
||||
val parentResolutionsForDependency =
|
||||
parentResultsByModuleIdentifier[metadataSourceComponent.toModuleIdentifier()].orEmpty()
|
||||
val fragmentsVisibleByParents =
|
||||
parentResolutionsForDependency.filterIsInstance<ChooseVisibleSourceSetsImpl>()
|
||||
.flatMapTo(mutableSetOf()) { it.allVisibleSourceSetNames }
|
||||
val visibleFragmentNames = visibleFragments.map { it.fragmentName }.toSet()
|
||||
|
||||
ChooseVisibleSourceSetsImpl(
|
||||
metadataSourceComponent,
|
||||
isResolvedAsProject,
|
||||
projectStructureMetadata,
|
||||
visibleFragmentNames,
|
||||
visibleFragmentNames.minus(fragmentsVisibleByParents),
|
||||
visibleTransitiveDependencies.map { resolvedDependenciesByModuleId.getValue(it.module.moduleIdentifier) }.toSet(),
|
||||
checkNotNull(getMetadataExtractor(project, resolvedComponentResult, configurationToResolve, true))
|
||||
)
|
||||
}
|
||||
}
|
||||
results.add(result)
|
||||
fragmentResolutionQueue.addAll(visibleTransitiveDependencies.filterNot(visited::contains))
|
||||
}
|
||||
|
||||
val resultSourceComponents = results.mapTo(mutableSetOf()) { it.dependency }
|
||||
resolvedComponentsByModuleId.values.minus(resultSourceComponents).forEach {
|
||||
results.add(MetadataDependencyResolution.ExcludeAsUnrequested(it, it.toProjectOrNull(project)))
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
+34
-14
@@ -5,16 +5,35 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.NamedDomainObjectProvider
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.plugins.JavaBasePlugin
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCommonSourceSetProcessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.addSourcesToKotlinCompileTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.buildKotlinProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.createGenerateProjectStructureMetadataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.withType
|
||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleFragment
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
abstract class KotlinPm20GradlePlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
@@ -43,21 +62,17 @@ abstract class KotlinPm20GradlePlugin : Plugin<Project> {
|
||||
modules.create(KotlinGradleModule.TEST_MODULE_NAME)
|
||||
main { makePublic() }
|
||||
}
|
||||
setupMetadataCompilationAndDependencyResolution(project)
|
||||
}
|
||||
|
||||
private fun setupMetadataCompilation() {
|
||||
// TODO dependency transformations in tasks
|
||||
// TODO classpath configurations
|
||||
// TODO compilation data -> tasks
|
||||
// TODO project structure metadata -> metadata artifact
|
||||
// TODO task outputs -> metadata artifact
|
||||
// TODO export the metadata artifact in consumable configurations
|
||||
private fun setupMetadataCompilationAndDependencyResolution(project: Project) {
|
||||
project.pm20Extension.modules.all { module ->
|
||||
configureMetadataResolutionAndBuild(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class KotlinPm20ProjectExtension : KotlinTopLevelExtension() {
|
||||
internal lateinit var project: Project
|
||||
|
||||
open class KotlinPm20ProjectExtension(project: Project) : KotlinTopLevelExtension(project) {
|
||||
val modules: NamedDomainObjectContainer<KotlinGradleModule> by lazy {
|
||||
project.objects.domainObjectContainer(
|
||||
KotlinGradleModule::class.java,
|
||||
@@ -79,6 +94,11 @@ open class KotlinPm20ProjectExtension : KotlinTopLevelExtension() {
|
||||
|
||||
fun main(configure: KotlinGradleModule.() -> Unit = { }) = main.apply(configure)
|
||||
fun test(configure: KotlinGradleModule.() -> Unit = { }) = test.apply(configure)
|
||||
|
||||
internal val metadataCompilationRegistryByModuleId: MutableMap<KotlinModuleIdentifier, MetadataCompilationRegistry> =
|
||||
mutableMapOf()
|
||||
|
||||
internal var rootPublication: MavenPublication? = null
|
||||
}
|
||||
|
||||
val KotlinGradleModule.jvm: KotlinJvmVariant
|
||||
|
||||
+9
@@ -9,9 +9,11 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationOutput
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
|
||||
|
||||
interface KotlinCompilationData<T : KotlinCommonOptions> {
|
||||
val project: Project
|
||||
@@ -34,4 +36,11 @@ interface KotlinCompilationData<T : KotlinCommonOptions> {
|
||||
val ownModuleName: String
|
||||
|
||||
val kotlinOptions: T
|
||||
|
||||
val friendPaths: Iterable<FileCollection>
|
||||
}
|
||||
|
||||
fun KotlinCompilationData<*>.isMain(): Boolean = when (this) {
|
||||
is KotlinCompilation<*> -> isMain()
|
||||
else -> compilationPurpose == KotlinGradleModule.MAIN_MODULE_NAME
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.plugins.BasePluginConvention
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptionsImpl
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationOutput
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinCompilationOutput
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.filterModuleName
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.createTransformedMetadataClasspath
|
||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
|
||||
internal class KotlinFragmentMetadataCompilationData(
|
||||
override val project: Project,
|
||||
val fragment: KotlinGradleFragment,
|
||||
private val module: KotlinGradleModule,
|
||||
private val compileAllTask: TaskProvider<DefaultTask>,
|
||||
val metadataCompilationRegistry: MetadataCompilationRegistry,
|
||||
private val resolvedMetadataFiles: Lazy<Iterable<ResolvedMetadataFilesProvider>>
|
||||
) : KotlinCompilationData<KotlinMultiplatformCommonOptions> {
|
||||
|
||||
override val owner
|
||||
get() = project.pm20Extension
|
||||
|
||||
override val compilationPurpose: String
|
||||
get() = fragment.fragmentName
|
||||
|
||||
override val compilationClassifier: String
|
||||
get() = lowerCamelCaseName(module.name, "metadata")
|
||||
|
||||
override val kotlinSourceDirectoriesByFragmentName: Map<String, SourceDirectorySet>
|
||||
get() = mapOf(fragment.fragmentName to fragment.kotlinSourceRoots)
|
||||
|
||||
override val compileKotlinTaskName: String
|
||||
get() = lowerCamelCaseName("compile", fragment.disambiguateName(""), "KotlinMetadata")
|
||||
|
||||
override val compileAllTaskName: String
|
||||
get() = compileAllTask.name
|
||||
|
||||
override val compileDependencyFiles: FileCollection by project.provider {
|
||||
createTransformedMetadataClasspath(
|
||||
project,
|
||||
resolvableMetadataConfiguration(fragment.containingModule),
|
||||
lazy { fragment.refinesClosure.minus(fragment).map { metadataCompilationRegistry.byFragment(it).output.classesDirs } },
|
||||
resolvedMetadataFiles
|
||||
)
|
||||
}
|
||||
|
||||
override val output: KotlinCompilationOutput = DefaultKotlinCompilationOutput(
|
||||
project,
|
||||
project.provider { project.buildDir.resolve("processedResources/${fragment.disambiguateName("metadata")}") }
|
||||
)
|
||||
|
||||
override val languageSettings: LanguageSettingsBuilder = DefaultLanguageSettingsBuilder() // FIXME apply settings
|
||||
|
||||
override val platformType: KotlinPlatformType
|
||||
get() = KotlinPlatformType.common
|
||||
|
||||
override val moduleName: String
|
||||
get() { // FIXME deduplicate with ownModuleName
|
||||
val baseName = project.convention.findPlugin(BasePluginConvention::class.java)?.archivesBaseName
|
||||
?: project.name
|
||||
val suffix = if (module.moduleClassifier == null) "" else "_${module.moduleClassifier}"
|
||||
return filterModuleName("$baseName$suffix")
|
||||
}
|
||||
|
||||
override val ownModuleName: String
|
||||
get() = moduleName
|
||||
|
||||
override val kotlinOptions: KotlinMultiplatformCommonOptions = // FIXME expose for configuration
|
||||
KotlinMultiplatformCommonOptionsImpl()
|
||||
|
||||
override val friendPaths: Iterable<FileCollection>
|
||||
get() = metadataCompilationRegistry.run {
|
||||
fragment.refinesClosure.minus(fragment)
|
||||
.map {
|
||||
metadataCompilationRegistry.byFragment(it).output.classesDirs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MetadataCompilationRegistry {
|
||||
private val compilationDataPerFragment = mutableMapOf<KotlinGradleFragment, KotlinFragmentMetadataCompilationData>()
|
||||
|
||||
fun register(fragment: KotlinGradleFragment, compilationData: KotlinFragmentMetadataCompilationData) {
|
||||
compilationDataPerFragment[fragment]?.let { error("compilation data for fragment $fragment already registered") }
|
||||
compilationDataPerFragment[fragment] = compilationData
|
||||
withAllCallbacks.forEach { it.invoke(compilationData) }
|
||||
}
|
||||
|
||||
fun byFragment(fragment: KotlinGradleFragment): KotlinFragmentMetadataCompilationData =
|
||||
compilationDataPerFragment.getValue(fragment)
|
||||
|
||||
private val withAllCallbacks = mutableListOf<(KotlinFragmentMetadataCompilationData) -> Unit>()
|
||||
|
||||
fun withAll(action: (KotlinFragmentMetadataCompilationData) -> Unit) {
|
||||
compilationDataPerFragment.forEach { action(it.value) }
|
||||
withAllCallbacks += action
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -16,11 +16,13 @@ import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.toModuleDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleDependency
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleFragment
|
||||
import org.jetbrains.kotlin.project.model.refinesClosure
|
||||
import javax.inject.Inject
|
||||
|
||||
open class KotlinGradleFragment @Inject constructor(
|
||||
@@ -31,6 +33,8 @@ open class KotlinGradleFragment @Inject constructor(
|
||||
override fun getName(): String = fragmentName
|
||||
|
||||
// TODO pull up to KotlinModuleFragment
|
||||
// FIXME apply to compilation
|
||||
// FIXME check for consistency
|
||||
val languageSettings: LanguageSettingsBuilder = DefaultLanguageSettingsBuilder()
|
||||
|
||||
protected val project: Project
|
||||
@@ -80,7 +84,7 @@ open class KotlinGradleFragment @Inject constructor(
|
||||
get() = _directRefinesDependencies.map { it.get() }
|
||||
|
||||
override val declaredModuleDependencies: Iterable<KotlinModuleDependency>
|
||||
get() = TODO("Not yet implemented")
|
||||
get() = project.configurations.getByName(apiConfigurationName).allDependencies.map { it.toModuleDependency(project) } // FIXME impl
|
||||
|
||||
override val kotlinSourceRoots: SourceDirectorySet by lazy {
|
||||
project.objects.sourceDirectorySet(
|
||||
@@ -104,7 +108,12 @@ open class KotlinGradleFragment @Inject constructor(
|
||||
companion object {
|
||||
const val COMMON_FRAGMENT_NAME = "common"
|
||||
}
|
||||
|
||||
override fun toString(): String = "fragment $fragmentName in $containingModule"
|
||||
}
|
||||
|
||||
internal fun KotlinModuleFragment.disambiguateName(simpleName: String) =
|
||||
lowerCamelCaseName(fragmentName, containingModule.moduleIdentifier.moduleClassifier ?: KotlinGradleModule.MAIN_MODULE_NAME, simpleName)
|
||||
|
||||
val KotlinGradleFragment.refinesClosure: Set<KotlinGradleFragment>
|
||||
get() = (this as KotlinModuleFragment).refinesClosure.map { it as KotlinGradleFragment }.toSet()
|
||||
|
||||
+10
-3
@@ -12,9 +12,8 @@ import org.gradle.api.NamedDomainObjectSet
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
import org.jetbrains.kotlin.project.model.KotlinModule
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||
import org.jetbrains.kotlin.project.model.LocalModuleIdentifier
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.project.model.*
|
||||
import javax.inject.Inject
|
||||
|
||||
open class KotlinGradleModule(
|
||||
@@ -87,7 +86,15 @@ open class KotlinGradleModule(
|
||||
|
||||
override val runtimeOnlyConfigurationName: String
|
||||
get() = common.runtimeOnlyConfigurationName
|
||||
|
||||
override fun toString(): String = "$moduleIdentifier (Gradle)"
|
||||
}
|
||||
|
||||
internal val KotlinGradleModule.resolvableMetadataConfigurationName: String
|
||||
get() = lowerCamelCaseName(name, "DependenciesMetadata")
|
||||
|
||||
internal val KotlinGradleModule.isMain
|
||||
get() = moduleIdentifier.moduleClassifier == null
|
||||
|
||||
fun KotlinGradleModule.variantsContainingFragment(fragment: KotlinModuleFragment): Iterable<KotlinGradleVariant> =
|
||||
variants.filter { fragment in it.refinesClosure }
|
||||
+1
-1
@@ -20,7 +20,7 @@ open class KotlinGradleModuleFactory(private val project: Project) : NamedDomain
|
||||
}
|
||||
|
||||
protected open fun registerDefaultCommonFragment(module: KotlinGradleModule) {
|
||||
module.fragments.create(KotlinGradleFragment.COMMON_FRAGMENT_NAME, KotlinGradleFragment::class.java)
|
||||
module.fragments.register(KotlinGradleFragment.COMMON_FRAGMENT_NAME, KotlinGradleFragment::class.java)
|
||||
}
|
||||
|
||||
protected open fun addDefaultRefinementDependencyOnCommon(module: KotlinGradleModule) {
|
||||
|
||||
+7
@@ -58,6 +58,13 @@ class KotlinJvmVariantCompilationData(val variant: KotlinJvmVariant) : KotlinCom
|
||||
override val platformType: KotlinPlatformType
|
||||
get() = variant.platformType
|
||||
|
||||
override val friendPaths: Iterable<FileCollection>
|
||||
// TODO for now, all output classes of the module are considered friends, even those not on the classpath
|
||||
get() {
|
||||
// FIXME support compiling against the JARs
|
||||
return variant.containingModule.project.pm20Extension.modules.flatMap { it.variants.map { it.compilationOutputs.classesDirs } }
|
||||
}
|
||||
|
||||
override val moduleName: String
|
||||
get() = // TODO accurate module names that don't rely on all variants having a main counterpart
|
||||
variant.containingModule.project.pm20Extension.modules
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ExtractableMetadataFiles
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
internal open class TransformKotlinGranularMetadataForFragment
|
||||
@Inject constructor(
|
||||
@get:Internal
|
||||
@field:Transient
|
||||
val fragment: KotlinGradleFragment,
|
||||
//FIXME annotations
|
||||
private val transformation: FragmentGranularMetadataResolver
|
||||
) : DefaultTask() {
|
||||
|
||||
@get:OutputDirectory
|
||||
val outputsDir: File by project.provider {
|
||||
project.buildDir.resolve("kotlinFragmentDependencyMetadata").resolve(fragment.disambiguateName(""))
|
||||
}
|
||||
|
||||
@Suppress("unused") // Gradle input
|
||||
@get:InputFiles
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
internal val allSourceSetsMetadataConfiguration: FileCollection by lazy {
|
||||
project.files(resolvableMetadataConfiguration(fragment.containingModule))
|
||||
}
|
||||
|
||||
@Suppress("unused") // Gradle input
|
||||
@get:Input
|
||||
internal val inputFragmentsAndVariants: Map<String, Iterable<String>> by project.provider {
|
||||
val participatingFragments = fragment.refinesClosure
|
||||
participatingFragments.associateWith { it.containingModule.variantsContainingFragment(it) }
|
||||
.entries.associate { (fragment, variants) ->
|
||||
fragment.name to variants.map { it.fragmentName }.sorted()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused") // Gradle input
|
||||
@get:Input
|
||||
internal val inputVariantDependencies: Map<String, Set<List<String?>>> by project.provider {
|
||||
val participatingFragments = fragment.refinesClosure
|
||||
val participatingCompilations = participatingFragments.flatMap { it.containingModule.variantsContainingFragment(it) }
|
||||
participatingCompilations.associate { variant ->
|
||||
variant.fragmentName to project.configurations.getByName(variant.compileDependencyConfigurationName)
|
||||
.allDependencies.map { listOf(it.group, it.name, it.version) }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
@get:Internal
|
||||
@delegate:Transient // exclude from Gradle instant execution state
|
||||
internal val metadataDependencyResolutions: Iterable<MetadataDependencyResolution> by project.provider {
|
||||
transformation.resolutions
|
||||
}
|
||||
|
||||
private val extractableFilesByResolution: Map<out MetadataDependencyResolution, ExtractableMetadataFiles>
|
||||
get() = metadataDependencyResolutions
|
||||
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||
.associateWith { it.getExtractableMetadataFiles(outputsDir) }
|
||||
|
||||
@get:Internal
|
||||
internal val filesByResolution: Map<MetadataDependencyResolution, FileCollection>
|
||||
get() = extractableFilesByResolution.mapValues { (_, value) ->
|
||||
project.files(value.getMetadataFilesPerSourceSet(false).values).builtBy(this)
|
||||
}
|
||||
|
||||
private val extractableFiles by project.provider { extractableFilesByResolution.values }
|
||||
|
||||
@TaskAction
|
||||
fun transformMetadata() {
|
||||
if (outputsDir.isDirectory) {
|
||||
outputsDir.deleteRecursively()
|
||||
}
|
||||
outputsDir.mkdirs()
|
||||
|
||||
extractableFiles.forEach { it.getMetadataFilesPerSourceSet(doProcessFiles = true) }
|
||||
}
|
||||
}
|
||||
|
||||
internal class FragmentResolvedMetadataProvider(
|
||||
taskProvider: TaskProvider<out TransformKotlinGranularMetadataForFragment>
|
||||
) : ResolvedMetadataFilesProvider {
|
||||
override val buildDependencies: Iterable<TaskProvider<*>> = listOf(taskProvider)
|
||||
override val metadataResolutions: Iterable<MetadataDependencyResolution> by taskProvider.map { it.metadataDependencyResolutions }
|
||||
override val metadataFilesByResolution: Map<MetadataDependencyResolution, FileCollection> by taskProvider.map { it.filesByResolution }
|
||||
}
|
||||
+13
-2
@@ -12,7 +12,7 @@ import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationOutput
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinCompilationOutput
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.fromPlatform
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.publishedConfigurationName
|
||||
import org.jetbrains.kotlin.gradle.tasks.withType
|
||||
import org.jetbrains.kotlin.project.model.KotlinAttributeKey
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleVariant
|
||||
@@ -26,7 +26,7 @@ abstract class KotlinGradleVariant(
|
||||
abstract val platformType: KotlinPlatformType
|
||||
|
||||
override val variantAttributes: Map<KotlinAttributeKey, String>
|
||||
get() = mapOf(KotlinPlatformTypeAttribute to KotlinPlatformTypeAttribute.fromPlatform(platformType)) // TODO user attributes
|
||||
get() = mapOf(KotlinPlatformTypeAttribute to kotlinPlatformTypeAttributeFromPlatform(platformType)) // TODO user attributes
|
||||
|
||||
// TODO generalize with KotlinCompilation?
|
||||
val compileDependencyConfigurationName: String
|
||||
@@ -48,8 +48,15 @@ abstract class KotlinGradleVariant(
|
||||
// TODO generalize exposing outputs: what if a variant has more than one such configurations or none?
|
||||
val apiElementsConfigurationName: String
|
||||
get() = disambiguateName("apiElements")
|
||||
|
||||
open val gradleVariantNames: Set<String>
|
||||
get() = listOf(apiElementsConfigurationName, publishedConfigurationName(apiElementsConfigurationName)).toSet()
|
||||
|
||||
override fun toString(): String = "variant $fragmentName in $containingModule"
|
||||
}
|
||||
|
||||
private fun kotlinPlatformTypeAttributeFromPlatform(platformType: KotlinPlatformType) = platformType.name
|
||||
|
||||
// TODO: rewrite with the artifacts API
|
||||
internal val KotlinGradleVariant.defaultSourceArtifactTaskName: String
|
||||
get() = disambiguateName("sourcesJar")
|
||||
@@ -71,4 +78,8 @@ abstract class KotlinGradleVariantWithRuntimeDependencies(
|
||||
// TODO generalize exposing outputs: what if a variant has more than one such configurations or none?
|
||||
val runtimeElementsConfigurationName: String
|
||||
get() = disambiguateName("runtimeElements")
|
||||
|
||||
override val gradleVariantNames: Set<String>
|
||||
get() = super.gradleVariantNames +
|
||||
listOf(runtimeElementsConfigurationName, publishedConfigurationName(runtimeElementsConfigurationName))
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCommonSourceSetProcessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.addSourcesToKotlinCompileTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.buildKotlinProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.disambiguateName
|
||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.createGenerateProjectStructureMetadataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.withType
|
||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleFragment
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
internal fun configureMetadataResolutionAndBuild(module: KotlinGradleModule) {
|
||||
val project = module.project
|
||||
createResolvableMetadataConfigurationForModule(module)
|
||||
|
||||
val metadataCompilationRegistry = MetadataCompilationRegistry()
|
||||
project.pm20Extension.metadataCompilationRegistryByModuleId[module.moduleIdentifier] =
|
||||
metadataCompilationRegistry
|
||||
|
||||
configureMetadataCompilationsAndCreateRegistry(module, metadataCompilationRegistry)
|
||||
|
||||
configureMetadataJarTask(module, metadataCompilationRegistry)
|
||||
generateAndExportProjectStructureMetadata(module)
|
||||
}
|
||||
|
||||
private fun generateAndExportProjectStructureMetadata(
|
||||
module: KotlinGradleModule
|
||||
) {
|
||||
val project = module.project
|
||||
val projectStructureMetadata = project.createGenerateProjectStructureMetadataTask(module.moduleClassifier)
|
||||
project.tasks.withType<Jar>().named(metadataJarName(module)).configure { jar ->
|
||||
jar.from(projectStructureMetadata.map { it.resultFile }) { spec ->
|
||||
spec.into("META-INF")
|
||||
.rename { MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME }
|
||||
}
|
||||
}
|
||||
GlobalProjectStructureMetadataStorage.registerProjectStructureMetadata(project) {
|
||||
checkNotNull(buildKotlinProjectStructureMetadata(project))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createResolvableMetadataConfigurationForModule(module: KotlinGradleModule) {
|
||||
val project = module.project
|
||||
project.configurations.create(module.resolvableMetadataConfigurationName).apply {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
|
||||
module.fragments.all { fragment ->
|
||||
project.addExtendsFromRelation(name, fragment.apiConfigurationName)
|
||||
project.addExtendsFromRelation(name, fragment.implementationConfigurationName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureMetadataCompilationsAndCreateRegistry(
|
||||
module: KotlinGradleModule,
|
||||
metadataCompilationRegistry: MetadataCompilationRegistry
|
||||
) {
|
||||
val project = module.project
|
||||
val metadataResolutionByFragment = mutableMapOf<KotlinGradleFragment, FragmentGranularMetadataResolver>()
|
||||
module.fragments.all { fragment ->
|
||||
val transformation = FragmentGranularMetadataResolver(fragment, lazy {
|
||||
fragment.refinesClosure.minus(fragment).map {
|
||||
metadataResolutionByFragment.getValue(it)
|
||||
}
|
||||
})
|
||||
metadataResolutionByFragment[fragment] = transformation
|
||||
createExtractMetadataTask(project, fragment, transformation)
|
||||
}
|
||||
val compileAllTask = project.registerTask<DefaultTask>(lowerCamelCaseName(module.moduleClassifier, "metadataClasses"))
|
||||
module.fragments.all { fragment ->
|
||||
createMetadataCompilation(fragment, compileAllTask, metadataCompilationRegistry)
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureMetadataJarTask(
|
||||
module: KotlinGradleModule,
|
||||
registry: MetadataCompilationRegistry
|
||||
) {
|
||||
val project = module.project
|
||||
val allMetadataJar = project.registerTask<Jar>(metadataJarName(module)) { task ->
|
||||
task.archiveAppendix.set("metadata")
|
||||
task.from()
|
||||
}
|
||||
registry.withAll { compilationData ->
|
||||
allMetadataJar.configure { jar ->
|
||||
jar.from(compilationData.output.allOutputs) { spec ->
|
||||
spec.into(compilationData.fragment.fragmentName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun metadataJarName(module: KotlinGradleModule) =
|
||||
lowerCamelCaseName(module.moduleClassifier, "metadataJar")
|
||||
|
||||
private fun createMetadataCompilation(
|
||||
fragment: KotlinGradleFragment,
|
||||
compileAllTask: TaskProvider<DefaultTask>,
|
||||
metadataCompilationRegistry: MetadataCompilationRegistry
|
||||
): KotlinCompilationData<out KotlinMultiplatformCommonOptions> {
|
||||
val module = fragment.containingModule
|
||||
val project = module.project
|
||||
|
||||
val metadataCompilationData =
|
||||
KotlinFragmentMetadataCompilationData(
|
||||
project,
|
||||
fragment,
|
||||
module,
|
||||
compileAllTask,
|
||||
metadataCompilationRegistry,
|
||||
lazy {
|
||||
fragment.refinesClosure.map {
|
||||
FragmentResolvedMetadataProvider(
|
||||
project.tasks.withType<TransformKotlinGranularMetadataForFragment>()
|
||||
.named(transformFragmentMetadataTaskName(it))
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
metadataCompilationRegistry.register(fragment, metadataCompilationData)
|
||||
|
||||
addSourcesToKotlinCompileTask(
|
||||
project,
|
||||
metadataCompilationData.compileKotlinTaskName,
|
||||
/*FIXME*/ emptyList(),
|
||||
{ fragment.kotlinSourceRoots },
|
||||
lazyOf(true)
|
||||
)
|
||||
|
||||
KotlinCommonSourceSetProcessor(
|
||||
metadataCompilationData,
|
||||
KotlinTasksProvider(),
|
||||
project.getKotlinPluginVersion() ?: "undefined"
|
||||
).run()
|
||||
|
||||
return metadataCompilationData
|
||||
}
|
||||
|
||||
private fun createExtractMetadataTask(
|
||||
project: Project,
|
||||
fragment: KotlinGradleFragment,
|
||||
transformation: FragmentGranularMetadataResolver
|
||||
) {
|
||||
project.tasks.register(
|
||||
transformFragmentMetadataTaskName(fragment),
|
||||
TransformKotlinGranularMetadataForFragment::class.java,
|
||||
fragment,
|
||||
transformation
|
||||
).configure { task ->
|
||||
task.dependsOn(Callable {
|
||||
fragment.refinesClosure.mapNotNull { refined ->
|
||||
if (refined !== fragment)
|
||||
project.tasks.named(transformFragmentMetadataTaskName(refined))
|
||||
else null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use this function once more than one platform is supported
|
||||
private fun disableMetadataCompilationIfNotYetSupported(
|
||||
metadataCompilationData: KotlinFragmentMetadataCompilationData
|
||||
) {
|
||||
val fragment = metadataCompilationData.fragment
|
||||
val platforms = fragment.containingModule.variantsContainingFragment(fragment).map { it.platformType }.toSet()
|
||||
if (platforms != setOf(KotlinPlatformType.native) && platforms.size == 1
|
||||
|| platforms == setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm)
|
||||
) {
|
||||
fragment.containingModule.project.tasks.named(metadataCompilationData.compileKotlinTaskName).configure {
|
||||
it.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformFragmentMetadataTaskName(fragment: KotlinModuleFragment) =
|
||||
lowerCamelCaseName("resolve", fragment.disambiguateName("Metadata"))
|
||||
+4
-22
@@ -5,36 +5,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
||||
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.plugins.BasePluginConvention
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
|
||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.addSourcesToKotlinCompileTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.project.model.*
|
||||
import org.jetbrains.kotlin.project.model.refinesClosure
|
||||
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
//region Fragment
|
||||
|
||||
//endregion Fragment
|
||||
|
||||
//region Variant
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
open class KotlinJvmVariantFactory(module: KotlinGradleModule) :
|
||||
AbstractKotlinGradleVariantWithRuntimeDependenciesFactory<KotlinJvmVariant>(module) {
|
||||
override fun instantiateFragment(name: String) = KotlinJvmVariant(module, name)
|
||||
|
||||
+83
-89
@@ -15,13 +15,13 @@ import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.*
|
||||
@@ -38,7 +38,8 @@ internal const val ALL_COMPILE_METADATA_CONFIGURATION_NAME = "allSourceSetsCompi
|
||||
internal const val ALL_RUNTIME_METADATA_CONFIGURATION_NAME = "allSourceSetsRuntimeDependenciesMetadata"
|
||||
|
||||
internal val Project.isKotlinGranularMetadataEnabled: Boolean
|
||||
get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true
|
||||
get() = project.topLevelExtension is KotlinPm20ProjectExtension ||
|
||||
PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true
|
||||
|
||||
internal val Project.isCompatibilityMetadataVariantEnabled: Boolean
|
||||
get() = PropertiesProvider(this).enableCompatibilityMetadataVariant == true
|
||||
@@ -231,7 +232,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
||||
val generateMetadata = project.createGenerateProjectStructureMetadataTask()
|
||||
|
||||
allMetadataJar.configure {
|
||||
it.from(generateMetadata.map { it.resultXmlFile }) { spec ->
|
||||
it.from(generateMetadata.map { it.resultFile }) { spec ->
|
||||
spec.into("META-INF").rename { MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME }
|
||||
}
|
||||
}
|
||||
@@ -437,91 +438,28 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
||||
): FileCollection {
|
||||
val project = compilation.target.project
|
||||
|
||||
// Adjust metadata compilation to support source set hierarchies, i.e. use both the outputs of dependsOn source set compilation
|
||||
// and their dependencies metadata transformed for compilation:
|
||||
return project.files(
|
||||
project.provider {
|
||||
val sourceSet = compilation.defaultSourceSet
|
||||
val sourceSet = compilation.defaultSourceSet
|
||||
|
||||
val transformationTaskHolders = sourceSet.withAllDependsOnSourceSets().mapNotNull { hierarchySourceSet ->
|
||||
project.locateTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(hierarchySourceSet.name))
|
||||
}
|
||||
|
||||
val allResolutionsByComponentId: Map<ComponentIdentifier, List<MetadataDependencyResolution>> =
|
||||
mutableMapOf<ComponentIdentifier, MutableList<MetadataDependencyResolution>>().apply {
|
||||
transformationTaskHolders.forEach {
|
||||
val resolutions = it.get().metadataDependencyResolutions
|
||||
resolutions.forEach { resolution ->
|
||||
getOrPut(resolution.dependency.id) { mutableListOf() }.add(resolution)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val transformedFilesByResolution: Map<MetadataDependencyResolution, FileCollection> =
|
||||
transformationTaskHolders.flatMap { it.get().filesByResolution.toList() }.toMap()
|
||||
|
||||
val dependsOnCompilationOutputs = sourceSet.resolveAllDependsOnSourceSets().mapNotNull { hierarchySourceSet ->
|
||||
val dependencyCompilation = project.getMetadataCompilationForSourceSet(hierarchySourceSet)
|
||||
dependencyCompilation?.output?.classesDirs
|
||||
}
|
||||
|
||||
val artifactView = fromFiles.incoming.artifactView { view ->
|
||||
view.componentFilter { id ->
|
||||
allResolutionsByComponentId[id].let { resolutions ->
|
||||
resolutions == null || resolutions.any { it !is MetadataDependencyResolution.ExcludeAsUnrequested }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutableSetOf<Any /* File | FileCollection */>().apply {
|
||||
addAll(dependsOnCompilationOutputs)
|
||||
artifactView.artifacts.forEach { artifact ->
|
||||
val resolutions = allResolutionsByComponentId[artifact.id.componentIdentifier]
|
||||
if (resolutions == null) {
|
||||
add(artifact.file)
|
||||
} else {
|
||||
val chooseVisibleSourceSets =
|
||||
resolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||
|
||||
if (chooseVisibleSourceSets.isNotEmpty()) {
|
||||
// Wrap the list into a FileCollection, as some older Gradle version failed to resolve the classpath
|
||||
add(project.files(chooseVisibleSourceSets.map { transformedFilesByResolution.getValue(it) }))
|
||||
} else if (resolutions.any { it is MetadataDependencyResolution.KeepOriginalDependency }) {
|
||||
add(artifact.file)
|
||||
} // else: all dependency transformations exclude this dependency as unrequested; don't add any files
|
||||
}
|
||||
}
|
||||
|
||||
// Add a build dependency on the granular metadata transformations of the dependency source sets
|
||||
add(project.files().builtBy(transformationTaskHolders))
|
||||
}
|
||||
val dependsOnCompilationOutputs = lazy {
|
||||
sourceSet.withAllDependsOnSourceSets().mapNotNull { hierarchySourceSet ->
|
||||
val dependencyCompilation = project.getMetadataCompilationForSourceSet(hierarchySourceSet)
|
||||
dependencyCompilation?.output?.classesDirs.takeIf { hierarchySourceSet != sourceSet }
|
||||
}
|
||||
)
|
||||
/*
|
||||
val transformedFilesByOriginalFiles = project.provider {
|
||||
transformationTaskHolders
|
||||
.flatMap { it.get().filesByOriginalFiles.toList() }
|
||||
.groupBy({ it.first }, valueTransform = { it.second })
|
||||
}
|
||||
|
||||
val builtBySet = mutableSetOf<FileCollection>()
|
||||
val resultFiles = project.files(Callable {
|
||||
val originalFiles = fromFiles.toSet()
|
||||
val filesToAdd = mutableSetOf<File>()
|
||||
val filesToExclude = mutableSetOf<File>()
|
||||
|
||||
transformedFilesByOriginalFiles.get().forEach { (original, replacement) ->
|
||||
if (original.all { it in originalFiles }) {
|
||||
builtBySet.addAll(replacement)
|
||||
filesToAdd += replacement.flatMap { it.files }
|
||||
filesToExclude += original
|
||||
}
|
||||
val resolvedMetadataFilesProviders = lazy {
|
||||
val transformationTaskHolders = sourceSet.withAllDependsOnSourceSets().mapNotNull { hierarchySourceSet ->
|
||||
project.locateTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(hierarchySourceSet.name))
|
||||
}
|
||||
transformationTaskHolders.map { SourceSetResolvedMetadataProvider(it) }
|
||||
}
|
||||
|
||||
originalFiles - filesToExclude + filesToAdd
|
||||
})
|
||||
return resultFiles.builtBy(builtBySet)
|
||||
*/
|
||||
return createTransformedMetadataClasspath(
|
||||
project,
|
||||
fromFiles,
|
||||
dependsOnCompilationOutputs,
|
||||
resolvedMetadataFilesProviders
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCommonMainElementsConfiguration(target: KotlinMetadataTarget) {
|
||||
@@ -545,11 +483,69 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
||||
project.artifacts.add(name, project.tasks.getByName(target.legacyArtifactsTaskName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createGenerateProjectStructureMetadataTask(): TaskProvider<GenerateProjectStructureMetadata> =
|
||||
project.registerTask("generateProjectStructureMetadata") { task ->
|
||||
task.lazyKotlinProjectStructureMetadata = lazy { checkNotNull(buildKotlinProjectStructureMetadata(project)) }
|
||||
internal fun Project.createGenerateProjectStructureMetadataTask(moduleClassifier: String? = null): TaskProvider<GenerateProjectStructureMetadata> =
|
||||
project.registerTask(lowerCamelCaseName("generate", moduleClassifier, "ProjectStructureMetadata")) { task ->
|
||||
task.lazyKotlinProjectStructureMetadata = lazy { checkNotNull(buildKotlinProjectStructureMetadata(project)) }
|
||||
}
|
||||
|
||||
internal interface ResolvedMetadataFilesProvider {
|
||||
val buildDependencies: Iterable<TaskProvider<*>>
|
||||
val metadataResolutions: Iterable<MetadataDependencyResolution>
|
||||
val metadataFilesByResolution: Map<MetadataDependencyResolution, FileCollection>
|
||||
}
|
||||
|
||||
internal fun createTransformedMetadataClasspath(
|
||||
project: Project,
|
||||
fromFiles: Configuration,
|
||||
parentCompiledMetadataFiles: Lazy<Iterable<FileCollection>>,
|
||||
metadataResolutionProviders: Lazy<Iterable<ResolvedMetadataFilesProvider>>
|
||||
): FileCollection {
|
||||
return project.files(
|
||||
project.provider {
|
||||
val allResolutionsByComponentId: Map<ComponentIdentifier, List<MetadataDependencyResolution>> =
|
||||
mutableMapOf<ComponentIdentifier, MutableList<MetadataDependencyResolution>>().apply {
|
||||
metadataResolutionProviders.value.forEach {
|
||||
it.metadataResolutions.forEach { resolution ->
|
||||
getOrPut(resolution.dependency.id) { mutableListOf() }.add(resolution)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val transformedFilesByResolution: Map<MetadataDependencyResolution, FileCollection> =
|
||||
metadataResolutionProviders.value.flatMap { it.metadataFilesByResolution.toList() }.toMap()
|
||||
|
||||
val artifactView = fromFiles.incoming.artifactView { view ->
|
||||
view.componentFilter { id ->
|
||||
allResolutionsByComponentId[id].let { resolutions ->
|
||||
resolutions == null || resolutions.any { it !is MetadataDependencyResolution.ExcludeAsUnrequested }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutableSetOf<Any /* File | FileCollection */>().apply {
|
||||
addAll(metadataResolutionProviders.value.map { project.files().builtBy(it.buildDependencies) })
|
||||
addAll(parentCompiledMetadataFiles.value)
|
||||
artifactView.artifacts.forEach { artifact ->
|
||||
val resolutions = allResolutionsByComponentId[artifact.id.componentIdentifier]
|
||||
if (resolutions == null) {
|
||||
add(artifact.file)
|
||||
} else {
|
||||
val chooseVisibleSourceSets =
|
||||
resolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||
|
||||
if (chooseVisibleSourceSets.isNotEmpty()) {
|
||||
// Wrap the list into a FileCollection, as some older Gradle version failed to resolve the classpath
|
||||
add(project.files(chooseVisibleSourceSets.map { transformedFilesByResolution.getValue(it) }))
|
||||
} else if (resolutions.any { it is MetadataDependencyResolution.KeepOriginalDependency }) {
|
||||
add(artifact.file)
|
||||
} // else: all dependency transformations exclude this dependency as unrequested; don't add any files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
internal fun isSharedNativeSourceSet(project: Project, sourceSet: KotlinSourceSet): Boolean {
|
||||
@@ -560,9 +556,7 @@ internal fun isSharedNativeSourceSet(project: Project, sourceSet: KotlinSourceSe
|
||||
}
|
||||
|
||||
internal fun dependsOnClosureWithInterCompilationDependencies(project: Project, sourceSet: KotlinSourceSet): Set<KotlinSourceSet> =
|
||||
sourceSet.getSourceSetHierarchy().toMutableSet().apply {
|
||||
/** exclude self from the results of [getSourceSetHierarchy] */
|
||||
remove(sourceSet)
|
||||
sourceSet.resolveAllDependsOnSourceSets().toMutableSet().apply {
|
||||
addAll(getVisibleSourceSetsFromAssociateCompilations(project, sourceSet))
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -34,6 +34,8 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCommonCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinFragmentMetadataCompilationData
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.refinesClosure
|
||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets
|
||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||
@@ -60,7 +62,9 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
|
||||
|
||||
args.moduleName = this@KotlinCompileCommon.moduleName
|
||||
|
||||
if ((taskData.compilation as? KotlinCommonCompilation)?.isKlibCompilation == true) {
|
||||
if ((taskData.compilation as? KotlinCommonCompilation)?.isKlibCompilation == true ||
|
||||
taskData.compilation is KotlinFragmentMetadataCompilationData
|
||||
) {
|
||||
args.expectActualLinker = true
|
||||
}
|
||||
|
||||
@@ -97,6 +101,14 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
|
||||
val defaultKotlinSourceSet: KotlinSourceSet = compilation.defaultSourceSet
|
||||
outputPathsFromMetadataCompilationsOf(defaultKotlinSourceSet.resolveAllDependsOnSourceSets())
|
||||
}
|
||||
is KotlinFragmentMetadataCompilationData -> {
|
||||
val fragment = compilation.fragment
|
||||
project.files(
|
||||
fragment.refinesClosure.minus(fragment).map {
|
||||
compilation.metadataCompilationRegistry.byFragment(it).output.classesDirs
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> error("unexpected compilation type") // FIXME support PM20 variant
|
||||
}
|
||||
}
|
||||
|
||||
+1
-9
@@ -307,15 +307,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
||||
|
||||
@get:Internal // takes part in the compiler arguments
|
||||
val friendPaths: FileCollection = project.files(
|
||||
project.provider {
|
||||
taskData.compilation.run {
|
||||
if (this !is AbstractKotlinCompilation<*>) return@run project.files() // FIXME support PM20
|
||||
mutableListOf<FileCollection>().also { allCollections ->
|
||||
associateWithTransitiveClosure.forEach { allCollections.add(it.output.classesDirs) }
|
||||
allCollections.add(friendArtifacts)
|
||||
}
|
||||
}
|
||||
}
|
||||
project.provider { taskData.compilation.friendPaths }
|
||||
)
|
||||
|
||||
private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
|
||||
|
||||
@@ -9,7 +9,7 @@ package org.jetbrains.kotlin.project.model
|
||||
// TODO think about state management: unresolved -> (known dependency graph?) ... -> completely resolved
|
||||
// it seems to be important to learn whether or not the model is final
|
||||
interface ModuleDependencyResolver {
|
||||
fun resolveDependency(moduleDependency: KotlinModuleDependency): KotlinModule?
|
||||
fun resolveDependency(requestingModule: KotlinModule, moduleDependency: KotlinModuleDependency): KotlinModule?
|
||||
}
|
||||
|
||||
// TODO merge with ModuleDependencyResolver?
|
||||
@@ -34,4 +34,6 @@ sealed class DependencyGraphResolution(val requestingModule: KotlinModule) {
|
||||
class DependencyGraphNode(
|
||||
val module: KotlinModule,
|
||||
val dependenciesByFragment: Map<KotlinModuleFragment, Iterable<DependencyGraphNode>>
|
||||
)
|
||||
) {
|
||||
override fun toString(): String = "node ${module}"
|
||||
}
|
||||
@@ -17,6 +17,7 @@ open class KotlinAttributeKey(
|
||||
|
||||
object KotlinPlatformTypeAttribute : KotlinAttributeKey("org.jetbrains.kotlin.platform.type") {
|
||||
const val JVM = "jvm"
|
||||
const val ANDROID_JVM = "androidJvm"
|
||||
const val JS = "js"
|
||||
const val NATIVE = "native"
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ interface KotlinModule {
|
||||
// TODO: isSynthetic?
|
||||
}
|
||||
|
||||
class BasicKotlinModule(
|
||||
open class BasicKotlinModule(
|
||||
override val moduleIdentifier: KotlinModuleIdentifier
|
||||
) : KotlinModule {
|
||||
override val fragments = mutableListOf<BasicKotlinModuleFragment>()
|
||||
|
||||
Reference in New Issue
Block a user