含有CGO代码的项目如何实现跨平台编译

目前小菜刀的项目中须要用到SQLite数据库,https://github.com/mattn/go-sqlite3支持database/sql接口,采用Go的标准接口有利于项目后续扩展,所以选择了该驱动。可是,它是基于CGO实现的,因此跨平台编译会比较麻烦,小菜刀总结了一些经验,特分享给读者朋友们。linux


什么是跨平台编译?android

简单地说, 就是在一个平台上生成另外一个平台上的可执行代码。这里须要注意的是,所谓平台,实际上包含两个概念:体系架构(Architecture)、操做系统 (Operating System)。同一个体系架构能够运行不一样的操做系统;一样,同一个操做系统也能够在不一样的体系架构上运行。ios

咱们知道Go语言是支持跨平台编译的,在以前的文章《Go交叉编译》中有详细介绍过怎么操做。Go实现跨平台编译的思想其实很简单:经过保存能够生成最终机器码的多份翻译代码,在编译时根据 GOARCH=xxxGOOS=xxx参数(对应体系架构和操做系统)进行初始化设置,最终调用对应平台编写的特定方法来生成机器码,从而实现跨平台编译。c++


CGO编译存在的问题git

有一点须要注意:Go所谓的跨平台编译只是针对Go代码部分,它是Go的交叉编译器(cross-compiler toolchains)。当咱们使用了CGO时,要想实现跨平台编译,同时须要让C/C++代码也支持跨平台。github

 1package main
2
3/*
4#include <stdio.h>
5
6void printint(int v) {
7    printf("printint: %d\n", v);
8}
9*/

10import "C"
11
12func main() {
13    v := 42
14    C.printint(C.int(v))
15}

小菜刀的开发机器:amd64架构,darwin系统。目标编译平台:amd64架构,linux系统。现想将上述含有CGO的代码编译为目标平台的可执行文件。web

1$ GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o main main.go

经过以上命令,获得编译错误以下:sql

1/usr/local/go/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
2ld: warning: ignoring file /var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-link-220081766/go.o, building for macOS-x86_64 but attempting to link with file built for unknown-unsupported file format ( 0x7F 0x45 0x4C 0x46 0x02 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 )
3Undefined symbols for architecture x86_64:
4  "_main", referenced from:
5     implicit entry/start for main executable
6ld: symbol(s) not found for architecture x86_64
7clang: error: linker command failed with exit code 1 (use -v to see invocation)

能够看到,因为CGO的存在,跨平台编译失败。那该如何解决呢?docker

其实思路能够很简单:和Go同样,当咱们拥有目标平台的C/C++代码翻译系统后,天然就可以编译为目标平台的可执行文件。数据库


Mac下的可行方案

下载linux编译工具链

1brew install FiloSottile/musl-cross/musl-cross

或者windows编译工具链

1brew install mingw-w64

以linux编译工具链为例。在下载完毕后,/usr/local/bin下会存在如下对应平台C/C++编译器

1x86_64-linux-musl-addr2line   x86_64-linux-musl-elfedit     x86_64-linux-musl-gcov        x86_64-linux-musl-objcopy
2x86_64-linux-musl-ar          x86_64-linux-musl-g++         x86_64-linux-musl-gcov-dump   x86_64-linux-musl-objdump
3x86_64-linux-musl-as          x86_64-linux-musl-gcc         x86_64-linux-musl-gcov-tool   x86_64-linux-musl-ranlib
4x86_64-linux-musl-c++         x86_64-linux-musl-gcc-9.2.0   x86_64-linux-musl-gprof       x86_64-linux-musl-readelf
5x86_64-linux-musl-c++filt     x86_64-linux-musl-gcc-ar      x86_64-linux-musl-ld          x86_64-linux-musl-size
6x86_64-linux-musl-cc          x86_64-linux-musl-gcc-nm      x86_64-linux-musl-ld.bfd      x86_64-linux-musl-strings
7x86_64-linux-musl-cpp         x86_64-linux-musl-gcc-ranlib  x86_64-linux-musl-nm          x86_64-linux-musl-strip

上述指定下载命令只下载了x86_64体系下的编译器,但其实并不止这些。可经过brew info musl-cross命令进行查看。

 1$ brew info musl-cross
2filosottile/musl-cross/musl-cross: stable 0.9.9 (bottled), HEAD
3Linux cross compilers based on musl libc
4https://github.com/richfelker/musl-cross-make
5/usr/local/Cellar/musl-cross/0.9.9 (1,851 files, 245.8MB) *
6  Poured from bottle on 2020-11-16 at 17:09:31
7From: https://github.com/filosottile/homebrew-musl-cross/blob/master/musl-cross.rb
8==> Dependencies
9Build: gnu-sed ✔, make ✔
10==> Options
11--with-aarch64
12    Build cross-compilers targeting arm-linux-muslaarch64
13--with-arm
14    Build cross-compilers targeting arm-linux-musleabi
15--with-arm-hf
16    Build cross-compilers targeting arm-linux-musleabihf
17--with-i486
18    Build cross-compilers targeting i486-linux-musl
19--with-mips
20    Build cross-compilers targeting mips-linux-musl
21--with-mips64
22    Build cross-compilers targeting mips64-linux-musl
23--with-mips64el
24    Build cross-compilers targeting mips64el-linux-musl
25--with-mipsel
26    Build cross-compilers targeting mipsel-linux-musl
27--without-x86_64
28    Do not build cross-compilers targeting x86_64-linux-musl
29--HEAD
30    Install HEAD version

此时,经过指定C/C++编译器为/usr/local/bin/x86_64-linux-musl-gcc,替换默认的C/C++编译器(本机编译,可经过go env CC查看),便可完成含有CGO的Go代码交叉编译任务。

1$ GOOS=linux CC="/usr/local/bin/x86_64-linux-musl-gcc" GOARCH=amd64 CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static" main.go

最终,在本机mac系统上就编译获得了amd64 linux平台的可执行文件。


Docker解决方案

在小菜刀经过上述方式完成cgo的跨平台编译之余,找到了另一种可行方案:基于Docker容器的xgo打包工具。

它的实现也很简单:将多平台所须要的Go工具链,C/C++交叉编译器和头文件/库都组装到Docker容器中(所以,在镜像拉取时,会下载大量的依赖资源),再借助xgo打包工具实现跨平台编译。

  1. Docker安装(省略)

  2. 拉取镜像

1docker pull karalabe/xgo-latest
  1. 打包工具安装

1go get github.com/karalabe/xgo

轻量级的命令包装器,它的做用就是简化复杂的Docker命令。

  1. 跨平台编译

指定要编译的导入路径便可,其他工做由xgo完成。在本例中,代码位置位于$GOPATH/src/workspace/example/cgoDemo2/

1xgo $GOPATH/src/workspace/example/cgoDemo2/

编译以后,本目录下会存在如下各平台可执行文件

 1$ ls -al
2total 44960
3drwxr-xr-x  23 slp  staff      736 Nov 17 11:43 .
4drwxr-xr-x  39 slp  staff     1248 Nov 16 17:59 ..
5-rwxr-xr-x   1 slp  staff  1761872 Nov 17 11:42 cgoDemo2-android-16-386
6drwxr-xr-x   5 slp  staff      160 Nov 17 11:42 cgoDemo2-android-16-aar
7-rwxr-xr-x   1 slp  staff  1778464 Nov 17 11:42 cgoDemo2-android-16-arm
8-rwxr-xr-x   1 slp  staff   902436 Nov 17 11:43 cgoDemo2-darwin-10.6-386
9-rwxr-xr-x   1 slp  staff  1053816 Nov 17 11:43 cgoDemo2-darwin-10.6-amd64
10-rwxr-xr-x   1 slp  staff  1065232 Nov 17 11:43 cgoDemo2-ios-5.0-arm64
11-rwxr-xr-x   1 slp  staff   978016 Nov 17 11:43 cgoDemo2-ios-5.0-armv7
12drwxrwxrwx   3 slp  staff       96 Nov 17 11:43 cgoDemo2-ios-5.0-framework
13-rwxr-xr-x   1 slp  staff  1084208 Nov 17 11:42 cgoDemo2-linux-386
14-rwxr-xr-x   1 slp  staff  1226072 Nov 17 11:42 cgoDemo2-linux-amd64
15-rwxr-xr-x   1 slp  staff  1093728 Nov 17 11:42 cgoDemo2-linux-arm-5
16-rwxr-xr-x   1 slp  staff  1074348 Nov 17 11:43 cgoDemo2-linux-arm-6
17-rwxr-xr-x   1 slp  staff  1073800 Nov 17 11:43 cgoDemo2-linux-arm-7
18-rwxr-xr-x   1 slp  staff  1196520 Nov 17 11:43 cgoDemo2-linux-arm64
19-rwxr-xr-x   1 slp  staff  1152088 Nov 17 11:43 cgoDemo2-linux-mips
20-rwxr-xr-x   1 slp  staff  1274272 Nov 17 11:43 cgoDemo2-linux-mips64
21-rwxr-xr-x   1 slp  staff  1271464 Nov 17 11:43 cgoDemo2-linux-mips64le
22-rwxr-xr-x   1 slp  staff  1148892 Nov 17 11:43 cgoDemo2-linux-mipsle
23-rwxr-xr-x   1 slp  staff  1712214 Nov 17 11:43 cgoDemo2-windows-4.0-386.exe
24-rwxr-xr-x   1 slp  staff  2115121 Nov 17 11:43 cgoDemo2-windows-4.0-amd64.exe
25-rw-r--r--   1 slp  staff      157 Nov 16 16:51 main.go

默认状况下,xgo会尝试编译Go运行时所支持的全部平台。若是咱们只想构建特定的几个目标系统,可使用逗号分隔的--targets选项控制,例如--targets=windows/amd64,linux/amd64表明编译目标仅包括amd64架构的windows和linux平台。

1$ xgo --targets=windows/amd64,linux/amd64 $GOPATH/src/workspace/example/cgoDemo2/

目前支持的平台列表以下

  • 操做系统: android, darwin, ios, linux, windows

  • 架构: 386, amd64, arm-5, arm-6, arm-7, arm64, mips, mipsle, mips64, mips64le

xgo提供了比较灵活的编译方案,经过$ xgo -h查看选项信息,更多详情可点击文末的xgo连接。


参考连接
[easy windows and linux cross-compilers for macOS] 
https://blog.filippo.io/easy-windows-and-linux-cross-compilers-for-macos/
[musl-cross-make] 
https://github.com/richfelker/musl-cross-make
[homebrew-musl-cross] 
https://github.com/FiloSottile/homebrew-musl-cross
[xgo] 
https://github.com/karalabe/xgo








往期推荐



本文分享自微信公众号 - Golang技术分享(gh_1ac13c0742b7)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索