Search code examples
javagradlekotlin-multiplatform

Gradle KMM custom attribute doesn't get propagated to project's KMM dependencies


Assume I have 3 gradle projects with the following paths and file contents:

:sub1 is a KMM project:

plugins {
    kotlin("multiplatform")
}

val customAttribute = Attribute.of("custom", String::class.java)

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
    targetHierarchy.default()

    jvm {
        attributes {
            attribute(customAttribute, "normal")
        }
    }
    jvm("custom") {
        attributes {
            attribute(customAttribute, "customValue")
        }
    }

    sourceSets {
        val commonMain by getting
    }
}

:sub2 is a KMM project that depends on :sub1:

plugins {
    kotlin("multiplatform")
}

val customAttribute = Attribute.of("custom", String::class.java)

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
    targetHierarchy.default()

    jvm {
        attributes {
            attribute(customAttribute, "normal")
        }
    }
    jvm("custom") {
        attributes {
            attribute(customAttribute, "customValue")
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                api(project(":sub1"))
            }
        }
    }
}

:parent which is a JVM project that depends on :sub2:

plugins {
    id("kotlin")
    id("org.jetbrains.kotlin.jvm")
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

val customAttribute = Attribute.of("custom", String::class.java)

dependencies {
    api(project(":sub2")) {
        attributes {
            attribute(customAttribute, "customValue")
        }
    }
}

Building :project causes the following error:

   > Could not resolve project :sub1.
     Required by:
         project :project > project :sub2
      > The consumer was configured to find a library for use during compile-time, compatible with Java 17, preferably in the form of class files, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm'. However we cannot choose between the following variants of project :sub1
          - jvmApiElements
          - jvmRuntimeElements
          - customApiElements
          - customRuntimeElements

How do I make it so that the "customValue" of customAttribute declared in :project propagates down to the project :sub1 from :sub2, instead of only going down to :sub2, so that the "custom" JVM target is chosen?

I have tried to set a Gradle project property during the configuration step, but I can't seem to find a way to guarantee project configuration order, so I can't reliably use properties either.


Solution

  • According to error message in project :sub2 Gradle can't choose between jvm and jvm("custom") for :sub1.

    Targets should be marked on both the library and consumer sides with a custom attribute, which Gradle uses during dependency resolution.

    So in project :sub2 you either need to specify variant attribute for :sub1 project or move dependency from common source. For example:

    kotlin {
        ...
        sourceSets {
            val jvmMain by getting {
                dependencies {
                    api(project(":sub1")) {
                        attributes {
                            attribute(customAttribute, "normal")
                        }
                    }
                }
            }
            val customMain by getting {
                dependencies {
                    api(project(":sub1")) {
                        attributes {
                            attribute(customAttribute, "customValue")
                        }
                    }
                }
            }
        }
    }
    

    Take a look at Kotlin Multiplatform documentations for several targets