diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index e15ac44f6a8..e701bb22b49 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -774,4 +774,59 @@ class NewMultiplatformIT : BaseGradleIT() { } } } + + @Test + fun testPublishMultimoduleProjectWithNoMetadata() { + val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app") + val appProject = Project("sample-app", gradleVersion, "new-mpp-lib-and-app") + + with(libProject) { + setupWorkingDir() + appProject.setupWorkingDir() + appProject.projectDir.copyRecursively(projectDir.resolve("sample-app")) + + projectDir.resolve("settings.gradle").writeText("include 'sample-app'") // also disables feature preview 'GRADLE_METADATA' + gradleBuildScript("sample-app").modify { + it.replace("'com.example:sample-lib:1.0'", "project(':')") + "\n" + """ + apply plugin: 'maven-publish' + group = "com.exampleapp" + version = "1.0" + publishing { + repositories { + maven { url "file://${'$'}{rootProject.projectDir.absolutePath.replace('\\', '/')}/repo" } + } + } + """.trimIndent() + } + + gradleBuildScript().appendText("\n" + """ + publishing { + publications { + jvm6 { + groupId = "foo" + artifactId = "bar" + version = "42" + } + } + } + """.trimIndent()) + + build("publish") { + assertSuccessful() + assertFileContains( + "repo/com/exampleapp/sample-app-nodejs/1.0/sample-app-nodejs-1.0.pom", + "com.example", + "sample-lib-nodejs", + "1.0" + ) + assertFileContains( + "repo/com/exampleapp/sample-app-jvm8/1.0/sample-app-jvm8-1.0.pom", + "foo", + "bar", + "42" + ) + assertFileExists("repo/foo/bar/42/bar-42.jar") + } + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index 31e0a35d77b..6e37c98cd25 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -135,7 +135,7 @@ class KotlinMultiplatformPlugin( } } (this as MavenPublicationInternal).publishWithOriginalFileName() - artifactId = "${project.name}-${variant.target.name.toLowerCase()}" + artifactId = variant.target.defaultArtifactId groupId = project.group.toString() version = project.version.toString() } @@ -234,4 +234,6 @@ class KotlinMultiplatformPlugin( companion object { const val METADATA_TARGET_NAME = "metadata" } -} \ No newline at end of file +} + +internal val KotlinTarget.defaultArtifactId get() = "${project.name}-${name.toLowerCase()}" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt index d7fd89719c4..43ff703f9ff 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt @@ -6,26 +6,25 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.DependencyConstraint -import org.gradle.api.artifacts.ModuleDependency -import org.gradle.api.artifacts.PublishArtifact +import org.gradle.api.artifacts.* import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.Usage import org.gradle.api.capabilities.Capability import org.gradle.api.component.ComponentWithVariants import org.gradle.api.internal.component.SoftwareComponentInternal import org.gradle.api.internal.component.UsageContext +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent -import org.jetbrains.kotlin.gradle.plugin.usageByName +import org.jetbrains.kotlin.utils.ifEmpty class KotlinSoftwareComponent( private val project: Project, private val name: String, private val kotlinTargets: Iterable ) : SoftwareComponentInternal, ComponentWithVariants { - + override fun getUsages(): Set = emptySet() override fun getVariants(): Set = @@ -60,7 +59,10 @@ internal class KotlinPlatformUsageContext( get() = project.configurations.getByName(dependencyConfigurationName) override fun getDependencies(): MutableSet = - configuration.incoming.dependencies.withType(ModuleDependency::class.java) + if (publishWithGradleMetadata) + configuration.incoming.dependencies.withType(ModuleDependency::class.java) + else + rewriteMppDependenciesToTargetModuleDependencies(this, configuration).toMutableSet() override fun getDependencyConstraints(): MutableSet = configuration.incoming.dependencyConstraints @@ -77,3 +79,66 @@ internal class KotlinPlatformUsageContext( // FIXME this is a stub for a function that is not present in the Gradle API that we compile against fun getGlobalExcludes(): Set = emptySet() } + +private fun rewriteMppDependenciesToTargetModuleDependencies( + context: KotlinPlatformUsageContext, + configuration: Configuration +): Set = with(context.kotlinTarget.project) { + val target = context.kotlinTarget + val moduleDependencies = configuration.incoming.dependencies.withType(ModuleDependency::class.java).ifEmpty { return emptySet() } + + val targetMainCompilation = target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) + ?: return moduleDependencies // Android is not yet supported + + val targetCompileDependenciesConfiguration = project.configurations.getByName( + when (context.dependencyConfigurationName) { + target.apiElementsConfigurationName -> targetMainCompilation.compileDependencyConfigurationName + target.runtimeElementsConfigurationName -> + (targetMainCompilation as KotlinCompilationToRunnableFiles).runtimeDependencyConfigurationName + else -> error("unexpected configuration") + } + ) + + val resolvedCompileDependencies by lazy { // don't resolve if no project dependencies on MPP projects are found + targetCompileDependenciesConfiguration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.associateBy { + Triple(it.moduleGroup, it.moduleName, it.moduleVersion) + } + } + + moduleDependencies.map { dependency -> + when (dependency) { + !is ProjectDependency -> dependency + else -> { + val dependencyProject = dependency.dependencyProject + val dependencyProjectKotlinExtension = dependencyProject.multiplatformExtension + ?: return@map dependency + + if (dependencyProjectKotlinExtension.isGradleMetadataAvailable) + return@map dependency + + val resolved = resolvedCompileDependencies[Triple(dependency.group, dependency.name, dependency.version)] + ?: return@map dependency + + val resolvedToConfiguration = resolved.configuration + + val dependencyTarget = dependencyProjectKotlinExtension.targets.singleOrNull { + resolvedToConfiguration in setOf( + it.apiElementsConfigurationName, + it.runtimeElementsConfigurationName, + it.defaultConfigurationName + ) + } ?: return@map dependency + + val publicationDelegate = (dependencyTarget.component as KotlinVariant).publicationDelegate + + dependencies.module( + listOf( + publicationDelegate?.groupId ?: dependency.group, + publicationDelegate?.artifactId ?: dependencyTarget.defaultArtifactId, + publicationDelegate?.version ?: dependency.version + ).joinToString(":") + ) as ModuleDependency + } + } + }.toSet() +}