交叉编译Go程序


有几个Go的程序需要放在树莓派上跑,由于树莓派的性能太差、编译工具版本又低,想想还是在其他设备上编译好可执行文件直接拿来用方便,毕竟无需依赖的静态编译是Go的亮点嘛。

Go对交叉编译的支持是极好的:对于没有使用CGO的程序,只需要通过设置$CGO_ENABLED、$GOOS、$GOARCH参数即可轻松地实现跨平台编译;对于使用CGO的程序,大部分情况可以通过配置$CC参数使用自行准备的交叉编译工具进行编译。

cross-compile.png

下面以macOS为例,简单记录一下过程中踩的坑吧


不使用CGO的交叉编译

I. 一般做法

在macOS编译Linux和Windows的arm可执行程序:

    CGO_ENABLED=0 GOOS=linux GOARCH=arm go build main.go
    CGO_ENABLED=0 GOOS=windows GOARCH=arm go build main.go

其中:

  • $CGO_ENABLED:0表示关闭CGO
  • $GOOS:目标平台(编译后要运行的目标平台)的操作系统(darwin、freebsd、linux、windows)
  • $GOARCH:目标平台(编译后要运行的目标平台)的体系架构(386、amd64、arm)分别对应(32位、64位、ARM平台)的架构

树莓派属于ARM平台,对于编译给ARM使用的Go程序,需要根据实际情况配置$GOARM,这是用来控制CPU的浮点协处理器的参数。$GOARM默认是6,对于不支持VFP使用软件运算的老版本ARM平台要设置成5,支持VFPv1的设置成6,支持VFPv3的设置成7,详细说明如下:

GOARM=5: use software floating point; when CPU doesn't have VFP co-processor

GOARM=6: use VFPv1 only; default if cross compiling; usually ARM11 or better cores (VFPv2 or better is also supported)

GOARM=7: use VFPv3; usually Cortex-A cores

综上,给跑着linux的老版本树莓派编译Go程序的命令为:

    CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build main.go

II. 关于协处理器

协处理器是协助CPU完成其无法执行或执行效率、效果低下的处理工作而开发和应用的处理器。

这种CPU无法执行的专项工作有很多,比如设备间的信号传输、接入设备的管理等;执行效率、效果低下的有图形处理、声频处理等。为了进行这些专项处理,各种辅助处理器就诞生了。现在的计算机构架(x86、amd64)中,整数运算器与浮点运算器已经集成在一起,一般来讲因此浮点处理器内建于CPU中的协处理器,不算是辅助处理器,不过ARM架构因为低功耗的原因是个例外。

ARM架构中的VFP coprocessor是典型的协处理器,它提供浮点数基本运算(加、减、乘、除、开方、比较、取反)以及向量(vectors)功能,一般来讲,VFP可以同时支持最多8组单精度4组双精度浮点数的运算。

简单地说,使用协处理器可以提升某一方面的性能,可以粗暴地认为是一种硬件加速。

使用CGO的交叉编译

III. 什么是CGO

C语言经过数十年发展,各个方面的开源代码、闭源库已经非常丰富。这对于一门现代编程语言而言,如何用好现成的C代码就显得极为重要,CGO就是一个令人惊异的技术,它允许Go与C的类库交互操作,让Go能够使用C语言积累的各种资源。

CGO作为Go中使用C语言代码的一种方式,在获得好处的同时就必定得有付出,这是亘古不变之理:由于CGO相当于使用了C语言,所以编译使用CGO部分的代码就必须按C语言路子来,而C语言原生的跨平台支持相对Go来说可是差远了,使得使用CGO后要编译跨平台的可执行文件就麻烦多了。

目前解决CGO跨平台编译问题的思路有:

  1. 用目标平台的工具链进行交叉编译
  2. 用原生代码重写CGO实现的功能

IV. 用目标平台的工具链进行交叉编译

以树莓派的ARM平台为例,用ARM GNU Linux工具链,编译使用了CGO的Go项目。

macOS的文件系统默认是大小写不敏感的,而交叉编译工具链是基于大小写敏感的文件系统的,所以应当新建一个大小写敏感的磁盘镜像用于安放ARM GNU Linux工具链。打开Disk Utility,然后 File>New Image>Blank Image,然后在弹出窗口中设置名称为armx(名称自定),大小大于512MB,格式为Mac OS Extended的映像。

DiskUtility.png

下载或者按说明自行编译ARM GNU Linux工具链。

假设工具链包已经在~/Download目录中,解压缩工具链到到新建的磁盘镜像中:

    tar -zxv -C /Volumes/armx/ --strip-components 1 -f ~/Download/ARMx-2009q3-67.tar.bz2

ARM-Tool.png

现在,你就已经能够直接使用ARM工具链了,比如要编译main.c程序,可以在终端执行:

    /Volumes/Armx/bin/arm-none-linux-gnueabi-gcc main.c -o main

就可以获得一个名为main的可执行程序,这个程序在macOS是不能运行的,只能在ARM平台设备上的Linux系统中才能执行。

好了,现在开始编译用了CGO的Go程序给树莓派用吧:

    CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=/Volumes/Armx/bin/arm-none-linux-gnueabi-gcc GOARM=5 go build -x main.go

其中$CC参数指定的是ARM工具链位置,如果不出意外的话,编译完成后就可以得到可以在树莓派上运行的Go程序了。

ARMfile.png

如果使用Beego提供的bee工具进行编译和打包,效果也是一样的的。

    CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=/Volumes/Armx/bin/arm-none-linux-gnueabi-gcc GOARM=5 ~/go/bin/bee pack main.go 

Beego-pack.png

交叉编译过程中如果出现错误,多半是因为使用CGO部分的代码,在不同的平台存在不兼容的问题,需要根据错误提示逐项解决。

V. 用原生代码重写CGO实现的功能

这一小节是废话,用原生Go代码重写之后问题划归成了无需CGO的一类类,也就不存在CGO的交叉编译问题了。


「倘若有所帮助,不妨酌情赞赏!」

Holmesian

感谢您的支持!

使用微信扫描二维码完成支付


相关文章

发表新评论
仅有 1 条评论
  1. 今日新闻

    文章不错非常喜欢

    今日新闻 回复