bazel 的使用

本地构建以及使用 buildbuddy 远程构建

Posted by Jerry Chen on September 17, 2023

写在前面

一些网站

  • bazel 官网:https://bazel.google.cn/

  • buildbuddy 官网:https://bazel.google.cn/

Bazel 和 Bazelisk 的区别

  • bazel 是 Google 开源的构建工具,用于构建和测试各种软件项目。

  • bazelisk 是一个工具,用于管理和安装 Bazel 版本。主要优势在于它能够简化 Bazel 版本的切换和管理。

    Bazel 的版本更新频繁,不同版本之间可能存在不兼容性。

    Bazelisk 可以自动下载和切换到适合你的项目的 Bazel 版本,从而避免了版本兼容性问题。

安装 Bazelisk

安装

bazelisk 虽然是 google 推荐的 bazel 使用工具,但是它不能离线使用;

通过 Releases 发布页面下载相应平台的安装包进行安装。

以 Linux x86-64 平台为例:

1
2
3
4
5
6
7
8
# 下载 bazelisk
wget https://github.com/bazelbuild/bazelisk/releases/download/v1.18.0/bazelisk-linux-amd64

# 增加执行权限
chmod +x bazelisk-linux-amd64

# 移动到系统目录并重命名为 bazel
sudo mv bazelisk-linux-amd64 /usr/local/bin/bazel

第一次执行 bazel(实际是 bazelisk) 命令时:

1
bazel version

会经历如下步骤:

  1. 执行 bazelisk 的 version 指令,比如得到:

    1
    
    Bazelisk version: v1.18.0
    
  2. 检查当前目录和上层(上上层…)目录是否存在 WORKSPACE 文件确定该级目录为工作区根目录;

    • [建议] 如果工作区根目录存在 .bazelversion 文件,从中读出指定的 bazel 版本;

      1
      
      6.3.2
      
    • 如果工作区根目录存在 .bazeliskrc 文件,从中读出指定的 bazel 版本;

      1
      
      USE_BAZEL_VERSION=6.3.2
      
    • 如果从文件无法得到 bazel 版本,那就会去检查 USE_BAZEL_VERSION 环境变量;

    • 如果从文件和环境变量都无法得到 bazel 版本,那就会从云端读到最新的 bazel 版本(需要联网);

  3. 检查本地是否有对应的 bazel 版本:

    • 如果存在就使用这个版本;
    • 如果不存在就从云端下载这个版本(需要联网);
  4. 使用对应版本的 bazel(真实的)时,bazel(实际是 bazelisk)会把传参原样转发;

bash 补全

生成 bazel-complete.bash 文件:

1
2
3
4
5
6
7
8
9
# 下载 bazel-complete-header.bash
wget https://raw.githubusercontent.com/bazelbuild/bazel/master/scripts/bazel-complete-header.bash
# 下载 bazel-complete-template.bash
wget https://raw.githubusercontent.com/bazelbuild/bazel/master/scripts/bazel-complete-template.bash
# 生成 bazel-help-completion
bazel help completion > bazel-help-completion

# 合并 3 个文件
cat bazel-complete-header.bash bazel-complete-template.bash bazel-help-completion > bazel-complete.bash

bazel-complete.bash 放到 /etc/bash_completion.d 目录中:

1
sudo cp bazel-complete.bash /etc/bash_completion.d/

安装 Bazel

安装

如果你有离线使用需求,应该直接安装 bazel 二进制预编译文件;

下载地址:点这

1
2
3
4
5
6
7
wget https://github.com/bazelbuild/bazel/releases/download/6.4.0/bazel-6.4.0-installer-linux-x86_64.sh

# 给执行权限
chmod +x bazel-6.4.0-installer-linux-x86_64.sh

# 进行安装
sudo ./bazel-6.4.0-installer-linux-x86_64.sh

bash 补全

添加下面的指令到 ~/.bashrc 中:

1
source /usr/local/lib/bazel/bin/bazel-complete.bash

bazel 进行构建 c++

本地构建

克隆代码仓:

本教程的示例项目位于 examples/cpp-tutorial 目录中。

1
git clone https://github.com/bazelbuild/examples
第一阶段:BUILD 中一个 target

代码结构如下:

1
2
3
4
5
6
7
8
examples
└── cpp-tutorial
    └──stage1
       ├── main
       │   ├── BUILD
       │   └── hello-world.cc
       ├── .bazelversion		// 创建一个,写你的 bazel 版本号,比如 6.3.2
       └── WORKSPACE

BUILD 文件中只有一个 target:

1
2
3
4
5
6
load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

编译方法:

BUILD 文件和命令行中,bazel 使用标签来引用目标,例如 //main:hello-world

其语法为://path/to/package:target-name

1
bazel build //main:hello-world
第二阶段:BUILD 中多个 target

代码结构如下:

1
2
3
4
5
6
7
8
9
10
examples
└── cpp-tutorial
    └──stage2
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── .bazelversion		// 创建一个,写你的 bazel 版本号,比如 6.3.2
       └── WORKSPACE

BUILD 文件中有多个 target:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

编译方法:

BUILD 文件和命令行中,bazel 使用标签来引用目标,例如 //main:hello-world

其语法为://path/to/package:target-name

1
bazel build //main:hello-world
第三阶段:多个 BUILD

代码结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
examples
└── cpp-tutorial
    └──stage2
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       ├── .bazelversion		// 创建一个,写你的 bazel 版本号,比如 6.3.2
       └── WORKSPACE

lib 目录中的 BUILD 文件:

1
2
3
4
5
6
7
8
load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

main 目录中的 BUILD 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "//lib:hello-time",
    ],
)

编译方法:

BUILD 文件和命令行中,bazel 使用标签来引用目标,例如 //main:hello-world

其语法为://path/to/package:target-name

1
bazel build //main:hello-world

buildbuddy 远程构建

  1. 打开 https://www.buildbuddy.io/ 并登录;

  2. 按个人中心的 QuickStart 进行设置 .bazelversion

    • api key 验证;
    • 完全缓存;
    • 使能远程执行;
    1
    2
    3
    4
    5
    6
    
    build --bes_results_url=https://app.buildbuddy.io/invocation/
    build --bes_backend=grpcs://remote.buildbuddy.io
    build --remote_cache=grpcs://remote.buildbuddy.io
    build --remote_timeout=3600
    build --remote_executor=grpcs://remote.buildbuddy.io
    build --remote_header=x-buildbuddy-api-key=V1A5A5q9URMhvBQO7MyO
    
  3. 在我们的工作区根目录创建 .bazelversion,并写入上一步得到的信息,然后执行:

    1
    
    bazel build //main:hello-world --jobs=80
    

其他

bazel 推荐规则:rules

最佳实践:best-practices

c++ 用例:cpp-use-cases

配置 c++ 工具链:ccp-toolchain-config

查看依赖关系图:cpp-dependency