如何构建无需Rosetta即可在M1 Xcode运行的KMM XCFramework
解决M1设备Xcode无需Rosetta运行KMM XCFramework的问题
问题背景
在M1设备的Xcode中不启用Rosetta运行时,使用自行构建的KMM XCFramework出现链接错误,提示找不到iOS Simulator-arm64架构的符号。原构建脚本仅打包了真机arm64和模拟器x86_64架构,无法适配M1原生模拟器。
原Shell构建脚本
#!/bin/bash ./gradlew :shared:packForXCodeArm -PXCODE_CONFIGURATION=Release ./gradlew :shared:packForXCodeX64 -PXCODE_CONFIGURATION=Release FRAMEWORK_NAME="shared" ARM64PATH="shared/build/xcode-framework-arm/${FRAMEWORK_NAME}.framework" X64PATH="shared/build/xcode-framework-X64/${FRAMEWORK_NAME}.framework" UNIVERSAL_PATH="shared/build/xcode-framework-universal/" xcodebuild -create-xcframework -framework "${ARM64PATH}" -framework "${X64PATH}" -output "${UNIVERSAL_PATH}/${FRAMEWORK_NAME}.xcframework"
原build.gradle.kts配置
val packForXcodeArm by tasks.creating(Sync::class) { group = "build" val mode = System.getenv("CONFIGURATION") ?: "DEBUG" val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosArm64").binaries.getFramework(mode) inputs.property("mode", mode) dependsOn(framework.linkTask) val targetDir = File(buildDir, "xcode-framework-arm") from({ framework.outputDirectory }) into(targetDir) } val packForXcodeX64 by tasks.creating(Sync::class) { group = "build" val mode = System.getenv("CONFIGURATION") ?: "DEBUG" val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosX64").binaries.getFramework(mode) inputs.property("mode", mode) dependsOn(framework.linkTask) val targetDir = File(buildDir, "xcode-framework-X64") from({ framework.outputDirectory }) into(targetDir) }
报错信息
ld: warning: ignoring file /Users/.../Library/Developer/Xcode/DerivedData/froeling_ios-dwqfcfqkcvofjtgtuipjhkedehfx/Build/Products/Debug-iphonesimulator/shared.framework/shared, building for iOS Simulator-arm64 but attempting to link with file built for iOS Simulator-x86_64 Undefined symbols for architecture arm64: "_OBJC_CLASS_$_SharedNetworkRequests", referenced from: objc-class-ref in LoginView.o
解决方案
要适配M1原生模拟器,需要补充打包iOS Simulator-arm64架构,并将其与模拟器x86_64合并为通用模拟器framework,再与真机framework一起构建XCFramework。
1. 更新build.gradle.kts,添加Simulator Arm64打包任务
在原配置基础上,新增针对iosSimulatorArm64目标的打包任务:
val packForXcodeSimulatorArm by tasks.creating(Sync::class) { group = "build" val mode = System.getenv("CONFIGURATION") ?: "DEBUG" val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosSimulatorArm64").binaries.getFramework(mode) inputs.property("mode", mode) dependsOn(framework.linkTask) val targetDir = File(buildDir, "xcode-framework-simulator-arm") from({ framework.outputDirectory }) into(targetDir) }
同时确保KMM的iOS目标已包含iosSimulatorArm64:
kotlin { ios { binaries.framework { baseName = "shared" } targets { iosArm64() iosX64() iosSimulatorArm64() // 确认此目标已添加 } } // 其他配置... }
2. 更新Shell构建脚本
修改脚本,打包三个架构并合并模拟器架构:
#!/bin/bash # 打包三个目标架构 ./gradlew :shared:packForXcodeArm -PXCODE_CONFIGURATION=Release ./gradlew :shared:packForXcodeX64 -PXCODE_CONFIGURATION=Release ./gradlew :shared:packForXcodeSimulatorArm -PXCODE_CONFIGURATION=Release FRAMEWORK_NAME="shared" # 各架构framework路径 DEVICE_ARM_PATH="shared/build/xcode-framework-arm/${FRAMEWORK_NAME}.framework" SIM_X64_PATH="shared/build/xcode-framework-X64/${FRAMEWORK_NAME}.framework" SIM_ARM_PATH="shared/build/xcode-framework-simulator-arm/${FRAMEWORK_NAME}.framework" # 临时目录:合并后的模拟器通用framework UNIVERSAL_SIM_PATH="shared/build/xcode-framework-universal-simulator" # 最终XCFramework输出目录 FINAL_UNIVERSAL_PATH="shared/build/xcode-framework-universal" # 创建合并后的模拟器framework目录 mkdir -p "${UNIVERSAL_SIM_PATH}/${FRAMEWORK_NAME}.framework" # 复制资源文件和Info.plist(从x64架构复制即可) cp -r "${SIM_X64_PATH}/." "${UNIVERSAL_SIM_PATH}/${FRAMEWORK_NAME}.framework/" # 合并模拟器的x86_64和arm64二进制文件 lipo -create "${SIM_X64_PATH}/${FRAMEWORK_NAME}" "${SIM_ARM_PATH}/${FRAMEWORK_NAME}" -output "${UNIVERSAL_SIM_PATH}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" # 构建最终的XCFramework,包含真机和通用模拟器framework xcodebuild -create-xcframework \ -framework "${DEVICE_ARM_PATH}" \ -framework "${UNIVERSAL_SIM_PATH}/${FRAMEWORK_NAME}.framework" \ -output "${FINAL_UNIVERSAL_PATH}/${FRAMEWORK_NAME}.xcframework"
3. 验证结果
构建完成后,使用lipo -info查看XCFramework内的二进制架构:
lipo -info shared/build/xcode-framework-universal/shared.xcframework/ios-arm64/shared.framework/shared # 输出:Non-fat file: ... is architecture: arm64 lipo -info shared/build/xcode-framework-universal/shared.xcframework/ios-x86_64-simulator/shared.framework/shared # 输出:Architectures in the fat file: ... are: x86_64 arm64
此时XCFramework同时支持真机arm64、模拟器x86_64和arm64,无需启用Rosetta即可在M1设备的Xcode中正常运行。
内容的提问来源于stack exchange,提问作者swalkner
相关产品推荐
相关产品推荐

