交叉编译只是为了能在一个平台上编译出其他平台可运行的程序,Go 作为一个跨平台的语言,它提供的类库势必也是跨平台的,比如说程序的系统调用相关的功能,能根据所处环境选择对应的源码进行编译。让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃,这就是另外一个概念叫—条件编译。
在 Go 中,也称之为Build Constraints 编译约束,添加编译约束的方式有2种分别:
- 编译标签(build tag)
- 文件后缀
编译标签
编译标签是一种通过在源码文件顶部添加注释,来决定文件是否参与编译的约束方式。其格式如下:
// +build <tags>
注意: // +build 的下一行必须是空行,否则会被解析为包注释。
// +build linux
// main package comment
package main
tags说明:
- 以空格分开表示 AND
- 以逗号分开表示 OR
- !表示 NOT
标签可以指定为以下内容:
- 操作系统,环境变量中 GOOS 的值,如: linux 、 darwin 、 windows 等等。
- 操作系统的架构,环境变量中 GOARCH 的值,如: arch64 、 x86 、 i386 等等。
- 使用的编译器, gc 或者 gccgo 。
- 是否开启CGO, cgo 。
- golang版本号:比如Go Version 1.1为 go1.1 ,Go Version 1.12版本为 go1.12 ,以此类推。
- 其它自定义标签,通过 go build -tags 指定的值。
例如,编译条件为 (linux AND 386) OR (darwin AND (NOT cgo))
// +build linux,386 darwin,!cgo
另外一个文件可以有多个编译约束,比如条件为 (linux OR darwin) AND amd64
// +build linux darwin
// +build amd64
也可以使用 ignore 标签将一个文件从编译中排除。
// +build ignore
文件后缀
除了编译标签,第二种添加编译约束的方法是通过源码文件的文件名实现的,这种方案比构造标签方案更简单。编译器也会根据文件后缀来自动选择编译文件:
$filename_$GOOS.go
$filename_$GOARCH.go
$filename_$GOOS_$GOARCH.go
$filename : 源文件名称。
$GOOS : 表示操作系统,从环境变量中获取。
$GOARCH : 表示系统架构,从环境变量中获取。
后缀的顺序记住不要颠倒,后缀中同时出现系统和架构名时,需要保持 $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
go:build 与 +build 的区别
//go:build
是 Go 1.17 中引入的新条件编译指令格式。它旨在替换
// +build
指令。为何要采用新的格式呢?
对比一下新旧格式的区别就知道了:
//go:build linux && amd64 || darwin
// +build linux,amd64 darwin
注意://go:build之间没有空格;// +build之间有空格;
显而易见的优势:
- go:build 这种格式,对 coder 来说,更容易理解其逻辑组合
- 与 //go:embed 和 //go:generate 这些命令相比较,格式上进行了统一
错误检查
用go vet命令也可以检测到这个缺少空行的错误,初期可以用这个命令来避免缺少空行的错误
go vet mypkg
mypkg.go:1: +build comment appears too late in file
exit status 1
查看环境
go env
参考
-
Go build tag 实现条件编译
-
条件编译
-
由一条编译报错信息,引出两个Go编译的重要知识
-
golang 编译约束 //go:build dev // +build命令,自定义文件后缀编译