github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/internal/tool/tool.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 tool implements the “go tool” command. 6 package tool 7 8 import ( 9 "context" 10 "encoding/json" 11 "github.com/bir3/gocompiler/src/cmd/gocmd/flag" 12 "fmt" 13 "github.com/bir3/gocompiler/src/go/build" 14 "github.com/bir3/gocompiler/src/internal/platform" 15 "os" 16 "github.com/bir3/gocompiler/exec" 17 "os/signal" 18 "sort" 19 "strings" 20 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 23 ) 24 25 var CmdTool = &base.Command{ 26 Run: runTool, 27 UsageLine: "go tool [-n] command [args...]", 28 Short: "run specified go tool", 29 Long: ` 30 Tool runs the go tool command identified by the arguments. 31 With no arguments it prints the list of known tools. 32 33 The -n flag causes tool to print the command that would be 34 executed but not execute it. 35 36 For more about each tool command, see 'go doc cmd/<command>'. 37 `, 38 } 39 40 var toolN bool 41 42 // Return whether tool can be expected in the gccgo tool directory. 43 // Other binaries could be in the same directory so don't 44 // show those with the 'go tool' command. 45 func isGccgoTool(tool string) bool { 46 switch tool { 47 case "cgo", "fix", "cover", "godoc", "vet": 48 return true 49 } 50 return false 51 } 52 53 func init() { 54 base.AddChdirFlag(&CmdTool.Flag) 55 CmdTool.Flag.BoolVar(&toolN, "n", false, "") 56 } 57 58 func runTool(ctx context.Context, cmd *base.Command, args []string) { 59 if len(args) == 0 { 60 listTools() 61 return 62 } 63 toolName := args[0] 64 // The tool name must be lower-case letters, numbers or underscores. 65 for _, c := range toolName { 66 switch { 67 case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_': 68 default: 69 fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName) 70 base.SetExitStatus(2) 71 return 72 } 73 } 74 75 toolPath, err := base.ToolPath(toolName) 76 if err != nil { 77 if toolName == "dist" && len(args) > 1 && args[1] == "list" { 78 // cmd/distpack removes the 'dist' tool from the toolchain to save space, 79 // since it is normally only used for building the toolchain in the first 80 // place. However, 'go tool dist list' is useful for listing all supported 81 // platforms. 82 // 83 // If the dist tool does not exist, impersonate this command. 84 if impersonateDistList(args[2:]) { 85 return 86 } 87 } 88 89 // Emit the usual error for the missing tool. 90 _ = base.Tool(toolName) 91 } 92 93 if toolN { 94 cmd := toolPath 95 if len(args) > 1 { 96 cmd += " " + strings.Join(args[1:], " ") 97 } 98 fmt.Printf("%s\n", cmd) 99 return 100 } 101 args[0] = toolPath // in case the tool wants to re-exec itself, e.g. cmd/dist 102 toolCmd := &exec.Cmd{ 103 Path: toolPath, 104 Args: args, 105 Stdin: os.Stdin, 106 Stdout: os.Stdout, 107 Stderr: os.Stderr, 108 } 109 err = toolCmd.Start() 110 if err == nil { 111 c := make(chan os.Signal, 100) 112 signal.Notify(c) 113 go func() { 114 for sig := range c { 115 toolCmd.Process.Signal(sig) 116 } 117 }() 118 err = toolCmd.Wait() 119 signal.Stop(c) 120 close(c) 121 } 122 if err != nil { 123 // Only print about the exit status if the command 124 // didn't even run (not an ExitError) or it didn't exit cleanly 125 // or we're printing command lines too (-x mode). 126 // Assume if command exited cleanly (even with non-zero status) 127 // it printed any messages it wanted to print. 128 if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || cfg.BuildX { 129 fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err) 130 } 131 base.SetExitStatus(1) 132 return 133 } 134 } 135 136 // listTools prints a list of the available tools in the tools directory. 137 func listTools() { 138 f, err := os.Open(build.ToolDir) 139 if err != nil { 140 fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err) 141 base.SetExitStatus(2) 142 return 143 } 144 defer f.Close() 145 names, err := f.Readdirnames(-1) 146 if err != nil { 147 fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err) 148 base.SetExitStatus(2) 149 return 150 } 151 152 sort.Strings(names) 153 for _, name := range names { 154 // Unify presentation by going to lower case. 155 // If it's windows, don't show the .exe suffix. 156 name = strings.TrimSuffix(strings.ToLower(name), cfg.ToolExeSuffix()) 157 158 // The tool directory used by gccgo will have other binaries 159 // in addition to go tools. Only display go tools here. 160 if cfg.BuildToolchainName == "gccgo" && !isGccgoTool(name) { 161 continue 162 } 163 fmt.Println(name) 164 } 165 } 166 167 func impersonateDistList(args []string) (handled bool) { 168 fs := flag.NewFlagSet("go tool dist list", flag.ContinueOnError) 169 jsonFlag := fs.Bool("json", false, "produce JSON output") 170 brokenFlag := fs.Bool("broken", false, "include broken ports") 171 172 // The usage for 'go tool dist' claims that 173 // “All commands take -v flags to emit extra information”, 174 // but list -v appears not to have any effect. 175 _ = fs.Bool("v", false, "emit extra information") 176 177 if err := fs.Parse(args); err != nil || len(fs.Args()) > 0 { 178 // Unrecognized flag or argument. 179 // Force fallback to the real 'go tool dist'. 180 return false 181 } 182 183 if !*jsonFlag { 184 for _, p := range platform.List { 185 if !*brokenFlag && platform.Broken(p.GOOS, p.GOARCH) { 186 continue 187 } 188 fmt.Println(p) 189 } 190 return true 191 } 192 193 type jsonResult struct { 194 GOOS string 195 GOARCH string 196 CgoSupported bool 197 FirstClass bool 198 Broken bool `json:",omitempty"` 199 } 200 201 var results []jsonResult 202 for _, p := range platform.List { 203 broken := platform.Broken(p.GOOS, p.GOARCH) 204 if broken && !*brokenFlag { 205 continue 206 } 207 if *jsonFlag { 208 results = append(results, jsonResult{ 209 GOOS: p.GOOS, 210 GOARCH: p.GOARCH, 211 CgoSupported: platform.CgoSupported(p.GOOS, p.GOARCH), 212 FirstClass: platform.FirstClass(p.GOOS, p.GOARCH), 213 Broken: broken, 214 }) 215 } 216 } 217 out, err := json.MarshalIndent(results, "", "\t") 218 if err != nil { 219 return false 220 } 221 222 os.Stdout.Write(out) 223 return true 224 }