Source file
src/cmd/go/main.go
1
2
3
4
5
6
7 package main
8
9 import (
10 "context"
11 "flag"
12 "fmt"
13 "internal/buildcfg"
14 "log"
15 "os"
16 "path/filepath"
17 rtrace "runtime/trace"
18 "slices"
19 "strings"
20
21 "cmd/go/internal/base"
22 "cmd/go/internal/bug"
23 "cmd/go/internal/cfg"
24 "cmd/go/internal/clean"
25 "cmd/go/internal/doc"
26 "cmd/go/internal/envcmd"
27 "cmd/go/internal/fix"
28 "cmd/go/internal/fmtcmd"
29 "cmd/go/internal/generate"
30 "cmd/go/internal/help"
31 "cmd/go/internal/list"
32 "cmd/go/internal/modcmd"
33 "cmd/go/internal/modfetch"
34 "cmd/go/internal/modget"
35 "cmd/go/internal/modload"
36 "cmd/go/internal/run"
37 "cmd/go/internal/telemetrycmd"
38 "cmd/go/internal/telemetrystats"
39 "cmd/go/internal/test"
40 "cmd/go/internal/tool"
41 "cmd/go/internal/toolchain"
42 "cmd/go/internal/trace"
43 "cmd/go/internal/version"
44 "cmd/go/internal/vet"
45 "cmd/go/internal/work"
46 "cmd/go/internal/workcmd"
47 "cmd/internal/telemetry"
48 "cmd/internal/telemetry/counter"
49 )
50
51 func init() {
52 base.Go.Commands = []*base.Command{
53 bug.CmdBug,
54 work.CmdBuild,
55 clean.CmdClean,
56 doc.CmdDoc,
57 envcmd.CmdEnv,
58 fix.CmdFix,
59 fmtcmd.CmdFmt,
60 generate.CmdGenerate,
61 modget.CmdGet,
62 work.CmdInstall,
63 list.CmdList,
64 modcmd.CmdMod,
65 workcmd.CmdWork,
66 run.CmdRun,
67 telemetrycmd.CmdTelemetry,
68 test.CmdTest,
69 tool.CmdTool,
70 version.CmdVersion,
71 vet.CmdVet,
72
73 help.HelpBuildConstraint,
74 help.HelpBuildmode,
75 help.HelpC,
76 help.HelpCache,
77 help.HelpEnvironment,
78 help.HelpFileType,
79 modload.HelpGoMod,
80 help.HelpGopath,
81 modfetch.HelpGoproxy,
82 help.HelpImportPath,
83 modload.HelpModules,
84 modfetch.HelpModuleAuth,
85 help.HelpPackages,
86 modfetch.HelpPrivate,
87 test.HelpTestflag,
88 test.HelpTestfunc,
89 modget.HelpVCS,
90 }
91 }
92
93 var _ = go11tag
94
95 var counterErrorsGOPATHEntryRelative = counter.New("go/errors:gopath-entry-relative")
96
97 func main() {
98 log.SetFlags(0)
99 telemetry.MaybeChild()
100 cmdIsGoTelemetryOff := cmdIsGoTelemetryOff()
101 if !cmdIsGoTelemetryOff {
102 counter.Open()
103 }
104 handleChdirFlag()
105 toolchain.Select()
106
107 if !cmdIsGoTelemetryOff {
108 telemetry.MaybeParent()
109 }
110 flag.Usage = base.Usage
111 flag.Parse()
112 counter.Inc("go/invocations")
113 counter.CountFlags("go/flag:", *flag.CommandLine)
114
115 args := flag.Args()
116 if len(args) < 1 {
117 base.Usage()
118 }
119
120 cfg.CmdName = args[0]
121 if args[0] == "help" {
122 counter.Inc("go/subcommand:" + strings.Join(append([]string{"help"}, args[1:]...), "-"))
123 help.Help(os.Stdout, args[1:])
124 return
125 }
126
127 if cfg.GOROOT == "" {
128 fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n")
129 os.Exit(2)
130 }
131 if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() {
132 fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT)
133 os.Exit(2)
134 }
135 switch strings.ToLower(cfg.GOROOT) {
136 case "/usr/local/go":
137 counter.Inc("go/goroot:usr-local-go")
138 case "/usr/lib/go":
139 counter.Inc("go/goroot:usr-lib-go")
140 case "/usr/lib/golang":
141 counter.Inc("go/goroot:usr-lib-golang")
142 case `c:\program files\go`:
143 counter.Inc("go/goroot:program-files-go")
144 case `c:\program files (x86)\go`:
145 counter.Inc("go/goroot:program-files-x86-go")
146 default:
147 counter.Inc("go/goroot:other")
148 }
149
150
151
152
153 if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(cfg.GOROOT) {
154 fmt.Fprintf(os.Stderr, "warning: both GOPATH and GOROOT are the same directory (%s); see https://go.dev/wiki/InstallTroubleshooting\n", gopath)
155 } else {
156 for _, p := range filepath.SplitList(gopath) {
157
158
159 if p == "" {
160 continue
161 }
162
163
164
165 if strings.HasPrefix(p, "~") {
166 fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p)
167 os.Exit(2)
168 }
169 if !filepath.IsAbs(p) {
170 if cfg.Getenv("GOPATH") == "" {
171
172
173 cfg.BuildContext.GOPATH = ""
174 } else {
175 counterErrorsGOPATHEntryRelative.Inc()
176 fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
177 os.Exit(2)
178 }
179 }
180 }
181 }
182
183 cmd, used := lookupCmd(args)
184 cfg.CmdName = strings.Join(args[:used], " ")
185 if len(cmd.Commands) > 0 {
186 if used >= len(args) {
187 help.PrintUsage(os.Stderr, cmd)
188 base.SetExitStatus(2)
189 base.Exit()
190 }
191 if args[used] == "help" {
192
193 counter.Inc("go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "-" + strings.Join(args[used:], "-"))
194 help.Help(os.Stdout, append(slices.Clip(args[:used]), args[used+1:]...))
195 base.Exit()
196 }
197 helpArg := ""
198 if used > 0 {
199 helpArg += " " + strings.Join(args[:used], " ")
200 }
201 cmdName := cfg.CmdName
202 if cmdName == "" {
203 cmdName = args[0]
204 }
205 counter.Inc("go/subcommand:unknown")
206 fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cmdName, helpArg)
207 base.SetExitStatus(2)
208 base.Exit()
209 }
210
211
212
213
214 if cfg.CmdName != "tool" {
215 counter.Inc("go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-"))
216 }
217 telemetrystats.Increment()
218 invoke(cmd, args[used-1:])
219 base.Exit()
220 }
221
222
223
224 func cmdIsGoTelemetryOff() bool {
225 restArgs := os.Args[1:]
226
227
228
229
230
231 skipChdirFlag := func() {
232 if len(restArgs) == 0 {
233 return
234 }
235 switch a := restArgs[0]; {
236 case a == "-C", a == "--C":
237 if len(restArgs) < 2 {
238 restArgs = nil
239 return
240 }
241 restArgs = restArgs[2:]
242
243 case strings.HasPrefix(a, "-C="), strings.HasPrefix(a, "--C="):
244 restArgs = restArgs[1:]
245 }
246 }
247 skipChdirFlag()
248 cmd, used := lookupCmd(restArgs)
249 if cmd != telemetrycmd.CmdTelemetry {
250 return false
251 }
252 restArgs = restArgs[used:]
253 skipChdirFlag()
254 return len(restArgs) == 1 && restArgs[0] == "off"
255 }
256
257
258
259
260
261
262
263 func lookupCmd(args []string) (cmd *base.Command, used int) {
264 cmd = base.Go
265 for used < len(args) {
266 c := cmd.Lookup(args[used])
267 if c == nil {
268 break
269 }
270 if c.Runnable() {
271 cmd = c
272 used++
273 break
274 }
275 if len(c.Commands) > 0 {
276 cmd = c
277 used++
278 if used >= len(args) || args[0] == "help" {
279 break
280 }
281 continue
282 }
283
284 break
285 }
286 return cmd, used
287 }
288
289 func invoke(cmd *base.Command, args []string) {
290
291 if cmd != envcmd.CmdEnv {
292 buildcfg.Check()
293 if cfg.ExperimentErr != nil {
294 base.Fatal(cfg.ExperimentErr)
295 }
296 }
297
298
299
300
301
302
303 cfg.OrigEnv = toolchain.FilterEnv(os.Environ())
304 cfg.CmdEnv = envcmd.MkEnv()
305 for _, env := range cfg.CmdEnv {
306 if os.Getenv(env.Name) != env.Value {
307 os.Setenv(env.Name, env.Value)
308 }
309 }
310
311 cmd.Flag.Usage = func() { cmd.Usage() }
312 if cmd.CustomFlags {
313 args = args[1:]
314 } else {
315 base.SetFromGOFLAGS(&cmd.Flag)
316 cmd.Flag.Parse(args[1:])
317 flagCounterPrefix := "go/" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "/flag"
318 counter.CountFlags(flagCounterPrefix+":", cmd.Flag)
319 counter.CountFlagValue(flagCounterPrefix+"/", cmd.Flag, "buildmode")
320 args = cmd.Flag.Args()
321 }
322
323 if cfg.DebugRuntimeTrace != "" {
324 f, err := os.Create(cfg.DebugRuntimeTrace)
325 if err != nil {
326 base.Fatalf("creating trace file: %v", err)
327 }
328 if err := rtrace.Start(f); err != nil {
329 base.Fatalf("starting event trace: %v", err)
330 }
331 defer func() {
332 rtrace.Stop()
333 f.Close()
334 }()
335 }
336
337 ctx := maybeStartTrace(context.Background())
338 ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
339 cmd.Run(ctx, cmd, args)
340 span.Done()
341 }
342
343 func init() {
344 base.Usage = mainUsage
345 }
346
347 func mainUsage() {
348 help.PrintUsage(os.Stderr, base.Go)
349 os.Exit(2)
350 }
351
352 func maybeStartTrace(pctx context.Context) context.Context {
353 if cfg.DebugTrace == "" {
354 return pctx
355 }
356
357 ctx, close, err := trace.Start(pctx, cfg.DebugTrace)
358 if err != nil {
359 base.Fatalf("failed to start trace: %v", err)
360 }
361 base.AtExit(func() {
362 if err := close(); err != nil {
363 base.Fatalf("failed to stop trace: %v", err)
364 }
365 })
366
367 return ctx
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382 func handleChdirFlag() {
383 _, used := lookupCmd(os.Args[1:])
384 used++
385 if used >= len(os.Args) {
386 return
387 }
388
389 var dir string
390 switch a := os.Args[used]; {
391 default:
392 return
393
394 case a == "-C", a == "--C":
395 if used+1 >= len(os.Args) {
396 return
397 }
398 dir = os.Args[used+1]
399 os.Args = slices.Delete(os.Args, used, used+2)
400
401 case strings.HasPrefix(a, "-C="), strings.HasPrefix(a, "--C="):
402 _, dir, _ = strings.Cut(a, "=")
403 os.Args = slices.Delete(os.Args, used, used+1)
404 }
405 counter.Inc("go/flag:C")
406
407 if err := os.Chdir(dir); err != nil {
408 base.Fatalf("go: %v", err)
409 }
410 }
411
View as plain text