最近考察了一家APM厂商的Go探针程序,据说通过引入一个包,可以将探针引入到项目中,整个过程无需更改其他代码。 没想到,当我第一次引入包并尝试创建它时,它发生了冲突。
main.go:10:2: build constraints exclude all Go files in /xxx/github.com/xxx/agnet/xxxx
编译器在编译时直接排除某个包下的所有文件。 这是什么意思? 该包下没有可以在当前构建环境中创建的Go文件。 推测该包源码的创建标签注明不允许在Mac环境下创建。 我打开源代码看了看。 确实,所有文件的创建标签都是这样声明的。
// +build linux
// +build amd64
这称为条件编译或约束编译。 那么如果我想在Mac下编译一个可以在Linux上运行的可执行文件该怎么办呢? Go还支持一个称为交叉编译的功能,用于跨平台编译。 具体如何使用呢? 例如,在这种情况下,您需要编译一个可以在Mac环境下的Linux系统的amd64架构下运行的可执行文件。 你必须编译它:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
但后来想研究一下为什么在Mac上不允许编译。 我看到这个包的probe是使用CGO实现的,在Linux系统下调用了一个用C语言实现的工具命令。 看到这里,我已经不想再继续研究这个包了,所以为了让这篇文章不那么明显:)编译源码包代码 linux,我们就Go语言的交叉编译和条件编译这两个知识点重新准备考试吧。
交叉编译
交叉编译用于在一个平台上为另一个平台生成可执行程序。 Go的命令集原生支持交叉编译,使用方法也非常简单,比如之前已经演示过的。
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
参数说明
GOARCH:目标平台的系统架构[386、amd64、arm]。 目前市场上的个人电脑通常都是amd64架构。
了解了这些参数后,我们来看一下 Mac、Linux、Windows 三个平台上交叉编译的例子。 由于家庭条件不好,我还没有尝试过Windows。 我在网上找到了这个命令。 如有错误,还请在评论中留言,帮助我改正。
Mac下编译,Linux或Windows下可执行程序
# linux可执行程序
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
# Windows 可执行程序
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
在Linux下编译并在Mac或Windows下执行。
# Mac 平台可执行程序
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
# Windows可执行程序
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
在 Windows、Mac 或 Linux 下执行
需要写一个批处理程序并在里面进行设置,因为Windows下的终端不支持shell,这一点与Mac和Linux有点不同。
# Mac 下执行
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build main.go
# Linux 去执行
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build main.go
条件编译
交叉编译只是在一个平台上编译出可以在其他平台上运行的程序。 作为跨平台的语言,它提供的解释器也必须是跨平台的。 例如,程序的系统调用相关功能可以根据所有环境选择相应的源代码进行编译。 让编译器只编译符合条件的代码,抛弃不符合条件的代码。 这是另一个概念编译源码包代码 linux,称为条件编译。
在 Go 中,它也称为构建约束。 添加编译约束有两种方法:
编译标签
编译标签是一种约束形式,它在源代码文件的底部添加注释来确定文件是否参与编译。 其格式如下:
// +build
注意: // +build 的下一行必须是空行,否则会被解析为包注释。
// +build linux
// main package comment
package main
标签说明:
标签可以指定为:
例如编译条件为 (linux AND 386) OR (darwin AND (NOT cgo))
// +build linux,386 darwin,!cgo
另一个文件可以有多个编译约束,例如 (linux OR darwin) AND amd64
// +build linux darwin
// +build amd64
您还可以使用忽略标记从编译中排除文件。
// +build ignore
文件扩展名
除了编译标签之外,添加编译约束的第二种方式是通过源代码文件的文件名。 该解决方案比构造标签解决方案更简单。 编译器还会根据文件后缀手动选择编译文件:
$filename_$GOOS.go
$filename_$GOARCH.go
$filename_$GOOS_$GOARCH.go
请记住不要颠倒后缀的顺序。 当系统和架构名称同时出现在后缀中时,需要保持$filename_$GOOS_$GOARCH.go的顺序。
Go 中的每个外部库都有许多以不同系统名称结尾的文件。 以下是Go的os外部库源码的部分截图:
对文件后缀添加编译约束的两种方式如何选择?
构建标签和文件名后缀在功能上重叠。 例如,如果名为 mypkg_linux.go 的文件包含构建标记 // +build linux,则该文件将变得多余。
一般来说,当只需要指定一种特定平台时,我们选择文件名后缀形式。 例如:
mypkg_linux.go // 只在 linux 系统编译
mypkg_windows_amd64.go // 只在 windows amd 64位 平台编译
相反,如果您的文件需要指定在多个平台或架构上使用,或者您需要排除特定平台,我们选择创建标签。 例如:
// 在所有类unix平台编译
// +build darwin dragonfly freebsd linux netbsd openbsd
// 在非Windows平台编译
// +build !windows
总结
编译错误居然导致一篇文章发表不了……啊……(咳)引出了两个很重要的知识点:交叉编译和条件编译(编译约束)。 其实这两个知识点我也很早之前写过一篇文章。 这次相当于从头分析问题,从头开始。 我希望你喜欢它。
参考链接
喜欢明哥文章的朋友