How to publish fat jar from Kotlin multi-project MPP?

I need to publish a single fat jar built from Kotlin MPP project which consists of several sub-projects.
This fat jar is to be consumed in Kotlin jvm or just plain java project.

I’ve tried to build fat jar with gradle task like this:

task jvmJfxClassesAllJar(type: ShadowJar) {
    baseName artifactBaseName
    version artifactVersion
    configurations = [project.configurations.jvmRuntimeClasspath]
}

or like this:

task jvmJfxClassesAllJar(type: ShadowJar) {
    baseName artifactBaseName
    version artifactVersion

    def target = kotlin.targets.jvm
    from target.compilations.main.output
    def runtimeClasspath = target.compilations.main.runtimeDependencyFiles
    configurations = [runtimeClasspath]
}

As was suggested in this discussion: Kotlin 1.3-M2: new multiplatform projects model

In both cases the resulting jar look just fine to me - I can see all class files in there.

But when added to a kotlin.jvm project as a library IDEA refuses to resolve classes and packages in this library starting from the topmost package.

Gradle build fails differently - it complains that some static members are private when they are public in the source code.

At the same time if I publish multiple jars (per every subproject) then I can consume them in another kotlin.jvm gradle project just fine (IDEA wouldn’t connect sources but this is minor issue).

Apparently fat jar I’m building lacking something or is corrupted in some other way.

What is the right way to build it?

Some details on the errors would be very helpful. What I strongly suspect is that you are hitting a problem with attributes. MPP uses gradle attributes to distinguish between different platform targets. These attributes apply to configurations (and thus also artifacts), but hand-written ones may not have these attributes.

In parallel, when consuming such multi-target artifacts/projects, the consumer must also use attributes. One so that resolution knows the attribute values that apply to the project, but also to specify resolution when there is no full match (or missing value). MPP does this automatically for you, I’m not sure that the Kotlin plugin now does this, but non-kotlin projects will certainly not do this and you have to do it by hand.

I have some sample plugin code for a buildSrc plugin that does this in my project:

Gradle build reported that COLOR is private in class:

class Aes<T> private constructor(val name: String, val isNumeric: Boolean = true) : TypedKey<T> {
...
    companion object {
...
        val COLOR: Aes<Color> = Aes("color", false)
        val FILL: Aes<Color> = Aes("fill", false)
...

And IDEA couldn’t resolve even the first package component in import statement when importing Aes class.

I agree that my hand-made fat jar is lacking some features and I wonder what is the correct way of dealing with such case in Kotlin multiplatform.

The visibility issue with Gradle build is solved by changing the dependency type from implementation to api :slight_smile:

Now building in Gradle or IDEA and runtime works seemingly fine.

But IDE is still having problem with resolving packages - red in editor.

@alshan please, check if your fat-jar contains .kotlin_metadata files. If it does, then it’s a https://youtrack.jetbrains.com/issue/KT-25709, and the simple workaround is to exclude .kotlin_metadata-files from the fat-jar

Adding filters like this:

exclude '**/*.kotlin_metadata'
exclude '**/*.kotlin_module'
exclude '**/*.kotlin_builtins'

to ShadowJar task helped build jar which work in both IDEA and Gradle. Thanx!