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 ok := scanFile(arg, info, true)
96 if !ok && *versionM {
97 base.SetExitStatus(1)
98 }
99 }
100 }
101 }
102
103
104 func scanDir(dir string) {
105 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
106 if d.Type().IsRegular() || d.Type()&fs.ModeSymlink != 0 {
107 info, err := d.Info()
108 if err != nil {
109 if *versionV {
110 fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
111 }
112 return nil
113 }
114 scanFile(path, info, *versionV)
115 }
116 return nil
117 })
118 }
119
120
121 func isGoBinaryCandidate(file string, info fs.FileInfo) bool {
122 if info.Mode().IsRegular() && info.Mode()&0111 != 0 {
123 return true
124 }
125 name := strings.ToLower(file)
126 switch filepath.Ext(name) {
127 case ".so", ".exe", ".dll":
128 return true
129 default:
130 return strings.Contains(name, ".so.")
131 }
132 }
133
134
135
136
137
138
139 func scanFile(file string, info fs.FileInfo, mustPrint bool) bool {
140 if info.Mode()&fs.ModeSymlink != 0 {
141
142 i, err := os.Stat(file)
143 if err != nil || !i.Mode().IsRegular() {
144 if mustPrint {
145 fmt.Fprintf(os.Stderr, "%s: symlink\n", file)
146 }
147 return false
148 }
149 info = i
150 }
151
152 bi, err := buildinfo.ReadFile(file)
153 if err != nil {
154 if mustPrint {
155 if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) {
156 fmt.Fprintf(os.Stderr, "%v\n", file)
157 } else {
158
159
160
161
162
163 if isGoBinaryCandidate(file, info) {
164 fmt.Fprintf(os.Stderr, "%s: %v\n", file, err)
165 }
166 }
167 }
168 return false
169 }
170
171 fmt.Printf("%s: %s\n", file, bi.GoVersion)
172 bi.GoVersion = ""
173 mod := bi.String()
174 if *versionM && len(mod) > 0 {
175 fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t"))
176 }
177 return true
178 }
179
View as plain text