交叉编译Go程序

注意:本文最后更新于 537 天前,有关的内容可能已经发生变化,请参考使用。

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

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

cross-compile.png

I. 重点在这里

1、简单来说,没有用到 CGO 的 Golang 程序,直接用编译器自带的跨平台特性即可全平台编译;

2、用到了 CGO 需要发布到ARM平台的Golang程序,推荐自己配置ARM交叉编译环境;

3、用到了 CGO 需要发布到Win/Linux/Mac平台的Golang程序,推荐用使用go-ui-crossbuild 。


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

不使用CGO的交叉编译

II. 一般做法

在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

III. 关于协处理器

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

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

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

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

使用CGO的交叉编译

IV. 什么是CGO

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

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

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

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

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

以树莓派的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部分的代码,在不同的平台存在不兼容的问题,需要根据错误提示逐项解决。

VI. 使用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 。


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

Holmesian

感谢您的支持!

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


相关文章

发表新评论
已有 6 条评论
  1. 徐祥振

    请问 CGO 交叉编译的依赖问题如何解决呢,一直提示找不到包

    徐祥振 回复
  2. xiaobo

    这是真的看不懂。。。

    xiaobo 回复
  3. 头条新闻

    文章不错非常喜欢

    头条新闻 回复
  4. 今日头条新闻

    文章不错支持一下吧

    今日头条新闻 回复
  5. 往东行

    看着头大,看不懂,哈哈哈

    往东行 回复
  6. 今日新闻

    文章不错非常喜欢

    今日新闻 回复