通常编译 protobuf 会使用 protoc 手动编译,更好一点可以写一个 Makefile 指令来做。
不过在 Go 中提供一种在源文件定义类似 Makefile 指令 generate。当运行 go generate 后,编译器会找到所有包含 //go:generate command argument...
的注释,然后运行后面的命令。
那这样的话就不需要再写一个 Makefile 指令了。
使用 go generate 工具编译 protobuf
新建一个文件目录 test,然后编辑 doc.go 文件。BTW,doc.go 是约定俗成写包文档的文件,通常不会写逻辑代码,所以这里写 generate 指令最好不过了。
generate 指令只能在 go 文件中使用,而且需要注意的是和传统注释不同的是 //
后面不能有空格。
然后编辑 test.proto 文件
1 2 3 4 5 6 7
| syntax="proto3";
message Info { uint32 info_id = 1; string title = 2; string content = 3; }
|
另外 go build 等其它命令不会调用 go generate
,必须手动显式调用 go generate
。不过这报错了,提示找不到文件。
1 2
| *.proto: No such file or directory doc.go:1: running "protoc": exit status 1
|
这个问题在文档里有说明,generate 并不处理 glob。那我们这里修改 doc.go 当 sh 直接处理就行了。
另外也要注意,双引号会被 go 进行解析,所以该转义的地方需要注意转义。
自动生成 Stringer 接口
在 golang 博客中 generate code介绍了一种类似宏指令的方式。
假设定义一组不同类型的药物的枚举:
1 2 3 4 5 6 7 8 9 10 11
| package painkiller
type Pill int
const ( Placebo Pill = iota Aspirin Ibuprofen Paracetamol Acetaminophen = Paracetamol )
|
通常为了能直接 print 其枚举名称,我们会给 Pill
实现 Stringer 接口。
1 2 3 4 5 6 7 8 9 10 11 12 13
| func (p Pill) String() string { switch p { case Placebo: return "Placebo" case Aspirin: return "Aspirin" case Ibuprofen: return "Ibuprofen" case Paracetamol: return "Paracetamol" } return fmt.Sprintf("Pill(%d)", p) }
|
不过有了 generate 指令我们可以不用手写这些逻辑代码。
下载并安装 stringer
1
| go get golang.org/x/tools/cmd/stringer
|
在 Pill 包名称上添加一句 //go:generate stringer -type=Pill
。通常为了和文档区分开,我们还要加一个空行。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
package painkiller
type Pill int
const ( Placebo Pill = iota Aspirin Ibuprofen Paracetamol Acetaminophen = Paracetamol )
|
这时候会自动生成 pill_string.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
package painkiller
import "fmt"
const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"
var _Pill_index = [...]uint8{0, 7, 14, 23, 34}
func (i Pill) String() string { if i < 0 || i+1 >= Pill(len(_Pill_index)) { return fmt.Sprintf("Pill(%d)", i) } return _Pill_name[_Pill_index[i]:_Pill_index[i+1]] }
|
命令格式
1 2 3 4 5 6 7
| go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
-v 输出被处理的包名和源文件名 -n 显示不执行命令 -x 显示并执行命令
-run 正则匹配要运行的指令
|
还可以在命令中定义别名,不过只有当前文件内有效。
1 2
| //go:generate -command YACC go tool yacc //go:generate YACC -o test.go -p parse test.y
|
另外还支持下面这些变量:
1 2 3 4 5 6 7 8 9 10 11 12
| $GOARCH CPU架构 (arm、amd64等) $GOOS 操作系统类型(linux、windows等) $GOFILE 当前处理中的文件名(就是文件名,不包含路径) $GOLINE 当前命令在文件中的行号 $GOPACKAGE 当前处理文件的包名(只是当前包名,不包含路径) $DOLLAR 美元符号
|