内容简介:好久没有写博客了,有一年多了吧,想想那些能够安心码字的日子还甚是怀念,于是今晚无论外界条件怎样的恶劣,这一篇是一定要更新的。想必 CocoaPods 和 Carthage 对于 iOS 开发者而言都不会陌生,今天的这一篇我们就来看看混合使用这两者,以及多个去年下半年,我们内部搭建了一个私有的 CI 平台,主要用于跑一些自动化和日常的测试打包。由于机器性能很强悍,很快成了大家构建新包的首选,原本应该是个愉快的事情,可是在系统这样跑了几个月后,恼人的问题突然就出现了:大家在自己的机器上编译没任何问题,而在 CI
好久没有写博客了,有一年多了吧,想想那些能够安心码字的日子还甚是怀念,于是今晚无论外界条件怎样的恶劣,这一篇是一定要更新的。
想必 CocoaPods 和 Carthage 对于 iOS 开发者而言都不会陌生,今天的这一篇我们就来看看混合使用这两者,以及多个 .xcassets
的情况下,一些莫名其妙的问题。
问题的出现
去年下半年,我们内部搭建了一个私有的 CI 平台,主要用于跑一些自动化和日常的测试打包。由于机器性能很强悍,很快成了大家构建新包的首选,原本应该是个愉快的事情,可是在系统这样跑了几个月后,恼人的问题突然就出现了:大家在自己的机器上编译没任何问题,而在 CI 里发起构建,每次都会失败在 Build Phases
中的 [CP] Copy Pods Resources
这一步,查看详细日志的话,主要是下面的这段错误:
error: None of the input catalogs contained a matching stickers icon set or app icon set named "AppIcon".
我们的主工程比较大,代码和资源文件都很多,技术选型上是采用了 CocoaPods ( 1.5.x 版本
) 来做模块化,模块拥有各自的 .xcassets
来存放资源,所以会有多个 .xcassets
文件。在此基础上,我们还依赖了一些 Swift 开源库,所以也使用了 Carthage 来管理依赖。
第一次解决问题
问题必须解决,但我们的 .xcassets
中,肯定是有一个含有 “AppIcon” 的,于是我找了一个时间,仔细分析了下错误的详细日志,发现里面有几处这样的警告:
warning: The app icon set name "AppIcon" is used by multiple app icon sets.
这个警告提示我们 “AppIcon” 冲突了,然后看了下冲突的路劲,尽然都是在 Carthage/Checkouts 目录下。由于我们依赖的 Carthage 库是以源码编译成 Framework 的,而这些源码中有示例和测试项目,其中包括了一些 .xcassets
,最主要的是 CocoaPods 把这些目录下的 .xcassets
都编译到了最终输出的目标中。
相关 issue: https://github.com/CocoaPods/CocoaPods/issues/6159#issuecomment-296698412
不查不知道,一查吓一跳啊,有种“我们 App 被偷偷植入了一些莫名其妙的资源”的感觉,也很庆幸以往的 AppIcon 能正常显示,甚至是很意外它尽然能正常显示。与此同时,错误日志中还有一些其他资源名称冲突的警告,我把这些资源名称和它对应的 .xcassets
名称一一对应的提取了出来,然后在模块间查找、对比,发现尽然存在名称一致但长相完全不同的图片,庆幸的是较新的图片得到了显示。捏了一把冷汗,在手动处理完所有名称冲突后,我把 CI 服务中的构建脚本修改了下,在编译前执行了下面命令:
rm -rf Carthage/Checkouts/**/*.xcassets || :
删除了这些不相干、也没任何作用的 .xcassets
。做完这一切,我在 CI 上发起了一个构建,然后真的就成功了。可这没法解释为啥原先本地没问题,隐隐觉得还没有找到问题的主线,这次只是完成了一个支线任务。
大家又开始愉快地使用 CI 了。
第二次解决问题
时间飞快,好景不长,过了一个月左右,相同的问题、相同的错误信息再一次出现在我面前,而我再一次翻起那详细日志时,里面已没有了任何重复冲突的警告。这一次,我开始认真思考:本地发起构建和通过 CI 发起构建到执行 [CP] Copy Pods Resources
这一步,到底有什么区别?通过一系列测试,发现 Shell 的环境不同,但无法确定环境中哪些会影响到这一步执行,没办法,只能细看下 CocoaPods 这一步自动生成的脚本了:
# 其中 #{Target Name} 为主工程输出目标名称 Pods/Target Support Files/Pods-#{Target Name}/Pods-#{Target Name}-resources.sh
这个脚本主要就是拷贝和编译通过 CocoaPods 所依赖的资源文件,和 .xcassets
相关的主要是 XCASSET_FILES
这个变量,以及最后的这段脚本:
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" else printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" fi
所有的 .xcassets
文件都存在了 XCASSET_FILES
这个数组里面了,然后这个数组传递给 actool
来进行编译成 Assets.car
文件。脚本看完后,通过在文件头部加上 set -x
开启了它的调试,在茫茫的输出中,我死盯着 XCASSET_FILES
这个变量,然后惊人的发现这个变量中所有依赖而来的 .xcassets
都变成了两份!真心不知道 actool
在面对这些重复的路劲是怎么的处理,又捏了一把冷汗,于是乎在 Podfile
中加上了这样一些代码:
post_install do |installer| # 其中 #{Target Name} 为主工程输出目标名称 copy_pods_resources_path = "Pods/Target Support Files/Pods-#{Target Name}/Pods-#{Target Name}-resources.sh" str1 = 'printf "%s\0" "${XCASSET_FILES[@]}"' str2 = 'printf "%s\n" "${XCASSET_FILES[@]}" | sort -u | tr \'\n\' \'\\\\0\' ' text = File.read(copy_pods_resources_path) new_contents = text.gsub(str1, str2) File.open(copy_pods_resources_path, "w") {|file| file.puts new_contents } end
通过对 CocoaPods 自动生成文件内容进行替换,我们插入了一段脚本,最终对 XCASSET_FILES
中的条目进行了去重。做完这一切,我又在 CI 上发起了一个构建,然后它又成功了,然后还是没法解释为啥本地没问题,所以,注定了这还是一个支线任务。
可是,大家再一次愉快地使用 CI 了。
第三次解决问题
过了很长一段时间,长到我都以为这个问题真的彻底解决了,但冷不丁的就在前几天,这个问题又出现了。都说事不过三,这问题一次又一次的反复,也实在是让我颜面尽失,大过年的,你这该死又淘气的 CocoaPods。按捺住心中的烦躁不安,我又一次仔细地把那自动生成的脚本撸了一遍,可能是内心足够安静了吧,这一次我尽然只凭理论分析,就找到了罪魁祸首 xargs
。
xargs
不仅能正确处理空格之类的转义,还会在超过一定的限制后,把传递给它的参数 分批
传递给后续的命令, XCASSET_FILES
中就是我们所存储的参数。 xargs
分批传递的限制主要是两个参数: -n
的条目限制和 -s
的大小限制,其中 -s
的大小限制受环境变量 ARG_MAX
影响。
所以,一旦我们的 XCASSET_FILES
被分批传递给了 actool
,其中只有某一批里面有“AppIcon”,其它的自然会报错。由于环境不同, ARG_MAX
值不一致,这也解释了一直没法解释的那个问题。前面的两次修复,都不经意间缩减了 XCASSET_FILES
中的内容,而我们的 .xcassets
文件在慢慢增多,一旦突破了限制,问题就又出现了。
感觉找打了主线,于是修改了下 Podfile 中的代码:
post_install do |installer| # 其中 #{Target Name} 为主工程输出目标名称 copy_pods_resources_path = "Pods/Target Support Files/Pods-#{Target Name}/Pods-#{Target Name}-resources.sh" str1 = 'printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0' str2 = 'printf "%s\n" "${XCASSET_FILES[@]}" | sort -u | tr \'\n\' \'\\\\0\' | xargs -0 -s 20480 -n 100' text = File.read(copy_pods_resources_path) new_contents = text.gsub(str1, str2) File.open(copy_pods_resources_path, "w") {|file| file.puts new_contents } end
搞完后,我在 CI 上再一次发起了构建,如预期的一样,再一次的成功了,我相信这问题不会再出现了。
大家又开始愉快地使用 CI 了。
总结一下
这个问题让我纠结了大半年时间,终于在这新春佳节里给彻底解决了,大体来说就是这样:
-
如果你混合使用了 CocoaPods 和 Carthage,确认下 Carthage 的所有目录里是否有
.xcassets
,如果有的话,确认下是否被打包到你最后的 App 里了 -
注意 CocoaPods 生成的脚本中,
XCASSET_FILES
里的条目有重复 -
CocoaPods 生成的脚本中,最终传递给
actool
编译的参数,一定不能被xargs
分批传递
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 记一次 Spring 项目打包问题排查
- webpack dll打包重复问题优化
- 草率了,又一个 Maven 打包的问题
- 关于css打包后过大的问题
- 如何解决以太坊上矿工选择性打包的问题?
- VUE-Router路由懒加载,打包问题(下午更改)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
移动应用的设计与开发
[美] 弗林 (Brian Fling) / 马晶慧 / 电子工业出版社 / 2010-5 / 59.80元
本书全面介绍了如何在移动设备上设计和开发应用程序。书中从介绍移动产业的生态环境和移动媒体开始,阐述产品策划的方法、产品架构、视觉设计和产品类型的选择,并详细描述了产品实现过程中所用到的一些技术、工具和概念,最后还简单介绍了如何获得利润和降低成本,肯定了iPhone在移动设备发展史上起到的巨大推动作用。本书不仅能让读者了解到移动设计和开发的知识,更重要的是,它揭示了移动开发的代价高昂、标准混乱的根本......一起来看看 《移动应用的设计与开发》 这本书的介绍吧!