Swift Package Bundle中禁止MLModel自动编译的方案咨询
问题根因
这不是Xcode 14 beta的bug,是Swift Package Manager(SPM)的默认设计逻辑:所有后缀为.mlmodel/.mlpackage的文件,无论你在资源配置中声明为.copy还是.process,都会被内置规则识别为待编译的CoreML模型,构建阶段会自动调用coremlc工具将其编译为.mlmodelc格式,原始文件不会被保留到最终的测试Bundle中,因此你遍历资源路径时只能看到编译后的产物。
SPM没有提供公开配置项关闭针对特定文件的自动编译规则,直接修改构建配置的路径走不通,下面是两个可落地的解决方案,按易用性排序:
方案1:修改文件后缀绕过内置识别(推荐,全版本兼容)
这是目前跨Xcode版本稳定性最高、成本最低的方案,核心就是绕开SPM对.mlmodel后缀的匹配规则:
- 将测试用的原始模型文件重命名,修改后缀为非CoreML识别的格式,比如把
testmodel.mlmodel改成testmodel.mlmodel_raw,放到测试target的Resources目录下 - 调整testTarget的资源配置,直接拷贝重命名后的文件:
targets: [ .target( name: "packageName", dependencies: ["package1"] ), .testTarget( name: "packageNameTests", dependencies: ["packageName"], resources: [ .copy("Resources/testmodel.mlmodel_raw"), ] ) ]
- 测试代码中读取到该文件后,先将其复制到临时目录并重命名回
.mlmodel后缀,即可用于验证自定义的模型编译流程,参考代码:
let fileManager = FileManager.default // 定位Bundle中存储的原始模型文件 guard let rawModelPath = Bundle.module.path(forResource: "testmodel", ofType: "mlmodel_raw") else { fatalError("测试原始模型文件缺失") } // 创建临时工作目录 let tempDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("ModelTestTemp", isDirectory: true) try? fileManager.createDirectory(at: tempDirURL, withIntermediateDirectories: true) // 将文件拷贝到临时目录并恢复.mlmodel后缀 let targetModelURL = tempDirURL.appendingPathComponent("testmodel.mlmodel") try? fileManager.removeItem(at: targetModelURL) try fileManager.copyItem(atPath: rawModelPath, toPath: targetModelURL.path) // 此时targetModelURL.path就是未编译的原始mlmodel文件路径,可直接传入你的编译测试逻辑
方案2:通过构建插件自定义拷贝逻辑(适合不愿修改原文件的场景)
如果不想修改原始模型文件的后缀,可以通过SPM构建插件手动拷贝文件,跳过默认编译流程:
- 首先在testTarget配置中用
.exclude规则把原始mlmodel文件从默认资源处理流程中剔除,避免被自动编译:
.testTarget( name: "packageNameTests", dependencies: ["packageName"], exclude: ["Resources/testmodel.mlmodel"], plugins: [ // 引入自定义的资源拷贝构建插件 ] )
- 编写轻量的SPM构建工具插件,在构建阶段手动将被排除的原始mlmodel文件拷贝到测试Bundle的资源目录下,因为文件已经被标记为exclude,SPM不会再对其执行CoreML编译操作。
注意:该方案需要编写额外的插件代码,且仅在Xcode 14及以上版本对测试target有稳定支持,低版本Xcode存在插件不生效的兼容性问题。
内容的提问来源于stack exchange,提问作者Feitan
相关产品推荐
相关产品推荐

