DEV Community

Ekaterina Petrova for Kotlin

Posted on

How a Kotlin Multiplatform library is published?

In the first part of the series, we've created our first multiplatform library and published it to the local Maven. Before going public, let's discover published artifacts to get an understanding of the multiplatform publishing format.

TL;DR: The Kotlin Gradle plugin creates and configures your library publications automatically, so you don't need to know the details of the publication scheme to successfully deliver your library. But if you are curious, let's discuss it a little!

Discovering your library structure

If you have published regular platform (for example, android) libraries before, you may notice that the publishing format of a multiplatform library differs quite a lot. There are multiple artifacts for one version of your library, their content format is different (.jar vs .klib), and the number of source sets doesn’t match the number of result artifacts.

This is because ordinary publishing is not enough for a multiplatform library. Multiplatform libraries have more complex structures compared to normal ones, so the publication is less trivial as well. Here are key differences to keep in mind:

  • Multiplatform libraries consist of multiple parts: common parts with expects declarations and platform-specific parts with actual implementations.
  • Platform-specific code may still be shared across similar platforms (e.g. in a source set shared between the iOS device and simulator, or shared code for all desktop platforms).
  • There should be the ability to publish all those parts from a multiple host because of Kotlin/Native cross-compilation limitations. For example, the artifacts for iOS or macOS can be built only on Mac-OS.

Considering all that complexity, adding the ordinary multiplatform dependency requires just a single line of code in your the build.gradle file:

dependencies {
    implementation("io.github.katerinapetrova:mpp-sample-lib:1.0.0")
}
Enter fullscreen mode Exit fullscreen mode

So how does this magic work? Maven doesn’t operate with targets, source sets, and compilations. It operates only with projects. A project is everything we build and could depend on. Each project is defined by groupId, artifactId, and version, and these three fields are used by Maven as a coordinate system. This is exactly what you are using when copy-pasting a dependency string to your build.gradle file. These coordinates are defined in a .pom file — an XML representation of a Maven project. Let’s take a look at what we have in the local Maven folder of the library we’ve just published:
Multiplatform library in the local Maven folder

We published only one library, but there are multiple folders. Each folder represents a separate maven project, containing different artifacts and a .pom file, which describes the project (we can also call them “artifacts”, as the name of the project is stored in the POM artifactId field).

So the Kotlin Gradle plugin has created publications, which were used by the maven-publish plugin to map the sources of our Kotlin Multiplatform library to the Maven artifacts, on which we later could depend on in the consumer project as Gradle modules. This is how it was done:
Kotlin Multiplatform Library Maven representation

Let’s figure out the main points of this transformation:

  • There is a separate publication for each target. The binary format depends on the target compiler backend:
  • There is also an additional root publication that stands for the whole library. It consists of two important parts:
    • Kotlin representation of common sources of your library. Kotlin toolchain will use it to provide exact and granular dependencies from your common code to a library's common code.
    • Gradle Module Metadata, which tells Gradle where to find other platform-specific parts of a library when you depend on the root artifact in the common source set. This is what allows you to write single-line dependency on a library in a common source set
  • With the hierarchical project structure enabled, the API of the library’s intermediate source sets is embedded into the library artifacts (common or platform, depending on which targets this set is divided between).

This transformation doesn’t look trivial and the described points are just the tip of the iceberg, but the good news is that all the work is done by the Kotlin Gradle plugin. It creates and configures all the relevant publications automatically, so you don't need to create them manually. That significantly reduces the chance of making a mistake, especially for popular configurations. 🎉

If you want to dive into the details of publication format, I suggest you watch an exciting talk by Dima Savinov, Kotlin Multiplatform team lead.

And now let’s go back to our main goal — making our library available to the world! Check out the last part of the series, which will guide you through the MavenCentral publication process 👀

Top comments (0)