fastlane DSL 源码浅析
其实应该叫【Fastlane 是如何执行起来的?】或者【FastFile 到底是什么?】会比较合适。但是读这部分源码的初衷,除了好奇以外,更多是想抽离 Fastlane 的 DSL 这部分逻辑,结合 Rake 的优势,写一个好使轻量的 DSL。
环境信息
fastlane 2.85.0
在使用 fastlane 的时候,通常会写一个 FastFile 作为配置文件,比如:
# FastFile |
入口
fastlane
命令位于源码工程目录的 bin/fastlane,在做了校验和配置之后,直接调用了
Fastlane::CLIToolsDistributor.take_off |
整个命令的入口,都在 fastlane/lib/fastlane 目录中,当然,也包括上面的 cli_tools_distributor.rb。
在 take_off
方法中,主要做了三件事:
- 校验依赖,版本等
- 根据 action 参数生成对应的 action cmd,然后执行 cmd
- 如果指定的 action 未找到,则准备查找自定义 lane
除此之外,cli_tools_distributor.rb 还有一个有趣的方法,叫 process_emojis
:
def process_emojis(tool_name) |
可以通过表情来调用 action,比如 fastlane 💪
Action
Action 是 Fastlane 定义的一些操作,比如 match
,gym
,pilot
等,这部分会优先查找。因为不是主要讨论的部分,而且代码也不难,直接看代码就懂:
# 通过指定的名称,直接引用,比如 require match |
在 start
之后,内部就是通过 commander
来解析入参了。
Lane
如果调用 fastlane my_lane name:sai company:meitu
这样的命令,很明显 my_lane
不是 Fastlane 的 action,那么会进入到解析 FastFile,查找自定义 lane 的步骤。
在经过几个步骤之后,Lane 的查找交给了 Fastlane::FastFile
实例,步骤如下:
- fastlane/commands_generator.rb 中,默认
:trigger
调用Fastlane::CommandLineHandler.handle(args, options)
传入除fastlane
以外的全部输入。 - fastlane/commande_line_handler.rb 的
handle
方法中,分离输入中的 lane name 和参数部分,然后调用LaneManager
的cruise_lane
方法,开始认认真真的处理 lane。
而在 cruise_lane
方法中,也就对 lane 进行查找。流程大致如下:
Parse
FastFile 会在当前执行目录下查找 fastlane/FastFile。初始化时,会调用 Fastlane::FastFile
的 parse
方法,对文件进行解析。
其实就一句…
# data: FastFile 文件内容,来自 File.read(FastFile) |
DSL
终于到 DSL 的部分了。来看下 FastFile 中出现的 desc
,lane
,before_all
,error
都是什么。
在 fast_file.rb 的 DSL 注释部分,能看到全部的 FastFile 支持的方法。也就是说 desc
,lane
这些看似像关键字的配置项,其实都是在调方法,这也得益于 Ruby 的语法糖与元编程能力。只看 lane
的实现,就能知道整个 FastFile 的解析方式了:
def lane(lane_name, &block) |
对不起,只看 lane
方法好像不行,顺便看下 runner.rb 的 add_lane
吧,这下是真的能知道了:
def add_lane(lane, override = false) |
也就是说 lane
负责将 lane name 和 block 代码存储到 runner 的 lanes 字典中。结构为:
lanes = { |
如果一不小心看了 before_all
的源码,把 before_all
的结构算上,整体如下:
before_all_blocks = { |
到此就一目了然了,当要调用 lane 的时候,就找到 runner 的 lanes
字典中存储的 LaneObject.block
,然后执行 block 就行。也就是 lane_manager.rb 解析完 FastFile 以后,执行的:
# ff 即 FastFile 对象 |
Method Missing
在 Rake task vs. Fastlane lane 提到过 Fastlane 的复用比 Rake 更优雅。最主要的原因,就是因为 lane 可以当成一个方法来直接调用,比如在 FastFile 中:
lane :lane_a do |options| |
对于 lane_a
这样 FastFile 中根本没有定义的方法,当然是抛出 NoMethodFound 的异常才对。在仔细看 fast_file.rb
也就是执行 FastFile 的类中,定义了一个方法:
def method_missing(method_sym, *arguments, &_block) |
method_missing
捕获了全部未找到的方法,并调用了 trigger_action_by_name
,明摆着未找到,就按照名称的方式,调用 lane 或者 action。这样,就解决了复用的问题。这比 Rake 的 Rake::Task["task_a"].invoke
不知优雅了多少倍。