cmd/go: move module build info formatting into runtime/debug
Previously, modload.PackageBuildInfo returned a string containing
information about modules used to build an executable. This string is
embedded in the binary and can be read with debug.ReadBuildInfo or
'go version -m'.
With this change, debug.BuildInfo now has a MarshalText method that
returns a string in the same format as modload.PackageBuildInfo.
Package.load now calls Package.setBuildInfo, which constructs a
debug.BuildInfo, formats it with MarshalText, then sets
Package.Internal.BuildInfo. This is equivalent to what
modload.PackageBuildInfo did.
modload.PackageBuildInfo is deleted, since it's no longer used.
For #37475
Change-Id: I5875a98cb64737637fec2a450ab2ffa7f1805707
Reviewed-on: https://go-review.googlesource.com/c/go/+/353886
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Jay Conrod <jayconrod@google.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/api/next.txt b/api/next.txt
index 1192fc9..cb729ea 100644
--- a/api/next.txt
+++ b/api/next.txt
@@ -1,3 +1,4 @@
+pkg runtime/debug, method (*BuildInfo) MarshalText() ([]byte, error)
pkg syscall (darwin-amd64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
pkg syscall (darwin-amd64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
pkg syscall (darwin-amd64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index a3a8de8..a7428ed 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -21,6 +21,7 @@
pathpkg "path"
"path/filepath"
"runtime"
+ "runtime/debug"
"sort"
"strconv"
"strings"
@@ -1921,9 +1922,8 @@
}
p.Internal.Imports = imports
p.collectDeps()
-
- if cfg.ModulesEnabled && p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 {
- p.Internal.BuildInfo = modload.PackageBuildInfo(pkgPath, p.Deps)
+ if p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 {
+ p.setBuildInfo()
}
// unsafe is a fake package.
@@ -2195,6 +2195,83 @@
}
}
+// setBuildInfo gathers build information, formats it as a string to be
+// embedded in the binary, then sets p.Internal.BuildInfo to that string.
+// setBuildInfo should only be called on a main package with no errors.
+//
+// This information can be retrieved using debug.ReadBuildInfo.
+func (p *Package) setBuildInfo() {
+ setPkgErrorf := func(format string, args ...interface{}) {
+ if p.Error == nil {
+ p.Error = &PackageError{Err: fmt.Errorf(format, args...)}
+ }
+ }
+
+ var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module
+ debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module {
+ dm := &debug.Module{
+ Path: mi.Path,
+ Version: mi.Version,
+ }
+ if mi.Replace != nil {
+ dm.Replace = debugModFromModinfo(mi.Replace)
+ } else {
+ dm.Sum = modfetch.Sum(module.Version{Path: mi.Path, Version: mi.Version})
+ }
+ return dm
+ }
+
+ var main debug.Module
+ if p.Module != nil {
+ main = *debugModFromModinfo(p.Module)
+ }
+
+ visited := make(map[*Package]bool)
+ mdeps := make(map[module.Version]*debug.Module)
+ var q []*Package
+ q = append(q, p.Internal.Imports...)
+ for len(q) > 0 {
+ p1 := q[0]
+ q = q[1:]
+ if visited[p1] {
+ continue
+ }
+ visited[p1] = true
+ if p1.Module != nil {
+ m := module.Version{Path: p1.Module.Path, Version: p1.Module.Version}
+ if p1.Module.Path != main.Path && mdeps[m] == nil {
+ mdeps[m] = debugModFromModinfo(p1.Module)
+ }
+ }
+ q = append(q, p1.Internal.Imports...)
+ }
+ sortedMods := make([]module.Version, 0, len(mdeps))
+ for mod := range mdeps {
+ sortedMods = append(sortedMods, mod)
+ }
+ module.Sort(sortedMods)
+ deps := make([]*debug.Module, len(sortedMods))
+ for i, mod := range sortedMods {
+ deps[i] = mdeps[mod]
+ }
+
+ pkgPath := p.ImportPath
+ if p.Internal.CmdlineFiles {
+ pkgPath = "command-line-arguments"
+ }
+ info := &debug.BuildInfo{
+ Path: pkgPath,
+ Main: main,
+ Deps: deps,
+ }
+ text, err := info.MarshalText()
+ if err != nil {
+ setPkgErrorf("error formatting build info: %v", err)
+ return
+ }
+ p.Internal.BuildInfo = string(text)
+}
+
// SafeArg reports whether arg is a "safe" command-line argument,
// meaning that when it appears in a command-line, it probably
// doesn't have some special meaning other than its own name.
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
index 8a97920..da50743 100644
--- a/src/cmd/go/internal/modload/build.go
+++ b/src/cmd/go/internal/modload/build.go
@@ -5,7 +5,6 @@
package modload
import (
- "bytes"
"context"
"encoding/hex"
"errors"
@@ -336,53 +335,6 @@
return info
}
-// PackageBuildInfo returns a string containing module version information
-// for modules providing packages named by path and deps. path and deps must
-// name packages that were resolved successfully with LoadPackages.
-func PackageBuildInfo(path string, deps []string) string {
- if !Enabled() {
- return ""
- }
- target, _ := findModule(loaded, path)
- mdeps := make(map[module.Version]bool)
- for _, dep := range deps {
- if m, ok := findModule(loaded, dep); ok {
- mdeps[m] = true
- }
- }
- var mods []module.Version
- delete(mdeps, target)
- for mod := range mdeps {
- mods = append(mods, mod)
- }
- module.Sort(mods)
-
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "path\t%s\n", path)
-
- writeEntry := func(token string, m module.Version) {
- mv := m.Version
- if mv == "" {
- mv = "(devel)"
- }
- fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv)
- if r, _ := Replacement(m); r.Path == "" {
- fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m))
- } else {
- fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
- }
- }
-
- if target.Path != "" {
- writeEntry("mod", target)
- }
- for _, mod := range mods {
- writeEntry("dep", mod)
- }
-
- return buf.String()
-}
-
// findModule searches for the module that contains the package at path.
// If the package was loaded, its containing module and true are returned.
// Otherwise, module.Version{} and false are returned.
diff --git a/src/runtime/debug/mod.go b/src/runtime/debug/mod.go
index 05cad61..11f995b 100644
--- a/src/runtime/debug/mod.go
+++ b/src/runtime/debug/mod.go
@@ -5,6 +5,8 @@
package debug
import (
+ "bytes"
+ "fmt"
"strings"
)
@@ -34,6 +36,41 @@
Replace *Module // replaced by this module
}
+func (bi *BuildInfo) MarshalText() ([]byte, error) {
+ buf := &bytes.Buffer{}
+ if bi.Path != "" {
+ fmt.Fprintf(buf, "path\t%s\n", bi.Path)
+ }
+ var formatMod func(string, Module)
+ formatMod = func(word string, m Module) {
+ buf.WriteString(word)
+ buf.WriteByte('\t')
+ buf.WriteString(m.Path)
+ mv := m.Version
+ if mv == "" {
+ mv = "(devel)"
+ }
+ buf.WriteByte('\t')
+ buf.WriteString(mv)
+ if m.Replace == nil {
+ buf.WriteByte('\t')
+ buf.WriteString(m.Sum)
+ } else {
+ buf.WriteByte('\n')
+ formatMod("=>", *m.Replace)
+ }
+ buf.WriteByte('\n')
+ }
+ if bi.Main.Path != "" {
+ formatMod("mod", bi.Main)
+ }
+ for _, dep := range bi.Deps {
+ formatMod("dep", *dep)
+ }
+
+ return buf.Bytes(), nil
+}
+
func readBuildInfo(data string) (*BuildInfo, bool) {
if len(data) < 32 {
return nil, false