package debug
import (
)
func () string
func () ( *BuildInfo, bool) {
:= modinfo()
if len() < 32 {
return nil, false
}
= [16 : len()-16]
, := ParseBuildInfo()
if != nil {
return nil, false
}
.GoVersion = runtime.Version()
return , true
}
type BuildInfo struct {
GoVersion string
Path string
Main Module
Deps []*Module
Settings []BuildSetting
}
type Module struct {
Path string
Version string
Sum string
Replace *Module
}
type BuildSetting struct {
Key, Value string
}
func ( string) bool {
return len() == 0 || strings.ContainsAny(, "= \t\r\n\"`")
}
func ( string) bool {
return strings.ContainsAny(, " \t\r\n\"`")
}
func ( *BuildInfo) () string {
:= new(strings.Builder)
if .GoVersion != "" {
fmt.Fprintf(, "go\t%s\n", .GoVersion)
}
if .Path != "" {
fmt.Fprintf(, "path\t%s\n", .Path)
}
var func(string, Module)
= func( string, Module) {
.WriteString()
.WriteByte('\t')
.WriteString(.Path)
.WriteByte('\t')
.WriteString(.Version)
if .Replace == nil {
.WriteByte('\t')
.WriteString(.Sum)
} else {
.WriteByte('\n')
("=>", *.Replace)
}
.WriteByte('\n')
}
if .Main != (Module{}) {
("mod", .Main)
}
for , := range .Deps {
("dep", *)
}
for , := range .Settings {
:= .Key
if quoteKey() {
= strconv.Quote()
}
:= .Value
if quoteValue() {
= strconv.Quote()
}
fmt.Fprintf(, "build\t%s=%s\n", , )
}
return .String()
}
func ( string) ( *BuildInfo, error) {
:= 1
defer func() {
if != nil {
= fmt.Errorf("could not parse Go build info: line %d: %w", , )
}
}()
var (
= "path\t"
= "mod\t"
= "dep\t"
= "=>\t"
= "build\t"
= "\n"
= "\t"
)
:= func( []string) (Module, error) {
if len() != 2 && len() != 3 {
return Module{}, fmt.Errorf("expected 2 or 3 columns; got %d", len())
}
:= [1]
:= ""
if len() == 3 {
= [2]
}
return Module{
Path: [0],
Version: ,
Sum: ,
}, nil
}
= new(BuildInfo)
var (
*Module
string
bool
)
for len() > 0 {
, , = strings.Cut(, )
if ! {
break
}
switch {
case strings.HasPrefix(, ):
:= [len():]
.Path = string()
case strings.HasPrefix(, ):
:= strings.Split([len():], )
= &.Main
*, = ()
if != nil {
return nil,
}
case strings.HasPrefix(, ):
:= strings.Split([len():], )
= new(Module)
.Deps = append(.Deps, )
*, = ()
if != nil {
return nil,
}
case strings.HasPrefix(, ):
:= strings.Split([len():], )
if len() != 3 {
return nil, fmt.Errorf("expected 3 columns for replacement; got %d", len())
}
if == nil {
return nil, fmt.Errorf("replacement with no module on previous line")
}
.Replace = &Module{
Path: string([0]),
Version: string([1]),
Sum: string([2]),
}
= nil
case strings.HasPrefix(, ):
:= [len():]
if len() < 1 {
return nil, fmt.Errorf("build line missing '='")
}
var , string
switch [0] {
case '=':
return nil, fmt.Errorf("build line with missing key")
case '`', '"':
, := strconv.QuotedPrefix()
if != nil {
return nil, fmt.Errorf("invalid quoted key in build line")
}
if len() == len() {
return nil, fmt.Errorf("build line missing '=' after quoted key")
}
if := [len()]; != '=' {
return nil, fmt.Errorf("unexpected character after quoted key: %q", )
}
, _ = strconv.Unquote()
= [len()+1:]
default:
var bool
, , = strings.Cut(, "=")
if ! {
return nil, fmt.Errorf("build line missing '=' after key")
}
if quoteKey() {
return nil, fmt.Errorf("unquoted key %q must be quoted", )
}
}
var string
if len() > 0 {
switch [0] {
case '`', '"':
var error
, = strconv.Unquote()
if != nil {
return nil, fmt.Errorf("invalid quoted value in build line")
}
default:
=
if quoteValue() {
return nil, fmt.Errorf("unquoted value %q must be quoted", )
}
}
}
.Settings = append(.Settings, BuildSetting{Key: , Value: })
}
++
}
return , nil
}