使用Gradle Kotlin DSL发布Kotlin MPP元数据的规范实现问询
Great question—this is a common gotcha with Kotlin Multiplatform (MPP) library publishing, especially when dealing with Gradle metadata and cross-MPP dependencies. Let's break down what's happening and how to fix it properly.
What's the Root Issue?
Your core problem is that your MPP library's publication metadata isn't structured correctly for other MPP projects to consume. The official documentation's promise of "single dependency auto-resolves target-specific artifacts" relies on your library publishing valid Gradle metadata that links the common metadata publication to your JVM/JS target artifacts.
Your current afterEvaluate hack works, but it's a workaround for missing proper publication configuration. The forced cast to MavenPublication and ad-hoc artifact ID renaming are red flags that we can clean up.
The Proper Solution
We'll fix this by aligning your publication setup with Kotlin MPP's conventions, eliminating the need for messy afterEvaluate logic while ensuring Gradle can correctly resolve your library across MPP projects.
Step 1: Ensure Targets and Source Sets Are Complete
First, make sure your kotlin block explicitly declares both JVM and JS targets (your provided build script was missing JS—likely a copy-paste oversight):
Step 2: Clean Up Publication Configuration
Instead of using afterEvaluate, use Gradle's type-safe withType API to configure all MavenPublication instances cleanly. This avoids forced casts and ensures we follow Kotlin's MPP naming conventions:
- The
metadatapublication uses your base project name as the artifact ID - JVM/JS target publications use
{project-name}-{target-name}as their artifact ID
Full Revised build.gradle.kts
group = "org.example" version = "1.0-SNAPSHOT" plugins { kotlin("multiplatform") version "1.3.72" `maven-publish` } repositories { mavenCentral() } kotlin { // Declare both JVM and JS targets jvm() js { browser() // Use nodejs() if targeting Node.js instead of browsers } sourceSets { val commonMain by getting { dependencies { implementation(kotlin("stdlib-common")) } } val jvmMain by getting { dependencies { implementation(kotlin("stdlib")) } } val jsMain by getting { dependencies { implementation(kotlin("stdlib-js")) } } } } publishing { // Configure all Maven publications type-safely publications.withType<MavenPublication> { artifactId = if (name.contains("metadata")) { project.name } else { "${project.name}-$name" } } // Add repository configuration (local or remote) repositories { mavenLocal() // For testing local consumption // Uncomment and configure for remote publishing: // maven { // url = uri("https://your-repo-url.com") // credentials { // username = System.getenv("REPO_USER") // password = System.getenv("REPO_PASS") // } // } } }
Why This Works Better
- No Hacky
afterEvaluate: Gradle'swithType<MavenPublication>automatically targets all publications generated by the Kotlin MPP plugin, so we don't need to wait for evaluation to modify them. - Type Safety: No forced casts—we're explicitly working with
MavenPublicationinstances, avoiding runtime errors. - Convention-Aligned Naming: This setup matches Kotlin MPP's expected artifact naming pattern, which is what Gradle metadata relies on to link the common
metadataartifact to your JVM/JS artifacts.
Testing the Fix
- Publish your library to
mavenLocal()using./gradlew publishToMavenLocal - In your other MPP project, add this dependency to the
commonMainsource set:dependencies { implementation("org.example:${your-library-name}:1.0-SNAPSHOT") } - Gradle will now automatically resolve the correct JVM/JS artifacts for each target—no need to add separate dependencies per target!
Bonus: Upgrade for Easier Setup
If you can upgrade to Kotlin 1.4+, the MPP plugin handles artifact ID naming automatically by default, eliminating the need for custom publication configuration entirely. Even if you stick with 1.3.72, the above setup is the cleanest, most maintainable fix.
内容的提问来源于stack exchange,提问作者Marvin

