1
0
mirror of https://github.com/golang/go synced 2024-11-13 18:20:32 -07:00

runtime/debug: add API to read module info in binary

When module is enabled, the go tool embeds build information
related to the module in the binary including the dependencies
and the replace information (See
src/cmd/go/internal/modload.PackageBuildInfo).

The newly introduced ReadBuildInfo reads the information and
makes it accessible programmatically.

Update #26404

Change-Id: Ide37022d609b4a8fb6b5ce02afabb73f04fbb532
Reviewed-on: https://go-review.googlesource.com/c/144220
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Hana Kim 2018-10-23 17:00:29 -04:00 committed by Hyang-Ah Hana Kim
parent 978cfa8e46
commit 45e9c5538b
2 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,40 @@
# Test to ensure runtime/debug.ReadBuildInfo parses
# the modinfo embedded in a binary by the go tool
# when module is enabled.
env GO111MODULE=on
cd x
go mod edit -require=rsc.io/quote@v1.5.2
go mod edit -replace=rsc.io/quote@v1.5.2=rsc.io/quote@v1.0.0
go run main.go
stderr 'Hello, world.'
stderr 'mod\s+x\s+\(devel\)'
stderr 'dep\s+rsc.io/quote\s+v1.5.2\s+'
stderr '=>\s+rsc.io/quote\s+v1.0.0\s+h1:'
-- x/go.mod --
module x
-- x/main.go --
package main
import "runtime/debug"
import "rsc.io/quote"
func main() {
println(quote.Hello())
m, ok := debug.ReadBuildInfo()
if !ok {
panic("failed debug.ReadBuildInfo")
}
println("mod", m.Main.Path, m.Main.Version)
for _, d := range m.Deps {
println("dep", d.Path, d.Version, d.Sum)
if r := d.Replace; r != nil {
println("=>", r.Path, r.Version, r.Sum)
}
}
}

105
src/runtime/debug/mod.go Normal file
View File

@ -0,0 +1,105 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package debug
import (
"strings"
)
// set using cmd/go/internal/modload.ModInfoProg
var modinfo string
// ReadBuildInfo returns the build information embedded
// in the running binary. The information is available only
// in binaries built with module support.
func ReadBuildInfo() (info *BuildInfo, ok bool) {
return readBuildInfo(modinfo)
}
// BuildInfo represents the build information read from
// the running binary.
type BuildInfo struct {
Path string // The main package path
Main Module // The main module information
Deps []*Module // Module dependencies
}
// Module represents a module.
type Module struct {
Path string // module path
Version string // module version
Sum string // checksum
Replace *Module // replaced by this module
}
func readBuildInfo(data string) (*BuildInfo, bool) {
if len(data) < 32 {
return nil, false
}
data = data[16 : len(data)-16]
const (
pathLine = "path\t"
modLine = "mod\t"
depLine = "dep\t"
repLine = "=>\t"
)
info := &BuildInfo{}
var line string
// Reverse of cmd/go/internal/modload.PackageBuildInfo
for len(data) > 0 {
i := strings.IndexByte(data, '\n')
if i < 0 {
break
}
line, data = data[:i], data[i+1:]
switch {
case strings.HasPrefix(line, pathLine):
elem := line[len(pathLine):]
info.Path = elem
case strings.HasPrefix(line, modLine):
elem := strings.Split(line[len(modLine):], "\t")
if len(elem) != 3 {
return nil, false
}
info.Main = Module{
Path: elem[0],
Version: elem[1],
Sum: elem[2],
}
case strings.HasPrefix(line, depLine):
elem := strings.Split(line[len(depLine):], "\t")
if len(elem) != 2 && len(elem) != 3 {
return nil, false
}
sum := ""
if len(elem) == 3 {
sum = elem[2]
}
info.Deps = append(info.Deps, &Module{
Path: elem[0],
Version: elem[1],
Sum: sum,
})
case strings.HasPrefix(line, repLine):
elem := strings.Split(line[len(repLine):], "\t")
if len(elem) != 3 {
return nil, false
}
last := len(info.Deps) - 1
if last < 0 {
return nil, false
}
info.Deps[last].Replace = &Module{
Path: elem[0],
Version: elem[1],
Sum: elem[2],
}
}
}
return info, true
}