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