
rules_go 与 gazelleBazel 支持很多内置的规则,语言相关规则有 Shell、Objective-C、C++ 和 Java,比如 sh_binary、cc_binary、cc_import、cc_library、java_binary、java_import等。但是 Go 编译内置规则没有支持,不过好在 Bazel 支持规则扩展,可以自定义 Go 相关规则,包括可以实现如 go_binary、go_library、go_test等规则。而 `rules_go`[1] 就是 Bazel 官方维护的 Go Bazel 开源扩展规则。`gazelle`[2] 这个项目可以将 Go 项目转为 Bazel 方式构建,包括生成 BUILD.bazel 文件,根据 go.mod 文件自动生成下载依赖模块规则 go_repository。这里简单介绍下 rules_go 和 gazelle 相关内容,更多可以参考官方相关文档。
rules_go 主要特性支持包括:
rules_go 的使用环境很简单:
cgo,则需要本机上有 C/C++ 工具链,默认的 Bazel 会尝试自动配置工具链Go 工具链,Bazel 会自动为每个项目下载最新版本,当然你也可以用 rules_go 里的工具链相关规则配置本地 Go 工具链或下载指定版本有两种方式使用 gazelle:
gazelle 本身就是用 Go 实现的一个工具,通过 Go 的方式使用它。比如:$ gazelle -go_prefix github.com/example/project
# Add or update a repository to latest version by import path
$ gazelle update-repos example.com/new/repo
# Add or update a repository to specified version/commit by import path
$ gazelle update-repos example.com/new/repo@v1.3.1
# 从 go.mod 导入存储库,修改和更新 Bazel 宏
$ gazelle update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories
# 设为-prune=true时,gazelle 将删除 Gopkg.lock/go.mod 文件中不再具有等效存储库的 go_repository 规则
$ gazelle update-repos -prune=true -from_file=go.mod -to_macro=repositories.bzl%go_repositories
gazelle 可以将配置指令以形式于 # gazelle:{key} {value} 注释方式放于 Bazel 构建文件中(BUILD),从而省去每次命令行都需要键入过程。更多的键值对设置可以参见这里:https://github.com/bazelbuild/bazel-gazelle#directives 。
gazelle 的另一种方式就是直接和 Bazel 集成使用,作为一个外部规则导入使用,WORKSPACE 文件中:。http_archive(
name = "bazel_gazelle",
sha256 = "cdb02a887a7187ea4d5a27452311a75ed8637379a1287d8eeb952138ea485f7d",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.1/bazel-gazelle-v0.21.1.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.1/bazel-gazelle-v0.21.1.tar.gz",
],
)
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
gazelle_dependencies()
然后在 Go 项目根目录的 BUILD 或 BUILD.bazel 文件中:
load("@bazel_gazelle//:def.bzl", "gazelle")
# gazelle:prefix github.com/example/project
gazelle(name = "gazelle")
对于 gazelle 规则,指定 Go 项目前缀,还可以:
load("@bazel_gazelle//:def.bzl", "gazelle")
# 建议是以注释方式也保留,Go 工具方式和 Bazel 方式都可以使用
# gazelle:prefix github.com/example/project
gazelle(
name = "gazelle",
prefix = "github.com/example/project",
)
最后,这里示例根据 go.mod 自动生成依赖仓库下载代码和相关 BUILD.bazel 文件:
# 自动添加一个外部依赖项目(非 go.mod 导入)
$ bazel run tools/cli:gazelle update-repos {repo-uri}
# 生成 BUILD.bazel 文件
$ bazel run tools/cli:gazelle
# 生成的依赖仓库下载代码自动生成到 go_repositories.bzl 文件中,然后自动生成导入代码到 WORKSPACE 文件中
$ bazel run tools/cli:gazelle -- update-repos -from_file=tools/cli/go.mod -to_macro=go_repositories.bzl%go_repositories
go build 到 bazel buildWORKSPACE 和 BUILD 文件,在 WORKSPACE 文件中导入 rules_go 和 gazelle 规则:load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# download rules_go
http_archive(
name = "io_bazel_rules_go",
sha256 = "8663604808d2738dc615a2c3eb70eba54a9a982089dd09f6ffe5d0e75771bc4f",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.23.6/rules_go-v0.23.6.tar.gz",
"https://github.com/bazelbuild/rules_go/releases/download/v0.23.6/rules_go-v0.23.6.tar.gz",
],
)
# load rules_go
load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
go_rules_dependencies()
go_register_toolchains()
# download gazelle
http_archive(
name = "bazel_gazelle",
sha256 = "cdb02a887a7187ea4d5a27452311a75ed8637379a1287d8eeb952138ea485f7d",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.1/bazel-gazelle-v0.21.1.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.1/bazel-gazelle-v0.21.1.tar.gz",
],
)
# load gazelle
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
gazelle_dependencies()
比如我们创建了一个 Go 的一个命令行工具项目,放于我们工程的 tools/cli 目录下,即 Go 项目 go.mod 文件在该目录下:
.
├── BUILD
├── WORKSPACE
└── tools
└── cli
├── BUILD
├── go.mod
└── cmd
└── ota_packer
└── main.go
tools/cli/BUILD 文件内容配置 gazelle:
load("@bazel_gazelle//:def.bzl", "gazelle")
# gazelle:prefix github.com/yicm/OtaPackageTool
gazelle(
name = "gazelle",
prefix = "github.com/yicm/OtaPackageTool",
)
在整个项目(非Go项目)根目录执行运行 gazelle 生成相关依赖模块导入代码和 BUILD.bazel 文件:
# 根据 go.mod,将go_repository规则写入一个单独的宏文件并将其加载到 WORKSPACE 文件中
$ bazel run tools/cli:gazelle -- update-repos -from_file=tools/cli/go.mod -to_macro=go_repositories.bzl%go_repositories
# 生成包的 BUILD.bazel 文件
$ bazel run tools/cli:gazelle
在 tools/cli/cmd/ota_packer 目录中生成了 BUILD.bazel 文件:
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/yicm/OtaPackageTool/cmd/ota_packer",
visibility = ["//visibility:private"],
)
go_binary(
name = "ota_packer",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
这里就很清楚了,目的就是生成 ota_packer 可执行文件。而在项目根目录生成了 go_repositories.bzl 文件,并在 WORKSPACE 文件中调用了 go_repositories 宏:
go_repositories.bzl:
load("@bazel_gazelle//:deps.bzl", "go_repository")
def go_repositories():
go_repository(
name = "co_honnef_go_tools",
importpath = "honnef.co/go/tools",
sum = "h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=",
version = "v0.0.1-2019.2.3",
)
......
WORKSPACE:
......
load("//:go_repositories.bzl", "go_repositories")
# gazelle:repository_macro go_repositories.bzl%go_repositories
go_repositories()
上面小节完成了 rules_go 和 gazelle 的配置和 Go 项目自动转成 Bazel 方式编译。而完成转换后,编译 Go 项目就是 Bazel 的使用了:
# 构建 ota_packer 目标
$ bazel build tools/cli/cmd/ota_packer:ota_packer
# 构建项目下所有目标
$ bazel build //...
$ bazel run tools/cli/cmd/ota_packer:ota_packer
通过在命令行选项 --action_env 可以设置环境变量,从而作用于 actions。比如设置 Go 代理:
--action_env=GOPROXY=https://goproxy.cn
在 .bazelrc 文件中,可以设置 build、test、run 命令的命令行默认选项:
build --action_env=GOPROXY=https://goproxy.cn
test --action_env=GOPROXY=https://goproxy.cn
run --action_env=GOPROXY=https://goproxy.cn
go build 到 bazel build?Bazel Go 规则集,可以让我们很方便地管理 Go 工具链和外部库,而无需依赖于本地安装的库。Bazel 地官方项目 Gazelle,可以用来生成 Go 和 Protocol Buffers 规则。借助 Gazelle,能够以最少的人工输入为 Go 项目中的大多数 Go 软件包生成 Bazel 规则。
Bazel 本身具有的构建特性包括分布式缓存和构建、增量构建,只有当我们的工程代码发生改变或某些依赖发生变化时,才会触发构建并更新缓存,从而对大型项目可以实现快速构建。且 Bazel 的沙箱特性,保证每个开发者的构建环境一致。
Go 本身的 Go Modules 依赖管理已经变得成熟,我们可以很方便的管理我们的依赖包和版本。当然,使用 Bazel Go Rules 的同时,我们还可以使用原生的 go build,即两种方式不会发生冲突。
所以从 go build 到 bazel build 是否有必要,需要根据你的项目来决定。
[1]rules_go: https://github.com/bazelbuild/rules_go
[2]gazelle: https://github.com/bazelbuild/bazel-gazelle
[3]nogo: https://github.com/bazelbuild/rules_go/blob/master/go/nogo.rst