mavenpublishkotlin-multiplatformkotlin-dokka

Kotlin multiplatform publish with dokka and sources


I am struggling to publish a Kotlin multiplatform project properly to maven (for now mavenLocal). I can add the dependency to another multiplatform project and use the code but I don't get any documentation and I am not sure if I am doing something wrong or if that is simply not possible at the moment.

From what I understand you cannot use the normal javadoc because it is bound to Java which does not make sense in a multiplatform environment. I read somewhere that in that case you should use the html version of dokka. I can see that I get a "javadoc.jar" with content to my mavenLocal but still in the IDE in an example project where I add my KMP library as a dependency, I don't see any documentation. Also, the code decompiling seems to be weird. I guess somehow the sources are also not properly resolved.

According to the documentation everything should automatically and perfectly work by simply adding the maven-publish and the dokka plugin. But it seems like this is not the case and actually nothing is working as I'd expect it :D

Does anyone know, how to properly set that up?

My gradle file looks like this:

plugins {
    kotlin("multiplatform") version "1.6.21"
    id("org.jetbrains.kotlinx.benchmark") version "0.4.2"
    id("org.jetbrains.dokka") version "1.6.21"
    `maven-publish`
    signing
}

group = "io.github.quillraven.fleks"
version = "1.4-KMP-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}

kotlin {
    targets {
        jvm {
            compilations {
                all {
                    kotlinOptions {
                        jvmTarget = "1.8"
                    }
                }
                val main by getting { }
                // custom benchmark compilation
                val benchmarks by compilations.creating {
                    defaultSourceSet {
                        dependencies {
                            // Compile against the main compilation's compile classpath and outputs:
                            implementation(main.compileDependencyFiles + main.output.classesDirs)
                        }
                    }
                }
            }
            withJava()
            testRuns["test"].executionTask.configure {
                useJUnitPlatform()
            }
        }
    }
    js(BOTH) {
        browser { }
    }
    val hostOs = System.getProperty("os.name")
    val isMingwX64 = hostOs.startsWith("Windows")
    val nativeTarget = when {
        hostOs == "Mac OS X" -> macosX64("native")
        hostOs == "Linux" -> linuxX64("native")
        isMingwX64 -> mingwX64("native")
        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    }

    sourceSets {
        val commonMain by getting { }
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
        val jvmMain by getting
        val jvmTest by getting
        val jvmBenchmarks by getting {
            dependsOn(commonMain)
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.2")
                implementation("com.badlogicgames.ashley:ashley:1.7.4")
                implementation("net.onedaybeard.artemis:artemis-odb:2.3.0")
            }
        }
        val jsMain by getting
        val jsTest by getting
        val nativeMain by getting
        val nativeTest by getting
    }
}

benchmark {
    targets {
        register("jvmBenchmarks")
    }
}

val javadocJar by tasks.registering(Jar::class) {
    archiveClassifier.set("javadoc")
    from(tasks.dokkaHtml)
}

publishing {
    repositories {
        maven {
            url = if (project.version.toString().endsWith("SNAPSHOT")) {
                uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
            } else {
                uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
            }

            credentials {
                username = System.getenv("OSSRH_USERNAME")
                password = System.getenv("OSSRH_TOKEN")
            }
        }
    }

    publications {
        val kotlinMultiplatform by getting(MavenPublication::class) {
            version = project.version.toString()
            groupId = project.group.toString()
            artifactId = "Fleks"
            artifact(javadocJar)

            pom {
                name.set("Fleks")
                description.set("A lightweight entity component system written in Kotlin.")
                url.set("https://github.com/Quillraven/Fleks")

                scm {
                    connection.set("scm:git:git@github.com:quillraven/fleks.git")
                    developerConnection.set("scm:git:git@github.com:quillraven/fleks.git")
                    url.set("https://github.com/quillraven/fleks/")
                }


                licenses {
                    license {
                        name.set("MIT License")
                        url.set("https://opensource.org/licenses/MIT")
                    }
                }

                developers {
                    developer {
                        id.set("Quillraven")
                        name.set("Simon Klausner")
                        email.set("quillraven@gmail.com")
                    }
                }
            }
        }

        signing {
            useInMemoryPgpKeys(System.getenv("SIGNING_KEY"), System.getenv("SIGNING_PASSWORD"))
            sign(kotlinMultiplatform)
        }
    }
}

// only sign if version is not a SNAPSHOT release.
// this makes it easier to publish to mavenLocal and test the packed version.
tasks.withType<Sign>().configureEach {
    onlyIf { !project.version.toString().endsWith("SNAPSHOT") }
}

When I run the publishToMavenLocal gradle task then I get following directories in my .m2 folder: enter image description here enter image description here enter image description here enter image description here enter image description here

When I then create an example project and add it as a dependency, then I don't see any quick documentation and also the decompiling is not working properly:

enter image description here enter image description here


Solution

  • Here is my example gradle file that publishes a kotlin multiplatform project with sources and javadocs to a maven repository.

    The project source is here: https://github.com/danbrough/misc_demos/tree/master/kotlin_multiplatform_dokka

    import org.gradle.api.tasks.testing.logging.TestExceptionFormat
    import org.gradle.api.tasks.testing.logging.TestLogEvent
    import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    plugins {
      kotlin("multiplatform")
      id("org.jetbrains.dokka")
      id("com.android.library")
      `maven-publish`
    }
    
    group = "dokka.test"
    version = "0.0.1"
    
    buildscript {
      repositories {
        mavenCentral()
        gradlePluginPortal()
      }
    }
    
    repositories {
      mavenCentral()
      google()
    }
    
    kotlin {
      jvm()
      android()
      linuxX64()
      macosX64()
    
      sourceSets {
        val commonTest by getting {
          dependencies {
            implementation(kotlin("test"))
          }
        }
      }
    
      val posixMain by sourceSets.creating {}
    
      targets.withType<KotlinNativeTarget>() {
        compilations["main"].defaultSourceSet.dependsOn(posixMain)
      }
    
    }
    
    tasks.withType<AbstractTestTask>() {
      testLogging {
        events = setOf(
          TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED
        )
        exceptionFormat = TestExceptionFormat.FULL
        showStandardStreams = true
        showStackTraces = true
      }
      outputs.upToDateWhen {
        false
      }
    }
    
    tasks.withType(KotlinCompile::class) {
      kotlinOptions {
        jvmTarget = "11"
      }
    }
    
    
    tasks.dokkaHtml.configure {
      outputDirectory.set(buildDir.resolve("dokka"))
    }
    
    
    val javadocJar by tasks.registering(Jar::class) {
      archiveClassifier.set("javadoc")
      from(tasks.dokkaHtml)
    }
    
    publishing {
    
      repositories {
        maven(project.buildDir.resolve("m2").toURI()) {
          name = "m2"
        }
      }
    
      publications.forEach {
        if (it !is MavenPublication) {
          return@forEach
        }
    
        it.artifact(javadocJar)
      }
    }
    
    android {
    
      compileSdk = 33
      sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
      namespace = project.group.toString()
    
      defaultConfig {
        minSdk = 23
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
      }
    
      compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
      }
    
      signingConfigs.register("release") {
        storeFile = File(System.getProperty("user.home"), ".android/keystore")
        keyAlias = "keyAlias"
        storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
        keyPassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
      }
    
    
      lint {
        abortOnError = false
      }
    
    
      buildTypes {
    
        getByName("debug") {
          //debuggable(true)
        }
    
        getByName("release") {
          isMinifyEnabled = true
          proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
          )
          signingConfig = signingConfigs.getByName("release")
        }
      }
    }