通常编译 protobuf 会使用 protoc 手动编译,更好一点可以写一个 Makefile 指令来做。
不过在 Go 中提供一种在源文件定义类似 Makefile 指令 generate。当运行 go generate 后,编译器会找到所有包含 //go:generate command argument...
的注释,然后运行后面的命令。
那这样的话就不需要再写一个 Makefile 指令了。
使用 go generate 工具编译 protobuf
新建一个文件目录 test,然后编辑 doc.go 文件。BTW,doc.go 是约定俗成写包文档的文件,通常不会写逻辑代码,所以这里写 generate 指令最好不过了。
1 | //go:generate protoc --go_out=. *.proto |
generate 指令只能在 go 文件中使用,而且需要注意的是和传统注释不同的是 //
后面不能有空格。
然后编辑 test.proto 文件
1 | syntax="proto3"; |
另外 go build 等其它命令不会调用 go generate
,必须手动显式调用 go generate
。不过这报错了,提示找不到文件。
1 | *.proto: No such file or directory |
这个问题在文档里有说明,generate 并不处理 glob。那我们这里修改 doc.go 当 sh 直接处理就行了。
1 | //go:generate sh -c "protoc --go_out=. *.proto" |
另外也要注意,双引号会被 go 进行解析,所以该转义的地方需要注意转义。
自动生成 Stringer 接口
在 golang 博客中 generate code介绍了一种类似宏指令的方式。
假设定义一组不同类型的药物的枚举:
1 | package painkiller |
通常为了能直接 print 其枚举名称,我们会给 Pill
实现 Stringer 接口。
1 | func (p Pill) String() string { |
不过有了 generate 指令我们可以不用手写这些逻辑代码。
下载并安装 stringer
1 | go get golang.org/x/tools/cmd/stringer |
在 Pill 包名称上添加一句 //go:generate stringer -type=Pill
。通常为了和文档区分开,我们还要加一个空行。
1 | //go:generate stringer -type=Pill |
这时候会自动生成 pill_string.go
1 | // generated by stringer -type Pill pill.go; DO NOT EDIT |
命令格式
1 | go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages] |
还可以在命令中定义别名,不过只有当前文件内有效。
1 | //go:generate -command YACC go tool yacc |
另外还支持下面这些变量:
1 | $GOARCH |