Go 语言天生支持交叉编译,只需设置几个环境变量,就能在一台机器上构建出多个平台的可执行文件。本文将系统梳理交叉编译的完整用法,包括常见场景、参数详解和实战技巧。
什么是交叉编译?
交叉编译(Cross Compilation)是指在一个平台上编译生成另一个平台的可执行程序。例如:
- 在 macOS 上编译出 Linux 服务器可运行的二进制文件
- 在 Windows 开发机上编译出 Linux Docker 容器可运行的程序
- 在 Linux CI/CD 环境中同时构建 macOS 和 Windows 的发行版
Go 语言的编译器原生支持交叉编译,不需要安装任何第三方工具,这是 Go 相比许多其他语言的巨大优势。
核心环境变量
交叉编译的关键在于三个环境变量:
| 变量 | 说明 | 常用值 |
|---|---|---|
CGO_ENABLED | 是否启用 CGO | 0(禁用,交叉编译时必须) |
GOOS | 目标操作系统 | linux, darwin, windows |
GOARCH | 目标 CPU 架构 | amd64, arm64, 386 |
CGO_ENABLED 为什么要设为 0?
交叉编译时必须禁用 CGO(CGO_ENABLED=0),因为 CGO 需要目标平台的 C 编译器和链接库,而这些在当前平台上通常不可用。禁用 CGO 后,Go 会使用纯 Go 实现的标准库,确保编译产物的可移植性。
GOOS 平台对照表
| GOOS 值 | 对应平台 |
|---|---|
linux | Linux |
darwin | macOS |
windows | Windows |
freebsd | FreeBSD |
android | Android |
ios | iOS |
GOARCH 架构对照表
| GOARCH 值 | 说明 | 典型设备 |
|---|---|---|
amd64 | 64 位 x86 | 大多数 PC 和服务器 |
386 | 32 位 x86 | 老旧 PC |
arm64 | 64 位 ARM | Apple M 系列、树莓派 4、AWS Graviton |
arm | 32 位 ARM | 树莓派 3、嵌入式设备 |
各平台交叉编译命令
在 macOS 上编译
# 编译 Linux amd64 版本(最常见:部署到服务器)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o myapp-linux main.go
# 编译 Linux arm64 版本(部署到 ARM 服务器,如 AWS Graviton)
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o myapp-linux-arm64 main.go
# 编译 Windows 版本
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o myapp.exe main.go
在 Linux 上编译
# 编译 macOS 版本(Intel Mac)
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o myapp-darwin main.go
# 编译 macOS 版本(Apple Silicon M1/M2/M3/M4)
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o myapp-darwin-arm64 main.go
# 编译 Windows 版本
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o myapp.exe main.go
在 Windows 上编译
Windows 的 CMD 和 PowerShell 设置环境变量的方式不同:
CMD(命令提示符):
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build -o myapp main.go
PowerShell:
$env:CGO_ENABLED="0"
$env:GOOS="linux"
$env:GOARCH="amd64"
go build -o myapp main.go
PowerShell 一行写法:
$env:CGO_ENABLED="0"; $env:GOOS="linux"; $env:GOARCH="amd64"; go build -o myapp main.go
实战:一键构建多平台版本
在项目根目录创建一个构建脚本,一次性编译所有平台的版本:
build.sh(macOS / Linux)
#!/bin/bash
APP_NAME="myapp"
VERSION="1.0.0"
BUILD_DIR="build"
# 清理
rm -rf $BUILD_DIR
mkdir -p $BUILD_DIR
# 定义目标平台
PLATFORMS=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
"windows/amd64"
)
for PLATFORM in "${PLATFORMS[@]}"; do
GOOS=${PLATFORM%/*}
GOARCH=${PLATFORM#*/}
OUTPUT="$BUILD_DIR/${APP_NAME}-${GOOS}-${GOARCH}"
# Windows 需要 .exe 后缀
if [ "$GOOS" = "windows" ]; then
OUTPUT="${OUTPUT}.exe"
fi
echo "Building $OUTPUT..."
CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build \
-ldflags="-s -w -X main.Version=$VERSION" \
-o $OUTPUT main.go
done
echo "✅ Build complete!"
ls -lh $BUILD_DIR/
编译参数说明
上面脚本中使用的 -ldflags 参数也值得了解:
| 参数 | 说明 |
|---|---|
-s | 去掉符号表,减小体积 |
-w | 去掉 DWARF 调试信息,进一步减小体积 |
-X main.Version=$VERSION | 编译时注入版本号变量 |
使用 -ldflags="-s -w" 通常可以减少 20%-30% 的二进制体积。
查看支持的平台列表
想知道 Go 支持编译哪些平台?运行:
go tool dist list
输出会列出所有支持的 GOOS/GOARCH 组合,截至 Go 1.22+,已支持超过 40 种组合。
常见问题
1. 编译报错:xxx requires cgo
某些包(如 github.com/mattn/go-sqlite3)依赖 CGO,无法直接交叉编译。解决方案:
- 寻找纯 Go 替代库(如用
modernc.org/sqlite替代go-sqlite3) - 使用 Docker 进行交叉编译
- 安装目标平台的交叉编译工具链
2. 编译出的文件无法执行
检查文件权限(Linux/macOS 需要添加执行权限):
chmod +x myapp-linux
3. Windows 编译的文件没有 .exe 后缀
当 GOOS=windows 时,Go 会自动添加 .exe 后缀,但如果你用 -o 手动指定了输出文件名,需要自己加上 .exe。
在 CI/CD 中的应用
在 GitHub Actions 中利用交叉编译自动构建多平台发行版:
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
- goos: windows
goarch: amd64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build
env:
CGO_ENABLED: 0
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: |
EXT=""
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
go build -ldflags="-s -w" -o myapp-$GOOS-$GOARCH$EXT main.go
- uses: softprops/action-gh-release@v1
with:
files: myapp-*
总结
| 场景 | 命令 |
|---|---|
| Mac → Linux 服务器 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build |
| Mac → Windows | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build |
| Linux → Mac (M芯片) | CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build |
| Win → Linux | $env:CGO_ENABLED="0"; $env:GOOS="linux"; $env:GOARCH="amd64"; go build |
Go 的交叉编译是其工具链中最实用的特性之一。掌握了这几个环境变量,你就能轻松实现”一次编写,到处运行”——而且不需要 JVM 😄
评论