1
2
3
4
5
6 package tool
7
8 import (
9 "cmd/internal/telemetry/counter"
10 "context"
11 "encoding/json"
12 "flag"
13 "fmt"
14 "go/build"
15 "internal/platform"
16 "os"
17 "os/exec"
18 "os/signal"
19 "sort"
20 "strings"
21
22 "cmd/go/internal/base"
23 "cmd/go/internal/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
44
45
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 counter.Inc("go/subcommand:tool")
62 listTools()
63 return
64 }
65 toolName := args[0]
66
67 for _, c := range toolName {
68 switch {
69 case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
70 default:
71 fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName)
72 base.SetExitStatus(2)
73 return
74 }
75 }
76
77 toolPath, err := base.ToolPath(toolName)
78 if err != nil {
79 if toolName == "dist" && len(args) > 1 && args[1] == "list" {
80
81
82
83
84
85
86 if impersonateDistList(args[2:]) {
87
88
89 counter.Inc("go/subcommand:tool-dist")
90 return
91 }
92 }
93
94 counter.Inc("go/subcommand:tool-unknown")
95
96 _ = base.Tool(toolName)
97 } else {
98
99 counter.Inc("go/subcommand:tool-" + toolName)
100 }
101
102 if toolN {
103 cmd := toolPath
104 if len(args) > 1 {
105 cmd += " " + strings.Join(args[1:], " ")
106 }
107 fmt.Printf("%s\n", cmd)
108 return
109 }
110 args[0] = toolPath
111 toolCmd := &exec.Cmd{
112 Path: toolPath,
113 Args: args,
114 Stdin: os.Stdin,
115 Stdout: os.Stdout,
116 Stderr: os.Stderr,
117 }
118 err = toolCmd.Start()
119 if err == nil {
120 c := make(chan os.Signal, 100)
121 signal.Notify(c)
122 go func() {
123 for sig := range c {
124 toolCmd.Process.Signal(sig)
125 }
126 }()
127 err = toolCmd.Wait()
128 signal.Stop(c)
129 close(c)
130 }
131 if err != nil {
132
133
134
135
136
137 if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || cfg.BuildX {
138 fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
139 }
140 base.SetExitStatus(1)
141 return
142 }
143 }
144
145
146 func listTools() {
147 f, err := os.Open(build.ToolDir)
148 if err != nil {
149 fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err)
150 base.SetExitStatus(2)
151 return
152 }
153 defer f.Close()
154 names, err := f.Readdirnames(-1)
155 if err != nil {
156 fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err)
157 base.SetExitStatus(2)
158 return
159 }
160
161 sort.Strings(names)
162 for _, name := range names {
163
164
165 name = strings.TrimSuffix(strings.ToLower(name), cfg.ToolExeSuffix())
166
167
168
169 if cfg.BuildToolchainName == "gccgo" && !isGccgoTool(name) {
170 continue
171 }
172 fmt.Println(name)
173 }
174 }
175
176 func impersonateDistList(args []string) (handled bool) {
177 fs := flag.NewFlagSet("go tool dist list", flag.ContinueOnError)
178 jsonFlag := fs.Bool("json", false, "produce JSON output")
179 brokenFlag := fs.Bool("broken", false, "include broken ports")
180
181
182
183
184 _ = fs.Bool("v", false, "emit extra information")
185
186 if err := fs.Parse(args); err != nil || len(fs.Args()) > 0 {
187
188
189 return false
190 }
191
192 if !*jsonFlag {
193 for _, p := range platform.List {
194 if !*brokenFlag && platform.Broken(p.GOOS, p.GOARCH) {
195 continue
196 }
197 fmt.Println(p)
198 }
199 return true
200 }
201
202 type jsonResult struct {
203 GOOS string
204 GOARCH string
205 CgoSupported bool
206 FirstClass bool
207 Broken bool `json:",omitempty"`
208 }
209
210 var results []jsonResult
211 for _, p := range platform.List {
212 broken := platform.Broken(p.GOOS, p.GOARCH)
213 if broken && !*brokenFlag {
214 continue
215 }
216 if *jsonFlag {
217 results = append(results, jsonResult{
218 GOOS: p.GOOS,
219 GOARCH: p.GOARCH,
220 CgoSupported: platform.CgoSupported(p.GOOS, p.GOARCH),
221 FirstClass: platform.FirstClass(p.GOOS, p.GOARCH),
222 Broken: broken,
223 })
224 }
225 }
226 out, err := json.MarshalIndent(results, "", "\t")
227 if err != nil {
228 return false
229 }
230
231 os.Stdout.Write(out)
232 return true
233 }
234
View as plain text