注册

Flutter 的 build 系统(一)

前言

对于Flutter开发者来说,build_runner 可以说并不是一个陌生的东西,很多package中就要求调用build_runner 来自动生成处理代码,比如说json_serializable;

但正如其描述中所述的那样,其是通过 Dart Build System来实现的,build_runner 和其又是一个什么关系,接下来就来学习一下dart的build系统

dart 的 build 系统

组成

dart 的 build系统,由 build_config、 build_modulesbuild_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 都干了什么

image.png

根据日志信息,build_runner 的流程基本遵循这样一个套路:

  • 生成和预编译build脚本
  • 处理输入环境和资源
  • 根据前面的脚本和输入信息,开始正式执行builder生成代码;
  • 缓存信息,用于下一回生成代码的时候增量判断使用;

接下来就看下这些编译脚本、输入环境、资源等不知所云的东西,到底是什么;

生成和预编译build脚本

生成部分:

首先来到build_runner的main函数部分,前面一大片对参数检测的拦截判断,真正执行命令的地方放在了最后:

image.png

在这个方法中最先做的事就是生成build脚本

image.png

其内容也很简单,说白了就是输出一个文件而已:

image.png

至于这个文件内容是什么,有什么用,先放到后面再说;现在先关注于整体流程;

那么现在可以得知,这步会在scriptLocaton这个路径上生成一个build脚本;而这个路径也不难得到:

image.png image.png image.png

其实就是 .dart_tool/build/entrypoint/build.dart 这个文件;

预编译部分:

在上面贴的generateAndRun方法中,生成文件之后就会执行一个 _createKernelIfNeeded 方法,其作用也正如其名,检测是否需要就创建内核文件;

image.png

image.png

而这个内核文件,也就是后缀为build.dart.dill 文件

image.png

同时,在这里也提到了一个新的概念:assetGraph,不过这些也是后面再细看的东西;

处理输入环境和资源

在编译完build脚本生成内核后,下面就是执行这个内核文件;在这里新开了一个isolate去执行这个文件:

image.png

接下来就该看下这个内核文件到底是什么……但是呢,内核文件这东西,本来就不是给人看的………………所以呢,可以从另一方面考虑下,比如说,既然内核文件看不了,那我就看内核文件的从哪编译来的,反正逻辑上也是大差不差,完全可以参考;

正好内核文件的来源,也就是那个build脚本,其位置在上面也提到过了;在我测试代码中,它最后是这样的:

image.png 其中的这个_i10,正是build_runner……看来兜兜转转又回来了?

应该说回来了,但没完全回来,上面提到的build_runner是bin目录下的;这次的build_runner是lib目录下的,入口还是不一样的;

在这里,build_runner build中的build这个参数才真正识别并开始执行;前面都是前戏;而执行这个build命令的是一个名为BuildCommandRunner的类,其内部内置了包括build在内的诸多函数命令:

image.png

由于测试的指令参数为build,所以命中的commend为 BuildCommand;而 BuildCommand 所做的事也基本集中在 src/generate/build.dart 这个文件中的build方法中了;自此开始真正去执行build_runner对应Builder中要求做的事;

其build方法所做的事还是比较容易看懂的:

image.png

  1. 配置环境(包括输入输出配置)
  2. 配置通用选项(build时候的配置项目)
  3. 调用BuildRunner.create创建Builder和生成所需数据,最后调用run执行;

而这部分所说的处理输入环境和资源就在 BuildRunner.create 这部分中;其会调用 BuildDefinition.prepareWorkspace方法;

image.png

而在这里就出现了上面提到的assetGraph,这里就是其创建和使用的地方:

image.png

所以,最终总结一下,处理输入环境和资源 这个环节所做的事就是根据配置生成输入输出、build过程中所需的各种参数,提供assetGraph这个东西;

具体这些配置入口在哪,从何而来,assetGraph又是什么东西,有什么作用,后面再看;

正式执行builder生成代码

这部分就是刚才提到的调用run方法的地方;

image.png

它的run方法咋看好像也不难懂的样子,主要是各种新名词有点多:

image.png

不过现在只跟随build流程来说的话,核心应该事其中的_safeBuild方法:

image.png

其所做的事,除了各种心跳log之外,应该就是更新assetGraph;执行_runPhases;另外毕竟事safeBuild嘛,所以新开了一个zone来处理;

image.png

_runPhases所做的事就是真正去执行build所做的事,生成代码之类的;比如说json_serializable中的build,就会走_runBuilder部分并最终调用runBuilder中的builder.build,也就是自定义Builder中需要自己实现的部分;

image.png

对了,关于像json_serializable的自定义Builder从何而来的问题,答案是一开始就已经集成进来了,在builder.dart中已经出现了其身影:

image.png

不过为什么build.dart 能得知具体有哪些builder?比如说json_serializable中的builder,是怎么加入到build.dart中的,那也是后面要看的东西;

缓存信息

再次回到 _safeBuild 这块,缓存信息的部分紧贴着run部分:

image.png

好像就写了一下文件,没了?

结语

这篇大体粗略的过了一下build这个命令都干了什么;不过像生成的文件内部结构、作用;配置信息来源,如何解析之类的问题还未解决;在后面会依次看看;

最后尝试实现一份自己的自定义Builder;


作者:lwlizhe
链接:https://juejin.cn/post/7133488621180420126
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册