Skip to content

Commit a632009

Browse files
xieyuschensamthanawalla
authored andcommitted
cmd/go: support -json flag in go version
It supports features described in the issue: * add -json flag for 'go version -m' to print json encoding of runtime/debug.BuildSetting to standard output. * report an error when specifying -json flag without -m. * print build settings on seperated line for each binary Fixes #69712 Change-Id: I79cba2109f80f7459252d197a74959694c4eea1f Reviewed-on: https://go-review.googlesource.com/c/go/+/619955 Reviewed-by: Sam Thanawalla <[email protected]> Reviewed-by: Junyang Shao <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 698f861 commit a632009

File tree

4 files changed

+64
-17
lines changed

4 files changed

+64
-17
lines changed

src/cmd/go/alldocs.go

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/internal/version/version.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package version
88
import (
99
"context"
1010
"debug/buildinfo"
11+
"encoding/json"
1112
"errors"
1213
"fmt"
1314
"io/fs"
@@ -21,7 +22,7 @@ import (
2122
)
2223

2324
var CmdVersion = &base.Command{
24-
UsageLine: "go version [-m] [-v] [file ...]",
25+
UsageLine: "go version [-m] [-v] [-json] [file ...]",
2526
Short: "print Go version",
2627
Long: `Version prints the build information for Go binary files.
2728
@@ -40,6 +41,9 @@ module version information, when available. In the output, the module
4041
information consists of multiple lines following the version line, each
4142
indented by a leading tab character.
4243
44+
The -json flag is similar to -m but outputs the runtime/debug.BuildInfo in JSON format.
45+
If flag -json is specified without -m, go version reports an error.
46+
4347
See also: go doc runtime/debug.BuildInfo.
4448
`,
4549
}
@@ -50,8 +54,9 @@ func init() {
5054
}
5155

5256
var (
53-
versionM = CmdVersion.Flag.Bool("m", false, "")
54-
versionV = CmdVersion.Flag.Bool("v", false, "")
57+
versionM = CmdVersion.Flag.Bool("m", false, "")
58+
versionV = CmdVersion.Flag.Bool("v", false, "")
59+
versionJson = CmdVersion.Flag.Bool("json", false, "")
5560
)
5661

5762
func runVersion(ctx context.Context, cmd *base.Command, args []string) {
@@ -68,6 +73,11 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) {
6873
argOnlyFlag = "-m"
6974
} else if !base.InGOFLAGS("-v") && *versionV {
7075
argOnlyFlag = "-v"
76+
} else if !base.InGOFLAGS("-json") && *versionJson {
77+
// Even though '-json' without '-m' should report an error,
78+
// it reports 'no arguments' issue only because that error will be reported
79+
// once the 'no arguments' issue is fixed by users.
80+
argOnlyFlag = "-json"
7181
}
7282
if argOnlyFlag != "" {
7383
fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag)
@@ -82,6 +92,12 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) {
8292
return
8393
}
8494

95+
if !*versionM && *versionJson {
96+
fmt.Fprintf(os.Stderr, "go: 'go version' with -json flag requires -m flag\n")
97+
base.SetExitStatus(2)
98+
return
99+
}
100+
85101
for _, arg := range args {
86102
info, err := os.Stat(arg)
87103
if err != nil {
@@ -155,7 +171,6 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) bool {
155171
if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) {
156172
fmt.Fprintf(os.Stderr, "%v\n", file)
157173
} else {
158-
159174
// Skip errors for non-Go binaries.
160175
// buildinfo.ReadFile errors are not fine-grained enough
161176
// to know if the file is a Go binary or not,
@@ -168,6 +183,15 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) bool {
168183
return false
169184
}
170185

186+
if *versionM && *versionJson {
187+
bs, err := json.MarshalIndent(bi, "", "\t")
188+
if err != nil {
189+
base.Fatal(err)
190+
}
191+
fmt.Printf("%s\n", bs)
192+
return true
193+
}
194+
171195
fmt.Printf("%s: %s\n", file, bi.GoVersion)
172196
bi.GoVersion = "" // suppress printing go version again
173197
mod := bi.String()

src/cmd/go/testdata/script/version.txt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ stdout '^go version'
88
stderr 'with arguments'
99
! go version -v
1010
stderr 'with arguments'
11+
! go version -json
12+
stderr 'with arguments'
1113

1214
# Check that 'go version' succeed even when it does not contain Go build info.
1315
# It should print an error if the file has a known Go binary extension.
@@ -22,8 +24,8 @@ stderr 'could not read Go build info'
2224
go version empty.dll
2325
stderr 'could not read Go build info'
2426

25-
# Neither of the two flags above should be an issue via GOFLAGS.
26-
env GOFLAGS='-m -v'
27+
# Neither of the three flags above should be an issue via GOFLAGS.
28+
env GOFLAGS='-m -v -json'
2729
go version
2830
stdout '^go version'
2931
env GOFLAGS=
@@ -57,6 +59,23 @@ stdout '^test2json.exe: .+'
5759
stdout '^\tpath\tcmd/test2json$'
5860
! stdout 'mod[^e]'
5961

62+
# Check -json flag
63+
go build -o test2json.exe cmd/test2json
64+
go version -m -json test2json.exe
65+
stdout '"Path": "cmd/test2json"'
66+
! stdout 'null'
67+
68+
# Check -json flag output with multiple binaries
69+
go build -o test2json.exe cmd/test2json
70+
go version -m -json test2json.exe test2json.exe
71+
stdout -count=2 '"Path": "cmd/test2json"'
72+
73+
# Check -json flag without -m
74+
go build -o test2json.exe cmd/test2json
75+
! go version -json test2json.exe
76+
! stdout '"Path": "cmd/test2json"'
77+
stderr 'with -json flag requires -m flag'
78+
6079
# Repeat the test with -buildmode=pie and default linking.
6180
[!buildmode:pie] stop
6281
[pielinkext] [!cgo] stop

src/runtime/debug/mod.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,29 +41,29 @@ func ReadBuildInfo() (info *BuildInfo, ok bool) {
4141
type BuildInfo struct {
4242
// GoVersion is the version of the Go toolchain that built the binary
4343
// (for example, "go1.19.2").
44-
GoVersion string
44+
GoVersion string `json:",omitempty"`
4545

4646
// Path is the package path of the main package for the binary
4747
// (for example, "golang.org/x/tools/cmd/stringer").
48-
Path string
48+
Path string `json:",omitempty"`
4949

5050
// Main describes the module that contains the main package for the binary.
51-
Main Module
51+
Main Module `json:""`
5252

5353
// Deps describes all the dependency modules, both direct and indirect,
5454
// that contributed packages to the build of this binary.
55-
Deps []*Module
55+
Deps []*Module `json:",omitempty"`
5656

5757
// Settings describes the build settings used to build the binary.
58-
Settings []BuildSetting
58+
Settings []BuildSetting `json:",omitempty"`
5959
}
6060

6161
// A Module describes a single module included in a build.
6262
type Module struct {
63-
Path string // module path
64-
Version string // module version
65-
Sum string // checksum
66-
Replace *Module // replaced by this module
63+
Path string `json:",omitempty"` // module path
64+
Version string `json:",omitempty"` // module version
65+
Sum string `json:",omitempty"` // checksum
66+
Replace *Module `json:",omitempty"` // replaced by this module
6767
}
6868

6969
// A BuildSetting is a key-value pair describing one setting that influenced a build.
@@ -89,8 +89,9 @@ type Module struct {
8989
type BuildSetting struct {
9090
// Key and Value describe the build setting.
9191
// Key must not contain an equals sign, space, tab, or newline.
92+
Key string `json:",omitempty"`
9293
// Value must not contain newlines ('\n').
93-
Key, Value string
94+
Value string `json:",omitempty"`
9495
}
9596

9697
// quoteKey reports whether key is required to be quoted.

0 commit comments

Comments
 (0)