1
2
3
4
5
6 package version
7
8 import (
9 "context"
10 "debug/buildinfo"
11 "errors"
12 "fmt"
13 "io/fs"
14 "os"
15 "path/filepath"
16 "runtime"
17 "strings"
18
19 "cmd/go/internal/base"
20 "cmd/go/internal/gover"
21 )
22
23 var CmdVersion = &base.Command{
24 UsageLine: "go version [-m] [-v] [file ...]",
25 Short: "print Go version",
26 Long: `Version prints the build information for Go binary files.
27
28 Go version reports the Go version used to build each of the named files.
29
30 If no files are named on the command line, go version prints its own
31 version information.
32
33 If a directory is named, go version walks that directory, recursively,
34 looking for recognized Go binaries and reporting their versions.
35 By default, go version does not report unrecognized files found
36 during a directory scan. The -v flag causes it to report unrecognized files.
37
38 The -m flag causes go version to print each file's embedded
39 module version information, when available. In the output, the module
40 information consists of multiple lines following the version line, each
41 indented by a leading tab character.
42
43 See also: go doc runtime/debug.BuildInfo.
44 `,
45 }
46
47 func init() {
48 base.AddChdirFlag(&CmdVersion.Flag)
49 CmdVersion.Run = runVersion
50 }
51
52 var (
53 versionM = CmdVersion.Flag.Bool("m", false, "")
54 versionV = CmdVersion.Flag.Bool("v", false, "")
55 )
56
57 func runVersion(ctx context.Context, cmd *base.Command, args []string) {
58 if len(args) == 0 {
59
60
61
62
63
64
65
66 var argOnlyFlag string
67 if !base.InGOFLAGS("-m") && *versionM {
68 argOnlyFlag = "-m"
69 } else if !base.InGOFLAGS("-v") && *versionV {
70 argOnlyFlag = "-v"
71 }
72 if argOnlyFlag != "" {
73 fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag)
74 base.SetExitStatus(2)
75 return
76 }
77 v := runtime.Version()
78 if gover.TestVersion != "" {
79 v = gover.TestVersion + " (TESTGO_VERSION)"
80 }
81 fmt.Printf("go version %s %s/%s\n", v, runtime.GOOS, runtime.GOARCH)
82 return
83 }
84
85 for _, arg := range args {
86 info, err := os.Stat(arg)
87 if err != nil {
88 fmt.Fprintf(os.Stderr, "%v\n", err)
89 base.SetExitStatus(1)
90 continue
91 }
92 if info.IsDir() {
93 scanDir(arg)
94 } else {
95 scanFile(arg, info, true)
96 }
97 }
98 }
99
100
101 func scanDir(dir string) {
102 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
103 if d.Type().IsRegular() || d.Type()&fs.ModeSymlink != 0 {
104 info, err := d.Info()
105 if err != nil {
106 if *versionV {
107 fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
108 }
109 return nil
110 }
111 scanFile(path, info, *versionV)
112 }
113 return nil
114 })
115 }
116
117
118 func isGoBinaryCandidate(file string, info fs.FileInfo) bool {
119 if info.Mode().IsRegular() && info.Mode()&0111 != 0 {
120 return true
121 }
122 name := strings.ToLower(file)
123 switch filepath.Ext(name) {
124 case ".so", ".exe", ".dll":
125 return true
126 default:
127 return strings.Contains(name, ".so.")
128 }
129 }
130
131
132
133
134
135 func scanFile(file string, info fs.FileInfo, mustPrint bool) {
136 if info.Mode()&fs.ModeSymlink != 0 {
137
138 i, err := os.Stat(file)
139 if err != nil || !i.Mode().IsRegular() {
140 if mustPrint {
141 fmt.Fprintf(os.Stderr, "%s: symlink\n", file)
142 }
143 return
144 }
145 info = i
146 }
147
148 bi, err := buildinfo.ReadFile(file)
149 if err != nil {
150 if mustPrint {
151 if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) {
152 fmt.Fprintf(os.Stderr, "%v\n", file)
153 } else {
154
155
156
157
158
159 if isGoBinaryCandidate(file, info) {
160 fmt.Fprintf(os.Stderr, "%s: %v\n", file, err)
161 }
162 }
163 }
164 return
165 }
166
167 fmt.Printf("%s: %s\n", file, bi.GoVersion)
168 bi.GoVersion = ""
169 mod := bi.String()
170 if *versionM && len(mod) > 0 {
171 fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t"))
172 }
173 }
174
View as plain text