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