当前位置: 首页 > 工具软件 > Bazel > 使用案例 >

bazel使用教程

呼延臻
2023-12-01

0、简介

bazel的所有代码都在当前工程,每个工程都是一个 WORKSPACE。每个WORKSPACE下有多个package(包含BUILD文件的文件夹被称为package),BUILD内是多个targets,同一个package内的targets默认互相可见,不同package之间targets的可见性需要手动定义,可以在每个package的BUILD文件顶部声明其中的targets对其他包的默认可见性。


一、安装bazel

参考:Installing Bazel on Ubuntu 

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构建文件用"#" 开头来添加单行注释 


二、编译运行

参考:Command-Line Reference

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.

工作原理

  • 加载与target有关的BUILD文件
  • 分析inputs和dependencies,生成 action graph
  • 执行graph,产出outputs

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来获得:

  • local_repository:本地
  • git_repository:git仓库
  • http_archive:网络下载

2、依赖于其他非Bazel工程

还有一种情况是另外一个工程不是Bazel工程,那么就需要另外一种方法来添加依赖引用

  • new_local_repository:本地
  • new_git_repository:git仓库
  • new_http_archive:网络下载

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

参考:C / C++ 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指定了链接规则

参考:

Introduction to Bazel: Building a C++ Project

Bazel入门:编译C++项目

Google软件构建工具Bazel原理及使用方法介绍

 类似资料: