8

I have a Kotlin Multiplatform Mobile library that I published to Maven Central. I am also trying to use this library in a non-KMM Android app. When I declare the dependency in the android app I get this error

Execution failed for task ':app:checkDebugAarMetadata'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
   > Could not resolve io.github.tyczj:tweedle-android:0.3.0.
     Required by:
         project :app
      > No matching variant of io.github.tyczj:tweedle-android:0.3.0 was found. The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
          - Variant 'commonMainMetadataElements-published' capability io.github.tyczj:tweedle-android:0.3.0:
              - Incompatible because this component declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'common' and the consumer needed a runtime of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm'
              - Other compatible attribute:
                  - Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
          - Variant 'metadataApiElements-published' capability io.github.tyczj:tweedle-android:0.3.0:
              - Incompatible because this component declares a usage of 'kotlin-metadata' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'common' and the consumer needed a runtime of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm'
              - Other compatible attribute:
                  - Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
          - Variant 'releaseApiElements-published' capability io.github.tyczj:tweedle-android:0.3.0 declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
              - Incompatible because this component declares an API of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release' and the consumer needed a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug'
          - Variant 'releaseRuntimeElements-published' capability io.github.tyczj:tweedle-android:0.3.0 declares a runtime of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
              - Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug'

I assume something is wrong with my configuration but I dont really know what that error is telling me, this is my first time creating a KMM library.

Here is my build.gradle.kts

import org.gradle.api.tasks.bundling.Jar

plugins {
    kotlin("multiplatform")
    id("com.android.library")
    id("kotlin-android-extensions")
    id("kotlinx-serialization")
    id("maven-publish")
    id("signing")
}

repositories {
    gradlePluginPortal()
    google()
    mavenCentral()
}

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

group = "io.github.tyczj"
version = "0.3.0"

kotlin {
    android{
        publishLibraryVariants("release")
    }
    ios {
        binaries {
            framework {
                baseName = "tweedle"
            }
        }
    }
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-core:1.5.1")
                implementation("io.ktor:ktor-client-json:1.5.1")
                implementation("io.ktor:ktor-client-serialization:1.5.1")
                implementation("io.ktor:ktor-client-logging:1.5.1")
                implementation("ch.qos.logback:logback-classic:1.2.3")
//                implementation("com.squareup.okio:okio:2.10.0")
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2-native-mt"){
                    version {
                        strictly("1.4.2-native-mt")
                    }
                }
            }
        }
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("androidx.core:core-ktx:1.3.2")
                implementation("io.ktor:ktor-client-android:1.5.1")
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2-native-mt")
                implementation("com.github.scribejava:scribejava-apis:8.3.0")
            }
        }
        val androidTest by getting {
            dependencies {
                implementation(kotlin("test-junit"))
                implementation("junit:junit:4.12")
            }
        }
        val iosMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-ios:1.5.1")
            }
        }
//        val iosTest by getting
    }
}

android {
    compileSdkVersion(30)
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    defaultConfig {
        minSdkVersion(26)
        targetSdkVersion(30)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }

    packagingOptions {
        excludes.add("META-INF/*.kotlin_module")
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().all {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
}

afterEvaluate {
    publishing {
        repositories {
            maven {
                name = "sonatype"
                url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
                credentials {
                    username = rootProject.ext["ossrhUsername"]?.toString()
                    password = rootProject.ext["ossrhPassword"]?.toString()
                }
            }
        }

        publications.withType<MavenPublication> {

            artifact(javadocJar.get())

            pom{
                name.set("Tweedle")
                description.set("Tweedle is an Android library built around the Twitter v2 API built fully in Kotlin using Kotlin Coroutines")
                url.set("https://github.com/tyczj/Tweedle")
                licenses {
                    license {
                        name.set("MIT")
                        url.set("https://opensource.org/licenses/MIT")
                    }
                }
                developers {
                    developer {
                        id.set("tyczj")
                        name.set("Jeff Tycz")
                        email.set("tyczj359@gmail.com")
                    }
                }
                scm {
                    url.set("https://github.com/tyczj/Tweedle")
                }
            }
        }
    }
}

val packForXcode by tasks.creating(Sync::class) {
    group = "build"
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
    val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
    val framework =
        kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(
            targetName
        ).binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    val targetDir = File(buildDir, "xcode-frameworks")
    from({ framework.outputDirectory })
    into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)

ext["signing.keyId"] = rootProject.ext["signing.keyId"]?.toString()
ext["signing.password"] = rootProject.ext["signing.password"]?.toString()
ext["signing.secretKeyRingFile"] = rootProject.ext["signing.secretKeyRingFile"]?.toString()

signing {
    sign(publishing.publications)
}

What do I need to do to be able to use the android dependency (io.github.tyczj:tweedle-android) in a non-KMM android app?

Update

I tried adding "debug" to android library variants

android{
    publishLibraryVariants("release", "debug")
}

as well as adding fallbacks

buildTypes {
    getByName("release") {
        isMinifyEnabled = false
    }
    val staging by creating{
        setMatchingFallbacks(listOf("release", "debug"))
    }
}

and that didnt do anything

If I create a new KMM project and just try to add the dependency I get somewhat of a different error saying

Failed building KotlinMPPGradleModel
org.gradle.internal.resolve.ArtifactNotFoundException: Could not find tweedle-android-0.3.1-samplessources.jar (io.github.tyczj:tweedle-android:0.3.1).
tyczj
  • 71,600
  • 54
  • 194
  • 296
  • I was seeing a very similar error, and I was initially thinking it had something to do with kapt, but I don't see you using it here. If I change some of the configuration with kapt in my project, using `configurations["kapt"].dependencies.add(..)` instead of `kapt.annotationProcessor(..)`, I avoid the issue, but the project doesn't build in the ide and I run into a jdk11 issue i think(package javax.annotation DNE), although building on the command line is successful. I guess I can live with that. Hopefully that gives you some ideas. – salbury May 19 '21 at 06:52
  • @tyxzj so I am trying to add your lib in a new project and it doesn't get added. when I run the cradle dependency graph it shows +--- com.tycz:tweedle-android:0.3.2 FAILED +--- com.tycz:tweedle-android-debug:0.3.2 FAILED – Peterstev Uremgba May 25 '21 at 22:20

2 Answers2

4

The error message referencing "samplessources" comes up when you include a dependency that's not built for the correct platforms. There's some discussion in this youtrack issue, as well as this other issue around emitting a better error message in this case.

In your case, it's presumably being triggered by implementation("ch.qos.logback:logback-classic:1.2.3") in your common dependencies. This is a JVM library, but you have an iOS target so it won't work in your common dependencies. Move it to your Android dependencies instead.

RussHWolf
  • 3,555
  • 1
  • 19
  • 26
  • hmm interesting, I was using that just for debugging requests in Ktor https://ktor.io/docs/logging.html#access_logger, so does that mean I wouldnt be able to log requests in iOS? – tyczj May 21 '21 at 12:49
  • You can log requests on iOS but you'll have to use a iOS-capable logger to do so. There's `Logger.DEFAULT` or `Logger.SIMPLE` built in which both forward to `println()` on iOS, or you can create your own that forwards to your choice of iOS logging API. – RussHWolf May 21 '21 at 12:56
  • What did you mean ~"**Move it to your Android dependencies instead.**" – IgorGanapolsky Jun 16 '21 at 20:17
  • 'Move it to your Android dependencies' -> i.e., make sure the block where you declare this dependency is like `android { ... dependencies { ...} }`. My issue was I was trying to use an Android Library inside a `:build-logic` module, I realised after reading this answer that of course this won't work :| – Saik Caskey Jan 10 '23 at 18:22
0

I guess you publish library as aar. In this case you should explicitly add @aar to gradle dependency

implementation 'io.github.tyczj:tweedle-android:0.3.0@aar'
Maxim Tulupov
  • 1,621
  • 13
  • 8
  • 1
    Still the same issue – tyczj May 20 '21 at 14:35
  • I added this dependency to my project and it compiles successfully. It seems the root cause - conflicting sources. Is the project sources available so I can try test myself? – Maxim Tulupov May 21 '21 at 03:15
  • 1
    This one works for me. 1. Comment line // implementation project(':tweedle') 2. Add the following deps. Separately for release and debug debugImplementation 'io.github.tyczj:tweedle-android-debug:0.3.2@aar' releaseImplementation 'io.github.tyczj:tweedle-android:0.3.2@aar' – Maxim Tulupov May 21 '21 at 13:41
  • 1
    Also I guess you want add library dependencies debugImplementation ('io.github.tyczj:tweedle-android-debug:0.3.2@aar') { transitive(true) } releaseImplementation ('io.github.tyczj:tweedle-android:0.3.2@aar') { transitive(true) } – Maxim Tulupov May 21 '21 at 13:49
  • Ah that does indeed work but how do I get it so that you dont have to have both debug and release dependencies and just one `implementation` like every other library? – tyczj May 21 '21 at 23:51
  • 1
    Sorry for the late response. Try do the following. In the file Tweedle/app/build.gradle keep only one dependency implementation ('io.github.tyczj:tweedle-android:0.3.2@aar') { transitive(true) } And add the debug section to buildType debug { matchingFallbacks = ['release'] } – Maxim Tulupov May 25 '21 at 04:47
  • This does absolutely nothing. – IgorGanapolsky Jun 16 '21 at 20:17