Go 程序构建编译

编译代码

go build 命令负责处理项目依赖,将代码编译成可执行文件。

在旧版中,go build 也用来编译库(生成 .a 文件)。现在不再需要显式地去编译一个库,构建程序时会自动下载编译依赖库,并缓存依赖库编译结果,以便后续再次使用。

基本语法

go build 命令语法如下:go build [-o output] [flags] [packages]

  • [-o output]:指定输出文件名和路径。
  • [flags]:读作标志,也就是命令参数或选项。
  • [packages]:指定要构建的源文件或包。

常用参数如下:

  • -a:强制重新编译所有相关源代码包,包括标准库的包。
  • -n:仅打印执行命令,不实际执行,用于调试。
  • -x:打印详细命令。
  • -v:显示正在编译的包名。
  • -race:启用数据竞争检测,常用于并发编程中。
  • -ldflags:将选项传递给链接器,例如设置二进制版本号或去掉调试信息等。
  • -work:打印编译时所在的工作目录路径,并在编译完成后保留它。

构建程序

不带任何参数运行 go build 命令时,如果当前目录中包含 Go 文件,且包含 main 包和 main 函数,会生成与当前目录同名的可执行文件:

go build

也可以指定具体文件或目录进行编译:

go build hello.go
go build -o api.exe ./cmd/api

如果在库目录中用 go build 命令,会编译当前目录下所有 Go 文件,但不会生成任何文件。用于验证库代码是否有编译错误。

交叉编译

默认情况下,编译生成的二进制文件只适合编译时所在系统平台。Go 语言支持交叉编译,可以通过设置环境变量 GOOSGOARCH 来指定目标操作系统和处理器架构,以实现交叉编译。例如在 Win 平台编译 Linux 版本程序:

set GOOS=linux
set GOARCH=amd64
go build -o target/hello_linux_amd64 main.go

set 是 cmd 命令,用于临时修改环境变量值,只会影响当前会话。如果上面命令在 powershell 中运行,不能修改环境变量值,替代命令为:$env:GOOS = "linux"

编译成功后,会自动新建 target 目录,在其中生成 Linux x64 版本可执行文件 hello_linux_amd64。如果构建目标文件已存在,会直接覆盖写入。

常用的 GOOS 和 GOARCH 值:

  • GOOSlinuxwindowsdarwin(macOS)、freebsd
  • GOARCHamd64(x64)、386(x86)、armarm64

压缩体积

通过指定一些编译标志,可压缩编译生成的二进制文件大小:

go build -ldflags="-s -w" main.go

标志 "-s -w",表示移除符号表和调试信息。调试信息会占用大量体积,移除后不影响日常使用,但生成物大小减小了三分之一。

要进一步压缩可执行文件大小,可尝试 UPX 压缩。下载解压后,将程序路径添加到系统变量:

setx PATH "%PATH%;D:\Software\Program\Upx"

跳转到项目路径,对文件进行最高级别压缩:

D:\Software\Programming\Go\new>upx -9 main.exe
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.4       Markus Oberhumer, Laszlo Molnar & John Reiser    May 9th 2024

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   1338368 ->    539648   40.32%    win64/pe     main.exe                                                                                                                                                                                Packed 1 file.

结果显示,文件大小压缩到 40%,最终体积从 2 MB 变为 0.5 MB。

使用 UPX 压缩后,常被杀毒软件误报。此外,UPX 压缩会改变文件原始结构,从而变得难以调试。

快速运行

开发时,可使用 go run 命令来快速测试代码。go run 命令会在临时目录中编译代码,运行完后自动清理痕迹。

基本语法

命令基本用法为:go run [flags] [files] [arguments...]

  • [flags]:参考编译代码的参数。
  • [files]:指定要编译和运行的文件。
  • [arguments]:传递给程序的参数。

运行源码

go build 不同,直接运行 go run 命令会提示没有找到 Go 文件,必须指定一个包含 main 函数的 Go 源文件:

go run main.go

如果源文件中有多个文件依赖,可以一次性传给命令:

go run main.go dependency.go

如果源程序有参数,在传入文件名后用 -- 分隔程序参数列表。支持多参数:

go run hello.go -- arg1 arg2

加入 -work 参数显示临时工作目录路径,并在运行完成后保留,用于调试分析:

PS D:\Software\Programming\Go\new> go run -work main.go -- 1194
WORK=C:\Users\ADMINI~1\AppData\Local\Temp\go-build3687578180

清理缓存

go clean 命令用于清理当前模块和相关依赖的缓存文件,帮助项目保持干净状态。

基本语法

命令基本用法为:go clean [clean flags] [build flags] [packages]

常用清理选项:

  • -i:用于删除与指定包相关的可执行文件。
  • -n:仅打印详细清理命令,不执行。
  • -x:和 -n 一样打印出详细清理命令,但会执行。
  • -cache:清除所有编译缓存。
  • -modcache:清理 Go 模块缓存。
  • -testcache:清除所有测试缓存。

清理生成物

在项目目录使用 go clean -i 命令,会删除所有编译产出的可执行文件。除了当前目录,也会到 $GOPATH\bin 目录中去搜索:

D:\Software\Programming\Go\new>go clean -i -n
cd D:\Software\Programming\Go\new
rm -f new new.exe new new.exe new.test new.test.exe new.test new.test.exe main main.exe main.test main.test.exe main_test main_test.ex
e
rm -f D:\Software\Program\go\bin\linux_amd64\new

清理编译缓存

编译缓存包含之前编译结果,复用能加速后续编译过程。具体路径配置在 $GOCACHE 变量中,例如 Linux 下面(使用 root 用户)为 /root/.cache/go-build。缓存目录下面又分多个子目录,目录中存放一些不带后缀名的文件:

root@k8s-204:~/.cache/go-build# ls
00  07  0e  15  1c  23  2a  31  38  3f  46  4d  54  5b  62  69  70  77  7e  85  8c  93  9a  a1  a8  af  b6  bd  c4  cb  d2  d9  e0  e7  ee  f5  fc
01  08  0f  16  1d  24  2b  32  39  40  47  4e  55  5c  63  6a  71  78  7f  86  8d  94  9b  a2  a9  b0  b7  be  c5  cc  d3  da  e1  e8  ef  f6  fd
02  09  10  17  1e  25  2c  33  3a  41  48  4f  56  5d  64  6b  72  79  80  87  8e  95  9c  a3  aa  b1  b8  bf  c6  cd  d4  db  e2  e9  f0  f7  fe
03  0a  11  18  1f  26  2d  34  3b  42  49  50  57  5e  65  6c  73  7a  81  88  8f  96  9d  a4  ab  b2  b9  c0  c7  ce  d5  dc  e3  ea  f1  f8  ff
04  0b  12  19  20  27  2e  35  3c  43  4a  51  58  5f  66  6d  74  7b  82  89  90  97  9e  a5  ac  b3  ba  c1  c8  cf  d6  dd  e4  eb  f2  f9  README
05  0c  13  1a  21  28  2f  36  3d  44  4b  52  59  60  67  6e  75  7c  83  8a  91  98  9f  a6  ad  b4  bb  c2  c9  d0  d7  de  e5  ec  f3  fa  trim.txt
06  0d  14  1b  22  29  30  37  3e  45  4c  53  5a  61  68  6f  76  7d  84  8b  92  99  a0  a7  ae  b5  bc  c3  ca  d1  d8  df  e6  ed  f4  fb
root@k8s-204:~/.cache/go-build# ll 77
total 16
drwxr-xr-x   2 root root   80 Jul 25 15:58 ./
drwxr-xr-x 258 root root 8192 Jul 25 15:58 ../
-rw-r--r--   1 root root  175 Jul 25 15:58 77d57df9bb89883adb64c446ffa4294b455731b60a934769f4ef61f505e1297c-a
root@k8s-204:~/.cache/go-build# cat 77/77d57df9bb89883adb64c446ffa4294b455731b60a934769f4ef61f505e1297c-a 
v1 77d57df9bb89883adb64c446ffa4294b455731b60a934769f4ef61f505e1297c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855                    0  172189428593703674

大多数缓存文件无法正常查看,但通常也不需要关心其内容,Go 工具链会根据过期策略自动清理编译缓存,只有在缓存目录过大时需要手动清理:

root@k8s-204:~/.cache/go-build# go clean -n -cache
rm -r /root/.cache/go-build/00 /root/.cache/go-build/01 /root/.cache/go-build/02 /root/.cache/go-build/03 /root/.cache/go-build/04 /root/.cache/go-build/05 
rm -f /root/.cache/go-build/log.txt

清理模块缓存

模块缓存中主要存放下载的模块,便于构建时直接使用,而不用每次都重新下载。模块缓存目录由 $GOMODCACHE 变量指定,默认为 $GOPATH/pkg/mod

使用 go clean -modcache 命令将简单粗暴地删除整个 pkg/mod 目录:

root@k8s-204:~/.cache/go-build# go clean -modcache -n
rm -rf /root/go/pkg/mod

一般很少用到这个命令。在持续集成环境中,甚至要想办法将 pkg/mod 目录持久化。仅仅在模块文件损坏导致行为异常时,可能需要这样整个清理。

清理后,重新安装项目依赖模块可以使用 go install 命令,或直接运行 go build 构建。

清理测试缓存

测试缓存和编译缓存作用类似,用于在代码或测试用例未发生变化时,加速测试运行。

测试缓存和编译缓存共用目录,如果用 go clean -cache 命令会一并清理测试缓存,而用 -testcache 参数只会删除测试结果缓存,用于确保测试结果不被缓存影响:

go clean -testcache