简洁版
如何在iOS项目中添加两个或多个Kotlin Native模块,而不会出现“重复符号”错误?
详细问题
假设一个多模块KMP项目,其中存在一个Android本地应用程序和一个iOS本地应用程序以及两个通用模块来保存共享的Kotlin代码。
.
├── android
│ └── app
├── common
│ ├── moduleA
│ └── moduleB
├── ios
│ └── app
模块 A 包含一个数据类 HelloWorld,并且没有依赖任何其他模块:
package hello.world.modulea
data class HelloWorld(
val message: String
)
模块B包含HelloWorld类的扩展函数,因此它依赖于模块A:
package hello.world.moduleb
import hello.world.modulea.HelloWorld
fun HelloWorld.egassem() = message.reversed()
这些模块的 build.gradle 配置如下:
- 模块 A
apply plugin: "org.jetbrains.kotlin.multiplatform"
apply plugin: "org.jetbrains.kotlin.native.cocoapods"
…
kotlin {
targets {
jvm("android")
def iosClosure = {
binaries {
framework("moduleA")
}
}
if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {…}
}
cocoapods {…}
sourceSets {
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"
}
androidMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
}
iosMain.dependencies {
}
}
}
- 模块 B
apply plugin: "org.jetbrains.kotlin.multiplatform"
apply plugin: "org.jetbrains.kotlin.native.cocoapods"
…
kotlin {
targets {
jvm("android")
def iosClosure = {
binaries {
framework("moduleB")
}
}
if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {…}
}
cocoapods {…}
sourceSets {
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"
implementation project(":common:moduleA")
}
androidMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
}
iosMain.dependencies {
}
}
}
看起来非常简单,如果我将安卓的gradle构建依赖配置如下,它甚至能够在安卓上运行:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"
implementation project(":common:moduleA")
implementation project(":common:moduleB")
}
然而,这似乎不是在iOS上组织多个模块的正确方法,因为运行
./gradlew podspec
后,我得到了BUILD SUCCESSFUL
的预期结果,并且下面列出了相应的pods:pod 'moduleA', :path => '…/HelloWorld/common/moduleA'
pod 'moduleB', :path => '…/HelloWorld/common/moduleB'
即使我运行了 pod install
命令,也会输出成功信息 Pod installation complete! There are 2 dependencies from the Podfile and 2 total pods installed.
,在 Xcode 的 Pods 部分中正确显示了模块 A 和模块 B。
但是,如果我尝试构建 iOS 项目,就会出现以下错误:
Ld …/Hello_World-…/Build/Products/Debug-iphonesimulator/Hello\ World.app/Hello\ World normal x86_64 (in target 'Hello World' from project 'Hello World')
cd …/HelloWorld/ios/app
…
duplicate symbol '_ktypew:kotlin.Any' in:
…/HelloWorld/common/moduleA/build/cocoapods/framework/moduleA.framework/moduleA(result.o)
…/HelloWorld/common/moduleB/build/cocoapods/framework/moduleB.framework/moduleB(result.o)
… a lot of duplicate symbol more …
duplicate symbol '_kfun:kotlin.throwOnFailure$stdlib@kotlin.Result<#STAR>.()' in:
…/HelloWorld/common/moduleA/build/cocoapods/framework/moduleA.framework/moduleA(result.o)
…/HelloWorld/common/moduleB/build/cocoapods/framework/moduleB.framework/moduleB(result.o)
ld: 9928 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
我对iOS的了解不多,所以在我的生疏眼里,每个模块似乎都添加了自己版本的内容,而没有使用某种分辨率策略来共享它。
如果我仅使用模块A,则代码可以正常工作和运行,因此我知道代码本身是正确的,问题在于如何管理多个模块,那么问题是如何在iOS上同时添加两个模块(模块A和模块B)并使其正常工作?
附言
我尽可能地缩减了代码,只保留我认为是问题源头的部分,但是完整的代码可在这里获得,如果您想检查片段中是否有遗漏的任何内容或者想运行并尝试解决问题...
framework { baseName = "moduleA" export("moduleB") transitiveExport = true }
- Michael Bakogiannis