github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/version/version.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package version implements the “go version” command. 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 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 20 ) 21 22 var CmdVersion = &base.Command{ 23 UsageLine: "go version [-m] [-v] [file ...]", 24 Short: "print Go version", 25 Long: `Version prints the build information for Go binary files. 26 27 Go version reports the Go version used to build each of the named files. 28 29 If no files are named on the command line, go version prints its own 30 version information. 31 32 If a directory is named, go version walks that directory, recursively, 33 looking for recognized Go binaries and reporting their versions. 34 By default, go version does not report unrecognized files found 35 during a directory scan. The -v flag causes it to report unrecognized files. 36 37 The -m flag causes go version to print each file's embedded 38 module version information, when available. In the output, the module 39 information consists of multiple lines following the version line, each 40 indented by a leading tab character. 41 42 See also: go doc runtime/debug.BuildInfo. 43 `, 44 } 45 46 func init() { 47 base.AddChdirFlag(&CmdVersion.Flag) 48 CmdVersion.Run = runVersion // break init cycle 49 } 50 51 var ( 52 versionM = CmdVersion.Flag.Bool("m", false, "") 53 versionV = CmdVersion.Flag.Bool("v", false, "") 54 ) 55 56 func runVersion(ctx context.Context, cmd *base.Command, args []string) { 57 if len(args) == 0 { 58 // If any of this command's flags were passed explicitly, error 59 // out, because they only make sense with arguments. 60 // 61 // Don't error if the flags came from GOFLAGS, since that can be 62 // a reasonable use case. For example, imagine GOFLAGS=-v to 63 // turn "verbose mode" on for all Go commands, which should not 64 // break "go version". 65 var argOnlyFlag string 66 if !base.InGOFLAGS("-m") && *versionM { 67 argOnlyFlag = "-m" 68 } else if !base.InGOFLAGS("-v") && *versionV { 69 argOnlyFlag = "-v" 70 } 71 if argOnlyFlag != "" { 72 fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag) 73 base.SetExitStatus(2) 74 return 75 } 76 fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) 77 return 78 } 79 80 for _, arg := range args { 81 info, err := os.Stat(arg) 82 if err != nil { 83 fmt.Fprintf(os.Stderr, "%v\n", err) 84 base.SetExitStatus(1) 85 continue 86 } 87 if info.IsDir() { 88 scanDir(arg) 89 } else { 90 scanFile(arg, info, true) 91 } 92 } 93 } 94 95 // scanDir scans a directory for binary to run scanFile on. 96 func scanDir(dir string) { 97 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { 98 if d.Type().IsRegular() || d.Type()&fs.ModeSymlink != 0 { 99 info, err := d.Info() 100 if err != nil { 101 if *versionV { 102 fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) 103 } 104 return nil 105 } 106 scanFile(path, info, *versionV) 107 } 108 return nil 109 }) 110 } 111 112 // isGoBinaryCandidate reports whether the file is a candidate to be a Go binary. 113 func isGoBinaryCandidate(file string, info fs.FileInfo) bool { 114 if info.Mode().IsRegular() && info.Mode()&0111 != 0 { 115 return true 116 } 117 name := strings.ToLower(file) 118 switch filepath.Ext(name) { 119 case ".so", ".exe", ".dll": 120 return true 121 default: 122 return strings.Contains(name, ".so.") 123 } 124 } 125 126 // scanFile scans file to try to report the Go and module versions. 127 // If mustPrint is true, scanFile will report any error reading file. 128 // Otherwise (mustPrint is false, because scanFile is being called 129 // by scanDir) scanFile prints nothing for non-Go binaries. 130 func scanFile(file string, info fs.FileInfo, mustPrint bool) { 131 if info.Mode()&fs.ModeSymlink != 0 { 132 // Accept file symlinks only. 133 i, err := os.Stat(file) 134 if err != nil || !i.Mode().IsRegular() { 135 if mustPrint { 136 fmt.Fprintf(os.Stderr, "%s: symlink\n", file) 137 } 138 return 139 } 140 info = i 141 } 142 143 bi, err := buildinfo.ReadFile(file) 144 if err != nil { 145 if mustPrint { 146 if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) { 147 fmt.Fprintf(os.Stderr, "%v\n", file) 148 } else { 149 150 // Skip errors for non-Go binaries. 151 // buildinfo.ReadFile errors are not fine-grained enough 152 // to know if the file is a Go binary or not, 153 // so try to infer it from the file mode and extension. 154 if isGoBinaryCandidate(file, info) { 155 fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) 156 } 157 } 158 } 159 return 160 } 161 162 fmt.Printf("%s: %s\n", file, bi.GoVersion) 163 bi.GoVersion = "" // suppress printing go version again 164 mod := bi.String() 165 if *versionM && len(mod) > 0 { 166 fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t")) 167 } 168 }