构建系统概念
构建系统概念
半翻译半整理自https://bazel.build/basics?hl=en
什么是构建系统
构建系统的 第一要务 是将系统源代码编译成可执行的文件。
在这基础之上,它允许通过机器自动创建 build,如提交代码到 GitHub 后自动触发构建。
编译器的不足
javac
可以将当前目录下的每个 Java 源文件编译成二进制类文件,它可以在当前目录的子目录中查找需要导入的代码,但是它无法处理位于磁盘上其他目录的代码(也就是没办法直接处理项目依赖的库文件)。
对于大多数 Java 程序员来说,这就是编译器的主要问题。
另外一个不足的地方是,如果系统比较庞大,会使用不同的编程语言来编写系统的不同组成部分,此时就会出现单一编译器无法胜任的情况。
也没几个公司有这种规模的系统吧。。。
还有一个就是开发人员需要手动去下载依赖包然后放到 lib
目录中(泪目,是青春了),那这样的问题就是没办法管理版本,而且也不知道有没有少包,比如 spring 依赖的一大堆东西,不知道自己下载的全不全。
shell 的不足
编译器的不足,或许可以使用 shell 脚本来解决,shell 本身可以做这个事情,唯一的问题就是当你用 shell 实现了这一套之后,就相当于是自己做了一个构建系统,那为什么不直接用现成的呢?
基于任务的构建系统
ant
、maven
、gradle
都是基于任务的构建系统。在构建脚本中可以指定每个任务的先后顺序。
构建系统可以通过脚本中配置的先后顺序,构造出一个有向无环图(DAG),
maven
、gradle
相比ant
,多了依赖管理的功能。
缺点
基于任务的构建系统,有三个主要缺点:
- 很难并行执行
- 很难增量构建
- 脚本的维护和调试很困难
这些缺点的根因是相同的,即任务的自由度过高,构建系统无法感知任务到底做了什么,有没有副作用。
任务也是由代码编写的,而在代码中可以做任何想做的事情,无法保证两个没有依赖关系的任务不存在 race condition。
基于工件的构建系统
相比于基于任务的构建系统
由任务来决定如何构建,基于工件的构建系统只需要开发人员在构建脚本中声明一些构建信息即可,由构建系统决定该如何构建。
削减了开发人员的自由度,换来的是构建系统对整个构建过程的可控性。
一个Bazel的例子如下:
java_binary(
name = "MyBinary",
srcs = ["MyBinary.java"],
deps = [
":mylib",
],
)
java_library(
name = "mylib",
srcs = ["MyLibrary.java", "MyHelper.java"],
visibility = ["//java/com/example/myproduct:__subpackages__"],
deps = [
"//java/com/example/common",
"//java/com/example/myproduct/otherlib",
],
)