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