感谢美团技术团队《美团 iOS 工程 zsource 命令背后的那些事儿》和 LevySu《iOS美团同款"ZSource"二进制调试实现》提供的思路,以下是我的插件实现过程。
一、二进制文件调试的实现原理
如果编译二进制文件时,保留了调试信息,XCode 会在进入断点调试时,根据调试信息中的编译目录,搜索源码文件,如果存在,则跳转显示到对应的行。所说的二进制调试热切换插件,其实现的功能,即下载源码到指定的目录,使 XCode 可以找到具体文件。
当然了,如果你愿意去读每一个二进制文件的调试信息,也可以不进行插件的开发。根据上文所述,使用 dwarfdump 命令读取调试信息,找到编译目录,然后通过 git 指令或者其它任何源码下载的方式,把源码下载下来,放到对应的编译目录下,也可以完成相同的效果。实际上插件完成的工作也就是如上所述的工作。插件的开发,是为了简化过程,同时推动编译打包的标准化。
二、插件的开发
下面介绍通过扩展 Cocoapods 命令的方式开发一款二进制调试热切换插件。
打开终端,进入一个自己喜欢的工作目录,执行以下指令,创建一个 Cocoapods 插件工程。
pod plugins create 'pusource'
根据 Cocoapods 的官方文档,一个插件可以做三件事情
- Hook into the install process, both before and after
- Add new commands to
pod- Do whatever they want, because Ruby is a very dynamic language
这里,我们要做的事情是为 Cocoapods 增加一个新的命令,叫做 pusource 。
查看工程目录,找到如下图的文件,我们后续的开发就在这个文件中完成。
下面我们来思考需要什么参数。在我的插件中,我设计接收以下参数
- 库名(目录名)
- git地址
- 本地工作目录
- 分支名或tag名
据此,我的指令初始化方法是这样的
def initialize(argv)
@name = argv.shift_argument
@git = argv.shift_argument
@localPath = argv.shift_argument
@tag = argv.option('tag')
@branch = argv.option('branch')
super
end
其中 @localPath 是可选参数,需要默认值。其默认值是统一的工程化编译机上的工作目录。一般是 Jenkins 中统一配置的 workspace 目录。相应的,@name 应该填写 Jenkins 中对应的 Job 名。
参数的校验和默认值的设置在 validate! 方法中完成
参数中,@tag 和 @branch 是通过 option 接收的,需要增加 option 的定义,即添加如下方法代码
def self.options
[
["--tag=/tag", 'The tag of the source code git'],
["--branch=/branch", 'The branch of the source code git']
].concat(super)
end
最后是具体的命令过程,代码如下
def run
if File.directory?(@localPath) == false
UI.puts "创建目录 "+@localPath
shell = 'sudo mkdir -p '+@localPath
UI.puts shell
system(shell)
end
local_path = @localPath+'/'+@name
if File.directory?(local_path) == true
# shell = 'sudo rm -rf '+local_path
UI.puts local_path+' 目录已存在,请检查是否是可用的源码'
return
end
shell = 'sudo git clone '+@git+' '+local_path+' --verbose'
if @branch.nil? == false
shell += ' --branch '+@branch
elsif @tag.nil? == false
shell += ' --branch '+@tag
end
UI.puts shell
system(shell)
end
这里有个可选择的逻辑,即当目标目录存在时,是删除然后重新下载呢,还是终止程序交由用户处理呢?看各人考虑了。
至此,这个插件的初版本,通过 gem 安装后就可以使用了。
三、后续
以上过程实现的只是最基本的核心功能,还有不少可优化、可扩展的地方。同时,在此附上一篇文章《iOS组件二进制源码调试热切换方案》,这篇文章提供了另外一种方案来实现二进制调试热切换,值得一看。
以上。