Skip to content

Commit 71ddc3d

Browse files
antohabySpace Team
authored and
Space Team
committed
[Gradle] Pass actual root module publication coordinates to GMDT
So it matches with PSM content. To illustrate consider two projects App and Lib. App depends on Lib. When lib has configured Maven Publication with custom GAV coordinates. They correctly appears in its PSM file this happens in [ExportRootModuleCoordinates]. However, when another project (App) resolves dependency to Lib it will get its default gav coordinates. And this will cause inconsistencies during Metadata Dependencies Transformations because Lib's PSM and resolved graph will have different GAVs. To solve this it is necessary to use correct root module GAV. ^KT-73620 Verification Pending
1 parent f7150fe commit 71ddc3d

File tree

6 files changed

+120
-5
lines changed

6 files changed

+120
-5
lines changed

libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppMetadataResolutionIT.kt

+79
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44
*/
55
package org.jetbrains.kotlin.gradle.mpp
66

7+
import org.gradle.api.publish.PublishingExtension
8+
import org.gradle.api.publish.maven.MavenPublication
79
import org.gradle.util.GradleVersion
10+
import org.jetbrains.kotlin.gradle.plugin.mpp.KmpIsolatedProjectsSupport
811
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
912
import org.jetbrains.kotlin.gradle.testbase.*
1013
import org.jetbrains.kotlin.gradle.util.replaceText
1114
import org.jetbrains.kotlin.gradle.util.testResolveAllConfigurations
1215
import org.jetbrains.kotlin.test.TestMetadata
1316
import org.jetbrains.kotlin.utils.addToStdlib.countOccurrencesOf
17+
import org.junit.jupiter.params.ParameterizedTest
18+
import kotlin.io.path.createDirectories
19+
import kotlin.io.path.writeText
1420
import kotlin.test.assertEquals
1521
import kotlin.test.assertTrue
1622

@@ -129,4 +135,77 @@ class MppMetadataResolutionIT : KGPBaseTest() {
129135
}
130136
}
131137
}
138+
139+
@GradleTest
140+
@GradleTestVersions
141+
@ParameterizedTest(name = "{0} isolated projects support: {1} {displayName}")
142+
@GradleTestExtraStringArguments("ENABLE", "DISABLE")
143+
fun testCustomGroupForMppPublicationInTransitiveDependencies(
144+
gradleVersion: GradleVersion,
145+
kmpIsolatedProjectsSupport: String,
146+
) {
147+
var buildOptions = defaultBuildOptions.copy(
148+
kmpIsolatedProjectsSupport = KmpIsolatedProjectsSupport.valueOf(kmpIsolatedProjectsSupport)
149+
)
150+
151+
fun GradleProject.configureKotlinMultiplatform() {
152+
buildScriptInjection {
153+
project.group = "default.group"
154+
155+
kotlinMultiplatform.jvm()
156+
kotlinMultiplatform.linuxX64()
157+
}
158+
}
159+
160+
project("base-kotlin-multiplatform-library", gradleVersion) {
161+
includeOtherProjectAsSubmodule("base-kotlin-multiplatform-library", newSubmoduleName = "lib1") {
162+
configureKotlinMultiplatform()
163+
buildScriptInjection {
164+
project.plugins.apply("maven-publish")
165+
val publishing = project.extensions.getByName("publishing") as PublishingExtension
166+
publishing.publications.withType(MavenPublication::class.java).configureEach {
167+
if (it.name == "kotlinMultiplatform") {
168+
it.groupId = "custom.group"
169+
it.artifactId = "custom-artifact-id"
170+
}
171+
}
172+
}
173+
174+
kotlinSourcesDir("commonMain")
175+
.also { it.createDirectories() }
176+
.resolve("Lib1.kt")
177+
.writeText("interface Lib1")
178+
}
179+
180+
includeOtherProjectAsSubmodule("base-kotlin-multiplatform-library", newSubmoduleName = "lib2") {
181+
configureKotlinMultiplatform()
182+
buildScriptInjection {
183+
kotlinMultiplatform.sourceSets.getByName("commonMain").dependencies {
184+
api(project(":lib1"))
185+
}
186+
}
187+
188+
kotlinSourcesDir("commonMain")
189+
.also { it.createDirectories() }
190+
.resolve("Lib2.kt")
191+
.writeText("interface Lib2 : Lib1")
192+
}
193+
194+
includeOtherProjectAsSubmodule("base-kotlin-multiplatform-library", newSubmoduleName = "lib3") {
195+
configureKotlinMultiplatform()
196+
buildScriptInjection {
197+
kotlinMultiplatform.sourceSets.getByName("commonMain").dependencies {
198+
api(project(":lib2"))
199+
}
200+
}
201+
202+
kotlinSourcesDir("commonMain")
203+
.also { it.createDirectories() }
204+
.resolve("Lib3.kt")
205+
.writeText("class Lib3 : Lib2, Lib1")
206+
}
207+
208+
build(":lib3:metadataCommonMainClasses", buildOptions = buildOptions)
209+
}
210+
}
132211
}

libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,9 @@ class TestProject(
423423
otherProjectName: String,
424424
pathPrefix: String = "",
425425
newSubmoduleName: String = otherProjectName,
426-
isKts: Boolean = false,
426+
isKts: Boolean = settingsGradleKts.exists(),
427427
localRepoDir: Path? = null,
428+
configure: GradleProject.() -> Unit = {},
428429
) {
429430
val otherProjectPath = if (pathPrefix.isEmpty()) {
430431
otherProjectName.testProjectPath
@@ -443,6 +444,7 @@ class TestProject(
443444
)
444445

445446
localRepoDir?.let { subProject(newSubmoduleName).configureLocalRepository(localRepoDir) }
447+
subProject(newSubmoduleName).configure()
446448
}
447449

448450
fun includeOtherProjectAsIncludedBuild(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
plugins {
2+
kotlin("multiplatform")
3+
}
4+
5+
// To be filled by test code using buildScriptInjection API

libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/base-kotlin-multiplatform-library/settings.gradle.kts

Whitespace-only changes.

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/KotlinSecondaryVariantsDataSharing.kt

+11-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ internal val Project.kotlinSecondaryVariantsDataSharing: KotlinSecondaryVariants
3939
internal interface KotlinShareableDataAsSecondaryVariant
4040

4141
/**
42-
* Service to share configuration state between Kotlin Projects as Configuration Secondary Variants
42+
* Service to share configuration state between Kotlin Projects as Configuration Secondary Variants.
43+
* So this data can be consumed during Task Execution. And is trackable via Task Inputs.
44+
*
45+
* This is an alternative to BuildServices that are not trackable as Task Inputs.
46+
*
4347
*/
4448
internal class KotlinSecondaryVariantsDataSharing(
4549
private val project: Project,
@@ -102,6 +106,12 @@ internal class KotlinSecondaryVariantsDataSharing(
102106
private val kotlinProjectSharedDataAttribute = Attribute.of("org.jetbrains.kotlin.project-shared-data", String::class.java)
103107

104108
/**
109+
* Represents a provider that can extract some [T] that was published by a project in the current build as
110+
* a secondary variant via [kotlinSecondaryVariantsDataSharing].
111+
* And the current (consumer) project has dependency to the producer project.
112+
*
113+
* Data is meant to be extracted at Task Execution Phase.
114+
*
105115
* This class is Configuration Cache safe. It can be stored in a Task field.
106116
*/
107117
internal class KotlinProjectSharedDataProvider<T : KotlinShareableDataAsSecondaryVariant>(

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt

+22-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.PreparedKotlinToolingDiagn
2121
import org.jetbrains.kotlin.gradle.plugin.internal.KotlinProjectSharedDataProvider
2222
import org.jetbrains.kotlin.gradle.plugin.internal.kotlinSecondaryVariantsDataSharing
2323
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ArtifactMetadataProvider
24+
import org.jetbrains.kotlin.gradle.plugin.mpp.publishing.KotlinProjectCoordinatesData
25+
import org.jetbrains.kotlin.gradle.plugin.mpp.publishing.consumeRootModuleCoordinates
2426
import org.jetbrains.kotlin.gradle.plugin.mpp.internal.projectStructureMetadataResolvableConfiguration
2527
import org.jetbrains.kotlin.gradle.plugin.sources.internal
2628
import org.jetbrains.kotlin.gradle.utils.*
@@ -106,6 +108,7 @@ internal class GranularMetadataTransformation(
106108
val projectData: Map<String, ProjectData>,
107109
val platformCompilationSourceSets: Set<String>,
108110
val projectStructureMetadataResolvableConfiguration: LazyResolvedConfiguration?,
111+
val coordinatesOfProjectDependencies: KotlinProjectSharedDataProvider<KotlinProjectCoordinatesData>?,
109112
val objects: ObjectFactory,
110113
val kotlinKmpProjectIsolationEnabled: Boolean,
111114
val sourceSetMetadataLocationsOfProjectDependencies: KotlinProjectSharedDataProvider<SourceSetMetadataLocations>,
@@ -115,11 +118,18 @@ internal class GranularMetadataTransformation(
115118
sourceSetName = kotlinSourceSet.name,
116119
resolvedMetadataConfiguration = LazyResolvedConfiguration(kotlinSourceSet.internal.resolvableMetadataConfiguration),
117120
sourceSetVisibilityProvider = SourceSetVisibilityProvider(project),
118-
projectStructureMetadataExtractorFactory = if (project.kotlinPropertiesProvider.kotlinKmpProjectIsolationEnabled) project.kotlinMppDependencyProjectStructureMetadataExtractorFactory else project.kotlinMppDependencyProjectStructureMetadataExtractorFactoryDeprecated,
119-
projectData = if (project.kotlinPropertiesProvider.kotlinKmpProjectIsolationEnabled) emptyMap<String, ProjectData>() else project.allProjectsData,
121+
projectStructureMetadataExtractorFactory =
122+
if (project.kotlinPropertiesProvider.kotlinKmpProjectIsolationEnabled) project.kotlinMppDependencyProjectStructureMetadataExtractorFactory
123+
else project.kotlinMppDependencyProjectStructureMetadataExtractorFactoryDeprecated,
124+
projectData =
125+
if (project.kotlinPropertiesProvider.kotlinKmpProjectIsolationEnabled) emptyMap<String, ProjectData>()
126+
else project.allProjectsData,
120127
platformCompilationSourceSets = project.multiplatformExtension.platformCompilationSourceSets,
121128
projectStructureMetadataResolvableConfiguration =
122129
kotlinSourceSet.internal.projectStructureMetadataResolvableConfiguration?.let { LazyResolvedConfiguration(it) },
130+
coordinatesOfProjectDependencies =
131+
if (project.kotlinPropertiesProvider.kotlinKmpProjectIsolationEnabled) project.kotlinSecondaryVariantsDataSharing.consumeRootModuleCoordinates(kotlinSourceSet.internal)
132+
else null,
123133
objects = project.objects,
124134
kotlinKmpProjectIsolationEnabled = project.kotlinPropertiesProvider.kotlinKmpProjectIsolationEnabled,
125135
sourceSetMetadataLocationsOfProjectDependencies = project.kotlinSecondaryVariantsDataSharing
@@ -130,6 +140,7 @@ internal class GranularMetadataTransformation(
130140
class ProjectData(
131141
val path: String,
132142
val sourceSetMetadataOutputs: LenientFuture<Map<String, SourceSetMetadataOutputs>>,
143+
val moduleId: Future<ModuleDependencyIdentifier>,
133144
) {
134145
override fun toString(): String = "ProjectData[path='$path']"
135146
}
@@ -353,7 +364,13 @@ internal class GranularMetadataTransformation(
353364
is ModuleComponentIdentifier -> ModuleDependencyIdentifier(componentId.group, componentId.module)
354365
is ProjectComponentIdentifier -> {
355366
if (componentId in params.build) {
356-
ModuleDependencyIdentifier(component.moduleVersion?.group, componentId.projectName)
367+
if (params.coordinatesOfProjectDependencies != null) {
368+
val projectCoordinates = params.coordinatesOfProjectDependencies.getProjectDataFromDependencyOrNull(this)
369+
projectCoordinates?.moduleId ?: ModuleDependencyIdentifier(component.moduleVersion?.group, componentId.projectName)
370+
} else {
371+
params.projectData[componentId.projectPath]?.moduleId?.getOrThrow()
372+
?: error("Cant find project Module ID by ${componentId.projectPath}")
373+
}
357374
} else {
358375
ModuleDependencyIdentifier(
359376
component.moduleVersion?.group ?: "unspecified",
@@ -374,10 +391,12 @@ private val Project.allProjectsData: Map<String, GranularMetadataTransformation.
374391

375392
private fun Project.collectAllProjectsData(): Map<String, GranularMetadataTransformation.ProjectData> {
376393
return rootProject.allprojects.associateBy { it.path }.mapValues { (path, currentProject) ->
394+
val moduleId = currentProject.future { ModuleIds.idOfRootModuleSafe(currentProject) }
377395

378396
GranularMetadataTransformation.ProjectData(
379397
path = path,
380398
sourceSetMetadataOutputs = currentProject.future { currentProject.collectSourceSetMetadataOutputs() }.lenient,
399+
moduleId = moduleId,
381400
)
382401
}
383402
}

0 commit comments

Comments
 (0)