转载Google软件构建工具Bazel FAQ

本文是我的翻译,原文在这里。欢迎转载,转载请注名本文作者和原始链接
注:如果想了解Bazel的原理,可以看看我之前翻译的Google Blaze原理及使用方法介绍系列

Bazel是什么?

Bazel是一个构建工具,即一个可以运行编译和测试来组装软件的工具,跟Make、Ant、Gradle、Buck、Pants和Maven一样。

Bazel有什么特殊之处

Bazel是设计用来配合Google的软件开发模式。有以下几个特点:

多语言支持:Bazel支持Java,Objective-C和C++,可以扩展来支持任意的编程语言
高级别的构建语言:工程是通过BUILD语言来描述的。BUILD语言以简洁的文本格式,描述了由多个小的互相关联的库、二进制程序和测试程序来组成的一个项目。而与之相比,Make这类的工具需要描述各个单独的文件和编译的命令
多平台支持:同一套工具和同样的BUILD文件可以用来构建不同架构和不同平台的软件。在Google,我们使用Bazel来构建在我们数据中心系统中运行的服务器端程序和在手机上运行的客户端应用程序。
重现性[Reproducibility]:在BUILD文件中,每个库,测试程序,二进制文件必须明确完整地指定直接依赖。当修改源代码文件后,Bazel使用这个依赖信息就可以知道哪些必须重新构建,哪些任务可以并行执行。这意味者所有的构建都是增量形式的并能够每次都生成相同的结果。
伸缩性[Scalability]:Bazel可以处理巨大的构建;在Google,一个服务器端程序超过100k的源码是常有的事情,如果没有文件被改动,构建过程需要大约200ms

为什么Google不使用…?

Make,Ninja: 通过这些工具都能够控制执行哪些命令来构建文件,但是需要用户书写正确的规则。

用户跟Bazel在更高级别上交互。例如,它有内置的”Java test”, “C++ binary”的规则[rule],有例如“目标平台”[target platform],“主机平台”[host platform]这种标记。这些规则都经历了充分的测试,是不会出错的。
Ant和Maven:Ant和Maven主要是面向Java,而Bazel可以处理多种语言。Bazel鼓励把代码库的内容划分成小的,可复用的单元,并且只重新构建需要重新构建的文件。这会提高在庞大的代码库上开发的速度。
Gradle: Bazel 配置文件比Gradle的要更加结构化,让Bazel能够准确理解每个行为的所作所为。使得能够有更多的并发和更好的可重现性

Pants, Buck: 这两个工具都是前Google员工在Twitter和Foursquare创造并开发的。他们都是在模仿Bazel,但是他们的特性跟Bazel是不同的,所以不会是Bazel的替代品

Bazel的起源是什么?

Bazel是Google内部用来构建自己的服务器端软件的工具。它已经被扩展成也可以构建连接到服务器端的客户端软件(iOS, Android)。

Bazel是内部工具的重写,然后开源?还是说它是一个fork?

Bazel和内部工具的大部分代码是一样的,它的规则每天被使用无数次。

为什么Google创建Bazel?

很久以前,Google使用大的,生成的Makefile来构建软件。这导致构建的速度慢而且不可靠,开始干扰开发人员的生产率和公司的敏捷度。因此,我们创造了Bazel

Bazel需要一个构建集群吗?

Google内部使用的Bazel确实使用了一个构建集群,所以Bazel在代码库里添加了一个远程构建缓存或者是远程执行系统的钩子。
我们开源的Bazel是在本地执行任务的。我们很自信这对于Bazel的大多数用户来说是足够快的。

Google开发模式是怎样的?

对于我们的服务器端代码库,我们的开发流程如下:

所有的服务器端代码库都在一个巨大的版本控制系统里
每个人都用Bazel构建软件
不同的组负责源码树的不同部分,所有的组件都是作为BUILD目标来用
分支主要是用来管理发布,所以每个人都在最新版本上开发软件

Bazel是以下理念的奠基石:由于Bazel需要所有的依赖都被完整地指定,我们可以预测改动影响了哪些程序和测试,并在提交前执行他们。

更多Google开发模式的背景知识,可以在工具组的博客上看到。译者注:这四篇文章的中文翻译见分布式构建软件

为什么开源Bazel?

构建软件应该是好玩并且容易的,缓慢而且不可预料的构建剥夺了编程的乐趣。

为什么我要使用Bazel?

Bazel可以成倍提高构建速度,因为它只重新编译需要重新编译的文件。类似的,它会跳过没有被改变的测试。
Bazel产出确定的结果。这消除了增量和干净构建,开发机器和持续集成之间的构建结果的差异。
Bazel可以使用同一个工程下的相同的工具来构建不同的客户端和服务器端应用程序。例如,你可以在一次提交里修改一个客户端/服务器协议,然后测试更新后的手机程序和服务器端程序能够正常工作,构建时使用的是同样的工具,利用的都是上面提到的Bazel的特性。

我可以看到例子吗?

是的,一个简单的例子,见:
https://github.com/google/bazel/blob/master/examples/cpp/BUILD

Bazel源代码本身提供了更复杂的例子,例如:
https://github.com/google/bazel/blob/master/src/main/java/BUILD
https://github.com/google/bazel/blob/master/src/test/java/BUILD

Bazel最擅长做什么?

Bazel适合于构建和测试有如下特点的项目:

有庞大代码库的项目
用(多种)需要编译的语言写的项目
在多平台上部署的项目
有大量测试的工程

Bazel在什么平台上运行

目前,在Linux和MacOS上。移植到其他Unix平台上是很简单的,提供的JDK是可用的。

支持Windows平台吗?

我们利用MinGW/MSYS,实验了一个Windows的移植版本,但是目前没有计划去在这个移植版本上花费精力。由于Bazel是基于Unix的,移植Bazel需要大量的工作。例如,Bazel大量使用了符号链接,这在Windows版本中支持的程度各不相同。

不应该使用Bazel的场景

Bazel试着在缓存方面智能一些。这意味者不适于不应该被缓存的构建步骤。例如,下面的步骤就不应该被Bazel控制:

从网络上获取数据的编译步骤
连接你的网站测试实例的测试步骤
改变站点云环境的发布步骤

Bazel试着缩小耗时的编译[compile]步骤。如果你只使用了解释性语言,例如JavaScript和Python,Bazel就没有吸引力。

Bazel的功能集合是否稳定?

核心的功能(C++、Java和shell规则)已经在Google内部大量使用了,所以经历了完整的测试,只可能出现很小很小的问题。类似的,最新的版本每天被我们在成百上千的目标上测试来回归功能,我们每个月会多次发布新版本。

总之,除了被标记为实验性质的功能,任何时候,Bazel都应该工作。对于非实验性质规则的修改肯定会做到向前兼容。更详细的支持的功能可以在我们的功能文档里找到。

作为二进制程序Bazel的稳定性如何?

在Google内部,Bazel极少崩溃。对于我们开源的Bazel也是一样的。

我应该如何开始使用Bazel?

看我们的开始使用的文档。

为什么我需要在包路径里添加一个 tools/目录?

你的工程肯定不是单独工作的。通常情况下,它是使用某个特定版本的JDK/C++ 编译器,使用一个固定版本的测试框架,在某个特定的操作系统版本上运行的。
为了保证我们即使升级了我们的开发机器,构建过程仍然是可重现的,Google会把这些工具中的绝大部分都版本控制起来,包含了工具链(toolchains)和Bazel本身。惯例是把这些放到一个叫做”tools”的目录。
Bazel允许JDK这样的工具放在工程目录之外,但是这个配置项(JDK在哪,C++编译器在哪?)仍然需要放在某个地方,这个地方也就是tools/目录。
Bazel的compile.sh脚本构建了一个配置文件的最小集合,适合运行来自标准系统目录的工具链 ,例如/usr/bin

Docker没有解决可重现[reproducibility]的问题吗?

利用Docker可以很容易的创建固定操作系统版本的沙箱,例如Ubuntu 12.04, Fedora 21。这解决了系统环境的可重现问题(例如,“需要哪个版本的/usr/bin/C++?”)。

它不能解决针对源代码修改的可重现问题。在Docker内部运行一个不完美的Makefile仍然会出现不可预料的结果。

在Google内部,为了可重现,我们把工具也放到版本控制里。这样我们能够像发现基础库的修改(“修复OpenSSL里的边界检查”)一样,发现针对工具的修改(“升级GCC到4.6.1”)。

能构建部署在Docker上的二进制程序吗?

利用Bazel,可以构建独立的,静态链接的C(++)二进制程序,Java的自包含的jar文件。这些程序在正常的Unix系统里需要极少的依赖,所以在Docker容器里安装同样也是简单的。

Bazel有构建更复杂程序的例子,例如消费一系列数据文件的Java程序,或者把另外一个程序作为子进程运行。可以把这种环境打包成单独的包,以便于可以在不同的系统里进行部署,包括Docker的镜像。但是我们目前没有代码这样做。

能够使用Bazel构建Docker镜像吗?

Bazel构建的程序的可重现性是相对于构建的源码而言的。Bazel的设计里面,它对源码树之外的环境是无法感知的。因此,Bazel不知道Docker自己的环境是否和Docker镜像相一致。所以,如果你跟Docker一起使用Bazel,我们推荐在跟部署的环境一样的环境下运行Bazel,来确保可重现性。

可以跟文件一样写规则产生Docker镜像。然而,由于Docker镜像跟正常的文件系统一样,有很多时间戳,这让可重现充满了挑战。

Bazel能自动地让我的构建可重现吗?

对于Java和C++可执行程序,如果你没有修改工具链,那答案是肯定的。如果构建步骤包含定制的东西(例如,在一个规则内通过shell脚本执行可执行程序),那就需要额外注意:

不要使用没有声明的依赖。沙箱模式的执行(-spawn_strategy=sandboxed, 只能用在Linux下)可以帮助发现未声明的依赖。
不要在产生的文件中存储时间戳。ZIP文件和其他的归档文件尤其需要注意这一点。
避免连接到网络。在沙箱里执行也是可以的。
避免使用了随机数的处理过程,特别是,在很多编程语言中,字典遍历是随机的。

有二进制的版本吗?

没有,但我们应该出二进制的版本。敬请期待!

我使用Eclipse/IntelliJ. Bazel如何跟IDE结合起来?

我们目前没有跟IDE交互的API,但是iOS规则可以根据BUILD目标来产生Xcode可用的工程。

Bazel如何跟Xcode交互?

Bazel产出可以使用任何输入和依赖的Xcode工程,可以直接从Xcode构建APP然后部署到模拟器和设备上。打开Bazel构建任何iOS目标后打印出的工程文件的路径就可以使用这个功能。不支持从Xcode里调用Bazel(例如基于proto文件来重新产生Objc源文件),也不支持从Bazel直接打开Xcode。

我使用Jenkins/CircleCI/TravisCI. Bazel如何跟持续集成系统结合起来?

如果构建或者测试过程失败,Bazel返回非0值,这对于基本的持续集成系统来说,已经够用了。由于Bazel不需清除构建就可以保持构建结果的正确性,所以持续集成系统可以配置成在启动一个构建/测试的时候不进行清除操作
关于返回值的更多细节,参见用户手册

未来Bazel会加入哪些功能?

我们一开始的目标是满足Google内部使用。这包括Google的主要编程语言(C++, Java, Go)和主要平台(Linux,Android,iOS)。由于一些原因,并不是所有的这些都是开源的。更多细节见路线图

关于Python呢?

可以把书写的Python的规则当作扩展(见下面的例子)。之后的例子是如何产生自包含python的zip文件

https://github.com/google/bazel/blob/master/tools/build_rules/py_rules.bzl
https://github.com/google/bazel/tree/master/examples/py

我们正在准备开源一套Google内部使用的Python规则的子集,这些规则可以当作辅助脚本而成为构建的一部分

我们目前没有计划要提供打包整个自满足的Python二进制的过程。

关于Go呢?

如果你的代码库里的代码,100%都是Go语言,那么 go 工具在构建和测试方面表现很出色,Bazel不会给你带来 go 工具这么大的收益。
在Google用Go写的服务器端代码是用Bazel构建的。然而,由于Go语言的代码和我们C++库的交互而导致使用Bazel构建Go语言部分的过程很复杂,并和 go 工具的惯例不兼容。因为这个原因,我们宁愿不开源目前的Go相关的规则

可以使用Bazel来构建我的LISP/Python/Haskell/Scala/Rust的工程吗?

Bazel有一套扩展机制来允许添加新的规则而不需要重新编译Bazel。文档见这里
但是到目前为止,这套扩展机制是实验性质的。

我需要更多的功能。我可以添加编译到Bazel里面的规则吗?

如果扩展机制对于你的场景来说不够用,请把相关建议邮件到这个组:bazel-discuss@googlegroups.com

我可以给Bazel代码贡献吗?

见我们的贡献指南

为什么目前Bazel的开发过程并不都是开源状态

我们仍然在大量重构Bazel内部公共的代码和我们内部扩展之间的接口部分。所以这部分要开源开发很困难。更多详细信息见我们的governance plan

如何联系Bazel开发组?

通过 bazel-discuss@googlegroups.com 来联系

怎么汇报bug?

给bazel-discuss@googlegroups.com发邮件,或者在GitHub上报bug

在代码中的单词”Blaze”是什么意思?

这是这个工具的内部名称。请使用Bazel来指代Bazel这个工具

为什么其他Google的工程(Android,Chrome)使用其他的构建工具?

之前Bazel一直都是内部使用的,所以开源项目,例如Chromium,Android等都不能使用它。而且,缺乏对Windows支持而导致
不能构建Windows应用程序,例如Chrome。

“Bazel”怎么发音?

跟美国英语中的“basil”(草本植物)一样的:”BAY-zel”.

 

 
标签: 编译linuxC++构建Bazel