`//go:build` 和 `// +build` 指令有什么区别?

What's the difference between `//go:build` and `// +build` directives?

例如,https://github.com/golang/sys/blob/master/cpu/cpu_gccgo_x86.go#L5

//go:build (386 || amd64 || amd64p32) && gccgo
// +build 386 amd64 amd64p32
// +build gccgo

package cpu

在我看来,作为构建标签,// +build ... 可以很好地工作。
为什么 //go:build 仍然明确指定?

顺便说一下,很难找到 //go:build 的手册,但是 // +build 很容易(https://pkg.go.dev/cmd/go#hdr-Build_constraints)

Go 1.18

现在首选新指令 //go:build,工具链将主动删除旧指令;如 Go 1.18 发行说明中所述:

In Go 1.18, go fix now removes the now-obsolete // +build lines in modules declaring go 1.18 or later in their go.mod files.

由于上述原因,如果您尝试使用 go.mod 1.17 或更低版本构建一个需要 1.18 或更高版本依赖项的模块,如果缺少依赖项 // +build,构建可能会失败行。

Go 1.17

//go:build 是用于指定 build constraints 的新条件编译指令。它是在 Go 1.17 中引入的。

它旨在取代旧的 // +build 指令;用例仍然相同:它“列出了文件应包含在包中的条件”。新语法带来了一些关键改进:

  • 与其他现有的 Go 指令和编译指示一致,例如//go:generate
  • 支持标准布尔表达式,例如//go:build foo && bar,而旧的 // +build 注释语法不太直观。例如,AND 用逗号 // +build foo,bar 表示,OR 用空格 // +build foo bar
  • go fmt 支持它,它会自动修复指令在源文件中的错误位置,从而避免 common mistakes 在指令和包语句之间不留空行。

这两个构建指令 将在几个 Go 版本中共存 以确保平稳过渡,如相关提案文档中的 outlined (在下面的引用中N是17,强调我的):

Go 1.N would start the transition. In Go 1.N:

  • Builds will start preferring //go:build lines for file selection. If there is no //go:build in a file, then any // +build lines still apply.

  • Builds will no longer fail if a Go file contains //go:build without // +build.

  • Builds will fail if a Go or assembly file contains //go:build too late in the file. Gofmt will move misplaced //go:build and // +build lines to their proper location in the file.

  • Gofmt will format the expressions in //go:build lines using the same rules as for other Go boolean expressions (spaces around all && and || operators).

  • If a file contains only // +build lines, gofmt will add an equivalent //go:build line above them.

  • If a file contains both //go:build and // +build lines, gofmt will consider the //go:build the source of truth and update the // +build lines to match, preserving compatibility with earlier versions of Go. Gofmt will also reject //go:build lines that are deemed too complex to convert into // +build format, although this situation will be rare. (Note the “If” at the start of this bullet. Gofmt will not add // +build lines to a file that only has //go:build.)

  • The buildtags check in go vet will add support for //go:build constraints. It will fail when a Go source file contains //go:build and // +build lines with different meanings. If the check fails, one can run gofmt -w.

  • The buildtags check will also fail when a Go source file contains //go:build without // +build and its containing module has a go line listing a version before Go 1.N. If the check fails, one can add any // +build line and then run gofmt -w, which will replace it with the correct ones. Or one can bump the go.mod go version to Go 1.N.

有关语法更改的更多信息:Golang conditional compilation