Flutter 的 build 系统(一)
前言
对于Flutter开发者来说,build_runner 可以说并不是一个陌生的东西,很多package中就要求调用build_runner 来自动生成处理代码,比如说json_serializable;
但正如其描述中所述的那样,其是通过 Dart Build System来实现的,build_runner 和其又是一个什么关系,接下来就来学习一下dart的build系统
dart 的 build 系统
组成
dart 的 build系统,由 build_config、 build_modules、build_resolvers、 build_runner、 build_test、 build_web_compilers 共同组合、完成了dart 的 build 系统;
- build_config 就是解析那个build.yaml文件,用来配置build_runner,没什么好说的,具体的功能后面再细说;
- build_modules 好像是解析module级别信息的一个库
- build_resolvers 从自述文件中分析,好像是一个给build_runner 每步提供所需信息的解析器?
- build_runner 整个build系统的核心部分,其他部分都是为了拓展和实现此功能而存在的;
- build_test 字面意思,一个测试库;
- build_web_compilers 用于web端的build系统;
作用
Flutter的build系统其实就是生成代码,对标的应该是JAVA的APT这块的东西;
另外,对于 dart 的 build 系统,官方是有这么一段介绍:
Although the Dart build system is a good alternative to reflection (which has performance issues) and macros (which Dart’s compilers don’t support), it can do more than just read and write Dart code. For example, the sass_builder package implements a builder that generates
.css
files from.scss
and.sass
files.
也就是说dart build理论上是可以来做很多人心心念念的反射的;
基本使用
如果仅仅是使用方面来说,build_runner 的使用非常简单;比如说我们最常用的一条命令就是:
flutter pub run build_runner build
也可以配置build.yaml来修改配置信息,生成符合需要的代码;
不过在输入上面那句build_runner build之后发生了什么,像build_config之类的在这个过程中各自起了什么作用,这就需要追踪一下;
build_runner 都干了什么
根据日志信息,build_runner 的流程基本遵循这样一个套路:
- 生成和预编译build脚本
- 处理输入环境和资源
- 根据前面的脚本和输入信息,开始正式执行builder生成代码;
- 缓存信息,用于下一回生成代码的时候增量判断使用;
接下来就看下这些编译脚本、输入环境、资源等不知所云的东西,到底是什么;
生成和预编译build脚本
生成部分:
首先来到build_runner的main函数部分,前面一大片对参数检测的拦截判断,真正执行命令的地方放在了最后:
在这个方法中最先做的事就是生成build脚本
其内容也很简单,说白了就是输出一个文件而已:
至于这个文件内容是什么,有什么用,先放到后面再说;现在先关注于整体流程;
那么现在可以得知,这步会在scriptLocaton这个路径上生成一个build脚本;而这个路径也不难得到:
其实就是 .dart_tool/build/entrypoint/build.dart 这个文件;
预编译部分:
在上面贴的generateAndRun方法中,生成文件之后就会执行一个 _createKernelIfNeeded
方法,其作用也正如其名,检测是否需要就创建内核文件;
而这个内核文件,也就是后缀为build.dart.dill 文件
同时,在这里也提到了一个新的概念:assetGraph
,不过这些也是后面再细看的东西;
处理输入环境和资源
在编译完build脚本生成内核后,下面就是执行这个内核文件;在这里新开了一个isolate去执行这个文件:
接下来就该看下这个内核文件到底是什么……但是呢,内核文件这东西,本来就不是给人看的………………所以呢,可以从另一方面考虑下,比如说,既然内核文件看不了,那我就看内核文件的从哪编译来的,反正逻辑上也是大差不差,完全可以参考;
正好内核文件的来源,也就是那个build脚本,其位置在上面也提到过了;在我测试代码中,它最后是这样的:
其中的这个_i10,正是build_runner……看来兜兜转转又回来了?
应该说回来了,但没完全回来,上面提到的build_runner是bin目录下的;这次的build_runner是lib目录下的,入口还是不一样的;
在这里,build_runner build中的build这个参数才真正识别并开始执行;前面都是前戏;而执行这个build命令的是一个名为BuildCommandRunner
的类,其内部内置了包括build在内的诸多函数命令:
由于测试的指令参数为build,所以命中的commend为 BuildCommand
;而 BuildCommand 所做的事也基本集中在 src/generate/build.dart 这个文件中的build方法中了;自此开始真正去执行build_runner对应Builder中要求做的事;
其build方法所做的事还是比较容易看懂的:
- 配置环境(包括输入输出配置)
- 配置通用选项(build时候的配置项目)
- 调用BuildRunner.create创建Builder和生成所需数据,最后调用run执行;
而这部分所说的处理输入环境和资源就在 BuildRunner.create
这部分中;其会调用 BuildDefinition.prepareWorkspace
方法;
而在这里就出现了上面提到的assetGraph
,这里就是其创建和使用的地方:
所以,最终总结一下,处理输入环境和资源 这个环节所做的事就是根据配置生成输入输出、build过程中所需的各种参数,提供assetGraph这个东西;
具体这些配置入口在哪,从何而来,assetGraph又是什么东西,有什么作用,后面再看;
正式执行builder生成代码
这部分就是刚才提到的调用run方法的地方;
它的run方法咋看好像也不难懂的样子,主要是各种新名词有点多:
不过现在只跟随build流程来说的话,核心应该事其中的_safeBuild
方法:
其所做的事,除了各种心跳log之外,应该就是更新assetGraph;执行_runPhases
;另外毕竟事safeBuild嘛,所以新开了一个zone来处理;
_runPhases
所做的事就是真正去执行build所做的事,生成代码之类的;比如说json_serializable中的build,就会走_runBuilder
部分并最终调用runBuilder
中的builder.build
,也就是自定义Builder中需要自己实现的部分;
对了,关于像json_serializable的自定义Builder从何而来的问题,答案是一开始就已经集成进来了,在builder.dart中已经出现了其身影:
不过为什么build.dart 能得知具体有哪些builder?比如说json_serializable中的builder,是怎么加入到build.dart中的,那也是后面要看的东西;
缓存信息
再次回到 _safeBuild
这块,缓存信息的部分紧贴着run部分:
好像就写了一下文件,没了?
结语
这篇大体粗略的过了一下build这个命令都干了什么;不过像生成的文件内部结构、作用;配置信息来源,如何解析之类的问题还未解决;在后面会依次看看;
最后尝试实现一份自己的自定义Builder;
作者:lwlizhe
链接:https://juejin.cn/post/7133488621180420126
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。