在应用程序中,添加代码版本信息,比如branch和commit,有利于代码发布后的维护。在golang中,如何自动添加代码的版本信息呢?
有两种方法:
使用go generate
go generate 命令是在Go语言 1.4 版本里面新添加的一个命令。当运行该命令时,它将扫描与当前包相关的源代码文件,找出所有包含//go:generate的特殊注释,提取并执行该特殊注释后面的命令。
示例代码
- main.go
package main
//go:generate sh gen-version.sh
import "fmt"
func main() {
fmt.Println("branch = ", branch)
fmt.Println("commit =", commit)
}
在执行go generate命令时,会自动执行其中的sh gen-version.sh命令。此脚本会自动生成version.go文件。
- gen-version.sh
echo "
// auto generated
// DON'T edit
package main
var branch = \"`git rev-parse --abbrev-ref HEAD`\"
var commit = \"`git rev-parse --short HEAD`\"
" > version.go
编译执行
$ go generate && go build
$ ./demoversion
branch = master
commit = 8ba44a0
注意
使用go generate命令时有以下几点需要注意:
- 该特殊注释必须在 .go 源码文件中;
- 特殊注释必须以//go:generate开头,双斜线后面没有空格。
- 每个源码文件可以包含多个 generate 特殊注释;
- 运行go generate命令时,才会执行特殊注释后面的命令;
- 当go generate命令执行出错时,将终止程序的运行;
使用go build的ldflags选项
使用go generate需要我们额外添加脚本文件,同时变量并没有在代码中声明,编码过程中IDE会持续显示错误。我们可以用ldflags解决这些问题。
ldflags用于在go build命令中,向golang linker传递参数。
ldflags中的-X选项,可以为代码中声明过的变量赋值。
-X importpath.name=value
Set the value of the string variable in importpath named name to value. This is only effective if the variable is declared in the source code either uninitialized or initialized to a constant string expression. -X will not work if the initializer makes a function call or refers to other variables. Note that before Go 1.5 this option took two separate arguments.
示例代码
- main.go
之前的main.go需要添加branch和commit变量的声明。//go:generate注释可以删除
package main
import "fmt"
var (
branch string
commit string
)
func main() {
fmt.Println("branch = ", branch)
fmt.Println("commit =", commit)
}
编译执行
$ go build \
-ldflags=\
"-X 'main.branch=$(git rev-parse --abbrev-ref HEAD)' -X 'main.commit=$(git rev-parse --short HEAD)'"
$ ./demoversion
branch = master
commit = 8ba44a0
ldflags的进阶用法
- 如果声明的变量不是在main包中,而是下层包中,如何传参?
比如,我们在pkg包中,声明了PackageInfo变量。参数中的importpath,即包路径,可以通过go tool nm命令得到。
Nm lists the symbols defined or used by an object file, archive, or executable. 此处编译是可以通过的,因为代码声明了所有变量。
输出如下
$ go tool nm demoversion|grep Package
116b9b0 B github.com/leanfra/demoversion/pkg.PackageInfo
我们就可以得到了实际的importpath值。
注意
- 外层使用双引号,确保传递的flag中的内容被截断
- importpath.name=value值使用单引号
- 要改变的变量需要是包级别的var string类型,不能是const类型
参考链接:
go linker: https://golang.org/cmd/link/
go tool nm: https://golang.org/cmd/nm/
go generate: https://blog.golang.org/generate