SPM集成含多xcframework的binaryTarget时依赖框架未被包含的问题
问题背景
我尝试用Swift Package Manager(SPM)集成一个二进制Target,该二进制包是.zip文件,包含主框架ffmpegkit.xcframework及其依赖的7个额外.xcframework。
SPM解压后,DerivedData/.../SourcePackages/artifacts/...路径下的文件夹结构如下(共8个框架):
我的Package.swift代码如下:
// swift-tools-version: 5.10 import PackageDescription let package = Package( name: "converter", platforms: [ .macOS(.v13), .iOS(.v14), ], products: [ .library( name: "converter", targets: ["converter"]) ], targets: [ .target( name: "converter", dependencies: [ .target(name: "ffmpeg-iOS", condition: .when(platforms: [.iOS])), .target(name: "ffmpeg-macOS", condition: .when(platforms: [.macOS])) ], path: "Sources/converter" ), .binaryTarget(name: "ffmpeg-iOS", url: "https://github.com/arthenica/ffmpeg-kit/releases/download/v6.0/ffmpeg-kit-full-6.0-ios-xcframework.zip", checksum: "c87ea1c77f0a8a6ba396c22fc33e9321befb8e85f8e8103046ddeb74fea66182"), .binaryTarget(name: "ffmpeg-macOS", url: "https://github.com/arthenica/ffmpeg-kit/releases/download/v6.0/ffmpeg-kit-full-6.0-macos-xcframework.zip", checksum: "8cab26eecd43b9389d37f64efaf43b9c6baf4e53614b62e6209d8ee8681b94b9") ] )
构建运行时出现dyld[85157]: Library not loaded: @rpath错误,原因是构建仅包含ffmpegkit.xcframework,未引入其他7个依赖框架——即使代码未import ffmpegkit也会触发该错误。
我目前的临时解决方案是:新建GitHub仓库存放这7个压缩后的.xcframework,将它们作为独立binaryTarget添加到Package.swift中,现在这些框架能随包安装,但原ffmpegkit.xcframework仍包含未被使用的7个框架。
疑问解答
1. 为什么SPM只嵌入ffmpegkit框架,忽略其他7个?
不是按字母顺序选择。SPM默认仅处理单个XCFramework的二进制包,当传入包含多个XCFramework的zip时,它不会自动识别框架间的依赖关系,只会挑选一个它判定为“主框架”的文件嵌入。
具体逻辑是:SPM会在解压后的目录中查找符合当前平台架构的XCFramework,优先选择名称与binaryTarget名称关联度高的,或者遍历到的第一个适配平台的框架。在你的场景中,ffmpegkit.xcframework是核心主框架,SPM识别到它的核心地位后,仅嵌入了该框架,未处理其他未被明确声明的依赖框架。
2. 如何让SPM直接使用打包在一起的所有框架?
有两种可行方案:
方案1:给二进制包添加子Package配置
将所有8个XCFramework放到同一目录下,在该目录中创建Package.swift,将每个XCFramework声明为binaryTarget并明确依赖关系(比如让ffmpegkit依赖其他7个框架),再将整个目录打包成zip。之后在项目的Package.swift中直接依赖这个包含子Package的zip即可。
子Package的Package.swift示例:
// swift-tools-version: 5.10 import PackageDescription let package = Package( name: "ffmpeg-kit-full", platforms: [.iOS(.v14), .macOS(.v13)], products: [ .library(name: "ffmpegkit", targets: ["ffmpegkit"]) ], targets: [ .binaryTarget(name: "ffmpegkit", path: "./ffmpegkit.xcframework"), .binaryTarget(name: "依赖框架1", path: "./依赖框架1.xcframework"), .binaryTarget(name: "依赖框架2", path: "./依赖框架2.xcframework"), // 剩余5个依赖框架按上述格式添加 ] )
方案2:在当前Package.swift中显式声明所有框架
无需额外创建仓库,直接修改项目的Package.swift,为每个依赖框架单独创建binaryTarget,使用同一个zip包URL,通过path参数指定框架在zip内的相对路径,并给ffmpegkit添加依赖声明。
修改后的Package.swift示例(仅展示核心部分):
// swift-tools-version: 5.10 import PackageDescription let package = Package( name: "converter", platforms: [.macOS(.v13), .iOS(.v14)], products: [.library(name: "converter", targets: ["converter"])], targets: [ .target( name: "converter", dependencies: [.target(name: "ffmpegkit")], path: "Sources/converter" ), // 主框架,添加对其他7个框架的依赖 .binaryTarget( name: "ffmpegkit", url: "https://github.com/arthenica/ffmpeg-kit/releases/download/v6.0/ffmpeg-kit-full-6.0-ios-xcframework.zip", checksum: "c87ea1c77f0a8a6ba396c22fc33e9321befb8e85f8e8103046ddeb74fea66182", path: "./ffmpegkit.xcframework", dependencies: [ .target(name: "依赖框架1"), .target(name: "依赖框架2") // 剩余5个依赖框架按此格式添加 ] ), // 依赖框架1 .binaryTarget( name: "依赖框架1", url: "https://github.com/arthenica/ffmpeg-kit/releases/download/v6.0/ffmpeg-kit-full-6.0-ios-xcframework.zip", checksum: "c87ea1c77f0a8a6ba396c22fc33e9321befb8e85f8e8103046ddeb74fea66182", path: "./依赖框架1.xcframework" ), // 剩余6个依赖框架按上述格式添加,注意区分iOS/macOS的zip包URL和checksum ] )
这样SPM会从同一个zip包中解压所有需要的XCFramework,并正确处理它们的依赖关系,不会遗漏任何框架。
内容的提问来源于stack exchange,提问作者Darren

