在 Windows、Linux、MacOS 下交叉编译 Golang

目录

Golang 1.5开始,交叉编译变得非常便捷:

  • 对于没有使用CGO的程序,只需设置GOOSGOARCHCGO_ENABLED这几个环境变量,即可直接利用编译器自带的跨平台特性实现跨平台编译
  • 对于使用CGO的程序,大部分情况下可以通过配置CC环境变量使用自行准备的交叉编译工具进行编译

关于使用CGO情况下的交叉编译,参见 交叉编译 Go 程序 | Holmesian Blog

1. 查看当前 Go 版本支持的编译平台

> go version
go version go1.13 windows/amd64

> go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/386   # Mac i386
darwin/amd64 # Mac amd64
darwin/arm
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
illumos/amd64
js/wasm
linux/386   # Linux i386 
linux/amd64 # Linux amd64
linux/arm
linux/arm64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/s390x
nacl/386
nacl/amd64p32
nacl/arm
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
windows/386   # Windows i386
windows/amd64 # Windows amd64
windows/arm

> go env
set GOHOSTARCH=amd64 # 本机的架构
set GOHOSTOS=windows # 本机的系统
set GOARCH=amd64     # 目标平台的架构,交叉编译时需要设置
set GOOS=windows     # 目标平台的系统,交叉编译时需要设置
set CGO_ENABLED=0    # 是否启用 CGO
...

最常用的大概是x86amd64架构:

  • darwin/386:对应 Mac x86
  • darwin/amd64:对应 Mac amd64
  • linux/386:对应 Linux x86
  • linux/amd64:对应 Linux amd64
  • Windows/386:对应 Windows x86
  • Windows/amd64:对应 Windows amd64

2. 设置环境变量并交叉编译

2.1 Windows

Windows 下编译 MacOSLinux64 位程序:

# For MacOS/amd64
set CGO_ENABLED=0
set GOOS=darwin
set GOARCH=amd64
go build main.go

# For Linux/amd64
set CGO_ENABLED=0
set GOOS=linux
set GOARCH=amd64
go build main.go

2.2 Linux

Linux 下编译 MacOSWindows64 位程序:

# For MacOS/amd64
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go

# For Windows/amd64
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go

2.3 MacOS

MacOS 下编译 WindowsLinux64 位程序:

# For Windows/amd64
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go

# For Linux/amd64
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go

3. 选择性编译

虽然 Golang 可以跨平台编译,但系统之间的差异性仍然存在。有些时候我们会直接调用操作系统函数,不同操作系统下的库可能会有不同的实现,比如syscall库。

命令go build没有内置#define或者预处理器之类的处理平台相关的代码取舍,而是采用 Tag 标记文件后缀的方式实现选择性编译

3.1 构建标记:build tag

为了实现根据不同的目标平台编译对应的源文件,需要在文件顶部添加构建标记build tag

// +build

标记遵循以下规则:

  1. A build tag is evaluated as the OR of space-separated options
  2. Each option evaluates as the AND of its comma-separated terms
  3. Each term is an alphanumeric word or, preceded by !, its negation

简单翻译一下:

  1. 空格
  2. 逗号,
  3. 叹号!

例如:

// +build A,B !C,D
// (A && B) || ((!C) && D)

再例如:

// +build !windows,386
//此文件在非 Windows 操作系统,且为 x86 处理器时编译

构建标记必须出现在文件顶部,可以有多个build tag,之间是AND的关系:

// +build linux darwin
// +build 386

另外需要注意build tagpackage xxx语句之间需要有空行分隔,也就是:

// +build linux darwin
// +build 386

package mypkg

3.2 文件后缀:_$GOOS.go

_$GOOS.go为后缀的文件只在此平台上编译,其他平台上编译时就当此文件不存在,完整的后缀如:

_$GOOS_$GOARCH.go

例如:

  • syscall_linux_amd64.go:只在 Linux/amd64 下编译
  • syscall_windows_386.go:只在 Windows/i386 下编译
  • syscall_windows.go:只在 Windows 下编译

4. 示例程序

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Printf("OS: %s\nArchitecture: %s\n", runtime.GOOS, runtime.GOARCH)
}

Windows/amd64 下为 Windows/amd64Linux/amd64 编译:

# For Windows/amd64
> go build cross.go

# For Linux/amd64
> set CGO_ENABLED=0
> set GOOS=linux
> set GOARCH=amd64
> go build cross.go

# Cmder
> ls -hl
total 3.0M
-rw-r--r-- 1 abelsu7 197609 1014K 10月 24 17:26 cross
-rwxr-xr-x 1 abelsu7 197609  2.0M 10月 24 17:15 cross.exe*
-rw-r--r-- 1 abelsu7 197609   140 10月 24 17:12 cross.go
-rw-r--r-- 1 abelsu7 197609   198 10月 23 17:58 go.mod
-rw-r--r-- 1 abelsu7 197609  1.5K 10月 23 17:57 go.sum
-rw-r--r-- 1 abelsu7 197609  1.9K 10月 24 15:48 main.go

Windows 下运行:

> .\cross.exe
OS: windows
Architecture: amd64

WSL 下运行:

> ./cross
OS: linux
Architecture: amd64

参考文章

  1. 交叉编译 Go 程序 | Holmesian Blog
  2. 交叉编译 Go 程序 | 鸟窝
  3. Golang 交叉编译与选择性编译 | CSDN
  4. Golang 在 Mac、Linux、Windows 下如何交叉编译 | CSDN
  5. Golang 交叉编译中的那些坑 | CSDN
  6. Cross compilation with Go 1,5 | Dave Cheney
  7. Building windows go programs on linux - golang/go | Github
  8. Cross Compile in Go (Golang) | Medium.com
  9. TinyGo Brings Go To Arduino | Hackaday
  10. Better way to install Golang (Go) on Raspberry Pi | E-Tinkers
  11. Recipe: Cross Compliling | The Go Cookbook
  12. Gin 实践 番外:Golang 交叉编译 - 煎鱼 | SegmentFault
  13. Gin 实践 番外:请入门 Makefile - 煎鱼 | SegmentFault