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