此篇介绍下Go程序的条件编译和交叉编译。
编译选项go tool
go tool compile -S main.go
输出汇编内容
指令集 -gcflags 用于将标识参数传递给 Go 编译器,如下:
-m 会打印出逃逸分析的优化策略,实际上最多总共可以用 4 个 -m,但是信息量较大,一般用 1 个就可以了。
-l 会禁用函数内联,在这里禁用掉 inline 能更好的观察逃逸情况,减少干扰。
为了避免编译器的优化,加上-gcflags '-l -N'选项,-gcflags是给编译器的选项,通过go tool compile可以看到选项列表,-l表示禁止内联,-N表示禁止优化。一般要看一些细节的时候,都需要把这两个选项带上。
$ go build -gcflags '-N -l -m' main.go
还通过反编译命令查看
$ go tool compile -S main.go
可以通过 go tool compile -help 查看所有允许传递给编译器的标识参数。
直接通过 go build -gcflags '-m -l' 就可以看到逃逸分析的过程和结果。
在一个函数上方增加一行 //go:noinline 编译指令,可以用来阻止编译器内联此函数。如果用 -gcflags='-l -N' 选项,则是在全局范围内禁止优化。
编译器对于内联操作的优化,是递归操作的,可以逐层往上层调用方进行内联。
Go二进制中注入编译信息
main.go
package main
import (
"fmt"
)
var (
AppName string // 应用名称
AppVersion string // 应用版本
BuildVersion string // 编译版本
BuildTime string // 编译时间
GitRevision string // Git版本
GitBranch string // Git分支
GoVersion string // Golang信息
)
func main() {
Version()
// 业务代码入口
}
// Version 版本信息
func Version() {
fmt.Printf("App Name:\t%s\n", AppName)
fmt.Printf("App Version:\t%s\n", AppVersion)
fmt.Printf("Build version:\t%s\n", BuildVersion)
fmt.Printf("Build time:\t%s\n", BuildTime)
fmt.Printf("Git revision:\t%s\n", GitRevision)
fmt.Printf("Git branch:\t%s\n", GitBranch)
fmt.Printf("Golang Version: %s\n", GoVersion)
}
编译脚本build.sh
#!/bin/bash
set -e
PROJECT_NAME="myprogram.com"
BINARY="myprogram"
OUTPUT_DIR=output
GOOS=$(go env GOOS)
APP_NAME=${PROJECT_NAME}
APP_VERSION=$(git log -1 --oneline)
BUILD_VERSION=$(git log -1 --oneline)
BUILD_TIME=$(date "+%FT%T%z")
GIT_REVISION=$(git rev-parse --short HEAD)
GIT_BRANCH=$(git name-rev --name-only HEAD)
GO_VERSION=$(go version)
CGO_ENABLED=0 go build -a -installsuffix cgo -v \
-ldflags "-s -X 'main.AppName=${APP_NAME}' \
-X 'main.AppVersion=${APP_VERSION}' \
-X 'main.BuildVersion=${BUILD_VERSION}' \
-X 'main.BuildTime=${BUILD_TIME}' \
-X 'main.GitRevision=${GIT_REVISION}' \
-X 'main.GitBranch=${GIT_BRANCH}' \
-X 'main.GoVersion=${GO_VERSION}'" \
-o ${OUTPUT_DIR}/${BINARY} main.go