目前小菜刀的項(xiàng)目中需要用到SQLite數(shù)據(jù)庫(kù)黑低,https://github.com/mattn/go-sqlite3支持database/sql接口止潘,采用Go的標(biāo)準(zhǔn)接口有利于項(xiàng)目后續(xù)擴(kuò)展,因此選擇了該驅(qū)動(dòng)。但是龄章,它是基于CGO實(shí)現(xiàn)的须板,所以跨平臺(tái)編譯會(huì)比較麻煩,小菜刀總結(jié)了一些經(jīng)驗(yàn)逃呼,特分享給讀者朋友們鳖孤。
什么是跨平臺(tái)編譯?
簡(jiǎn)單地說(shuō)抡笼, 就是在一個(gè)平臺(tái)上生成另一個(gè)平臺(tái)上的可執(zhí)行代碼苏揣。這里需要注意的是,所謂平臺(tái)蔫缸,實(shí)際上包含兩個(gè)概念:體系架構(gòu)(Architecture)腿准、操作系統(tǒng) (Operating System)。同一個(gè)體系架構(gòu)可以運(yùn)行不同的操作系統(tǒng);同樣吐葱,同一個(gè)操作系統(tǒng)也可以在不同的體系架構(gòu)上運(yùn)行街望。
我們知道Go語(yǔ)言是支持跨平臺(tái)編譯的,在之前的文章《Go交叉編譯》中有詳細(xì)介紹過(guò)怎么操作弟跑。Go實(shí)現(xiàn)跨平臺(tái)編譯的思想其實(shí)很簡(jiǎn)單:通過(guò)保存可以生成最終機(jī)器碼的多份翻譯代碼灾前,在編譯時(shí)根據(jù) GOARCH=xxx
和GOOS=xxx
參數(shù)(對(duì)應(yīng)體系架構(gòu)和操作系統(tǒng))進(jìn)行初始化設(shè)置,最終調(diào)用對(duì)應(yīng)平臺(tái)編寫(xiě)的特定方法來(lái)生成機(jī)器碼孟辑,從而實(shí)現(xiàn)跨平臺(tái)編譯哎甲。
CGO編譯存在的問(wèn)題
有一點(diǎn)需要注意:Go所謂的跨平臺(tái)編譯只是針對(duì)Go代碼部分,它是Go的交叉編譯器(cross-compiler toolchains)饲嗽。當(dāng)我們使用了CGO時(shí)炭玫,要想實(shí)現(xiàn)跨平臺(tái)編譯,同時(shí)需要讓C/C++代碼也支持跨平臺(tái)貌虾。
package main
/*
#include <stdio.h>
void printint(int v) {
printf("printint: %d\n", v);
}
*/
import "C"
func main() {
v := 42
C.printint(C.int(v))
}
小菜刀的開(kāi)發(fā)機(jī)器:amd64架構(gòu)吞加,darwin系統(tǒng)。目標(biāo)編譯平臺(tái):amd64架構(gòu)尽狠,linux系統(tǒng)∠魏現(xiàn)想將上述含有CGO的代碼編譯為目標(biāo)平臺(tái)的可執(zhí)行文件。
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o main main.go
通過(guò)以上命令袄膏,得到編譯錯(cuò)誤如下
/usr/local/go/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
ld: 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 )
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
可以看到践图,由于CGO的存在,跨平臺(tái)編譯失敗沉馆。那該如何解決呢码党?
其實(shí)思路可以很簡(jiǎn)單:和Go一樣,當(dāng)我們擁有目標(biāo)平臺(tái)的C/C++代碼翻譯系統(tǒng)后悍及,自然就能夠編譯為目標(biāo)平臺(tái)的可執(zhí)行文件闽瓢。
Mac下的可行方案
下載linux編譯工具鏈
brew install FiloSottile/musl-cross/musl-cross
或者windows編譯工具鏈
brew install mingw-w64
以linux編譯工具鏈為例。在下載完畢后心赶,/usr/local/bin下會(huì)存在以下對(duì)應(yīng)平臺(tái)C/C++編譯器
x86_64-linux-musl-addr2line x86_64-linux-musl-elfedit x86_64-linux-musl-gcov x86_64-linux-musl-objcopy
x86_64-linux-musl-ar x86_64-linux-musl-g++ x86_64-linux-musl-gcov-dump x86_64-linux-musl-objdump
x86_64-linux-musl-as x86_64-linux-musl-gcc x86_64-linux-musl-gcov-tool x86_64-linux-musl-ranlib
x86_64-linux-musl-c++ x86_64-linux-musl-gcc-9.2.0 x86_64-linux-musl-gprof x86_64-linux-musl-readelf
x86_64-linux-musl-c++filt x86_64-linux-musl-gcc-ar x86_64-linux-musl-ld x86_64-linux-musl-size
x86_64-linux-musl-cc x86_64-linux-musl-gcc-nm x86_64-linux-musl-ld.bfd x86_64-linux-musl-strings
x86_64-linux-musl-cpp x86_64-linux-musl-gcc-ranlib x86_64-linux-musl-nm x86_64-linux-musl-strip
上述指定下載命令只下載了x86_64體系下的編譯器扣讼,但其實(shí)并不止這些∮Ы校可通過(guò)brew info musl-cross
命令進(jìn)行查看椭符。
$ brew info musl-cross
filosottile/musl-cross/musl-cross: stable 0.9.9 (bottled), HEAD
Linux cross compilers based on musl libc
https://github.com/richfelker/musl-cross-make
/usr/local/Cellar/musl-cross/0.9.9 (1,851 files, 245.8MB) *
Poured from bottle on 2020-11-16 at 17:09:31
From: https://github.com/filosottile/homebrew-musl-cross/blob/master/musl-cross.rb
==> Dependencies
Build: gnu-sed ?, make ?
==> Options
--with-aarch64
Build cross-compilers targeting arm-linux-muslaarch64
--with-arm
Build cross-compilers targeting arm-linux-musleabi
--with-arm-hf
Build cross-compilers targeting arm-linux-musleabihf
--with-i486
Build cross-compilers targeting i486-linux-musl
--with-mips
Build cross-compilers targeting mips-linux-musl
--with-mips64
Build cross-compilers targeting mips64-linux-musl
--with-mips64el
Build cross-compilers targeting mips64el-linux-musl
--with-mipsel
Build cross-compilers targeting mipsel-linux-musl
--without-x86_64
Do not build cross-compilers targeting x86_64-linux-musl
--HEAD
Install HEAD version
此時(shí),通過(guò)指定C/C++編譯器為/usr/local/bin/x86_64-linux-musl-gcc
耻姥,替換默認(rèn)的C/C++編譯器(本機(jī)編譯销钝,可通過(guò)go env CC
查看),即可完成含有CGO的Go代碼交叉編譯任務(wù)琐簇。
$ 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
最終蒸健,在本機(jī)mac系統(tǒng)上就編譯得到了amd64 linux平臺(tái)的可執(zhí)行文件座享。
Docker解決方案
在小菜刀通過(guò)上述方式完成cgo的跨平臺(tái)編譯之余,找到了另外一種可行方案:基于Docker容器的xgo打包工具似忧。
它的實(shí)現(xiàn)也很簡(jiǎn)單:將多平臺(tái)所需要的Go工具鏈渣叛,C/C++交叉編譯器和頭文件/庫(kù)都組裝到Docker容器中(因此,在鏡像拉取時(shí)盯捌,會(huì)下載大量的依賴(lài)資源)淳衙,再借助xgo打包工具實(shí)現(xiàn)跨平臺(tái)編譯。
Docker安裝(省略)
拉取鏡像
docker pull karalabe/xgo-latest
- 打包工具安裝
go get github.com/karalabe/xgo
輕量級(jí)的命令包裝器饺著,它的作用就是簡(jiǎn)化復(fù)雜的Docker命令箫攀。
- 跨平臺(tái)編譯
指定要編譯的導(dǎo)入路徑即可,其余工作由xgo完成幼衰。在本例中靴跛,代碼位置位于$GOPATH/src/workspace/example/cgoDemo2/
xgo $GOPATH/src/workspace/example/cgoDemo2/
編譯之后,本目錄下會(huì)存在以下各平臺(tái)可執(zhí)行文件
$ ls -al
total 44960
drwxr-xr-x 23 slp staff 736 Nov 17 11:43 .
drwxr-xr-x 39 slp staff 1248 Nov 16 17:59 ..
-rwxr-xr-x 1 slp staff 1761872 Nov 17 11:42 cgoDemo2-android-16-386
drwxr-xr-x 5 slp staff 160 Nov 17 11:42 cgoDemo2-android-16-aar
-rwxr-xr-x 1 slp staff 1778464 Nov 17 11:42 cgoDemo2-android-16-arm
-rwxr-xr-x 1 slp staff 902436 Nov 17 11:43 cgoDemo2-darwin-10.6-386
-rwxr-xr-x 1 slp staff 1053816 Nov 17 11:43 cgoDemo2-darwin-10.6-amd64
-rwxr-xr-x 1 slp staff 1065232 Nov 17 11:43 cgoDemo2-ios-5.0-arm64
-rwxr-xr-x 1 slp staff 978016 Nov 17 11:43 cgoDemo2-ios-5.0-armv7
drwxrwxrwx 3 slp staff 96 Nov 17 11:43 cgoDemo2-ios-5.0-framework
-rwxr-xr-x 1 slp staff 1084208 Nov 17 11:42 cgoDemo2-linux-386
-rwxr-xr-x 1 slp staff 1226072 Nov 17 11:42 cgoDemo2-linux-amd64
-rwxr-xr-x 1 slp staff 1093728 Nov 17 11:42 cgoDemo2-linux-arm-5
-rwxr-xr-x 1 slp staff 1074348 Nov 17 11:43 cgoDemo2-linux-arm-6
-rwxr-xr-x 1 slp staff 1073800 Nov 17 11:43 cgoDemo2-linux-arm-7
-rwxr-xr-x 1 slp staff 1196520 Nov 17 11:43 cgoDemo2-linux-arm64
-rwxr-xr-x 1 slp staff 1152088 Nov 17 11:43 cgoDemo2-linux-mips
-rwxr-xr-x 1 slp staff 1274272 Nov 17 11:43 cgoDemo2-linux-mips64
-rwxr-xr-x 1 slp staff 1271464 Nov 17 11:43 cgoDemo2-linux-mips64le
-rwxr-xr-x 1 slp staff 1148892 Nov 17 11:43 cgoDemo2-linux-mipsle
-rwxr-xr-x 1 slp staff 1712214 Nov 17 11:43 cgoDemo2-windows-4.0-386.exe
-rwxr-xr-x 1 slp staff 2115121 Nov 17 11:43 cgoDemo2-windows-4.0-amd64.exe
-rw-r--r-- 1 slp staff 157 Nov 16 16:51 main.go
默認(rèn)情況下渡嚣,xgo會(huì)嘗試編譯Go運(yùn)行時(shí)所支持的所有平臺(tái)汤求。如果我們只想構(gòu)建特定的幾個(gè)目標(biāo)系統(tǒng),可以使用逗號(hào)分隔的--targets
選項(xiàng)控制严拒,例如--targets=windows/amd64,linux/amd64
代表編譯目標(biāo)僅包括amd64架構(gòu)的windows和linux平臺(tái)。
$ xgo --targets=windows/amd64,linux/amd64 $GOPATH/src/workspace/example/cgoDemo2/
目前支持的平臺(tái)列表如下
操作系統(tǒng):
android
,darwin
,ios
,linux
,windows
架構(gòu):
386
,amd64
,arm-5
,arm-6
,arm-7
,arm64
,mips
,mipsle
,mips64
,mips64le
xgo提供了比較靈活的編譯方案竖独,通過(guò)$ xgo -h
查看選項(xiàng)信息裤唠,更多詳情可點(diǎn)擊文末的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