CGO 跨平台静态编译
什么是跨平台编译?
- 跨平台编译:即交叉编译,是在一个平台上生成另一个平台上的可执行文件。所谓平台,实际上包含两个概念:体系架构(Architecture)、操作系统 (Operating System)。同一个体系架构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系架构上运行。
- 静态编译:在编译可执行文件的时候,将可执行文件需要调用的对应库都集成到可执行文件内部,使得可执行文件不需要其他任何依赖就能运行。
Go 实现跨平台编译的思想其实很简单:通过保存可以生成最终机器码的多份翻译代码,在编译时根据 GOARCH=xxx 和 GOOS=xxx 参数(对应体系架构和操作系统)进行初始化设置,最终调用对应平台编写的特定方法来生成机器码,从而实现跨平台编译。
有一点需要注意:Go 所谓的跨平台编译只是针对 Go 代码部分,它是 Go 的跨平台编译器(cross-compiler toolchains)。当我们使用了 CGO 时,要想实现跨平台编译,同时需要让 C/C++代码也支持跨平台。
官方 Cgo 这块目前有一篇 博客 和 命令行文档。比如 sqlite 的 golang 驱动 go-sqlite3 就是基于 Cgo 的实现。编译本地版本,Go 本身已经支持得非常好,基本不需要额外设置,直接通过 go build
编译即可,但是要想编译其他平台的二进制版本,就需要跨平台的 $(CC)
, $(CXX)
支持。
无 CGO 项目的交叉静态编译
在不启用 CGO 的情况下,跨平台编译是非常简单的,如下:
1 | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags '-s -w --extldflags "-static -fpic"' main.go |
CGO_ENABLED=0
这个值默认是 1,也就是开启的,需要手动指定为关闭,因为 CGO 是不支持跨平台编译的,使用go env CGO_ENABLED
查看默认值- GOOS,
GOARCH
构建的平台,GOOS=linux
是因为安卓底层就是 linux,aarch64
架构直接使用 arm64,如果GOARCH=arm
,则要使用GOARM=7
,指定 arm 版本可选 5,6,7,在 这里 看完整支持列表 -ldflags
编译选项,-s -w
去掉调试信息,可以减小构建后文件体积,--extldflags "-static -fpic"
完全静态编译,要跨平台编译放到其他系统和架构中运行,建议静态编译,否则程序启动的时候会提示找不到依赖的so
文件
这样编译生成的程序就可以任意放到指定平台下运行。由于 CGO 的存在,跨平台会编译失败。那该如何解决呢?
CGO 项目的交叉静态编译
和 Go 一样,当我们拥有目标平台的 C/C++代码编译器后,自然就能够编译为目标平台的可执行文件。
不同平台的编译器 下载地址
以 aarch64-linux
为例,下载 aarch64-linux
的编译器: https://musl.cc/aarch64-linux-musl-cross.tgz
解压,然后把解压好的目录下 bin
文件路径放到 PATH
环境变量中
此时,通过指定 C/C++编译器为/usr/local/bin/x86_64-linux-musl-gcc
,替换默认的 C/C++编译器(本机编译,可通过 go env CC
查看),即可完成含有 CGO 的 Go 代码跨平台编译任务。
1 | $ CGO_ENABLED=1 CC=aarch64-linux-musl-gcc CXX=aarch64-linux-musl-g++ GOOS=linux GOARCH=arm64 go build -o server -ldflags '-s -w --extldflags "-static -fpic"' main.go |
CGO_ENABLED=1
开启 CGO,因为项目用到了 C 语言的代码CC=aarch64-linux-musl-gcc
指定gcc
的编译器为aarch64-linux-musl-gcc
,这个默认值是gcc
,也就是当前操作系统和架构使用的gcc
,使用命令$(go env CC) --target-help
可以看看默认gcc
支持什么平台CXX=aarch64-linux-musl-g++
指定g++
的编译器为aarch64-linux-musl-g++
,规则和CC
一样,只是用来编C++
代码的,如果还用到了C++
代码,必须指定该项-ldflags '-s -w'
go 编译选项,-s -w
去掉调试信息,可以减小构建后文件体积
最终,在本机系统上就编译得到了 aarch64 linux 平台的可执行文件。