什么是Bazel
Bazel是一个类似于Make的编译工具,是Google为其内部软件开发的特点量身定制的工具,如今Google使用它来构建内部大多数的软件。(怪不得看起来很像Android.bp语法 O(∩_∩)O)
Google认为直接用Makefile构建软件速度太慢,结果不可靠,所以构建了一个新的工具叫做Bazel,Bazel的规则层级更高。
开始使用
Bazel的编译是基于工作区(workspace)的概念。
workspace
workspace存放了所有源代码和Bazel编译输出文件的目录,也就是整个项目的根目录。
workspace需要包含的必要文件:
WORKSPACE文件,用于指定当前文件夹就是一个Bazel的工作区。所以WORKSPACE文件总是存在于项目的根目录下。
BUILD文件,用于告诉Bazel怎么构建项目的不同部分。(如果工作区中的一个目录包含BUILD文件,那么它就是一个package)
要指定一个目录为Bazel的工作区,就只要在该目录下创建一个空的WORKSPACE文件即可。
但是在百度Apollo源码我们只能看到一个WORKSPACE.in
文件:
.
├── apollo_docker.sh
├── apollo.doxygen
├── apollo.sh
├── BUILD
├── CONTRIBUTING.md
├── CPPLINT.cfg
├── cyber
├── docker
├── docs
├── LICENSE
├── MANIFESTO.md
├── modules
├── README.md
├── readthedocs.yml
├── RELEASE.md
├── scripts
├── third_party
├── tools
├── ubuntu_18.04_env
└── WORKSPACE.in
根据官网的描述:
一个工作区是在文件系统包含的源文件要构建的软件,以及符号链接到包含生成输出目录的目录。每个工作空间目录都有一个名为的文本文件
WORKSPACE
,该文件可以为空,或者可以包含 对构建输出所需的外部依赖项的引用。包含名为的文件
WORKSPACE
的目录被视为工作空间的根。因此,Bazel会忽略工作空间中的任何目录树,这些工作树植根于包含WORKSPACE
文件的子目录(因为它们形成另一个工作空间)。Bazel还支持将
WORKSPACE.bazel
file作为文件的别名WORKSPACE
。如果两个文件都存在,WORKSPACE.bazel
将具有优先权。
可以看出bazel并不支持名为WORKSPACE.in
的文件.
继续搜索源码,在文件apollo.sh
中:
这里sed
的作用:
sed 可依照脚本的指令来处理、编辑文本文件。
Sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。
文件中大部分描述的就是编译过程中所需要的外部依赖.
具体语法可以参考官网.
BUILD文件
BUILD文件中包含了多个不同类型的bazel指令。
其中最重要的是编译规则(build rule),它告诉bazel怎么编译目标输出,是一个执行文件还是一个库。
BUILD文件中每一个编译规则被称为target,指向了一堆源文件和相关的依赖,一个target也可以指向其他target。
例子:
cc_binary(
name = "hello-world",
srcs = ["hello-world.c"],
)
其中的cc_binary
,name
,srcs
都是相关的target.这些语法和Android.bp的语法很像,就不在多说.
看一下Apollo源码中的写法:(以location为例)
load("//tools:cpplint.bzl", "cpplint")
package(default_visibility = ["//visibility:public"])
filegroup(
name = "localization_testdata",
srcs = glob(["testdata/*"]),
)
cpplint()
这里声明加载tools:cpplint.bzl
文件的cpplint函数:
def cpplint(data=None, extra_srcs=None):
"""For every rule in the BUILD file so far, adds a test rule that runs
cpplint over the C++ sources listed in that rule. Thus, BUILD file authors
should call this function at the *end* of every C++-related BUILD file.
By default, only the CPPLINT.cfg from the project root and the current
directory are used. Additional configs can be passed in as data labels.
Sources that are not discoverable through the "sources so far" heuristic can
be passed in as extra_srcs=[].
"""
# Iterate over all rules.
for rule in native.existing_rules().values():
# Extract the list of C++ source code labels and convert to filenames.
candidate_labels = (
_extract_labels(rule.get("srcs", ())) +
_extract_labels(rule.get("hdrs", ()))
)
source_labels = [
label for label in candidate_labels
if _is_source_label(label)
]
source_filenames = ["$(location %s)" % x for x in source_labels]
# Run the cpplint checker as a unit test.
if len(source_filenames) > 0:
_add_linter_rules(source_labels, source_filenames, rule["name"], data)
# Lint all of the extra_srcs separately in a single rule.
if extra_srcs:
source_labels = extra_srcs
source_filenames = ["$(location %s)" % x for x in source_labels]
_add_linter_rules(source_labels, source_filenames,
"extra_srcs_cpplint", data)
Bazel编译看来也不是那么简单的.....
我们看一下根目录的BUILD文件:
package(
default_visibility = ["//visibility:public"],
)
exports_files([
"CPPLINT.cfg",
])
其中导入了一个cfg文件:
# Stop searching for additional config files.
set noparent
# Disable a warning about C++ features that were not in the original
# C++11 specification (and so might not be well-supported).
filter=-build/c++11
# Disable header_guard warning
# Consider using #pragma once instead
filter=-build/header_guard
cfg文件中设置了一些编译器的使用.
我们再看一下Bazel自己的配置文件.bazelrc
# load bazelrc from the legacy location
# as recommended in https://github.com/bazelbuild/bazel/issues/6319
import %workspace%/tools/bazel.rc
导入tools/bazel.rc
文件,这个文件的内容如下:
# bazelrc file
# https://docs.bazel.build/versions/master/user-manual.html
# bazel >= 0.18 looks for %workspace%/.bazelrc (which redirects here)
# Older bazel versions look for %workspace%/tools/bazel.rc (this file)
# See https://github.com/bazelbuild/bazel/issues/6319
# +------------------------------------------------------------+
# | Startup Options |
# +------------------------------------------------------------+
startup --batch_cpu_scheduling
startup --host_jvm_args=-XX:-UseParallelGC
startup --output_user_root=/apollo/.cache/bazel
# +------------------------------------------------------------+
# | Test Configurations |
# +------------------------------------------------------------+
# By default prints output only from failed tests.
test --test_output=errors
# Work around the sandbox issue.
test --spawn_strategy=standalone
# Specify protobuf cc toolchain
test --proto_toolchain_for_cc="@com_google_protobuf//:cc_toolchain"
# +------------------------------------------------------------+
# | CPP Lint Tests & Unit Tests |
# +------------------------------------------------------------+
# By default, cpplint tests are run as part of `bazel test` alongside all of
# the other compilation and test targets. This is a convenience shortcut to
# only do the cpplint testing and nothing else.
# Do bazel test --config=cpplint <target> to enable this configuration.
# To enable the lint test, the BUILD *must* load the cpplint.bzl by having
# 'load("//tools:cpplint.bzl", "cpplint")' at the beginning and 'cpplint()'
# at the end.
test:cpplint --test_tag_filters=cpplint
test:cpplint --build_tests_only
# Regular unit tests.
test:unit_test --test_tag_filters=-cpplint
# Coverage tests
test:coverage --test_tag_filters=-cpplint
test:coverage --copt=--coverage
test:coverage --cxxopt=--coverage
test:coverage --cxxopt=-fprofile-arcs
test:coverage --cxxopt=-ftest-coverage
test:coverage --linkopt=-coverage
test:coverage --linkopt=-lgcov
test:coverage --linkopt=-lgcc
test:coverage --linkopt=-lc
# +------------------------------------------------------------+
# | Build Configurations |
# +------------------------------------------------------------+
# Do not show warnings from external dependencies.
build --output_filter="^//"
build --show_timestamps
# Work around the sandbox issue.
build --spawn_strategy=standalone
# Specify protobuf cc toolchain
build --proto_toolchain_for_cc="@com_google_protobuf//:cc_toolchain"
# build with profiling
build:cpu_prof --linkopt=-lprofiler
build --copt="-Werror=sign-compare"
build --copt="-Werror=return-type"
build --copt="-Werror=reorder"
build --copt="-Werror=unused-variable"
build --copt="-Werror=unused-but-set-variable"
build --copt="-Werror=switch"
# Strict check on type conversion.
# absl/strings/str_cat.h breaks the rule.
# build --per_file_copt=^modules/.*\.cc,-modules/tools/visualizer/.*\.cc,^cyber/.*\.cc@-Werror=conversion
# Enable C++14
build --cxxopt="-std=c++1y"
# Enable colorful output of GCC
# build --cxxopt="-fdiagnostics-color=always"
# +------------------------------------------------------------+
# | Python Configurations |
# +------------------------------------------------------------+
run --python_path=/usr/bin/python3
也是配置各种编译环境,写的还是蛮复杂的.