Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Latest commit

 

History

History
97 lines (67 loc) · 7.82 KB

multiple_commands_produce_framework.md

File metadata and controls

97 lines (67 loc) · 7.82 KB

这个文档对应主页提到的framework重复建构的问题,即内嵌依赖的xcframework和pod依赖的第三方库重复冲突,新版Xcode编译失败,在这里整理了更详细的分析。

复现问题

在Flutter Module & 插件库一侧

  • mmkv_flutter插件依赖了MMKV,编译后会生成MMKV.framework、MMKVCore.framework
  • sentry_flutter插件依赖了Sentry,编译后会生成Sentry.framework
  • 网络缓存插件依赖了FMDB,编译后会生成FMDB.framework

在iOS & Pods一侧

  • Podfile中同样依赖了Sentry和MMKV,还有个私有库依赖了FMDB

我在Flutter Module中使用了一些基于原生库的插件层,编译后会生成相关的framework,按照远程依赖的构建方案(未设置黑名单),导出的xcframework会集成到FlutterPluginSDKpod update之后会直接以framework形式内嵌到项目中,在项目中展开Pods / FlutterPluginSDK / frameworks路径的文件夹即可看到相关的xcframework。但同样在Pods下面也能找到FMDB、MMKV、...这个重复的第三方库,不过看到的是源码文件。此时编译iOS项目,会遇到失败并提示错误Multiple commands produce 'framework'

Multiple commands produce '/Users/user/Library/Developer/Xcode/DerivedData/MyProj-flazyqyatfvrvsgcoofvwrizuvot/Build/Products/Debug-iphoneos/MyProj.app/Frameworks/FMDB.framework':
1) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks2) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks”


Multiple commands produce '/Users/user/Library/Developer/Xcode/DerivedData/MyProj-flazyqyatfvrvsgcoofvwrizuvot/Build/Products/Debug-iphoneos/MyProj.app/Frameworks/MMKV.framework':
1) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks2) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks”


Multiple commands produce '/Users/user/Library/Developer/Xcode/DerivedData/MyProj-flazyqyatfvrvsgcoofvwrizuvot/Build/Products/Debug-iphoneos/MyProj.app/Frameworks/MMKVCore.framework':
1) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks2) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks”


Multiple commands produce '/Users/user/Library/Developer/Xcode/DerivedData/MyProj-flazyqyatfvrvsgcoofvwrizuvot/Build/Products/Debug-iphoneos/MyProj.app/Frameworks/Sentry.framework':
1) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks2) That command depends on command in Target 'MyProj' (project 'MyProj'): script phase “[CP] Embed Pods Frameworks”

Multiple commands produce '.framework'这个问题我以前也遇到过,当时是因为Today Target和主工程的Pod都依赖了同一个库,编译时也报这个错误。当时找到的原因是因为Xcode10之后,使用了新的构建系统,会检测重复构建产物。关于Xcode10新的构建系统,即New Build System,可以看官方的介绍文档Build System Release Notes for Xcode 10,虽然没提到Multiple commands produce,但是Google搜到的说法也是因为新版Xcode默认使用New Build System,涉及到重复构建产物。

Build System Release Notes for Xcode 10有提到duplicate output file问题,说是在不同的编译阶段build phases可能会出现重复输出文件错误。

Targets which have multiple asset catalogs that aren’t in the same build phase may produce an error regarding a “duplicate output file”. (39810274)

Workaround: Ensure that all asset catalogs are processed by the same build phase in the target.

而根据错误信息,Multiple commands produce错误都出现在script phase “[CP] Embed Pods Frameworks”脚本执行阶段,这个[CP] Embed Pods Frameworks在Xcode的Build Phases中即可找到,涉及1个shell脚本和几个xcfilelist文件,

  • Pods-MyProj-frameworks.sh
  • Pods-MyProj-frameworks-Debug-input-files.xcfilelist
  • Pods-MyProj-frameworks-Debug-output-files.xcfilelist
  • Pods-MyProj-frameworks-Release-input-files.xcfilelist
  • Pods-MyProj-frameworks-Release-output-files.xcfilelist

[CP] Embed Pods Frameworks

Pods-MyProj-frameworks.sh脚本会把Pods中依赖的第三方库都通过调用install_framework函数逐个生成签名的framework,不过没找到Multiple commands produce字样,但是能找到2个关键的信息,一个是文件导入的路径,即调用install_framework时传入的路径参数,另一个是输出的路径,即下面的destinationbinary

local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
binary="${destination}/${basename}.framework/${basename}"
  ...
  install_framework "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework"
  install_framework "${BUILT_PRODUCTS_DIR}/MMKV/MMKV.framework"
  install_framework "${BUILT_PRODUCTS_DIR}/MMKVCore/MMKVCore.framework"
  ...
  install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flutter/Flutter.framework"
  install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/FMDB/FMDB.framework"
  install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/MMKV/MMKV.framework"
  install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/MMKVCore/MMKVCore.framework"
  install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/mmkv_flutter/mmkv_flutter.framework"

再打开Pods-MyProj-frameworks-Debug-input-files.xcfilelistPods-MyProj-frameworks-Debug-output-files.xcfilelist文件,这2个文件分别声明了所有framework的输入路径和输出路径,跟Pods-MyProj-frameworks.sh脚本里面的用到的路径是一致的。从输入路径可以看到FMDB.framework / MMKV.framework / ...有不同的输入路径,在Pods-MyProj-frameworks-Debug-input-files.xcfilelist文件也可以看到,但是destinationbinary计算的输出路径是相同的,这个结论在Pods-MyProj-frameworks-Debug-output-files.xcfilelist文件也能得到验证,存在重复的输出路径。结合前面提到的Multiple commands produce错误信息,基本能定位到问题所在了,在一个Build Phase重复生成framework导致的错误。

Multiple commands produce错误都出现在script phase “[CP] Embed Pods Frameworks”脚本执行阶段

Pods-MyProj-frameworks-Debug-output-files.xcfilelist文件列出所有framework的输出路径,里面可以找到重复项,而正常情况是不会有重复项的。

...
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework
...
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MMKV.framework
...
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MMKV.framework

我目前尝试的可行方案有2种,其它的我没有试了。

    1. 选择旧的构建系统,具体的操作路径: Xcode -> File -> WorkSpace Settings -> Build System -> Legacy Build Sysyte,可行,但不建议,因为这个构建系统已经标记要废弃掉。
    1. 选择新的构建系统,但是要避免重复依赖,所以我从FlutterPluginSDK删除了重复依赖的xcframework。在构建脚本 flutter_build_script.sh中加了黑名单,把需要删除的xcframework 加到黑名单即可,这样编译的时候就不会重复framework。