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