在 Ubuntu 上编译 Go 程序,看起来只是执行一次 go build,但很多失败都发生在构建之前:环境变量没配对、模块依赖没整理好、目标平台参数写错,或者缓存、权限、CGO 把问题藏了起来。与其零散排查,不如按“先确认环境、再处理依赖、最后看构建细节”的顺序过一遍,通常更快找到根因。
下面这篇会围绕 Ubuntu 下最常见的编译环节展开,保留可直接使用的命令,并补充每一步该看什么、什么时候该继续优化、什么时候该回头检查版本或缓存,帮助你更稳地完成本地编译和交叉编译。
先确认 Ubuntu 上的 Go 环境是否可用
环境变量怎么配
在 Ubuntu 上,最先要确认的是 GOROOT、GOPATH 和 PATH。这三个变量分别决定 Go 安装目录、工作空间位置,以及命令能不能直接执行。
以 Go 1.20+ 为例,可以把下面内容加入 ~/.profile 或 ~/.bashrc:
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
保存后执行:
source ~/.profile
如果你写入的是 ~/.bashrc,则对应执行 source ~/.bashrc。完成后建议先跑一次 go env,确认 Go 实际读取到的环境变量与预期一致。
版本是否满足项目要求
环境变量正常,不代表一定能编过项目。很多仓库会明确要求 Go 版本,例如 Go 1.18+。先用下面的命令确认当前版本:
go version
如果版本过低,哪怕语法看起来差别不大,也可能因为标准库 API、模块行为或编译特性变化而失败。原文给出的参考是升级到最新稳定版,例如 Go 1.21;实际以项目文档要求为准。
依赖管理做对,编译才稳定
优先使用 Go Modules
在 Ubuntu 上做 Go 项目构建,依赖管理最好统一使用 Go Modules。Go 1.11+ 已原生支持,项目根目录初始化模块的命令是:

go mod init <模块名>
后续新增依赖可使用:
go get <包路径>
这样可以让依赖关系明确记录在 go.mod 和 go.sum 中,也更方便团队协作和持续集成环境复现。
代理与依赖整理别忽略
如果你在国内网络环境下拉取模块较慢,建议先配置模块代理:
go env -w GOPROXY=https://goproxy.cn,direct
依赖拉下来之后,不要只停在“能编过”这一步。定期执行下面的命令,可以清理未使用依赖,并让 go.mod 与 go.sum 保持同步:
go mod tidy
很多构建异常表面上像是代码问题,实际是依赖文件长期没有整理,导致模块状态和源码引用不一致。
交叉编译时重点看 GOOS 和 GOARCH
如果目标不是当前这台 Ubuntu 主机,而是其他操作系统或架构,就需要显式指定 GOOS 和 GOARCH。这是 Go 交叉编译里最基础也最容易写错的一步。
例如,构建 Linux amd64 的二进制文件:
GOOS=linux GOARCH=amd64 go build -o myprogram-linux main.go
常见组合包括:
GOOS=windows GOARCH=amd64:Windows 64 位GOOS=linux GOARCH=arm64:Linux ARM64
如果你在 Ubuntu 上给别的平台出包,建议把输出文件名也带上平台信息,例如 myprogram-linux,后续分发和排查都会更清楚。遇到交叉编译失败时,先确认目标平台参数是否和部署环境一致,再去怀疑代码本身。
需要提速或减小体积时,可以这样优化
编译缓存与并行参数
在反复修改代码、频繁构建的场景里,构建效率主要看缓存和并行度。原文提到编译缓存默认开启,也可以显式使用:

go build -buildcache true
如果机器是多核 CPU,可以通过 -p 指定并行进程数,例如:
go build -p 8
这类参数更适合本地开发或 CI 中的高频构建任务,能减少重复编译等待时间。
精简二进制与强制重编译
如果你更关心产物大小,可以在构建时移除调试信息和符号表:
go build -ldflags="-s -w" -o myprogram main.go
这里的 -ldflags="-s -w" 主要用于减小二进制体积,适合发布版本使用。
相反,如果你怀疑缓存导致旧结果被复用,或者依赖刚更新过,希望彻底重新编译,可以使用:
go build -a
它会忽略缓存,重新构建所有相关内容。这个选项不适合日常每次都开,但在定位“为什么明明改了却没生效”的问题时很实用。
遇到报错时,重点排查权限、CGO 和缓存
权限与路径错误怎么处理
如果编译或运行时出现 permission denied,先检查工作目录、输出目录和 GOPATH 相关路径权限。原文给出的处理示例是:

sudo chmod -R 755 $GOPATH
必要时也可以使用 sudo,但要谨慎,因为这往往意味着当前目录或文件归属已经不太合理,后续可能留下更多权限问题。更稳妥的思路是先确认是谁创建了这些文件,以及当前用户是否拥有正常读写执行权限。
项目用了 CGO,还要检查 GCC
如果项目依赖 CGO,也就是会调用 C 代码,那么仅有 Go 编译器还不够,还需要系统里的 C 编译环境。可以安装:
sudo apt install build-essential gcc
同时确认 CGO_ENABLED=1。原文说明这是默认开启的;如果项目确实不需要 CGO,也可以关闭以提升编译速度:
export CGO_ENABLED=0
这一步很关键,因为不少“在本机能编、在服务器不能编”的情况,问题就出在 CGO 依赖链和系统工具链不一致。
缓存异常时直接清理
当你遇到依赖版本冲突、缓存损坏,或者怎么重试都出现奇怪的构建异常时,可以直接清理构建缓存和模块缓存:
go clean -cache -modcache
清理完成后,再重新执行 go build。这通常是处理“环境看起来都对,但结果依然不对”的最后一轮快速排查手段。
一套更省时间的排查顺序
如果你不想每次都从头猜问题,可以按这个顺序检查:先看 go version 和 go env,确认 Go 版本与环境变量;再看 go.mod、go.sum 和 go mod tidy,确认依赖状态;接着核对 GOOS、GOARCH、CGO_ENABLED 是否符合目标平台;最后再处理权限、缓存和构建优化参数。
这样排查的好处是,能先排掉最常见、最基础的配置问题,再进入更具体的性能和兼容性问题,整体会比遇错就零散搜报错信息高效得多。









