Golang中debug.BuildInfo的Main.Version显示为devel的问题
Golang中debug.BuildInfo的Main.Version显示为devel的问题
我正在尝试从 debug.BuildInfo 获取“版本”信息:
package main
import (
"fmt"
"runtime/debug"
)
func main() {
bi, ok := debug.ReadBuildInfo()
if !ok {
fmt.Println("not ok")
return
}
fmt.Printf("Version: %s\n", bi.Main.Version)
}
输出:
Version: (devel)
我尝试了以下版本: go1.17.5 go1.18beta1
两个版本我都得到了相同的结果。
是否存在任何方法可以让 Go 编译器将 Main.Version 更新为 git 标签或 go.mod 中的版本(而不使用 -ldflags="-X path.to.variable=value")?
更多关于Golang中debug.BuildInfo的Main.Version显示为devel的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在 Go 1.18beta1 中,只要你的代码位于模块中,并且已在 git 中标记,这应该就能正常工作。
更多关于Golang中debug.BuildInfo的Main.Version显示为devel的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我仍然得到版本“(devel)”
package main
import (
"fmt"
"runtime/debug"
)
func main() {
bi, ok := debug.ReadBuildInfo()
if !ok {
panic("not ok")
}
fmt.Printf("%+v", bi)
}
输出:
&{GoVersion:go1.18beta1 Path:play.com/buildinfo Main:{Path:play.com/buildinfo Version:(devel) Sum: Replace:<nil>} Deps:[] Settings:[{Key:-compiler Value:gc} {Key:CGO_ENABLED Value:0} {Key:GOARCH Value:amd64} {Key:GOOS Value:darwin} {Key:GOAMD64 Value:v1} {Key:vcs Value:git} {Key:vcs.revision Value:bd21e4e6b920b1277fc1cbb3e02612220b0d4989} {Key:vcs.time Value:2022-01-11T14:46:27Z} {Key:vcs.modified Value:false}]}
运行:
git log
commit bd21e4e6b920b1277fc1cbb3e02612220b0d4989 (HEAD -> main, tag: v1.0.0)
Author: Micke
Date: Tue Jan 11 15:46:27 2022 +0100
intial
另请参阅
cmd/go: stamp the pseudo-version in builds generated by go build
标签: Proposal, Proposal-Accepted, modules, Proposal-FinalCommentPeriod
cmd/go 将依赖版本信息嵌入到二进制文件中,这非常有用。从 Go 1.18 开始,cmd/go 还将 VCS 信息嵌入到二进制文件中,这使得它比以前更加有用。
如 #37475 所述,人们使用 -ldflags='-X foo=bar' 将版本信息放入二进制文件,这需要一个额外的构建包装器。cmd/go 的新 VCS 标记功能应该可以减少对外部包装器的需求,但我担心它做得还不够。
在执行 go build 时,主模块的版本信息(即 Go 伪版本意义上的信息)没有被记录下来:
: emerald:ver; go build
: emerald:ver; go version -m hello | grep 'mod.*hello'
mod mgk.ro/hello (devel)
: emerald:ver;
在执行 go install 时,版本信息会按预期被记录:
: emerald:ver; go install robpike.io/ivy@latest
go: downloading robpike.io/ivy v0.1.124
: emerald:ver; go version -m `which ivy` | grep 'mod.*ivy'
mod robpike.io/ivy v0.1.12 h1:qI7dnEiXhorB+za07W6qX3sG+IvBK4EUl38vUHAf53Q=
: emerald:ver;
: emerald:ver;
我担心 cmd/go 的这个限制将继续迫使人们使用设置 -ldflags 的外部构建包装器,这是相当不幸的。
我并不是第一个希望在二进制文件中包含主模块版本信息的人,这已经在各种问题中被提出过,例如 #29814,该问题被标记为 #37475 的重复项,但它实际上并不是重复项,因为 #37475 是关于 VCS 信息的,而 #29814 是关于语义版本控制的。其他人要求此功能的例子还有 mvdan/sh#519 和 https://github.com/golang/go/issues/29228#issuecomment-449554128,其中提出了各种变通方法。
说到变通方法,我所知道的目前唯一有效的变通方法是创建一个本地模块代理并将 GOPROXY 传递给 go install,但这是一个开销极高的变通方法,而且 go install 无论如何也不能替代 go build,因为 go install 在 vendoring 的工作方式以及你可以在 go.mod 中放置什么内容方面有一些相当严格的限制,并且 go install 在交叉编译时不支持控制 GOBIN。
我意识到 Git 标签是一个本地概念,通过执行“错误”的 git 操作,可能会为相同的源代码生成不同的伪版本。对于 Git 的这个缺陷,我恐怕没有任何解决方案或建议,除了注意到即使在这种情况下哈希信息也被正确记录,并且在所有情况下,由于可以访问本地源代码,程序员总是可以执行某些本地操作,这有可能导致版本标签错误。Git 只是更容易意外地发生这种情况,但这种可能性始终存在。
我没有任何统计数据来支持这一点,但根据我的经验,大多数企业源代码都是通过 go build 构建的,而不是 go install,如果 Go 的版本概念能以某种方式通过 go build 进行标记,那就太好了。
这是一个常见问题,debug.BuildInfo 的 Main.Version 字段显示为 (devel) 是因为你的程序不是通过 go install、go build 或 go run 使用模块版本信息构建的。
当使用 go install 安装特定版本时,Main.Version 会自动填充。例如:
# 安装特定版本
go install github.com/example/project@v1.2.3
# 或者从本地模块安装
go install ./cmd/myapp
但如果你直接使用 go build 或 go run 构建当前目录的代码,而没有指定版本,Go 会使用 (devel) 作为占位符。
要获取实际的版本信息,你可以检查 debug.BuildInfo 的 Settings 字段,其中包含了 vcs.revision 和 vcs.time 等信息:
package main
import (
"fmt"
"runtime/debug"
)
func main() {
bi, ok := debug.ReadBuildInfo()
if !ok {
fmt.Println("not ok")
return
}
fmt.Printf("Main Version: %s\n", bi.Main.Version)
// 检查构建设置中的版本控制信息
for _, setting := range bi.Settings {
switch setting.Key {
case "vcs.revision":
fmt.Printf("Git Commit: %s\n", setting.Value)
case "vcs.time":
fmt.Printf("Commit Time: %s\n", setting.Value)
case "vcs.modified":
fmt.Printf("Modified: %s\n", setting.Value)
}
}
}
要确保版本信息被正确设置,你需要在构建时启用版本控制信息。从 Go 1.18 开始,这变得更加简单:
# Go 1.18+ 自动包含版本控制信息
go build -o myapp
# 或者显式启用
go build -ldflags="-buildvcs=true" -o myapp
# 对于 Go 1.17,需要显式启用
go build -ldflags="-buildvcs=true" -o myapp
如果你使用 go install 安装带有版本标签的模块,Main.Version 会被正确设置:
# 假设你的模块有 v1.0.0 标签
go install github.com/your/project@v1.0.0
然后运行程序时,Main.Version 会显示为 v1.0.0。
另外,你也可以直接从 go.mod 文件中读取版本信息作为备选方案:
package main
import (
"fmt"
"io/ioutil"
"runtime/debug"
"strings"
)
func getVersionFromGoMod() string {
data, err := ioutil.ReadFile("go.mod")
if err != nil {
return ""
}
lines := strings.Split(string(data), "\n")
if len(lines) > 0 && strings.HasPrefix(lines[0], "module ") {
// 解析模块路径和版本
for _, line := range lines {
if strings.Contains(line, "// indirect") {
continue
}
// 这里可以添加更复杂的解析逻辑
}
}
return ""
}
func main() {
bi, ok := debug.ReadBuildInfo()
if !ok {
return
}
version := bi.Main.Version
if version == "(devel)" {
// 尝试从其他来源获取版本
for _, setting := range bi.Settings {
if setting.Key == "vcs.revision" && setting.Value != "" {
version = setting.Value[:8] // 使用前8位提交哈希
break
}
}
}
fmt.Printf("Version: %s\n", version)
}
总结:(devel) 是正常行为,表示开发构建。要获取具体的版本信息,需要使用 go install 安装带标签的版本,或者从 Settings 字段中提取版本控制信息。

