交叉编译Go程序
有几个Go的程序需要放在树莓派上跑,由于树莓派的性能太差、编译工具版本又低,想想还是在其他设备上编译好可执行文件直接拿来用方便,毕竟无需依赖的静态编译是Go的亮点嘛。
Go对交叉编译的支持是极好的:对于没有使用CGO的程序,只需要通过设置$CGO_ENABLED、$GOOS、$GOARCH参数即可轻松地实现跨平台编译;对于使用CGO的程序,大部分情况可以通过配置$CC参数使用自行准备的交叉编译工具进行编译。
重点在这里
1、简单来说,没有用到 CGO 的 Golang 程序,直接用编译器自带的跨平台特性即可全平台编译;
2、用到了 CGO 需要发布到ARM平台的Golang程序,推荐自己配置ARM交叉编译环境;
3、用到了 CGO 需要发布到Win/Linux/Mac平台的Golang程序,推荐用使用go-ui-crossbuild 。
下面以macOS为例,简单记录一下过程中踩的坑吧。
不使用CGO的交叉编译
一般做法
在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
关于协处理器
协处理器是协助CPU完成其无法执行或执行效率、效果低下的处理工作而开发和应用的处理器。
这种CPU无法执行的专项工作有很多,比如设备间的信号传输、接入设备的管理等;执行效率、效果低下的有图形处理、声频处理等。为了进行这些专项处理,各种辅助处理器就诞生了。现在的计算机构架(x86、amd64)中,整数运算器与浮点运算器已经集成在一起,一般来讲因此浮点处理器内建于CPU中的协处理器,不算是辅助处理器,不过ARM架构因为低功耗的原因是个例外。
ARM架构中的VFP coprocessor是典型的协处理器,它提供浮点数基本运算(加、减、乘、除、开方、比较、取反)以及向量(vectors)功能,一般来讲,VFP可以同时支持最多8组单精度4组双精度浮点数的运算。
简单地说,使用协处理器可以提升某一方面的性能,可以粗暴地认为是一种硬件加速。
使用CGO的交叉编译
什么是CGO
C语言经过数十年发展,各个方面的开源代码、闭源库已经非常丰富。这对于一门现代编程语言而言,如何用好现成的C代码就显得极为重要,CGO就是一个令人惊异的技术,它允许Go与C的类库交互操作,让Go能够使用C语言积累的各种资源。
CGO作为Go中使用C语言代码的一种方式,在获得好处的同时就必定得有付出,这是亘古不变之理:由于CGO相当于使用了C语言,所以编译使用CGO部分的代码就必须按C语言路子来,而C语言原生的跨平台支持相对Go来说可是差远了,使得使用CGO后要编译跨平台的可执行文件就麻烦多了。
目前解决CGO跨平台编译问题的思路有:
- 用目标平台的工具链进行交叉编译
- 用原生代码重写CGO实现的功能
用目标平台的工具链进行交叉编译
以树莓派的ARM平台为例,用ARM GNU Linux工具链,编译使用了CGO的Go项目。
macOS的文件系统默认是大小写不敏感的,而交叉编译工具链是基于大小写敏感的文件系统的,所以应当新建一个大小写敏感的磁盘镜像用于安放ARM GNU Linux工具链。打开Disk Utility,然后 File>New Image>Blank Image,然后在弹出窗口中设置名称为armx(名称自定),大小大于512MB,格式为Mac OS Extended的映像。
假设工具链包已经在~/Download目录中,解压缩工具链到到新建的磁盘镜像中:
tar -zxv -C /Volumes/armx/ --strip-components 1 -f ~/Download/ARMx-2009q3-67.tar.bz2
现在,你就已经能够直接使用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程序了。
如果使用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
交叉编译过程中如果出现错误,多半是因为使用CGO部分的代码,在不同的平台存在不兼容的问题,需要根据错误提示逐项解决。
使用go-ui-crossbuild
go-ui-crossbuild是通过预制好的CGO跨平台编译环境,实现一键编译MacOS、Linux和Windows成品的项目,该项目的地址托管在Github上,使用起来也很简单,下载好docker镜像启动之后在go程序目录执行如下命令即可。
docker run -v $GOPATH/project_name:/go/src/project_name magj/go-ui-crossbuild gouicrossbuild project_name ./cmd/gui
如果涉及到自身的包依赖,可以参考下列命令,将本地的src都映射到docker中去,编译成功之后会在项目文件夹下生成一个build文件夹,里面分别有MacOS用的app文件,Windows用的exe文件和Linux用的可执行文件。
docker run -v /Users/holmesian/go/src/:/go/src/ magj/go-ui-crossbuild gouicrossbuild getDocument getDocument ./build
其中getDocument是项目名称,项目目录为/Users/holmesian/go/src/getDocument 。