简介
在Go语言中,go generate
命令是一个非常有用的工具,它可以帮助我们自动化地生成代码。本文将详细介绍如何使用 go generate
命令,并提供一些示例来说明它的用法。
什么是Generate命令?
go generate
命令是一个用于自动化生成Go代码的工具。它可以在Go源文件中的特殊注释中指定命令,然后在运行 go generate
命令时自动执行这些命令。这些命令可以用来生成代码、格式化代码、运行测试等等。
在Go 1.4版本中,go generate
命令首次发布,它的目的是为了解决Go语言中的一些重复性工作。自从发布以来,它已经成为了一个非常受欢迎的工具,广泛应用于Go语言的开发中。
语法
go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
- generate 由现有指令描述的运行命令文件。这些命令可以运行任何进程,但目的是创建或更新 Go 源文件。
- Go generate永远不会由 go build、go test 等等。它必须显式运行。
注释格式:
在Go源文件中使用 go generate
命令需要在源代码中添加特殊注释,格式如下:
其中, command
是要执行的命令, arguments
是传递给该命令的参数。
//go:generate command argument...
Generate命令的使用示例
例如,假设我们有一个名为 hello.go
的源文件,其中包含以下注释:
//go:generate echo "Hello, World!"
当我们在命令行中运行 go generate
命令时,它将自动执行上述注释中的命令,并在控制台中输出 "Hello, World!"。
Generate生成顺序
//go:generate echo "top"
package main
import "fmt"
//go:generate echo "middle"
func main() {
fmt.Println("hello, go generate")
}
//go:generate echo "tail"
$go generate main.go
top
middle
tail
正则匹配生成
go generate还可以通过-run使用正则式去匹配各源文件中go generate指示符中的命令,并仅执行匹配成功的命令:
// 未匹配到任何go generate指示符中的命令
$go generate -x -v -run "protoc" ./...
main.go
subpkg1/subpkg1.go
subpkg2/subpkg2.go
Generate应用场景
生成枚举类型的String方法
这里我们需要安装stringer工具
go install golang.org/x/tools/cmd/stringer@latest
假如有如下枚举
type BACnetComfirmedServiceChoice byte
const (
ServiceConfirmedAcknowledgeAlarm BACnetComfirmedServiceChoice = 0 //确认报警服务
ServiceConfirmedComfirmedCOVNotification BACnetComfirmedServiceChoice = 1 //证实COV通告服务
ServiceConfirmedComfirmedEventNotification BACnetComfirmedServiceChoice = 2 //证实事件通过服务
}
通常我们会为枚举类型手写String方法,这样在打印上面枚举常量时能输出有意义的内容:
func (d BACnetComfirmedServiceChoice ) String() string {
switch d {
case ServiceConfirmedAcknowledgeAlarm :
return "确认报警服务"
case ServiceConfirmedComfirmedCOVNotification :
return "证实COV通告服务"
case ServiceConfirmedComfirmedEventNotification:
return "证实事件通过服务"
}
return "确认报警服务"
}
如果一个项目中枚举常量类型有很多,逐个为其手写String方法费时费力。当枚举常量有变化的时候,手动维护String方法十分烦琐且易错。对于这种情况,使用go generate驱动stringer工具为这些枚举类型自动生成String方法的实现不失为一个较为理想的方案。下面就是利用go generate对上面示例的改造:
//go:generate stringer -type=BACnetComfirmedServiceChoice -linecomment
type BACnetComfirmedServiceChoice byte
const (
ServiceConfirmedAcknowledgeAlarm BACnetComfirmedServiceChoice = 0 //确认报警服务
ServiceConfirmedComfirmedCOVNotification BACnetComfirmedServiceChoice = 1 //证实COV通告服务
ServiceConfirmedComfirmedEventNotification BACnetComfirmedServiceChoice = 2 //证实事件通过服务
}
利用generate生成代码如下:
// Code generated by "stringer -type=BACnetComfirmedServiceChoice -linecomment"; DO NOT EDIT.
package generate
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ServiceConfirmedAcknowledgeAlarm-0]
_ = x[ServiceConfirmedComfirmedCOVNotification-1]
_ = x[ServiceConfirmedComfirmedEventNotification-2]
}
const _BACnetComfirmedServiceChoice_name = "确认报警服务证实COV通告服务证实事件通过服务"
var _BACnetComfirmedServiceChoice_index = [...]uint8{0, 18, 39, 63}
func (i BACnetComfirmedServiceChoice) String() string {
if i >= BACnetComfirmedServiceChoice(len(_BACnetComfirmedServiceChoice_index)-1) {
return "BACnetComfirmedServiceChoice(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _BACnetComfirmedServiceChoice_name[_BACnetComfirmedServiceChoice_index[i]:_BACnetComfirmedServiceChoice_index[i+1]]
}
protobuf
通过generate将protoc预生成指令写在代码中
package main
import (
"fmt"
msg "go-generate/protobuf/msg"
)
//go:generate protoc -I ./IDL msg.proto --gofast_out=./msg
func main() {
var m = msg.Request{
MsgID: "xxxx",
Field1: "field1",
Field2: []string{"field2-1", "field2-2"},
}
fmt.Println(m)
}
ebpf
在cilium/ebpf中使用generate调用其cmd下的bpf2go进行编译
/ $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf xdp.c -- -I../headers
func main() {
....
}
总结
go generate
命令是一个非常有用的工具,它可以帮助我们自动化地生成代码。在本文中,我们介绍了如何在Go源代码中使用 go generate
命令,并提供了一些示例来说明它的用法。使用 go generate
命令可以提高我们的开发效率,减少我们的重复性工作。