给Go程序加入编译版本时间等信息
先看效果
$./myapp -v gitcommitlog=d97d098e5bb4ad38a2a7968f273a256e10a0108f mod bininfo comment gitstatus=cleanly buildtime=2019.10.26.194341 goversion=go version go1.13 darwin/amd64 runtime=darwin/amd64
myapp 是一个演示用的 demo 程序,输入 -v
参数运行时,打印出程序的一些信息。以上信息对应的说明如下:
# gitcommitlog d97d098e5bb4ad38a2a7968f273a256e10a0108f: 源码最近一次 commit 的 sha 值 mod bininfo comment: 源码最近一次 commit 的描述信息 # gitstatus cleanly: 表示本地代码相对于最近一次 commit,并没有任何修改 如果本地代码有修改,此处会显示修改过的文件 # buildtime 2019.10.26.194341: 程序的编译时间为2019年10月26号19点43分41秒 # goversion go version go1.13 darwin/amd64: 程序编译使用的 go 版本为1.13,darwin 即 macos # runtime 程序运行时的平台,因为 go 的跨平台编译做的比较好,为了避免混淆,我 们在 goversion 打印了编译平台的同时,把运行平台也打印出来
ok,下面就来介绍是如何实现的。
依赖的知识点
go 语言编译时,可以通过 go build -ldflags
的方式向程序中指定的包中的变量传递值。
拿下面这个十来行的程序做个演示:
package main import "fmt" var foo string func main() { if foo == "" { fmt.println("foo is empty.") } else { fmt.printf("foo=%s\n", foo) } }
如果直接使用 go build
编译,运行的结果是 foo is empty.
。
如果使用 go build -ldflags "-x 'main.foo=test'"
编译,则运行的结果为 foo=test
。它的格式为 -x '<包名>.<变量名>=<值>'
编译期将感兴趣的信息传入程序中
通过上面这种手法,我们可以编写一个用于编译 go 程序的 shell 脚本,在脚本中获取一些编译时期的信息,传递到程序中。
比如:
# 获取源码最近一次 git commit log,包含 commit sha 值,以及 commit message gitcommitlog=`git log --pretty=oneline -n 1` # 检查源码在最近一次 git commit 基础上,是否有本地修改,且未提交的文件 gitstatus=`git status -s` # 获取当前时间 buildtime=`date +'%y.%m.%d.%h%m%s'` # 获取go的版本 buildgoversion=`go version` # 之后将上面这些变量传递到 go build -ldflags 中,编译 go 程序。 # 完整的 shell 脚本地址后文有。
更进一步
其实前面也提到, go 不仅仅支持在编译时向 main 包中的变量传递值,也支持向非 main 包传递。
基于以上前提,为了以后写不同应用程序时,减少模板代码的拷贝,我专门写了一个 package (),代码如下:
package bininfo import ( "fmt" "runtime" "strings" ) var ( // 初始化为 unknown,如果编译时没有传入这些值,则为 unknown gitcommitlog = "unknown" gitstatus = "unknown" buildtime = "unknown" buildgoversion = "unknown" ) // 返回单行格式 func stringifysingleline() string { return fmt.sprintf("gitcommitlog=%s. gitstatus=%s. buildtime=%s. goversion=%s. runtime=%s/%s.", gitcommitlog, gitstatus, buildtime, buildgoversion, runtime.goos, runtime.goarch) } // 返回多行格式 func stringifymultiline() string { return fmt.sprintf("gitcommitlog=%s\ngitstatus=%s\nbuildtime=%s\ngoversion=%s\nruntime=%s/%s\n", gitcommitlog, gitstatus, buildtime, buildgoversion, runtime.goos, runtime.goarch) } // 对一些值做美化处理 func beauty() { if gitstatus == "" { // gitstatus 为空时,说明本地源码与最近的 commit 记录一致,无修改 // 为它赋一个特殊值 gitstatus = "cleanly" } else { // 将多行结果合并为一行 gitstatus = strings.replace(strings.replace(gitstatus, "\r\n", " |", -1), "\n", " |", -1) } } func init() { beauty() }
然后我们用一个 demo 程序 来演示如何使用,代码如下:
package main import ( "flag" "fmt" "os" "github.com/q191201771/naza/pkg/bininfo" ) func main() { v := flag.bool("v", false, "show bin info") flag.parse() if *v { _, _ = fmt.fprint(os.stderr, bininfo.stringifymultiline()) os.exit(1) } fmt.println("my app running...") fmt.println("bye...") }
最后,是我们的 脚本,源码如下:
#!/usr/bin/env bash set -x # 获取源码最近一次 git commit log,包含 commit sha 值,以及 commit message gitcommitlog=`git log --pretty=oneline -n 1` # 将 log 原始字符串中的单引号替换成双引号 gitcommitlog=${gitcommitlog//\'/\"} # 检查源码在git commit 基础上,是否有本地修改,且未提交的内容 gitstatus=`git status -s` # 获取当前时间 buildtime=`date +'%y.%m.%d.%h%m%s'` # 获取 go 的版本 buildgoversion=`go version` # 将以上变量序列化至 ldflags 变量中 ldflags=" \ -x 'github.com/q191201771/naza/pkg/bininfo.gitcommitlog=${gitcommitlog}' \ -x 'github.com/q191201771/naza/pkg/bininfo.gitstatus=${gitstatus}' \ -x 'github.com/q191201771/naza/pkg/bininfo.buildtime=${buildtime}' \ -x 'github.com/q191201771/naza/pkg/bininfo.buildgoversion=${buildgoversion}' \ " root_dir=`pwd` # 如果可执行程序输出目录不存在,则创建 if [ ! -d ${root_dir}/bin ]; then mkdir bin fi # 编译多个可执行程序 cd ${root_dir}/demo/add_blog_license && go build -ldflags "$ldflags" -o ${root_dir}/bin/add_blog_license && cd ${root_dir}/demo/add_go_license && go build -ldflags "$ldflags" -o ${root_dir}/bin/add_go_license && cd ${root_dir}/demo/taskpool && go build -ldflags "$ldflags" -o ${root_dir}/bin/taskpool && cd ${root_dir}/demo/slicebytepool && go build -ldflags "$ldflags" -o ${root_dir}/bin/slicebytepool && cd ${root_dir}/demo/myapp && go build -ldflags "$ldflags" -o ${root_dir}/bin/myapp && ls -lrt ${root_dir}/bin && cd ${root_dir} && ./bin/myapp -v && echo 'build done.'
写在最后
本文中的 package bininfo,编译脚本,示例代码都在我的 github 项目 () 中。
这个仓库包含了我平时学习 go 练手写的一些基础库代码。有些已经在我的线上服务中使用了。后续我还会写一些文章介绍这个仓库中的其他包。
感谢阅读,如果觉得文章还不错的话,顺手给我的 来个 star 就更好啦。 :)
原文链接:
原文出处: ()
原文作者: yoko
版权声明: 本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。
推荐阅读