之前简单了解过 bazel ,前一阵给 Envoy CI 加 Go 代码风格的检查,又折腾了一番 bazel 的扩展规则,把当时的笔记扩展记录下来,以后没准还需要折腾。
背景
背景是这样的,Envoy CI 原来使用 python
脚本来做代码风格检查,但是那一套规则,对于 Go 并不匹配,所以想改用 gofmt
来检查 Go 代码。
其实最简单的方式是,在 Envoy CI 的 base docker image 中安装 golang 的包,但是他们觉得 golang 包太大了…
那么此时,就需要通过 bazel 来安装 golang,并且把 gofmt
暴露出来。
rules_go
按照 phlax 的提示,rules_go 已经引入了 gofmt
,接下来就看,怎么用上了。
经过一通翻 rules_go
的代码,大概搞明白了 bazel 扩展规则的基本套路。
我的思路是,就是通过自定义规则,来产生 gofmt
文件,类似于编译 Envoy,然后,在检查脚本里就可以用上 gofmt 了。
最终效果是,在 tools/code_format/BUILD
里调用自定义规则:
1 | gen_gofmt( |
通过 build
来生成 gofmt
:
1 | bazel build //tools/code_format:gofmt |
rule
自定义规则,分为两部分,首先是上层的定义:rule
rule 可以理解为函数申明,可以有入参,通常是编译的源文件,如下示例中没有;还有返回值,也就是产出物。
rule 本身,比较简单,只是申明了基本信息,包括 rule implementation,还有对入参的定义。
如下示例,是基于 rules_go 的自定义的 go rule 规则 ,有几个点:
- 申明了
gen_gofmt
这个 rule,实现在_gen_gofmt_impl
里 - 依赖了
rules_go
中的 go toolchain,toolchain 会提供一个执行环境,比如通常的rule_cc
是提供的是 C 编译器的执行环境 - 入参处理,是在 attrs 中完成,示例并没有入参,只是将
go_context_data
放进上下文中
1 | gen_gofmt = rule( |
rule implementation
另一部分就是,rule implementation,也就是用 skylark
(类 python 的语言)函数来描述具体的生产过程。
如下示例,有几个点:
- go_context 是
rules_go
提供的函数,将 toolchain 中的内容暴露出来 - action 是 bazel 内置的,用来申明执行什么任务,可以多个叠加,这里就只是简单的执行
cp
命令 - 返回值也就是产出物
1 | def _gen_gofmt_impl(ctx): |
体会
bazel 确实很灵活,可以支持很复杂的扩展,但是上手门槛也高。
如果,有一个现成的示例可以抄,那么使用门槛也不高,简单 copy & paste,了解一些上层的概念也够用了。
但是,如果简单模式走不通,那就比较麻烦了,得把 bazel 的底层逻辑搞懂了,这就是门槛了。
我觉得,最主要的还是,通识性不够强,不够普世,bazel 自己撸的一套方案,用得人少,门槛就搞了。
结局
最后,phlax 发现可以这样来执行 gofmt
:bazel run @go_sdk//:bin/gofmt
,自定义规则就可以删掉了,汗 …
还是通识性不够强,用的人少,走的弯路就多了。
ChatGPT
其实,这种笔记也挺无聊的,让我们问问 ChatGPT 能做到什么程度呢?
1 | 我:请写一篇如何实现 bazel 自定义规则的介绍文章,大约 200 字 |
这两个问题,我给 90 分,不过后面还有更细节的问题,比如,如何使用 rules_go
中的 gofmt
,就回答错了。
或许,ChatGPT 还需要进化,又或者需要更多我这样的无聊笔记投喂训练,才能更深的理解?