Golang -ldflags 的一个技巧

Posted poslua

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang -ldflags 的一个技巧相关的知识,希望对你有一定的参考价值。

我在开发 go 的项目时,习惯上会在编译后的二进制文件中注入 git 等版本信息后再发布。一直以来都是这么做的:

 
   
   
 
  1. package main

  2. import (

  3.    "fmt"

  4.    "os"

  5.    "runtime"

  6. )

  7. var buildstamp = ""

  8. var githash = ""

  9. func main() {

  10.    args := os.Args

  11.    if len(args) == 2 && (args[1] == "--version" || args[1] == "-v") {

  12.        fmt.Printf("Git Commit Hash: %s\n", githash)

  13.        fmt.Printf("UTC Build Time : %s\n", buildstamp)

  14.        return

  15.    }

  16. }

然后编译的时候,通过链接选项 -X 来动态传入版本信息:

 
   
   
 
  1. flags="-X main.buildstamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X main.githash=`git describe --long --dirty --abbrev=14`"

  2. go build -ldflags "$flags" -x -o build-version main.go

这样编译后的程序就可以通过 -v 参数查看版本信息了

 
   
   
 
  1. [root@KONG:bin (master)]# ./build-version -v

  2. Git Commit Hash: 1.3-5-ge9edea3e8f986d

  3. UTC Build Time : 2018-09-30_03:40:36AM

  4. [root@KONG:bin (master)]#

然而在多人协作的项目时,大家的 go 版本可能会不同,所以我又尝试将 go version 信息植入到程序中。

 
   
   
 
  1. package main

  2. import (

  3.    "fmt"

  4.    "os"

  5.    "runtime"

  6. )

  7. var buildstamp = ""

  8. var githash = ""

  9. var goversion = ""

  10. func main() {

  11.    args := os.Args

  12.    if len(args) == 2 && (args[1] == "--version" || args[1] == "-v") {

  13.        fmt.Printf("Git Commit Hash: %s\n", githash)

  14.        fmt.Printf("UTC Build Time : %s\n", buildstamp)

  15.        fmt.Printf("Golang Version : %s\n", goversion)

  16.        return

  17.    }

  18. }

通过添加编译参数: -X main.goversion=$(go version),然而却失败了。通过错误输出可以判断是因为 go version 的输出有空格,导致编译被打断。之后我又改了下编译参数,添加了单引号: -X main.goversion='$(go version)',遗憾的是,编译依然无法通过。

记得之前,我看到过 codis 的源码是通过一个 shell 脚本生成一个额外的 go package 来处理的:

 
   
   
 
  1. version=`git log --date=iso --pretty=format:"%cd @%H" -1`

  2. if [ $? -ne 0 ]; then

  3.    version="unknown version"

  4. fi

  5. compile=`date +"%F %T %z"`" by "`go version`

  6. if [ $? -ne 0 ]; then

  7.    compile="unknown datetime"

  8. fi

  9. describe=`git describe --tags 2>/dev/null`

  10. if [ $? -eq 0 ]; then

  11.    version="${version} @${describe}"

  12. fi

  13. cat << EOF | gofmt > pkg/utils/version.go

  14. package utils

  15. const (

  16.    Version = "$version"

  17.    Compile = "$compile"

  18. )

  19. EOF

  20. cat << EOF > bin/version

  21. version = $version

  22. compile = $compile

  23. EOF

但是这样,每开启一个新项目都需要额外的 shell 脚本,也够麻烦的。所以,很长时间我都是这么来偷懒的:

 
   
   
 
  1. package main

  2. import (

  3.    "fmt"

  4.    "os"

  5.    "runtime"

  6. )

  7. var buildstamp = ""

  8. var githash = ""

  9. var goversion = fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)

  10. func main() {

  11.    args := os.Args

  12.    if len(args) == 2 && (args[1] == "--version" || args[1] == "-v") {

  13.        fmt.Printf("Git Commit Hash: %s\n", githash)

  14.        fmt.Printf("UTC Build Time : %s\n", buildstamp)

  15.        fmt.Printf("Golang Version : %s\n", goversion)

  16.        return

  17.    }

  18. }

但是这样的话,也有一个问题:就是在交叉编译的时候无法正确反应出 go 的版本。比如,你是在 OSX 下编译 linux 的可执行程序,这时候你通过 -v 参数查看显示的也是 linux 平台,而不是期待的 darwin 平台。

不过,近日我在闲逛 go nuts 时,看到一个贴子:v1.5 -ldflags -X change breaks when string has a space,谈到了这个技巧:如果要赋值的变量包含空格,需要用引号将 -X 后面的变量和值都扩起来。原来如此,再次 build 一下,还真好用:

 
   
   
 
  1. flags="-X 'main.goversion=$(go version)'"

  2. go build -ldflags "$flags" -x -o build-version main.go

子曰:三人行,必有我师焉。看来,社区还得逛起来呀~~


以上是关于Golang -ldflags 的一个技巧的主要内容,如果未能解决你的问题,请参考以下文章

Android课程---Android Studio使用小技巧:提取方法代码片段

代码片段 - Golang 实现简单的 Web 服务器

代码片段 - Golang 实现集合操作

VS中添加自定义代码片段——偷懒小技巧

你可能不知道的JavaScript代码片段和技巧(下)

你可能不知道的JavaScript代码片段和技巧(上)