| // 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" | |
| ) | |
| // exported from runtime | |
| func 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 module containing the main package | |
| 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" | |
| ) | |
| readEntryFirstLine := func(elem []string) (Module, bool) { | |
| if len(elem) != 2 && len(elem) != 3 { | |
| return Module{}, false | |
| } | |
| sum := "" | |
| if len(elem) == 3 { | |
| sum = elem[2] | |
| } | |
| return Module{ | |
| Path: elem[0], | |
| Version: elem[1], | |
| Sum: sum, | |
| }, true | |
| } | |
| var ( | |
| info = &BuildInfo{} | |
| last *Module | |
| line string | |
| ok bool | |
| ) | |
| // 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") | |
| last = &info.Main | |
| *last, ok = readEntryFirstLine(elem) | |
| if !ok { | |
| return nil, false | |
| } | |
| case strings.HasPrefix(line, depLine): | |
| elem := strings.Split(line[len(depLine):], "\t") | |
| last = new(Module) | |
| info.Deps = append(info.Deps, last) | |
| *last, ok = readEntryFirstLine(elem) | |
| if !ok { | |
| return nil, false | |
| } | |
| case strings.HasPrefix(line, repLine): | |
| elem := strings.Split(line[len(repLine):], "\t") | |
| if len(elem) != 3 { | |
| return nil, false | |
| } | |
| if last == nil { | |
| return nil, false | |
| } | |
| last.Replace = &Module{ | |
| Path: elem[0], | |
| Version: elem[1], | |
| Sum: elem[2], | |
| } | |
| last = nil | |
| } | |
| } | |
| return info, true | |
| } |