Golang交叉编译多平台二进制文件一文搞定

  • 发表于
  • go

Go跨平台交叉编译介绍

golang依托语言特性跨平台、性能效率、周边都还算不错,有不少开发者把Go做为跨平台SDK来封装,起到了不错的效果,今天写篇Go如何交叉编译多平台二进制文件的文章,以达到跨平台调用的目的。

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

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

首先当然是安装go的脚手架,这部分自行补充,我们看一下go支持的跨平台交叉编译支持列表:

GOOS - Target Operating SystemGOARCH - Target Platform
系统平台

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

➜root ✗ go version
go version go1.18.2 darwin/amd64

➜root ✗ go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
illumos/amd64
ios/amd64
ios/arm64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
openbsd/mips64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
windows/386
windows/amd64
windows/arm
windows/arm64

支持非常广泛,最常用的大概是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

对应关系补充

arm -> armv7
arm64 -> arm64
386 -> i386=i686
amd64 -> x86_64
armeabi-v7a -> armv7a-linux-androideabi
arm64-v8a -> aarch64-linux-android
x86 -> i686-linux-android
x86-64 -> x86_64-linux-android

Go多平台交叉编译示例 macOS

以下示例是Mac下go交叉编译跨平台的 动态库与静态库 .so .a .dll

这里有几点要注意

  1. package 一定要是 main(强制规定)
  2. 一定要包含 main 函数(强制规定)
  3. import "", 不能少, 因为要编译出 c(c++)的头文件
  4. 每个方法前要加//export 方法名, 这里要注意
    1. // 和 export间不能有空格
    2. 方法名和 go 的方法名必须完全一样
    3. 方法名不能是 c 内置的方法名, 比如remove就不行

main.go

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello Gophers")
}

注意:下面的 -ldflags "-w -s" 参数很多时候并不需要,自行调整。

编译变量说明

  1. GOOS=目标平台(编译后要运行的目标平台)的操作系统(darwin、freebsd、linux、windows、android、IOS)
  2. GOARCH=目标平台(编译后要运行的目标平台)的体系架构(386、amd64、arm)分别对应(32位、64位、ARM平台)的架构
  3. CGO_ENABLED=1 是否开启CGO编译,0关闭CGO
  4. CC=ndk 里的 clang,需要是对应 CPU 版本的, API 呢建议是最低
    1. 这里单独说明一下, 高版本的 ndk 用的是 clang, 低版本用的是 gcc, 这个根据你情况来, 个人建议直接用 21.0.6113669版本, 这个我测试过, 没有问题, ndk20 我也试过, 也是 ok 的
  5. go build 命令
    1. -buildmode=c-shared 构建类型, 使用 go help buildmode查看, 安卓一般用c-shared就可以了, 意思就是 c 类型的共享库(动态库)
    2. -o 后面跟输出的位置, 一般建议使用 libxxx.so 的格式, 相对的, 头文件也会被生成在同一目录下, xxx 就是库的名字, 对应到 Java 里加载库的方法就是System.loadLibrary("xxx") 而不是System.loadLibrary("libxxx")
    3. 最后一个是需要编译的 go 文件

CPU 类型和设备类型可以使用go tool dist list查看

macOS 下编译 macos 二进制

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

macOS 下编译 Windows .dll

//64位使用 x86_64-w64-mingw32-gcc
//可能需要mac 安装 brew install mingw-w64
//32位
GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -buildmode=c-shared -ldflags "-w -s" -o cShareDll.dll cShareDll.go
//64位
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -buildmode=c-shared -ldflags "-w -s" -o cShareDll.dll cShareDll.go

//编译macos 64位 so 或者 dylib delphi C/C++ 可以调用
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -buildmode=c-shared -ldflags "-w -s" -o cShareDll.so cShareDll.go

//默认参数是 debug 版,加编译参数 -ldflags "-w -s" 才是 release. -w 为去掉调试信息,-s 为去掉符号表,文件大小会减小

macOS 下编译 Linux .so

brew install FiloSottile/musl-cross/musl-cross
//为了交叉编译基于cgo的项目,需要设置CC或CCX环境变量标志来构建到 x86_64-linux-musl-gcc和x86_64-linux-musl-g++(或其他相关架构)。
//该环境变量标志应放在GOOS和GOARCH两个环境变量标志之前。

//64位
CC=x86_64-linux-musl-gcc GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -buildmode=c-shared -o cShareDll.so cShareDll.g

macOS 下编译 Android .so

//安装 NDK 环境设置
//编辑.zshrc 添加以下变量
export ANDROID_HOME="/Users/M2Macbook/Library/Android"
export ANDROID_NDK_HOME="$ANDROID_HOME/android-ndk-r21b"
export PATH="$PATH:/Users/M2Macbook/Library/Android/android-ndk-r21b/toolchains/llvm/prebuilt/darwin-x86_64/bin"

//编译命令
//armeabi-v7a
GOOS=android GOARCH=arm CGO_ENABLED=1 CC=armv7a-linux-androideabi21-clang go build -buildmode=c-shared -ldflags "-w -s" -o output/android/armeabi-v7a/cShareDll.so cShareDll.go
//arm64-v8a
GOOS=android GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-android21-clang go build -buildmode=c-shared -ldflags "-w -s" -o output/android/arm64-v8a/cShareDll.so cShareDll.go

//利用strip 工具来减少Android 的 so的大小
//strip 工具会去掉debug和符号信息
//测试可以减少到原来的 60%左右工具在 android-ndk 的目录中
//类似
cd android-ndk/android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/arm-linux-androideabi/bin
./strip [yourOSFile

macOS 下编译 iOS .a

brew install FiloSottile/musl-cross/musl-cross
//为了交叉编译基于cgo的项目,需要设置CC或CCX环境变量标志来构建到 x86_64-linux-musl-gcc和x86_64-linux-musl-g++(或其他相关架构)。
//该环境变量标志应放在GOOS和GOARCH两个环境变量标志之前。

//64位
CC=x86_64-linux-musl-gcc GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -buildmode=c-shared -o cShareDll.so cShareDll.

下面补充其它平台下编译示例

Go多平台交叉编译示例 Windows

在 Windows 下编译 MacOS 和 Linux 的 64 位程序:

# 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

Go多平台交叉编译示例 Linux

在 Linux 下编译 MacOS 和 Windows 的 64 位程序:

# 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

参考文章