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