[Bazel]构建Golang项目
- 1 `rules_go` 与 `gazelle`
- 2 `go build` 到 `bazel build`
- 2.1 初始化 Bazel 构建
- 2.2 编译 Go 项目
- 2.3 测试编译结果
- 2.4 Go 环境变量设置
- 3 我们需要 `go build` 到 `bazel build`?
1 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
主要特性支持包括:
- 构建库、二进制可执行文件、测试(go_library、go_binary、go_test)
- Vendoring
- cgo
- 交叉编译
- 通过 nogo[3] 进行构建时代码分析
- Protocol buffers
- proto_library
- go_proto_library
- go_proto_compiler
- 远程执行
rules_go
的使用环境很简单:
- Bazel
- 如果使用
cgo
,则需要本机上有C/C++
工具链,默认的 Bazel 会尝试自动配置工具链 - Bash、patch、cat 和 PATH 中的其他一些 Unix 工具
- 无需安装
Go
工具链,Bazel 会自动为每个项目下载最新版本,当然你也可以用rules_go
里的工具链相关规则配置本地 Go 工具链或下载指定版本- go_register_toolchains(go_version="1.14.0") : 下载指定的 Go SDK,默认最新版本
有两种方式使用 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
2 go build
到 bazel build
2.1 初始化 Bazel 构建
- 项目根目录创建
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()
2.2 编译 Go 项目
上面小节完成了 rules_go
和 gazelle
的配置和 Go
项目自动转成 Bazel
方式编译。而完成转换后,编译 Go
项目就是 Bazel
的使用了:
# 构建 ota_packer 目标
$ bazel build tools/cli/cmd/ota_packer:ota_packer
# 构建项目下所有目标
$ bazel build //...
2.3 测试编译结果
$ bazel run tools/cli/cmd/ota_packer:ota_packer
2.4 Go 环境变量设置
通过在命令行选项 --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
3 我们需要 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
- android 项目中出现红色感叹号的解决方法
- SharePoint Foundation 2010
- ASP.NET MVC Action Filters
- Android:StatFs类 获取系统/sdcard存储空间信息
- 数据挖掘干货
- 高效 Mac 人士必备:实现工作/家庭间网络环境切换的自动化
- android中AVD的使用
- ASP.NET MVC 2示例Tailspin Travel UI层分析
- CSS 命名之Dialog, Modal, Popup, Popover, Lightbox 等的区别
- Eclipse JAVA文件注释乱码
- 2018年小程序的红利趋势预测,懂的来……或许你将成为下个富翁
- VUE 入门基础(6)
- 五年换4高管,6000员工裁95%剩300人,王健林为何抛弃万达网科?
- Android Permission中英对照
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 解密日志文件工具类
- InnoDB表优化
- SAP Spartacus如何创建自定义route页面
- SAP Spartacus的url parameter
- 来讲讲你对ThreadLocal的理解
- 用了这个jupyter插件,我已经半个月没打开过excel了
- vue接入腾讯地图(二)【标注&定位实战】
- 图像处理笔记(4)----OpenCV对象追踪
- MySQL 数据恢复
- 【从0到1学习边缘容器系列2】之 边缘应用管理
- 【从0到1学习边缘容器系列-3】应用容灾之边缘自治
- Hacking with iOS: SwiftUI Edition - 里程碑:项目 13 - 15
- HDU 1896 优先队列用法
- 蓝桥杯省内模拟赛C++
- C++ STL (标准模板库) 详细内容讲解