//
// Usage:
//
-// go version [-m] [-v] [file ...]
+// go version [-m] [-v] [-json] [file ...]
//
// Version prints the build information for Go binary files.
//
// information consists of multiple lines following the version line, each
// indented by a leading tab character.
//
+// The -json flag is similar to -m but outputs the runtime/debug.BuildInfo in JSON format.
+// If flag -json is specified without -m, go version reports an error.
+//
// See also: go doc runtime/debug.BuildInfo.
//
// # Report likely mistakes in packages
import (
"context"
"debug/buildinfo"
+ "encoding/json"
"errors"
"fmt"
"io/fs"
)
var CmdVersion = &base.Command{
- UsageLine: "go version [-m] [-v] [file ...]",
+ UsageLine: "go version [-m] [-v] [-json] [file ...]",
Short: "print Go version",
Long: `Version prints the build information for Go binary files.
information consists of multiple lines following the version line, each
indented by a leading tab character.
+The -json flag is similar to -m but outputs the runtime/debug.BuildInfo in JSON format.
+If flag -json is specified without -m, go version reports an error.
+
See also: go doc runtime/debug.BuildInfo.
`,
}
}
var (
- versionM = CmdVersion.Flag.Bool("m", false, "")
- versionV = CmdVersion.Flag.Bool("v", false, "")
+ versionM = CmdVersion.Flag.Bool("m", false, "")
+ versionV = CmdVersion.Flag.Bool("v", false, "")
+ versionJson = CmdVersion.Flag.Bool("json", false, "")
)
func runVersion(ctx context.Context, cmd *base.Command, args []string) {
argOnlyFlag = "-m"
} else if !base.InGOFLAGS("-v") && *versionV {
argOnlyFlag = "-v"
+ } else if !base.InGOFLAGS("-json") && *versionJson {
+ // Even though '-json' without '-m' should report an error,
+ // it reports 'no arguments' issue only because that error will be reported
+ // once the 'no arguments' issue is fixed by users.
+ argOnlyFlag = "-json"
}
if argOnlyFlag != "" {
fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag)
return
}
+ if !*versionM && *versionJson {
+ fmt.Fprintf(os.Stderr, "go: 'go version' with -json flag requires -m flag\n")
+ base.SetExitStatus(2)
+ return
+ }
+
for _, arg := range args {
info, err := os.Stat(arg)
if err != nil {
if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) {
fmt.Fprintf(os.Stderr, "%v\n", file)
} else {
-
// Skip errors for non-Go binaries.
// buildinfo.ReadFile errors are not fine-grained enough
// to know if the file is a Go binary or not,
return false
}
+ if *versionM && *versionJson {
+ bs, err := json.MarshalIndent(bi, "", "\t")
+ if err != nil {
+ base.Fatal(err)
+ }
+ fmt.Printf("%s\n", bs)
+ return true
+ }
+
fmt.Printf("%s: %s\n", file, bi.GoVersion)
bi.GoVersion = "" // suppress printing go version again
mod := bi.String()
stderr 'with arguments'
! go version -v
stderr 'with arguments'
+! go version -json
+stderr 'with arguments'
# Check that 'go version' succeed even when it does not contain Go build info.
# It should print an error if the file has a known Go binary extension.
go version empty.dll
stderr 'could not read Go build info'
-# Neither of the two flags above should be an issue via GOFLAGS.
-env GOFLAGS='-m -v'
+# Neither of the three flags above should be an issue via GOFLAGS.
+env GOFLAGS='-m -v -json'
go version
stdout '^go version'
env GOFLAGS=
stdout '^\tpath\tcmd/test2json$'
! stdout 'mod[^e]'
+# Check -json flag
+go build -o test2json.exe cmd/test2json
+go version -m -json test2json.exe
+stdout '"Path": "cmd/test2json"'
+! stdout 'null'
+
+# Check -json flag output with multiple binaries
+go build -o test2json.exe cmd/test2json
+go version -m -json test2json.exe test2json.exe
+stdout -count=2 '"Path": "cmd/test2json"'
+
+# Check -json flag without -m
+go build -o test2json.exe cmd/test2json
+! go version -json test2json.exe
+! stdout '"Path": "cmd/test2json"'
+stderr 'with -json flag requires -m flag'
+
# Repeat the test with -buildmode=pie and default linking.
[!buildmode:pie] stop
[pielinkext] [!cgo] stop
type BuildInfo struct {
// GoVersion is the version of the Go toolchain that built the binary
// (for example, "go1.19.2").
- GoVersion string
+ GoVersion string `json:",omitempty"`
// Path is the package path of the main package for the binary
// (for example, "golang.org/x/tools/cmd/stringer").
- Path string
+ Path string `json:",omitempty"`
// Main describes the module that contains the main package for the binary.
- Main Module
+ Main Module `json:""`
// Deps describes all the dependency modules, both direct and indirect,
// that contributed packages to the build of this binary.
- Deps []*Module
+ Deps []*Module `json:",omitempty"`
// Settings describes the build settings used to build the binary.
- Settings []BuildSetting
+ Settings []BuildSetting `json:",omitempty"`
}
// A Module describes a single module included in a build.
type Module struct {
- Path string // module path
- Version string // module version
- Sum string // checksum
- Replace *Module // replaced by this module
+ Path string `json:",omitempty"` // module path
+ Version string `json:",omitempty"` // module version
+ Sum string `json:",omitempty"` // checksum
+ Replace *Module `json:",omitempty"` // replaced by this module
}
// A BuildSetting is a key-value pair describing one setting that influenced a build.
type BuildSetting struct {
// Key and Value describe the build setting.
// Key must not contain an equals sign, space, tab, or newline.
+ Key string `json:",omitempty"`
// Value must not contain newlines ('\n').
- Key, Value string
+ Value string `json:",omitempty"`
}
// quoteKey reports whether key is required to be quoted.