在Go项目中,会存在展示该项目的版本信息,以及打包日期,项目版本,Go版本等;继Go新项目-编译项目的细节(4)以后再记录下编译优化方向,刚好也有项目版本输出的需求。
关键词:ldflags、gcflag、stripped
ldflags
**************** ldflags ****************
- 当把应用程序部署到生产环境中时,用版本信息和其他元数据构建二进制文件将改善你的监控、日志和调试过程,增加识别信息以帮助跟踪你的构建时间.
go 的 ldflags参数
- -w 去掉调试信息
- -s 去掉符号表
- -X 注入变量, 编译时赋值
-w 和 -s 通常一起使用,用来减少可执行文件的体积。但删除了调试信息后,可执行文件将无法使用 gdb/dlv 调试
使用范围可以在go install 、go build、go run 、go test中使用
Module=github.com/pubgo/xxx
GOPATH=$(shell go env GOPATH)
Version=$(shell git tag --sort=committerdate | tail -n 1)
GoVersion=$(shell go version)
BuildTime=$(shell date "+%F %T")
CommitID=$(shell git rev-parse HEAD)
LDFLAGS:=-ldflags "-X 'github.com/pubgo/xxx/version.GoVersion=${GoVersion}' \
-X 'github.com/pubgo/xxx/version.BuildTime=${BuildTime}' \
-X 'github.com/pubgo/xxx/version.GoPath=${GOPATH}' \
-X 'github.com/pubgo/xxx/version.CommitID=${CommitID}' \
-X 'github.com/pubgo/xxx/version.Module=${Module}' \
-X 'github.com/pubgo/xxx/version.Version=${Version:-v0.0.1}'"
编译
go build ${LDFLAGS} -mod vendor -race -v -o main main.go
go build -ldflags "-w -s" -mod vendor -race -v -o main main.go
go build -ldflags "-w -s" -race -v -o main main.go
gcflags
**************** gcflags ****************
- -N参数代表禁止优化,
- -l参数代表禁止内联,
- -c int: 编译过程中的并发数,默认是1
go在编译目标程序的时候会嵌入运行时(runtime)的二进制,
禁止优化和内联可以让运行时(runtime)中的函数变得更容易调试.
go build -gcflags='all=-N -l' main.go
1. go tool compile -N -l -S main.go
2. go tool compile -N -l main.go
1. go tool objdump main.o //反汇编出代码
2. go tool objdump -s Do main.o //反汇编特定的函
3. go build -gcflags -S main.go
4. go tool compile 和 go build -gcflags -S 生成的是过程中的汇编,和最终的机器码的汇编可以通过go tool objdump生成
逃逸分析
该参数不光优化编译的二进制包,还能进行项目的逃逸分析
go build -gcflags="-m -l" ./main.go
- 这一命令中,-m 表示打印出逃逸分析信息,-l 表示禁止内联,可以更好地观察逃逸。
- 从以下输出结果可以看到,发生了逃逸,也就是说指针作为函数返回值的时候,一定会发生逃逸。
// 变量 m 没有逃逸,反而被变量 m 引用的变量 s 逃逸到了堆上。
// 所以被map、slice 和 chan 这三种类型引用的指针一定会发生逃逸的。
func main() {
m := map[int]*string{}
s := "小胡"
m[0] = &s
}
stripped
文件属性解释词:stripped,曾经在编译Go项目过程中,没有转为Linux适配的二进制,而导致出错(cannot execute binary file
),就是采用 file 文件名
命令来分析二进制文件的属性;来快速定位问题。
**************** stripped ****************
- 同样名字的动态库,带not stripped库会大很多。
- stripped 表示执行程序中剔除了符号表信息
- not stripped 表示没有剔除
- 一般最终的程序都会使用strip来减小可执行文件的体积,而调试中的程序则不使用strip。
- strip 是一个命令,就是负责剔除符号表信息,这样执行程序的尺寸会变小,但是不便调试
主程序
- 用于项目编译过程展示:项目版本号、日期、git版本、go版本;
var (
version = "" // "1.0.0"
buildstamp = "" // time.Now()
githash = "" // "1.0.0"
goversion = "" // fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)
// VERSION string
// BUILD_TIME string
// GO_VERSION string
)
func main() {
// fmt.Printf("%s\n%s\n%s\n", VERSION, BUILD_TIME, GO_VERSION)
args := os.Args
if len(args) == 2 && (args[1] == "--version" || args[1] == "-v") {
fmt.Printf("Version : %s\n", version)
fmt.Printf("Git Commit Hash: %s\n", githash)
fmt.Printf("CST Build Time : %s\n", buildstamp)
fmt.Printf("Golang Version : %s\n", goversion)
return
}
}
build.sh
展示一个编译的shell脚本,补充上篇在写项目编译的过程只是给出了命令,没有通过脚本执行,这样提升了了编译的效率
#!/bin/bash
echo "(1)Windows下 编译 Linux流程:"
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" main.go
# 文件 file main的属性
# main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
echo "(2)Linux 下编译 Mac 和 Windows 平台64位可执行程序:"
#CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build
#CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
echo "(3)Mac 下编译 Linux 和 Windows平台 64位 可执行程序:"
#CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
#CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
echo "动态传值1-完整版(注入-版本号、时间date、版本git、go版本): -u 是展示UTC时间"
#flags="-X main.version=1.0.0 -X main.buildstamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X main.githash=`git describe --long --dirty --abbrev=14` -X 'main.goversion=$(go version)'"
flags="-X main.version=1.0.0 -X main.buildstamp=`date '+%Y-%m-%d_%H:%M:%S'` -X main.githash=`git describe --long --dirty --abbrev=14` -X 'main.goversion=$(go version)'"
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$flags" -x -o build-version main.go
# 文件 file bulid-version的属性
# build-version: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
echo "手动传值2 (注入:版本号、时间、go版本) :"
# data = UTC
#CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "-X main.VERSION=1.0.0 -X 'main.BUILD_TIME=`date -u '+%Y-%m-%d_%I:%M:%S%p'`' -X 'main.GO_VERSION=$(go version)'"
# data = CST
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-X main.VERSION=1.0.0 -X 'main.BUILD_TIME=`date`' -X 'main.GO_VERSION=$(go version)'"
参考:https://www.digitalocean.com/community/tutorials/using-ldflags-to-set-version-information-for-go-applications