英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论