Skip to content

Commit

Permalink
runtime/debug: add API to read module info in binary
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
hyangah committed Nov 13, 2018
1 parent 978cfa8 commit 45e9c55
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/cmd/go/testdata/script/mod_modinfo.txt
Original file line number Diff line number Diff line change
@@ -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 changes: 105 additions & 0 deletions src/runtime/debug/mod.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 45e9c55

Please sign in to comment.