1

[golang] go:generate

 2 years ago
source link: https://wayou.github.io/2021/10/05/_golang__go_generate/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[golang] go:generate

2021 10 月 5 日

[golang] go:generate

考察如下的场景:

你写了个 Go 程序,使其能够打印出版本信息比如:

$ myapp -v
v1.0.0 

简单地可以在程序里写死,但问题也很明显,每次发版都需要手动更新,更致命地,任何依赖人工介入的操作都有可能被遗忘。

如果有工具可以自动化这个操作就好了,事实上,Go 自带的工具链中确实有对应机制来自动化这个场景。

-ldflags

link tool 接收形如 X importpath.name=value 的入参,通过 ldflags 指定。效果是自动将模块 importpathname 替换为指定的 value 值。推而广之,在编译时就可以通过命令行将版本替换到代码中了。

一般地,我们并不直接使用 link tool,还是正常使用 go buildgo rungo install,执行这些命令时会自动透传 -ldflags 参数给 link tool。

以下是一个简单示例:

// main.go

package main

import (
	"fmt"
)

var VersionString = "unset"

func main() {
	fmt.Println("Version:", VersionString)
}
$ go run main.go
Version: unset

$ go run -ldflags '-X main.VersionString=1.0' main.go
Version: 1.0

当然,这里 value 可以是任意值,只要执行时进行正确的传递即可,即,如果值当中包含空格,引号这些,需要在组织命令时做好转义。

通过 git rev-parse HEAD 将 Git 版本戳作为入参:

$ git rev-parse HEAD
db5c7db9fe3b632407e9f0da5f7982180c438929

$ go run -ldflags "-X main.VersionString=`git rev-parse HEAD`" main.go
Version: db5c7db9fe3b632407e9f0da5f7982180c438929

将日期作为入参:

$ go run -ldflags "-X 'main.VersionString=1.0 (beta)'" main.go
Version: 1.0 (beta)
$ go run -ldflags "-X 'main.VersionString=`date`'" main.go
Version: Sun Nov 27 16:42:10 EST 2016

实现了入参的自由传递,自动化过程就是将上面的脚本放入 makefile 或 ci 流程中这么简单了,而我们的代码则无须人工修改。

go:generate

进一步地,通过在需要的地方以注释的方式直接编写需要执行的操作,go:generate 解析代码中的这些注释并执行。这样不必在命令行中进行脚本的拼接。

一个示例:

// main.go

package main

//go:generate echo Hello, Go Generate!

func Add(x, y int) int {
	return x + y
}

运行结果:

$ go generate
Hello, Go Generate!

其中因为 ! 叹号不是有效的 bash 命令,go:generate 会自动忽略而正确执行。

go:generate 的完整文档参见这里

解决外部依赖问题

一般地,对于 go:generate 能够执行的语句,并没有严格限制,这可以是 bash 脚本,可以是三方工具,只要编译 Go 程序的环境中保证需要执行的工具已经被正确安装可执行即可,比如上面使用的 git, 获取日期的工具等。

这就涉及到外部不确定性的依赖,导致程序无法在任意环境正常编译。解决办法是始终让 go:generate 通过 go run 运行另外的 Go 程序。因为如果环境能够编译 Go 程序,那么肯定安装了 Golang,这样其他 Go 工具也是能够正常执行的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK