go build 参数
-o 参数
假设代码内容如下
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
编译之后的可执行文件名称,例如
go build -o hello.exe
这样生成的二进制文件的名称就是 hello.exe
-a 参数
强制重新编译所有包(包含标准库)
go build -a
-p 参数
编译时使用的 cpu 数量,如下所示,指定编译使用 2 个cpu
go build -p 2
-v 参数
显示待编译包的名称,这个见 -x
,这个和 -x
参数一起用,不然没有显示任何内容
-x 参数
显示正在执行的编译命令。go build 添加 -x -v
选项,可以输出构建的执行细节。-v 用于输出当前正在编译的包,而-x 则用于输出 go build 执行的每一个命令。
go build -x -v
-n 参数
仅显示编译的命令,但是不执行
go build -n
![[go build 编译详解-20231224172831941.webp|800]]
-work 参数
显示临时的工作目录,编译后不删
go build -work
输出 WORK=D:\project\go\goTempDir\go-build3416180188
,编译后这个目录还会保存
-race 参数
启用数据竞争检查(仅amd64)。-race 命令行选项可以在构建时开启竞态检查。在程序运行时,如果发现对数据的并发竞态访问,就会给出警告。
go build -race
-gcflags 参数
编译器参数。go build
实质上是通过调用 go 自带的 compile 工具对 go 代码进行编译的。
- 在 linux 下的位置为
$GOROOT/pkg/tool/linux_amd64/compile
。 - 在 windows 下的位置为
$GOROOT/pkg/tool/windows_amd64/compile.exe
。
go build 可以经过 -gcflags
向 compile 工具传递编译所需的命令标志选项集合。这些命令行标志选项是传递给 go 编译器的,因此可以使用下面的命令查看编译器支持的选项集合:
go tool compile -help
下面是一些常用的命令行标志选项:
-l
:关闭内敛。-N
:关闭代码优化-m
:输出逃逸分析的分析决策过程(哪些变量在栈上分配,哪些变量在堆上分配)。-S
:输出汇编代码。
在运行调试器对程序进行调试之前,我们通常使用 -N -l
两个选项关闭对代码的内联和优化,这样能得到更多的调试信息。这个在使用 gdp 调试的是就要
go build -gcflags="-N -l"
-ldflags 参数
链接器参数。go build
实质上是通过调用 go 自带的 link 工具对 go 代码进行编译的。
- 在 linux 下的位置为
$GOROOT/pkg/tool/linux_amd64/link
。 - 在 windows 下的位置为
$GOROOT/pkg/tool/windows_amd64/link.exe
。
使用下面的命令可以查看链接器支持的选项集合:
go tool link -help
下面三个是常用的命令行选项标志:
-X
:设定包中 string 类型变量的值。-s
:不生成符号表。-w
:不生成 DWARF(Debugging With Attributed Record Formats)调试信息。-H
:设置可执行文件格式,eg:-H windowgui
,告诉编译器是 gui 程序,不要控制台黑框打开
-X 选项的例子:可以在编译时指定程序的版本。
package main
import (
"fmt"
"os"
)
// 全局变量
var version string
func main() {
if len(os.Args) > 1 && os.Args[1] == "version" {
fmt.Println(version)
}
}
编译指定变量值,一定要带上包名称,如果多个的话,用空格隔开 -X main.version=v0.1 -X main.author=wqy
go build -ldflags="-X main.version=v0.1"
执行二进制文件
./main.exe version
# 输出
v0.1
默认情况下,go build 构建出的可执行文件中都是包含 符号表
和 DWARF 格式的调试信息
,这虽然让最终二进制文件的体积都增加了,但是符号表和调试信息对于生产环境下程序异常的现场保存和在线调试都有着重要意义。
如果不在意这些信息,或者对应用的大小比较敏感,那么可以通过-s
和-w
选项将这些信息从最终的二进制文件中剔除。
go build -ldflags="-s -w -X main.version=v0.1"
查看汇编代码
以下命令会输出汇编代码
go tool compile -S main.go
或者用
go run -gcflags="-S" main.go
交叉编译
所谓交叉编译,指的是在一个平台下编译出其他平台所需的可执行文件。
如果只是单纯的 go 代码,没有使用 cgo 调用 c 代码,交叉编译非常方便,只需要使用 GOOS, GOARCH 环境变量指定编译目标平台即可。
GOOS=linux go build
# 32位
GOOS=linux GOARCH=386 go build
包含 CGO 代码时,需要指定交叉编译器,我平时用 zig,详细使用,可以查阅相关文档。
GOOS=Linux GOARCH=386 CC="zig cc -target x86-Linux" CXX="zig c++ -target x86-Linux" go build -o test
频繁敲写这些代码比较麻烦,可以边写 Makefile 文件,文件名称就命名为 Makefile 或 MAKEFILE
dep:
@go mod tidy
# 代表执行 make linux 之前先执行 dep
linux:dep
@GOOS=linux GOARCH=amd64 go build -o main main.go
@echo "linux exec is build"
# 代表执行 make windows 之前先执行 dep
windows:dep
@GOOS=windows GOARCH=amd64 go build -o main.exe main.go
@echo "windows exec is build"
写好 Makefile 文件后,只需要执行 make linux
或者 make windows
命令即可。
如果想要确认文件是否是对应的架构,可以使用 upx 压缩二进制命令,该命令在压缩的时候,会输出架构
upx -9 main
输出
条件编译
方案一
将平台信息加入文件名尾部
hello_windows_386.go
,后面的 386 代表是在 32 位系统架构下,也可以是amd64
,可以不指定。
使用的时候,可以使用 go build -x
查看编译过程,查看使用哪个文件。
假设 main 包有 version_windows.go
文件,内容如下
import "fmt"
var OS = "windows"
func PrintOs() {
fmt.Println("windows io")
}
version_linux.go
文件,内容如下
package main
var OS = "linux"
func PrintOs() {
fmt.Println("linux io")
}
然后,main 包在 window 平台下就可以使用该变量了,window 平台编译的时候会使用该变量
package main
import "fmt"
func main() {
fmt.Println(OS)
PrintOs()
}
在 window 下编译,go build
,执行编译后二进制文件,输出结果会是 windows
。在 window 下交叉编译 GOOS=linux go build
,然后去 linux 下执行二进制文件,输出结果是 linux
。
通过这种方式,就可以实现在不同系统底下,调用不同的内容。
方案二
使用 go build
标签来指定这个文件是哪个操作系统下生效。
需要注意的是,标签和 package 语句之间需要有空行。并且这个新写法是 go 1.17 版本后生效,旧的写法和这个写法不一样,是 //+build windows
如果要再指定系统位数,可以这样 //go build linux && amd64
,用 &&
符号,或者的话用 ||
,非的用 !
。老版本用 ,
和 空格
来代表与或非。例如
// go build windows || linux
// go build windows && amd64
// go build windows && !386
// +build windows,amd64 #并且的关系
// +build windows linux #或者关系
方案三
使用 tag 标签。
然后编译的时候,指定 tag 即可。其实方案二,应该是系统默认会传操作系统的 tag,不用我们手动 -tags
参数指定
如果 tag 是这样,代表是有 release 和 linux 标签才可以,-tags
参数后面用空格隔开代表是并且的关系。