rules_go
与 gazelle
Bazel
支持很多内置的规则,语言相关规则有 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 build
WORKSPACE
和 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