How to publish your own BoM (Bill of Materials) for Kotlin Multiplatform libraries on Maven Central

Vivien Mahé
ProAndroidDev
Published in
5 min readMar 8, 2024

--

JetBrains Kotlin Multiplatform + BoM (Bill Of Materials) + Sonatype Maven Central Repository + GitHub Actions

As a developer working on various Kotlin Multiplatform projects, whether for your job or just for fun, you might find that as your projects grow, you’ll want to create your own libraries. This is a common practice that helps you reuse the same code across different projects, saving time.

While this practice improves your code quality, over time, you might encounter dependency version conflicts, often referred to as “dependency hell”. Here’s an example to illustrate what can happen:

Imagine you are working on two different modules within a project, ModuleA and ModuleB, both depending on the same library, LibraryX. However, ModuleA requires LibraryX version 1.2, while ModuleB needs LibraryX version 1.4 for its additional features.

When you try to build your project, the build system might fetch and use different versions of LibraryX for each module, leading to inconsistent behavior or compilation errors. Worse, if both modules are part of the same build process, the build system might only resolve to one version of LibraryX (say 1.2), causing ModuleB to potentially fail at runtime due to missing features available only in 1.4.

With a BoM (Bill of Materials), you could centrally manage the version of LibraryX, ensuring that all modules use a compatible version, or identify and resolve the version conflict upfront, deciding whether to upgrade ModuleA to use LibraryX 1.4 or find a different solution that suits both modules. This centralized management helps maintain consistency, reduces the risk of runtime issues, and simplifies dependency updates and troubleshooting.

Here are some examples of famous libraries using a BoM:

⚠️ Important: I assume you already have a working setup to automatically publish a KMP library to Maven Central repository. If this is not the case, I recommend you read my article “How to publish your Kotlin Multiplatform library on Maven Central”:

The project structure

A few key elements are required to create a BoM:

  • You need a multi-module project, where each module is a library.
  • An additional module is dedicated to the BoM configuration. This module contains only one file (build.gradle.kts) and does not include any code, resources, or other files. (We will create it later on.)

➡️ By default, every module that sets up a MavenPublication within that multi-module project is automatically included in the BoM.

Here’s what the project structure looks like:

myproject (root)
├── build.gradle.kts
├── bom (specific module for BoM configuration)
│ ├── build.gradle.kts
├── library1 (module)
│ ├── build.gradle.kts
│ ├── src
├── library2 (module)
│ ├── build.gradle.kts
│ ├── src

In this example, the BoM will include library1 and library2.

For my kmp-bom project, with the default configuration, the BoM will include the following modules: kmp-common , kmp-firebase and kmp-realm.

Project structure of my Tweener/kmp-bom project

If you don’t know how to setup a multi-module project, follow the official Gradle documentation here: https://docs.gradle.org/current/userguide/multi_project_builds.html

The BoM configuration

A BoM is typically a Maven pom.xml file where all the libraries are listed along with their specific version numbers, providing a centralized reference for managing project dependencies:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project1Version>1.0.0</project1Version>
<project2Version>1.0.0</project2Version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project2Version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<modules>
<module>parent</module>
</modules>
</project>

Now, you could create this pom.xml file and update it manually every time one of your libraries has a new version. However, we prefer to automate the process so that this pom.xml file is automatically generated.

➡️ To do so, we use this Gradle plugin that does a pretty good job with minimum configuration:

  • Configure the plugin for the pom.xmlfile generation.
  • Configure publishing to Maven Central and signing with GPG.

In your bom module (the specific module for the BoM configuration), create a build.gradle.kts file and add the following lines:

Exclude module from the BoM:

If you want to exclude a module from being collected into the BoM, you can add the excludeProject configuration to your bom module’s build.gradle.kts:

bomGenerator {
excludeProject("excluded-module")
}

Include external dependencies to theBoM:

You can also include external dependencies (from outside this project) to your BoM, by adding the includeDependency configuration to your bom module’s build.gradle.kts:

bomGenerator {
includeDependency("io.github.tweener:kmp-charts:")
}

Verify your BoM configuration:

To verify that this configuration is correctly set, you can deploy your BoM library to your local Maven repository using the publishToMavenLocal Gradle task:

./gradlew publishToMavenLocal

Add the BoM as a dependency in your projects

To use the BoM as a dependency in your projects, you must first add the BoM dependency itself, followed by the specific libraries you need:

implementation(project.dependencies.platform("com.company.example:bom:1.0.0"))
implementation("com.company.example:library1")
implementation("com.company.example:library2")

For instance, if the BoM specifies library1 to be at version 1.0.0 and library2 at version 2.0.0, you don’t need to specify these versions when adding the dependencies to these libraries.

Publish your BoM library to Maven Central via GitHub Actions

This is the last step of this process. After testing your BoM library and publishing it to your local Maven repository, you’re now ready to automate its publication to Maven Central using GitHub Actions.

➡️ To do so, follow these instructions from my other article.

There is only one difference in the setup: we need to specifically call the Gradle task to generate the pom.xml file when executing the publish GitHub Actions workflow:

./gradlew publishBomPublicationToSonatypeRepository

So here’s what the publish.xml looks like for a BoM library:

Check your BoM library on Maven Central

With this, your library is now fully set up for automated publishing on Maven Central. After creating a release version of your library, it will be accessible via a URL provided by Sonatype.

For example, in my case, my BoM has 3 libraries: kmp-common, kmp-firebase, kmp-realm. The URL is:

https://repo1.maven.org/maven2/io/github/tweener/

That’s all for this article! I hope you found it helpful, I would greatly appreciate your feedback! 🙏

Feel free to leave a comment below or reach out to me directly:

--

--