之前学习 golang 只是想了解一下这个新的热门语言,并没有深究,也没有机会在工作中使用。
今天刚才遇到一个关于 golang 交叉编译的问题。
需求是这样的,filebeat 官方没有支持 AIX 系统,领导想试试能不能自己编译出一个 AIX 系统的字节码文件,把 firebeat 安装在 AIX 上。
我之前学习 golang 的时候有看过关于 golang 编译出来的可执行文件内的库是静态连接,把库包含在了可执行文件里,所以编译出来的文件比较大。
刚好 filebeat 是用 golang 编写的,那么既然库都是静态链接的,那应该支持交叉编译,比如在 windows 环境编译一个 linux 环境的可执行文件。
命令
网上搜了一下,golang 果然是支持交叉编译的,主要是用到两个环境变量GOOS
和GOARCH
。
GOOS
表示编译的目标操作系统,当前版本可选值有:aix
、android
、darwin
、dragonfly
、freebsd
、hurd
、illumos
、js
、linux
、nacl
、netbsd
、openbs
、plan9
、solaris
、windows
、zos
GOARCH
表示编译的目标的 CPU 指令集,当前版本可选值有:386
、amd64
、amd64p32
、arm
、armbe
、arm64
、arm64be
、ppc64
、ppc64le
、mips
、mipsle
、mips64
、mips64le
、mips64p32
、mips64p32le
、ppc
、riscv
、riscv64
、s390
、s390x
、sparc
、sparc64
、wasm
具体的可选值可以在这里看到。
所以,如果想把一个项目编译为非当然系统的可执行文件,比如linux
amd64
时,可以执行以下命令
1 | $ env GOOS=linux GOARCH=amd64 go build |
代码
从上面可以看到 golang 可以通过命令和环境变量去交叉编译指定环境的可执行文件,那么就会有一个问题,比如我想在不同的环境编译不同代码该怎么办呢?虽然 golang 有os
库,但是不可能在每一个需要区分 OS 或者 ARCH 的地方都用runtime.GOOS
和runtime.GOARCH
通过 if-else 判断区分。
所以 golang 在编译时可以通过文件名限制文件适用于哪个系统和哪个指令集,<name>[_GOOS][_GOARCH].<extension>
。
比如文件名为gccgo_linux_amd64.go
的文件只会在当GOOS=linux
且GOARCH=amd64
时才会编译此文件,其他情况下编译默认的gccgo.go
文件。
文件名条件编译只支持单个 OS,单个 ARCH 和 OS/ARCH 组合,但是比如同时有多个 OS 或者多个 OS/ARCH 组合都需要编译同样内容时,创建多个相同内容的文件显然不太合理,这时要用到 golang 的一种注释,好像叫魔法注释
。
1 | // +build linux,386 darwin,!cgo |
格式像以上代码块,魔法注释的要求是必须在文件的真实代码之前,在魔法注释前允许有其他注释或空白。
其中逗号相当于AND
,空格相当于OR
,叹号相当于NOT
,这样就可以匹配多种环境。
注意:一旦文件包含魔法注释,在编译阶段魔法注释优先级高于文件命名规则。