Source file src/pkg/cmd/go/internal/version/version.go
1
2
3
4
5
6 package version
7
8 import (
9 "bytes"
10 "encoding/binary"
11 "fmt"
12 "os"
13 "path/filepath"
14 "runtime"
15 "strings"
16
17 "cmd/go/internal/base"
18 )
19
20 var CmdVersion = &base.Command{
21 UsageLine: "go version [-m] [-v] [file ...]",
22 Short: "print Go version",
23 Long: `Version prints the build information for Go executables.
24
25 Go version reports the Go version used to build each of the named
26 executable files.
27
28 If no files are named on the command line, go version prints its own
29 version information.
30
31 If a directory is named, go version walks that directory, recursively,
32 looking for recognized Go binaries and reporting their versions.
33 By default, go version does not report unrecognized files found
34 during a directory scan. The -v flag causes it to report unrecognized files.
35
36 The -m flag causes go version to print each executable's embedded
37 module version information, when available. In the output, the module
38 information consists of multiple lines following the version line, each
39 indented by a leading tab character.
40
41 See also: go doc runtime/debug.BuildInfo.
42 `,
43 }
44
45 func init() {
46 CmdVersion.Run = runVersion
47 }
48
49 var (
50 versionM = CmdVersion.Flag.Bool("m", false, "")
51 versionV = CmdVersion.Flag.Bool("v", false, "")
52 )
53
54 func runVersion(cmd *base.Command, args []string) {
55 if len(args) == 0 {
56 fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
57 return
58 }
59
60 for _, arg := range args {
61 info, err := os.Stat(arg)
62 if err != nil {
63 fmt.Fprintf(os.Stderr, "%v\n", err)
64 continue
65 }
66 if info.IsDir() {
67 scanDir(arg)
68 } else {
69 scanFile(arg, info, true)
70 }
71 }
72 }
73
74
75 func scanDir(dir string) {
76 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
77 if info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 {
78 scanFile(path, info, *versionV)
79 }
80 return nil
81 })
82 }
83
84
85 func isExe(file string, info os.FileInfo) bool {
86 if runtime.GOOS == "windows" {
87 return strings.HasSuffix(strings.ToLower(file), ".exe")
88 }
89 return info.Mode().IsRegular() && info.Mode()&0111 != 0
90 }
91
92
93
94
95
96 func scanFile(file string, info os.FileInfo, mustPrint bool) {
97 if info.Mode()&os.ModeSymlink != 0 {
98
99 i, err := os.Stat(file)
100 if err != nil || !i.Mode().IsRegular() {
101 if mustPrint {
102 fmt.Fprintf(os.Stderr, "%s: symlink\n", file)
103 }
104 return
105 }
106 info = i
107 }
108 if !isExe(file, info) {
109 if mustPrint {
110 fmt.Fprintf(os.Stderr, "%s: not executable file\n", file)
111 }
112 return
113 }
114
115 x, err := openExe(file)
116 if err != nil {
117 if mustPrint {
118 fmt.Fprintf(os.Stderr, "%s: %v\n", file, err)
119 }
120 return
121 }
122 defer x.Close()
123
124 vers, mod := findVers(x)
125 if vers == "" {
126 if mustPrint {
127 fmt.Fprintf(os.Stderr, "%s: go version not found\n", file)
128 }
129 return
130 }
131
132 fmt.Printf("%s: %s\n", file, vers)
133 if *versionM && mod != "" {
134 fmt.Printf("\t%s\n", strings.Replace(mod[:len(mod)-1], "\n", "\n\t", -1))
135 }
136 }
137
138
139
140
141
142 var buildInfoMagic = []byte("\xff Go buildinf:")
143
144
145
146 func findVers(x exe) (vers, mod string) {
147
148 text := x.DataStart()
149 data, err := x.ReadData(text, 64*1024)
150 if err != nil {
151 return
152 }
153 for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] {
154 if len(data) < 32 {
155 return
156 }
157 }
158
159
160 ptrSize := int(data[14])
161 bigEndian := data[15] != 0
162 var bo binary.ByteOrder
163 if bigEndian {
164 bo = binary.BigEndian
165 } else {
166 bo = binary.LittleEndian
167 }
168 var readPtr func([]byte) uint64
169 if ptrSize == 4 {
170 readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
171 } else {
172 readPtr = bo.Uint64
173 }
174 vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
175 if vers == "" {
176 return
177 }
178 mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
179 if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
180
181 mod = mod[16 : len(mod)-16]
182 } else {
183 mod = ""
184 }
185 return
186 }
187
188
189 func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
190 hdr, err := x.ReadData(addr, uint64(2*ptrSize))
191 if err != nil || len(hdr) < 2*ptrSize {
192 return ""
193 }
194 dataAddr := readPtr(hdr)
195 dataLen := readPtr(hdr[ptrSize:])
196 data, err := x.ReadData(dataAddr, dataLen)
197 if err != nil || uint64(len(data)) < dataLen {
198 return ""
199 }
200 return string(data)
201 }
202
View as plain text