Bazel 基本使用

本文是 Bazel 学习笔记,记录了 Bazel 的一些核心概念和基本使用方法,构建目标为 Go 语言项目。

概念

Target(目标)

目标是Bazel构建系统中的基本单元,它代表构建过程中的一个输出文件或一个可运行的程序。每个目标都有一个名称和一组属性,例如源文件、依赖项和构建规则。

Rule(规则)

规则定义了如何构建一个目标。规则包括目标类型、名称、属性和构建命令。Bazel提供了许多内置规则,例如cc_library 和 cc_binary,以支持不同类型的目标。

Workspace(工作区)

工作区是Bazel构建系统的最高级别的概念,它包含一个或多个项目。工作区由一个或多个目录组成,每个目录包含一个BUILD文件和其他相关文件。

Package(包)

包是Bazel构建系统中的一个逻辑单元,它包含一个或多个相关的目标和规则。每个包都有一个名称和一个相对路径,例如//myproject/mypackage。

Label(标签)

标签是Bazel中唯一标识一个目标或规则的方式,它由包名和目标名组成,例如//myproject/mypackage:mytarget。

Dependency(依赖)

依赖是指一个目标需要另一个目标才能构建的情况。Bazel使用依赖关系来自动构建和管理项目中的依赖项。

Configuration(配置)

配置是指构建过程中的一组参数,例如编译器选项、库路径和环境变量。Bazel支持多个配置,例如debug和release,以便在不同的环境中构建项目。

文件

WORKSPACE

用于将目录及其内容标识为 Bazel 工作区,并位于项目目录结构的根目录中。Bazel 构建项目时,所有输入都必须位于同一工作区中。除非链接,否则位于不同工作区中的文件彼此独立。

BUILD.bazel

用于告知 Bazel 如何构建项目的不同部分。工作区中包含 BUILD 文件的目录就是一个软件包。每个 BUILD 文件至少需要一条规则(即一组指令),用于告知 Bazel 如何构建所需的输出,例如可执行二进制文件或库。BUILD 文件中构建规则的每个实例都称为目标,指向一组特定的源文件和依赖项。目标还可以指向其他目标。

示例:

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

.bazelrc

bazel 是 Java 开发的,可以在该文件中设置 JVM,此外,可以配置 go 代理,可以配置目标系统等。

# 设置JVM
startup --host_jvm_args=-XX:+UseParallelGC --host_jvm_args=-Xmx6g --host_jvm_args=-Xms1g
# 设置CoreDump
startup --unlimit_coredumps

# 设置GOPROXY
test --action_env=GOPROXY=https://goproxy.cn
build --action_env=GOPROXY=https://goproxy.cn
run --action_env=GOPROXY=https://goproxy.cn

# 设置GOSUMDB
test --action_env=GOSUMDB=goproxy.cn/sumdb/sum.golang.org
build --action_env=GOSUMDB=goproxy.cn/sumdb/sum.golang.org
run --action_env=GOSUMDB=goproxy.cn/sumdb/sum.golang.org

# 设置编译目标平台
build --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64
run --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64

自定义 rule (*.bzl)

如果你的项目有一些复杂构造逻辑、或者一些需要复用的构造逻辑,那么可以将这些逻辑以函数形式保存在 .bzl 文件,供 WORKSPACE 或者 BUILD 文件调用。其语法跟 Python 类似:

def download_package():
    # 下载 Bazel Go语言 规则集
    if not native.existing_rule("io_bazel_rules_go"):
        http_archive(
            name = "io_bazel_rules_go",
            sha256 = "56d8c5a5c91e1af73eca71a6fab2ced959b67c86d12ba37feedb0a2dfea441a6",
            urls = [
                "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.37.0/rules_go-v0.37.0.zip",
                "https://github.com/bazelbuild/rules_go/releases/download/v0.37.0/rules_go-v0.37.0.zip",
            ],
        )

    # 下载 Bazel Gazelle 规则集
    if not native.existing_rule("bazel_gazelle"):
        http_archive(
            name = "bazel_gazelle",
            sha256 = "ecba0f04f96b4960a5b250c8e8eeec42281035970aa8852dda73098274d14a1d",
            urls = [
                "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.29.0/bazel-gazelle-v0.29.0.tar.gz",
                "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.29.0/bazel-gazelle-v0.29.0.tar.gz",
            ],
        )

.bazelversion

配置 bazel 版本。

步骤

设置 WORKSPACE

workspace(name = "go-bazel")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "io_bazel_rules_go",
    sha256 = "c8035e8ae248b56040a65ad3f0b7434712e2037e5dfdcebfe97576e620422709",
    urls = [
        "https://github.com/bazelbuild/rules_go/releases/download/v0.44.0/rules_go-v0.44.0.zip",
        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.44.0/rules_go-v0.44.0.zip",
    ],
)

http_archive(
    name = "bazel_gazelle",
    sha256 = "32938bda16e6700063035479063d9d24c60eda8d79fd4739563f50d331cb3209",
    urls = [
        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.35.0/bazel-gazelle-v0.35.0.tar.gz",
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.35.0/bazel-gazelle-v0.35.0.tar.gz",
    ],
)

load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")

############################################################
# Define your own dependencies here using go_repository.
# Else, dependencies declared by rules_go/gazelle will be used.
# The first declaration of an external repository "wins".
############################################################

# gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors

load("//:deps.bzl", "go_dependencies")

# gazelle:repository_macro deps.bzl%go_dependencies
go_dependencies()

go_rules_dependencies()

go_register_toolchains(version = "1.21.5")

gazelle_dependencies()

其中,Gazelle 是一个自动生成 Bazel 编译文件的工具,包括给 WORKSPACE 添加外部依赖、扫描源文件依赖自动生成BUILD.bazel文件等。Gazelle 原生支持Go和 protobuf。

Protobuf

需要在 WORKSPACE 中添加:

http_archive(
    name = "com_google_protobuf",
    sha256 = "d0f5f605d0d656007ce6c8b5a82df3037e1d8fe8b121ed42e536f569dec16113",
    strip_prefix = "protobuf-3.14.0",
    urls = [
        "https://github.com/protocolbuffers/protobuf/archive/v3.14.0.tar.gz",
        "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v3.14.0.tar.gz",
    ],
)

protobuf_deps()

命令

使用 Gazelle 构建 BUILD.bazel

bazel run //:gazelle

更新 golang 依赖:

bazel run //:gazelle-update-repos

跑测试:

bazel test //...

构建项目:

bazel build //...

Docker 镜像

在 WORKSPACE 中添加:

http_archive(
    name = "io_bazel_rules_docker",
    sha256 = "b1e80761a8a8243d03ebca8845e9cc1ba6c82ce7c5179ce2b295cd36f7e394bf",
    urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.25.0/rules_docker-v0.25.0.tar.gz"],
)

load(
    "@io_bazel_rules_docker//repositories:repositories.bzl",
    container_repositories = "repositories",
)

container_repositories()

load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps")

container_deps()

基础镜像

在 WOKRSPACE 中拉取:


load("@io_bazel_rules_docker//container:pull.bzl", "container_pull")

container_pull(
    name = "alpine_linux_amd64",
    registry = "index.docker.io",
    repository = "library/alpine",
    tag = "latest",
)

构建镜像

在 BUILD 中:

container_image(
    name = "image",
    base = "@alpine_linux_amd64//image",
    entrypoint = ["/servicea"],
    files = [
        ":servicea",
    ],
)

推送镜像

在 BUILD 中:

container_push(
    name = "image-push",
    format = "Docker",
    image = ":image",
    registry = "registry.cn-hangzhou.aliyuncs.com",
    repository = "bazel/greeter",
    tag = "latest",
)

参考

[1] Bazel 教程:构建 C++ 项目

[2] Kratos微服务工程Bazel构建指南

[3] go-bazel

[4] bazel-gazelle