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

huangapple go评论88阅读模式
英文:

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

问题

例如,https://github.com/golang/sys/blob/55b11dcdae8194618ad245a452849aa95e461114/cpu/cpu_gccgo_x86.go#L5-L9:

//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

英文:

For example, https://github.com/golang/sys/blob/55b11dcdae8194618ad245a452849aa95e461114/cpu/cpu_gccgo_x86.go#L5-L9:

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

package cpu

In my eyes, as a build tag, // +build ... can work well. Why is //go:build still explicitly specified?

By the way, it is hard to find the documentation for //go:build, but it was easy for // +build, https://pkg.go.dev/cmd/go#hdr-Build_constraints.

答案1

得分: 76

Go 1.18

新的指令//go:build现在更受推荐,工具链将主动删除旧的指令;正如Go 1.18发布说明中所提到的:

> 在Go 1.18中,go fix现在会删除在其go.mod文件中声明了go 1.18或更高版本的模块中的已过时的// +build行。

由于上述原因,如果你尝试构建一个具有go.mod版本为1.17或更低版本的模块,并且该模块依赖于1.18或更高版本的依赖项,如果依赖项缺少// +build行,构建可能会失败。

Go 1.17

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

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

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

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

> Go 1.N将开始过渡。在Go 1.N中:
>
> - 构建将开始优先选择//go:build行进行文件选择。如果文件中没有//go:build,则任何// +build行仍然适用。
>
> - 如果Go文件包含了//go:build而没有// +build,构建将不再失败。
>
> - 如果Go或汇编文件中的//go:build出现得太晚,构建将失败。Gofmt将把错位的//go:build和// +build行移动到文件中的正确位置。
>
> - Gofmt将使用与其他Go布尔表达式相同的规则格式化//go:build行中的表达式(在所有&&||运算符周围添加空格)。
>
> - 如果一个文件只包含// +build行,gofmt将在它们上面添加一个等效的//go:build行。
>
> - 如果一个文件同时包含//go:build// +build行,gofmt将认为//go:build是真实的来源,并更新// +build行以匹配,以保持与早期版本的Go的兼容性。Gofmt还将拒绝被认为太复杂而无法转换为// +build格式的//go:build行,尽管这种情况很少见。(请注意此条目开头的“如果”。Gofmt不会向只有//go:build的文件中添加// +build行。)
>
> - go vet中的buildtags检查将添加对//go:build约束的支持。当Go源文件包含具有不同含义的//go:build// +build行时,检查将失败。如果检查失败,可以运行gofmt -w
>
> - 当Go源文件包含//go:build而没有// +build,并且其包含模块在go行中列出了Go 1.N之前的版本时,buildtags检查也将失败。如果检查失败,可以添加任何// +build行,然后运行gofmt -w,它将用正确的行替换它们。或者可以将go.mod的go版本提升到Go 1.N。

有关语法更改的更多信息:Golang条件编译

英文:

Go 1.18

The new directive //go:build is now preferred and the toolchain will actively remove old directives; as mentioned in Go 1.18 release notes:

> 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.

Due to the above, if you attempt to build a module with go.mod at 1.17 or lower that requires a dependency at 1.18 or above, the build may fail if the dependency is missing // +build lines.

Go 1.17

//go:build is the new conditional compilation directive used to specify build constraints. It was introduced in Go 1.17.

It is meant to replace the old // +build directives; the use case is still same: it "lists the conditions under which a file should be included in the package". The new syntax brings a few key improvements:

  • consistency with other existing Go directives and pragmas, e.g. //go:generate
  • support for standard boolean expression, e.g. //go:build foo && bar, whereas the old // +build comment has less intuitive syntax. For example AND was expressed with commas // +build foo,bar and OR with spaces // +build foo bar
  • it's supported by go fmt, which will automatically fix incorrect placement of the directive in source files, thus avoiding common mistakes as not leaving a blank line between the directive and the package statement.

The two build directives will coexist over a few Go releases in order to ensure a smooth transition, as outlined in the relevant proposal document (in the quote below N is 17, emphasis mine):

> 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.

More info about syntax changes: Golang conditional compilation

huangapple
  • 本文由 发表于 2021年7月13日 18:27:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/68360688.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定