go文件第一行build注释有什么作用?

其专业术语为构建约束,其实不止是go文件,也可以运用在其他文件中。一句话总结其作用就是指定文件的编译场景,若满足条件则编译,反之就不会进行编译。
下面进行一个简单的总结,详细内容可以看看官方的设计文档:https://go.googlesource.com/proposal/+/master/design/draft-gobuild.md

什么是构建约束

构建约束(build constraint), 也叫做构建标记(build tag),在源文件中通过注释的方式指定编译环境,若要为不同的编译环境编写不同的go代码,则需要使用构建约束,例如:

1
// +build linux

上述的注释行意思是只有在linux环境下才会编译该文件,否则会忽略。
其编写格式需要注意以下几点:

  • 可以在任何文件源文件中编写
  • 必须在写在文件顶部附近,可以写多行
  • 为了区别package的文档注释,在约束后必须有空行

其支持类型比较广泛,有以下几种:

  • 指定编译的操作系统:如:windows,linux,darwin(对应runtime.GOOS)

  • 指定架构:如:amd64、386(对应runtime.GOARCH)

  • 指定使用的编译器,如:gccgo、gc

  • 指定go版本,如:go1.17、go1.18

  • 自定义tag,编译时通过指定-tags传入的值

    老版本语法

    go1.17之前是使用的老版本语法,1.17之后的支持比较完善了。
    老版本的构建语法为// +build这种形式,可以通过空格、逗号或多行进行组合,例如:

    1
    2
    3
    4
    // +build linux,386

    // 上述构建约束表示的是: linux and 386
    // 逗号表示and, 空格表示or

    比较复杂的写法:

    1
    2
    3
    // +build linux,386 darwin,!cgo

    // 上述构建约束表示的是: (linux and 386) or (darwin and (not cgo))

    也可以分成多行书写:

    1
    2
    3
    4
    // +build linux darwin
    // +build amd64

    // 相当于: (linux or darwin) and amd64

    通过上述可见老版本的写法比较复杂,非常容易出错

    新版本语法

    新版的构建约束使用//go:开头:

    1
    //go:build

    新版本语法有以下几点需要注意:

  • //go:之间不能有空格

  • 使用的是布尔表达式,而不是逗号和空格等

  • 一个文件只能有一行构建语句,而不是像老版那样支持多行

例如linux and 386现在这样写:

1
//go:build linux && 386

新的语法主体未Go spec的EBNF标记:

1
2
3
4
5
6
7
BuildLine      = "//go:build" Expr
Expr = OrExpr
OrExpr = AndExpr { "||" AndExpr }
AndExpr = UnaryExpr { "&&" UnaryExpr }
UnaryExpr = "!" UnaryExpr | "(" Expr ")" | tag
tag = tag_letter { tag_letter }
tag_letter = unicode_letter | unicode_digit | "_" | "."

也就是说,构建标记的语法与当前形式保持不变,但构建标记的组合现在使用Go的||, &&, !运算符和括号完成。