Commit 45e9c553 authored by Hana Kim's avatar Hana Kim Committed by Hyang-Ah Hana Kim

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: 's avatarRuss Cox <rsc@golang.org>
parent 978cfa8e
# 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)
}
}
}
// 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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment