$./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
參數運行時,打印出程序的一些信息。以上信息對應的說明以下:git
# 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,下面就來介紹是如何實現的。github
Go 語言編譯時,能夠經過 go build -ldflags
的方式向程序中指定的包中的變量傳遞值。golang
拿下面這個十來行的程序作個演示:shell
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.
。macos
若是使用 go build -ldflags "-X 'main.Foo=test'"
編譯,則運行的結果爲 Foo=test
。它的格式爲 -X '<包名>.<變量名>=<值>'
bash
經過上面這種手法,咱們能夠編寫一個用於編譯 Go 程序的 shell 腳本,在腳本中獲取一些編譯時期的信息,傳遞到程序中。app
好比:學習
# 獲取源碼最近一次 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 包傳遞。ui
基於以上前提,爲了之後寫不一樣應用程序時,減小模板代碼的拷貝,我專門寫了一個 package (package bininfo github地址),代碼以下:code
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 程序 myapp.go 來演示如何使用,代碼以下:
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...") }
最後,是咱們的 build.sh 腳本,源碼以下:
#!/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 項目 naza (https://github.com/q191201771/naza) 中。
這個倉庫包含了我平時學習 Go 練手寫的一些基礎庫代碼。有些已經在個人線上服務中使用了。後續我還會寫一些文章介紹這個倉庫中的其餘包。
感謝閱讀,若是以爲文章還不錯的話,順手給個人 github 項目 來個 star 就更好啦。 :)
原文連接: https://pengrl.com/p/37397/
原文出處: yoko blog (https://pengrl.com)
原文做者: yoko 本文歡迎任何形式轉載,轉載時完整保留本聲明信息(包含原文連接、原文出處、原文做者、版權聲明)便可。本文後續全部修改都會第一時間在原始地址更新。