0、简介
bazel的所有代码都在当前工程,每个工程都是一个 WORKSPACE。每个WORKSPACE下有多个package(包含BUILD文件的文件夹被称为package),BUILD内是多个targets,同一个package内的targets默认互相可见,不同package之间targets的可见性需要手动定义,可以在每个package的BUILD文件顶部声明其中的targets对其他包的默认可见性。
一、安装bazel
sudo apt install curl
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
sudo apt-get update
sudo apt-get install bazel # 安装bazel
sudo apt-get install --only-upgrade bazel # 升级bazel到最新版本
查看bazel版本
bazel version
BUILD构建文件用"#"
开头来添加单行注释
二、编译运行
bazel clean
bazel clean # 不会删除外部依赖
bazel clean --expunge # 会删除外部依赖
bazel clean --expunge --async
bazel build
bazel build :<exe name> # 在BUILD所在的package目录下执行,编译指定的target
bazel build :all # 编译该package下的所有target
bazel build ... # 编译该package下的所有target
bazel build <//path/to/package>:<exe name> # 在workspace下的任意目录执行,“//”表示workspace所在目录
bazel build :<exe name> --compilation_mode=dbg # debug mode
bazel build :<exe name> -c dbg # debug mode
bazel build :<exe name> --keep_going # 看到所有的错误
bazel build :<exe name> --config=<asan/tsan/msan> # Build the project with sanitizers by adding the --config=<asan/tsan/msan> build flag to select AddressSanitizer (asan), ThreadSanitizer (tsan) or MemorySanitizer (msan) accordingly.
bazel build :<exe name> --config local_first
bazel build :<exe name> --cxxopt=-ftemplate-backtrace-limit=0 // coredump时保留所有栈信息
bazel run
bazel run :<target name>
bazel run -- :<target name>
不需要先执行build,在执行run,run的时候会自动先build再执行
bazel test
bazel test :<target name>
bazel test :<target name> --test_output=summary --test_verbose_timeout_warnings --test_timeout=600,1200,2400,3600
--test_output=<summary, errors, all or streamed> default: "summary"
Specifies desired output mode. Valid values are 'summary' to output only test status summary, 'errors' to also print test logs for failed tests, 'all' to print logs for all tests and 'streamed' to output logs for all tests in real time (this will force tests to be executed locally one at a time regardless of --test_strategy value).
Tags: test_runner, terminal_output, execution
--[no]test_verbose_timeout_warnings default: "false"
If true, print additional warnings when the actual test execution time does not match the timeout defined by the test (whether implied or explicit).
Tags: affects_outputs
--test_timeout=<a single integer or comma-separated list of 4 integers> default: "-1"
Override the default test timeout values for test timeouts (in secs). If a single positive integer value is specified it will override all categories. If 4 comma-separated integers are specified, they will override the timeouts for short, moderate, long and eternal (in that order). In either form, a value of -1 tells bazel to use its default timeouts for that category.
工作原理
action graph: bazel依赖这个图来追踪文件变化,以及是否需要重新编译,并且还可以为用户提供代码之间的依赖关系图。
bazel query 'deps(//<path_to_package>:<target_name>)' # 查看target的依赖
bazel query "somepath(//<path_to_package>:<target1_name>,//<path_to_package>:<target2_name>)"
三、WORKSPACE文件
参考:Working with external dependencies
WORKSPACE文件主要就是命名workspace以及声明外部的依赖,这就包括外部依赖的获取方式及获取方法。WORKSPACE文件告诉Bazel如何去得到其他的工程源,然后package中的BUILD文件就可以根据WORKSPACE中的外部target名字写依赖关系。WORKSPACE文件允许用户的目标依赖其他文件系统的目标或者从网上下载的目标。除了通过bazel build可以自动获取外部依赖之外,还可以通过bazel fetch来获得。Bazel会缓存外部依赖,并且只有在WORKSPACE文件更改后才再次下载和更新。WORKSPACE文件的语法和BUILD文件一致,不过会用到一些特定的内置rule。所有的外部依赖会都下载到一个名为的软连接目录。具体的内容可以通过命令行获得:
ls $(bazel info output_base)/external
一共有三种外部依赖的主要类型:
1、依赖于其他Bazel工程
根据这个Bazel工程所处的位置不同,调用不同的内置rule来获得:
2、依赖于其他非Bazel工程
还有一种情况是另外一个工程不是Bazel工程,那么就需要另外一种方法来添加依赖引用
3、依赖于外部包
Maven仓库:Use the rule maven_jar (and optionally the rule maven_server) to download a jar from a Maven repository and make it available as a Java dependency.
Workspace Rules
bind
bind(name, actual, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, testonly, visibility)
给target命别名,不推荐使用
git_repository
git_repository(name, commit, init_submodules, remote, sha256, tag)
这个有很多限制不推荐使用,用http_archive 来代替使得更加鲁棒以及安全性能。
http_archive
http_archive(name, sha256, strip_prefix, type, url, urls)
下载一个压缩格式的Bazel仓库,并解压出来,然后绑定使用。这个Rule有一个属性strip_prefix,用来消除前缀目录。
举例:
(1) glog / gflags
目录结构
├── WORKSPACE
├── BUILD
└── main.cc
WORKSPACE
workspace(name = "bazel_test")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "com_github_gflags_gflags",
commit = "f7388c6655e699f777a5a74a3c9880b9cfaabe59",
remote = "https://github.com/gflags/gflags.git",
)
git_repository(
name = "glog",
commit = "0a2e5931bd5ff22fd3bf8999eb8ce776f159cda6",
remote = "https://github.com/google/glog.git",
)
BUILD
cc_binary(
name = "bazel_test",
srcs = ["main.cc"],
deps = [
"@com_github_gflags_gflags//:gflags",
"@glog",
],
)
main.cc
#include <glog/logging.h>
#include <gflags/gflags.h>
DEFINE_string(name, "alan", "your name");
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
FLAGS_colorlogtostderr = 1;
gflags::ParseCommandLineFlags(&argc, &argv, true);
LOG(INFO) << "hello " << FLAGS_name;
return 0;
}
执行
bazel run -- :bazel_test --name alan
(2) gtest
目录结构
├── WORKSPACE
├── gtest.BUILD
├── main.cc
└── BUILD
WORKSPACE
workspace(name = "bazel_test")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "gtest",
url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
build_file = "@//:gtest.BUILD",
strip_prefix = "googletest-release-1.7.0",
)
gtest.BUILD
cc_library(
name = "main",
srcs = glob(
["src/*.cc"],
exclude = ["src/gtest-all.cc"]
),
hdrs = glob([
"include/**/*.h",
"src/*.h"
]),
copts = ["-Iexternal/gtest/include"],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
)
BUILD
cc_test(
name = "bazel_test",
srcs = ["main.cc"],
copts = ["-Iexternal/gtest/include"],
deps = [
"@gtest//:main",
],
)
main.cc
#include "gtest/gtest.h"
int abs(int x) {
return x >= 0? x : -x;
}
TEST(BazelTest, AbsTest) {
EXPECT_EQ(abs(-1), 1);
}
执行
bazel test :bazel_test
参考:Introduction to Bazel: Common C++ Build Use Cases
(3) proto
目录结构
├── WORKSPACE
├── BUILD
├── main.cc
└── foo.proto
WORKSPACE
workspace(name = "bazel_test")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_proto",
sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208",
strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz",
"https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz",
],
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
rules_proto_dependencies()
rules_proto_toolchains()
BUILD
#load("@rules_cc//cc:defs.bzl", "cc_proto_library")
#load("@rules_proto//proto:defs.bzl", "proto_library")
cc_binary(
name = "bazel_test",
srcs = ["main.cc"],
deps = [":foo_cc_proto"],
)
proto_library(
name = "foo_proto",
srcs = [
"foo.proto",
],
)
cc_proto_library(
name = "foo_cc_proto",
deps = [
":foo_proto",
],
)
main.cc
#include "foo.pb.h"
int main(int argc, char *argv[]) {
foo::Bar bar;
bar.set_id(10);
std::cout << "id: " << bar.id() << std::endl;
return 0;
}
foo.proto
syntax = "proto3";
package foo;
message Bar {
int32 id = 1;
}
执行
bazel run :bazel_test
参考:GitHub - bazelbuild/rules_proto: Protocol buffer rules for Bazel
(4) eigen
目录结构
├── WORKSPACE
├── BUILD
└── main.cc
WORKSPACE
workspace(name = "bazel_test")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_github_eigen_eigen",
sha256 = "dd254beb0bafc695d0f62ae1a222ff85b52dbaa3a16f76e781dce22d0d20a4a6",
strip_prefix = "eigen-eigen-5a0156e40feb",
urls = [
"http://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2",
],
build_file_content =
"""
cc_library(
name = 'eigen',
srcs = [],
includes = ['.'],
hdrs = glob(['Eigen/**']),
visibility = ['//visibility:public'],
)
"""
)
BUILD
cc_binary(
name = "bazel_test",
srcs = ["main.cc"],
deps = [
"@com_github_eigen_eigen//:eigen",
],
)
main.cc
#include <iostream>
#include <Eigen/Dense>
int main(int argc, char *argv[])
{
Eigen::Matrix3d m = Eigen::Matrix3d::Identity();
std::cout << m << std::endl;
return 0;
}
执行
bazel run :bazel_test
(5) ceres (依赖gflags、glog、eigen、benchmark)
目录结构
├── WORKSPACE
├── BUILD
└── main.cc
WORKSPACE
workspace(name = "bazel_test")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# External dependency: Google Flags; has Bazel build already.
http_archive(
name = "com_github_gflags_gflags",
sha256 = "6e16c8bc91b1310a44f3965e616383dbda48f83e8c1eaa2370a215057b00cabe",
strip_prefix = "gflags-77592648e3f3be87d6c7123eb81cbad75f9aef5a",
urls = [
"https://mirror.bazel.build/github.com/gflags/gflags/archive/77592648e3f3be87d6c7123eb81cbad75f9aef5a.tar.gz",
"https://github.com/gflags/gflags/archive/77592648e3f3be87d6c7123eb81cbad75f9aef5a.tar.gz",
],
)
# External dependency: Google Log; has Bazel build already.
http_archive(
name = "com_github_google_glog",
sha256 = "7083af285bed3995b5dc2c982f7de39bced9f0e6fd78d631f3285490922a0c3d",
strip_prefix = "glog-3106945d8d3322e5cbd5658d482c9ffed2d892c0",
urls = [
"https://github.com/drigz/glog/archive/3106945d8d3322e5cbd5658d482c9ffed2d892c0.tar.gz",
],
)
# External dependency: Eigen; has no Bazel build.
http_archive(
name = "com_github_eigen_eigen",
sha256 = "dd254beb0bafc695d0f62ae1a222ff85b52dbaa3a16f76e781dce22d0d20a4a6",
strip_prefix = "eigen-eigen-5a0156e40feb",
urls = [
"http://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2",
],
build_file_content =
"""
# TODO(keir): Replace this with a better version, like from TensorFlow.
# See https://github.com/ceres-solver/ceres-solver/issues/337.
cc_library(
name = 'eigen',
srcs = [],
includes = ['.'],
hdrs = glob(['Eigen/**']),
visibility = ['//visibility:public'],
)
"""
)
# External dependency: Google Benchmark; has no Bazel build.
http_archive(
name = "com_github_google_benchmark",
urls = ["https://github.com/google/benchmark/archive/56f52ee228783547f544d9ac4a533574b9010e3f.zip"],
sha256 = "8c1c6e90cd320b07504fabb86400f390faff2e599183ebd9396908817968ae79",
strip_prefix = "benchmark-56f52ee228783547f544d9ac4a533574b9010e3f",
build_file_content =
"""
cc_library(
name = "benchmark",
srcs = glob([
"src/*.h",
"src/*.cc",
]),
hdrs = glob(["include/benchmark/*.h"]),
copts = [
"-DHAVE_STD_REGEX",
],
includes = [
"include",
],
visibility = ["//visibility:public"],
)
"""
)
local_repository(
name = "ceres",
path = "/home/alan/3rdparty/ceres-solver-1.14.0",
)
BUILD
cc_binary(
name = "bazel_test",
srcs = ["main.cc"],
deps = [
"@ceres",
],
)
main.cc
#include <iostream>
#include <Eigen/Dense>
int main(int argc, char *argv[])
{
Eigen::Matrix3d m = Eigen::Matrix3d::Identity();
std::cout << m << std::endl;
return 0;
}
执行
bazel run :bazel_test
参考:
Non-linear Least Squares — Ceres Solver
https://ceres-solver.googlesource.com/ceres-solver/+/master/WORKSPACE
(6) opencv
链接本地编译安装好的opencv库
git clone https://github.com/opencv/opencv.git
cd opencv/
mkdir build install
cd build
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/home/alan/3rdparty/opencv/install ..
make install
目录结构
├── WORKSPACE
├── opencv.BUILD
├── main.cc
└── BUILD
WORKSPACE
workspace(name = "bazel_test")
new_local_repository(
name = "opencv",
path = "/home/alan/3rdparty/opencv/install",
build_file = "opencv.BUILD",
)
opencv.BUILD
cc_library(
name = "opencv",
srcs = glob(["lib/*.so*"]),
hdrs = glob(["include/**/*.hpp","include/**/*.h"]),
includes = ["include"],
visibility = ["//visibility:public"],
linkstatic = 1,
)
BUILD
cc_binary(
name = "bazel_test",
srcs = ["main.cc"],
deps = [
"@opencv//:opencv",
],
)
main.cc
#include <opencv2/opencv.hpp>
int main(int argc, char *argv[]) {
cv::Mat img = cv::imread("/home/alan/1.jpg");
std::cout << "Resolution: " << img.rows << " x " << img.cols << std::endl;
return 0;
}
执行
bazel run :bazel_test
参考:Building OpenCV code using Bazel
(7)自定义ros message (未验证?)
目录结构
├── WORKSPACE
├── BUILD
├── main.cc
└── msg
└── Test.msg
Test.msg
std_msgs/Header header
geometry_msgs/Pose pose
WORKSPACE
workspace(name = "bazel_test")
(ros)??
BUILD
package(default_visibility = ["//visibility:public"])
cc_ros_msg_package(
name = "alan_msg",
srcs = [
"msg/Test.msg",
],
)
cc_binary(
name = "bazel_test",
srcs = ["main.cc"],
deps = [
":alan_msg",
],
)
main.cc
#include "msg/Test.h"
int main(int argc, char *argv[]) {
my_msg::Test test;
return 0;
}
执行
bazel run :bazel_test
bazel run :bazel_test --config asan
BUILD文件:
tf_cc_binary:目标文件编译规则,为一个二进制可执行文件。name必须唯一,srcs指定了源文件,linkopts指定了链接规则,deps指定了依赖文件
cc_library:库文件编译规则,name指定了编译为库文件后的文件名,srcs和hdrs指定源文件和头文件,deps指定需要依赖的其他文件
tf_cc_test:测试文件规则
package:通用方法,定义的值会作用到下面的每个子rule中。default_visibility指定了这个包的默认可见规则。可见的情况下才能被其他package调用。
licenses:通用方法,默认的license
load:通用方法,加载.bzl文件
filegroup:通用方法,为多个编译目标target指定一个名字,glob是一个帮助函数,指定了目录中哪些文件会include,哪些会exclude。visibility指定了target的可见性,也就是可以被哪些package调用
name属性来命名规则,
deps属性来描述规则之间的依赖关系。使用冒号来分隔包名和规则名。如果某条规则所依赖的规则在其他目录下,就用"//"开头,如果在同一目录下,可以忽略包名而用冒号开头。
linkopts指定了链接规则
参考: