在发布和部署程序时,我们往往会有这样的需求:把版本号内置在程序里面,运行和部署程序的时候,可以用来知晓当前发布和部署的程序是什么版本。在一个编译好的可执行程序中,我们通常可以用类似: ./app_name -version 的方式,来获取当前程序的版本号。有了程序的版本号,更便于生产环境中,当程序出现问题时,工程师可以方便的根据版本号查找对应代码的改动,从而更容易定位到问题的所在。
这里主要介绍一下如何用Makefile以及Go本身所支持的编译特性,实现编译时自动生成版本号的功能。
在开始之前,假设我们程序发布的流程是这样:
- 编码完成,提交测试工程师测试
- 测试工程师测试出的bug都已解决,并重新测试和验证通过
- 工程师把修复好bug的代码合并到release分支,并建立git tag:2016.06.11.release ,准备用于最终程序的构建¼发布和部署
在编译生成最终要发布的程序时,我们希望程序可以通过 -v 参数,提供如下的版本信息:
- BuildTime: 显示程序编译和构建时的日期
- GitTag: 显示程序当前代码的git tag,用以在程序出现问题时,查找和定位对应版本的源代码
首先,我们需要实现一个demo程序,用于接收 -v 参数,在运行的时候,显示当前程序的编译构建时间,以及代码编译时所使用的git tag名称:
1 | package main |
在程序中,我们为git tag和build time定义了默认值。运行起来,是这样的:
1 | ./main -v |
我们已经有了一个程序,接收 -v 参数,并且能输出程序对应的git tag和构建时间。不过不足的地方在于,每一次发布程序的时候,我们需要手动修改这两个值,再进行编译,非常不人性化,而且在发布程序的时候,总会忘掉这一步。于是,得用上Go的link tool所提供的一个功能: -X 参数:
1 | -X importpath.name=value |
使用-X参数,允许我们在编译构建Go程序的时候,传入自定义的值,覆盖对应的import path下的指定变量。于是,我们可以在编译程序的时候这么干:
1 | go build -ldflags "-X main.GitTag=2016.06.11.release -X main.BuildTime=2016-06-11T16:11:49+0800" main.go |
编译成功后,运行 ./main -v 程序输出:
1 | ./main -v |
到目前为止,我们已经成功的实现了”Compile time variables”,这意味着在编译时,动态传入参数和值,让程序在编译的时候,动态生成我们所指定的版本号。
不过,这个git tag和build time是在我们编译的时候指定的,而且编译的命令很长,作为一个懒惰的程序员,每次编译的时候敲这么长的命令是很磨人的。得用一种更灵活的方法来实现编译时传入参数,可选的方式大概有两种:
- 写一个shell脚本来实现编译的自动化
- 写一个Makefile文件,用make命令来编译
shell脚本是一种比较常用的方式,这次我们来体验一下用Makefile实现Go程序的编译。于是,我们又折腾出来了一个Makefile文件:
1 | # This is how we want to name the binary output |
最后,我们直接用make命令传递参数,来编译我们的程序:
1 | make all tag=2016.06.11.release |
生成main可执行文件,然后运行查看结果:
1 | ./main -v |
程序输出了我们传入的git tag,以及当时编译的时间。