https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
c

Cas Van Luijtelaar

11/14/2023, 4:18 PM
similar to @Andrii Yanechko We have a KMM project which is compiled to a cocoapod itself. the project exists out of a bunch of sub-modules which all get exported into one “umbrella module”. One of the sub-modules uses a pod (which works just fine), but when we try to export it and build the umbrella we get a “not found” error. sub-module:
Copy code
plugins {
    `kmm-base-config`
    `kmm-artifact-publish`
    kotlin("native.cocoapods")
}

val iosDeploymentTarget: String by project

kotlin {
    sourceSets {
        val androidMain by getting {
            dependencies {
                implementation(libs.jmustache)
            }
        }
    }

    cocoapods {
        ios.deploymentTarget = iosDeploymentTarget
        pod("GRMustache", "~> 7.3.2")
    }
}
umbrella module:
Copy code
...

plugins {
    `kmm-base-config`
    `kmm-artifact-publish`
    kotlin("native.cocoapods")
}

kotlin {

    cocoapods {
        summary = "Cocoapod for $libBaseName."
        homepage = libSiteUrl
        ios.deploymentTarget = iosDeploymentTarget
        authors = libDeveloperOrg
        version = libBaseVersion
        license = "Unlicensed"
        name = libBaseName
        source = "{ :git => '$libGitUrl', :tag => '$libBaseVersion' }"
        extraSpecAttributes["vendored_frameworks"] = "'$libBaseName.xcframework'"
        xcodeConfigurationToNativeBuildType["CUSTOM_RELEASE"] =
            org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.RELEASE

        framework {
            isStatic = false
            baseName = libBaseName
            exportedProjects.forEach { export(project(":sub-module")) }
        }

        specRepos {
            url("<https://github.com/Kotlin/kotlin-cocoapods-spec.git>")
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
               api(project(":sub-module"))
            }
        }
    }
}

...
when building this it results in a:
Copy code
ld: framework not found GRMustache
same 1
🧵 1
a

a-dd

11/14/2023, 4:32 PM
And adding
pod("GRMustache",  version = "~> 7.3.2", linkOnly = true)
to umbrella doesn’t help, right? That’s strange Could you share a build log with
--info
?
c

Cas Van Luijtelaar

11/14/2023, 4:51 PM
this is with a linkOnly in the umbrella
a

a-dd

11/14/2023, 5:11 PM
Is there another framework declared in umbrella module?
You’re running
linkDebugFrameworkIosArm64
task and the task that builds the framework connected to cocoapods plugin would be called
link**Pod**DebugFrameworkIosArm64
with this configuration. And the problem is that cocoapods plugin links dependencies only to its own framework.
c

Cas Van Luijtelaar

11/15/2023, 9:34 AM
hmm yeah. Our build is a two step process
Copy code
./gradlew clean podspec podPublishReleaseXCFramework
./gradlew kSwiftMultiplatformLibraryPodspec
it seems the kswift step is the one failing, so I’ll have to look into what it is doing
👍 1
Kswift is just wrapping the
framework.linktask
Copy code
target.binaries
            .withType<Framework>()
            .configureEach { applyToAppleFramework(it, processor, kSwiftExtension) } 

 private fun applyToAppleFramework(
        framework: Framework,
        processor: KLibProcessor,
        kSwiftExtension: KSwiftExtension
    ) {
        val linkTask: KotlinNativeLink = framework.linkTask
        linkTask.doLast(PostProcessLinkTask(framework, processor, kSwiftExtension))
        registerPodspecTask(linkTask, kSwiftExtension)
    }
...
and looking at the logs it seems this linktask is the
linkDebugFrameworkIosArm64
I don’t quite understand how this relates to our setup and if/how we can change this to the
linkDebugFrameworkIosArm64
a

a-dd

11/15/2023, 12:14 PM
Can you show me ios targets definitions in
umbrella
?
Or in the convention plugin
There must be a framework declared somewhere, and with cocoapods plugin it’s a misconfiguration
c

Cas Van Luijtelaar

11/15/2023, 12:16 PM
Copy code
kotlin {
    val xcf = XCFramework()

    android {
        mavenPublication {
            artifactId = project.name
        }
    }

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = libBaseName
            xcf.add(this)
        }
    }

    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        compilations.get("main").kotlinOptions.freeCompilerArgs += "-Xexport-kdoc"
    }
}
a

a-dd

11/15/2023, 12:18 PM
Yep,
it.binaries.framework
declares the framework which can’t be built because it requires linking with the pod binary
c

Cas Van Luijtelaar

11/15/2023, 12:18 PM
so declare the linkOnly pod in here?
a

a-dd

11/15/2023, 12:22 PM
No (there’s no decent API for that currently). Is there a reason why you need another framework declaration?
c

Cas Van Luijtelaar

11/15/2023, 12:38 PM
this was in a buildSrc folder that was being added to every submodule. why? 🤷🏼‍♂️ I don’t quite understand the difference between the
XCFramework
and
framework
or what to define where
a

a-dd

11/15/2023, 1:00 PM
Let me write a short explanation of what’s happening: Framework — basically a bundle of a binary (for single architecture and platform) and headers XCFramework — bundle of multiple frameworks for different platforms/architectures Frameworks in KMP are declared within targets by invoking
sometarget.binaries.framework {}
In Kotlin world frameworks are needed only for integration to iOS world (when you build a final artifact/binary). XCFrameworks need to be declared only when you publish them directly (not through CocoaPods). CocoaPods plugin declares its own frameworks for each iOS target implicitly (it’s probably a design flaw, but it’s the way it works now). And when it builds binaries from Pod-dependencies it links then only to these frameworks and ignores any other. In practice, it means that: 1) If you’re using CocoaPods plugin, you shouldn’t declare any other frameworks. There’ll probably be an error or a warning for that soon. 2) One should declare frameworks or XCFrameworks only when it’s required. 3) And therefore declaring frameworks in a convention plugin is not a good idea, because most likely only one (maybe a few) module will need them.
c

Cas Van Luijtelaar

11/15/2023, 1:18 PM
thanks for that, that really should be documented somewhere!
👌 1
😢 1
removed the xcframework and instead defining it in the framework directly now:
Copy code
framework {
            isStatic = false
            baseName = libBaseName
            exportedProjects.forEach { export(project(":$it")) }

            iosX64()
            iosArm64()
            iosSimulatorArm64()
        }
running into some other kswift issues now so I cannot confirm it resolved it. mainly that
kSwiftMultiplatformLibraryPodspec
no longer exists
a

a-dd

11/15/2023, 1:29 PM
Now it looks like more of a kswift issue
btw declaring targets inside
framework
block doesn’t make much sense — it works but it’s basically the same as
Copy code
kotlin {
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    cocoapods { framework { .... } }
}
1
c

Cas Van Luijtelaar

11/24/2023, 12:24 PM
deploying has been working for us. unfortunately there are some issues with tests. mostly if one submodule depends on the submodule with the pods. and try to run the iOS tests from the submodule. it will fail with another “[pod] not found”. I guess one solution would be to add a “linkOnly” pod to all submodules that depend on it. but that’s obviously not very scalable
8 Views