华企号 后端开发 使用golang编译tag

使用golang编译tag

下面是代码片段

func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
if allTags != nil {
allTags[name] = true
}
// special tags
if ctxt.CgoEnabled && name == “cgo” {
return true
}
if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
return true
}
if ctxt.GOOS == “android” && name == “linux” {
return true
}
if ctxt.GOOS == “illumos” && name == “solaris” {
return true
}
if ctxt.GOOS == “ios” && name == “darwin” {
return true
}
if name == “unix” && unixOS[ctxt.GOOS] {
return true
}
if name == “boringcrypto” {
name = “goexperiment.boringcrypto” // boringcrypto is an old name for goexperiment.boringcrypto
}
// other tags
for _, tag := range ctxt.BuildTags {
if tag == name {
return true
}
}
for _, tag := range ctxt.ToolTags {
if tag == name {
return true
}
}
for _, tag := range ctxt.ReleaseTags {
if tag == name {
return true
}
}
return false
}

在源文件src/go/build/syslist.go中可以找到knownOS表示tag中可以填写的OS,以及填写unix标签时匹配的OSunixOS,还有可填写的架构knownArch

package build
// 已知的所有OS型号
var knownOS = map[string]bool{
“aix”: true,
“android”: true,
“darwin”: true,
“dragonfly”: true,
“freebsd”: true,
“hurd”: true,
“illumos”: true,
“ios”: true,
“js”: true,
“linux”: true,
“nacl”: true,
“netbsd”: true,
“openbsd”: true,
“plan9”: true,
“solaris”: true,
“windows”: true,
“zos”: true,
}
// 当使用unix时,在如下这些OS中生效
var unixOS = map[string]bool{
“aix”: true,
“android”: true,
“darwin”: true,
“dragonfly”: true,
“freebsd”: true,
“hurd”: true,
“illumos”: true,
“ios”: true,
“linux”: true,
“netbsd”: true,
“openbsd”: true,
“solaris”: true,
}
// 已知的所有架构
var knownArch = map[string]bool{
“386”: true,
“amd64”: true,
“amd64p32”: true,
“arm”: true,
“armbe”: true,
“arm64”: true,
“arm64be”: true,
“loong64”: true,
“mips”: true,
“mipsle”: true,
“mips64”: true,
“mips64le”: true,
“mips64p32”: true,
“mips64p32le”: true,
“ppc”: true,
“ppc64”: true,
“ppc64le”: true,
“riscv”: true,
“riscv64”: true,
“s390”: true,
“s390x”: true,
“sparc”: true,
“sparc64”: true,
“wasm”: true,
}

BuildTags

BuildTags是编译命令时-tags的参数列表,根据go help build描述-tags为逗号分隔字符串,兼容旧版空格分隔字符串,具体代码: src/cmd/go/internal/work/build.go#tagsFlag

因此go build -tags "a,b,c"这种是新版推荐写法,go build -tags "a b c"这种是旧版兼容写法

ToolTags

ToolTags是初始化时的工具标签,源码位置 src/cmd/go/internal/work/init.go#ToolTags

在源码中我看到ToolTags可以是race,msan,asan,分别对应go build -race,go build -msan``go build -asan,这三种编译参数。最常见的就是race用来检查程序是否存在竞态

ReleaseTags

ReleaseTags是go发布版本标签,源码: src/go/build/build.go#ReleaseTags

根据相关注释发现ReleaseTags最后一个值被认为是当前在用版本。根据这个标签的源码我们发现当使用//go:build go1.18的xxx.go文件时,使用大于等于go1.18版本的go取编译都会匹配成功,也就是说使用go1.19去编译//go:build go1.18的文件也是会成功的。

编译优化

  • 很多人都知道C语言可以在代码里面加上条件编译,认为Go只能基于文件进行条件编译,毕竟一个//go:build xxx将影响一个文件是否能被编译,以及文件命名格式也是影响整个文件是否被编译。
  • 如何做到在某个代码块里面嵌入条件编译呢?实际上Go源码已经有相关方案,可以参考 src/internal/race

race.go内容如下

//go:build race
// +build race
package race
const Enabled = true

norace.go内容如下

//go:build !race
// +build !race
package race
const Enabled = false

然后就会在源码中找到大量 if race.Enabled { ,这种代码,大家都知道执行go build -racerace.Enabled = true那么这部分判断就会执行,否则就不会执行。

我的问题是如果每次运行时都进行这些if判断虽然损耗不了多少性能但却非常不优雅。但实际上Go会在编译时检查确定的判断,当判断为false时这部分代码都会被优化掉,不会编译到可执行程序中。

验证编译优化

package main
const enable = false
func main() {
if enable {
println(“hello word”)
}
}

执行go tool compile -S main.go > a

package main
const enable = true
func main() {
if enable {
println(“hello word”)
}
}

执行go tool compile -S main.go > b

然后比较a,b的结果,可以明显看到当const enable = falseprintln("hello word")里面的常量字符串都不会编译到可执行程序中,所以我们可以放心使用编译优化这个功能完成条件编译。那些编译时就能计算出结果的if表达式大家可以放心编写相关逻辑,这个if是绝对不会在运行时去执行的。

只是麻烦的是代码中出现的函数,在两份条件编译文件里面都必须有声明,可以参考race.gonorace.go这两个文件的写法。

image

总结

有关条件编译的用法网上有很多资料,所以我这里主要研究条件编译原理,以及条件可以填写的所有值,和一些特殊的规则,这样在我编写相关条件时可以更加的心应手

作者: 华企网通王鹏程序员

我是程序员王鹏,热爱互联网软件开发和设计,专注于大数据、数据分析、数据库、php、java、python、scala、k8s、docker等知识总结。 我的座右铭:"业精于勤荒于嬉,行成于思毁于随"
上一篇
下一篇

发表回复

联系我们

联系我们

028-84868647

在线咨询: QQ交谈

邮箱: tech@68v8.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部