xcode
近期有自定义CoreImage的CIFilter的需求,前期通过CIKL 定义 CIKernel完成了任务,后面了解到CoreImage新特性支持metal的方式直接自定义 CIKernel,提高效率。
CIKL的方式,存在两个问题:
- 编写 kernel 的时候,没有报错提示,哪怕是参数名错误都无法检查处理。效率极低。
- 翻译转换,编译,都是发生到运行时,导致第一次使用滤镜的时候,耗时较久。
Metal: 在build阶段 就可以编译 链接 .metal文件

参考苹果的官方文档 Metal Shading Language for CoreImage Kernels ,在xcode integration 部分提到在build setting 设置 Other Metal Compiler Flags, 文档已经很老了(2018年的),新版的xcode已经没有这个选项了,如果不做处理,会有报错 "/air-lld:1:1: symbol(s) not found for target 'air64-apple-ios12.0.0'" and "air-lld command failed with exit code 1 (use -v to see invocation)"
build rules
新版xcode中可以通过配置build rules解决上面的报错
*.metal
1 | xcrun metal -c $MTL_HEADER_SEARCH_PATHS -fcikernel "${INPUT_FILE_PATH}" -o "${SCRIPT_OUTPUT_FILE_0}" |
output files : $(METAL_LIBRARY_OUTPUT_DIR)/$(INPUT_FILE_BASE).metallib
*.air
1 | xcrun metallib -cikernel "${INPUT_FILE_PATH}" -o "${SCRIPT_OUTPUT_FILE_0}" |
output files : $(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).air
如图:
cocoapods
build rules 的方式存在一个问题,如果metal shader文件在pod库中,在主工程从配置build rules无法针对pod中的resouce 生效,虽然可以手动针对pod target 配置build rules解决问题,但是这样配置是一次性的,无法提交保存,下一次pod update就清空了,所以到了这里就很自然的能想到通过pod 的post hook 来解决问题,接下来就是怎么用ruby 来写 pod hook 脚本了
通过之前在主工程配置build rules, 可以看到project.pbxproj文件的变更情况
1 | /* Begin PBXBuildRule section */ |
哈哈 ,这正是我们需要的build rule的字段
最终的 MetalBuildRule.rb 文件如下 :
1 | #!/usr/bin/ruby |
podfile 文件里加载 MetalBuildRule.rb, 配置hook
1 | post_install do |installer| |
framework
如果pod库是通过cocoapods-packager插件 打.a 或者 .framework的方式提供给主工程使用的话,发现还是会遇到上文提到的错误 "/air-lld:1:1: symbol(s) not found for target 'air64-apple-ios12.0.0'" and "air-lld command failed with exit code 1 (use -v to see invocation)"
这里需要简单了解下 cocoapods-packager 的原理,浅析 Cocoapods-Packager 实现.
因为 cocoapods-packager 会重新生成一个podfile 来构造一个打包用的工程,所以这个错误的出现跟文章最开始提到的情况是一模一样的,解法是不是也可以通过配置build rule来解呢,不过打包工程我们看起来好像无法干预,怎么解呢?
还是要回到cocoapods-packager插件来解决问题。
1 | https://github.com/CocoaPods/cocoapods-packager |
- git 代码拉下来
- 通过
ide(vscode/rubymine)打开插件工程 配置好工程ruby环境 - DEBUG 代码,找到干预点
- 设置
build rule - 生成
packager gem,安装
这里涉及到ruby gem bundle等ruby环境的基本命令/用法,可以自行google一下。
通过刚刚提到的插件原理,很容易找到干预点
1 | pod_utils.rb |
修改:
1 | def install_pod(platform_name, sandbox) |
最后重新生成、安装gem
1 | !/bin/bash |
podfile
使用自定义的cocoapods-packager打出来的二方库 Framework包,包内容里面已经替换成xxx.metallib文件了,所以主工程的podfile pod post hook 要根据二方库的接入方式做下处理。

我这边主工程是用过cocoapod-binary插件管理二方库的加入,源码&静态Framework,一般Release模式提升编译速度,都是以framework方式,Debug模式有时候需要在主工程Debug二方库,可以选择是源码方式接入。
最终的逻辑如下:
1 | post_install do |installer| |
DONE
references
MetalCIKLReference
Add custom build rule with Podfile post_install hook
xcodeproj
xcode工程文件解析
CocoaPods源码与插件断点调试