Go 语言天生支持交叉编译,只需设置几个环境变量,就能在一台机器上构建出多个平台的可执行文件。本文将系统梳理交叉编译的完整用法,包括常见场景、参数详解和实战技巧。

什么是交叉编译?

交叉编译(Cross Compilation)是指在一个平台上编译生成另一个平台的可执行程序。例如:

  • macOS 上编译出 Linux 服务器可运行的二进制文件
  • Windows 开发机上编译出 Linux Docker 容器可运行的程序
  • Linux CI/CD 环境中同时构建 macOS 和 Windows 的发行版

Go 语言的编译器原生支持交叉编译,不需要安装任何第三方工具,这是 Go 相比许多其他语言的巨大优势。

核心环境变量

交叉编译的关键在于三个环境变量:

变量说明常用值
CGO_ENABLED是否启用 CGO0(禁用,交叉编译时必须)
GOOS目标操作系统linux, darwin, windows
GOARCH目标 CPU 架构amd64, arm64, 386

CGO_ENABLED 为什么要设为 0?

交叉编译时必须禁用 CGO(CGO_ENABLED=0),因为 CGO 需要目标平台的 C 编译器和链接库,而这些在当前平台上通常不可用。禁用 CGO 后,Go 会使用纯 Go 实现的标准库,确保编译产物的可移植性。

GOOS 平台对照表

GOOS 值对应平台
linuxLinux
darwinmacOS
windowsWindows
freebsdFreeBSD
androidAndroid
iosiOS

GOARCH 架构对照表

GOARCH 值说明典型设备
amd6464 位 x86大多数 PC 和服务器
38632 位 x86老旧 PC
arm6464 位 ARMApple M 系列、树莓派 4、AWS Graviton
arm32 位 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 → WindowsCGO_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 😄