1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:generate ./mkalldocs.sh
6
7package main
8
9import (
10	"context"
11	"flag"
12	"fmt"
13	"log"
14	"os"
15	"path/filepath"
16	"runtime"
17	"strings"
18
19	"cmd/go/internal/base"
20	"cmd/go/internal/bug"
21	"cmd/go/internal/cfg"
22	"cmd/go/internal/clean"
23	"cmd/go/internal/doc"
24	"cmd/go/internal/envcmd"
25	"cmd/go/internal/fix"
26	"cmd/go/internal/fmtcmd"
27	"cmd/go/internal/generate"
28	"cmd/go/internal/get"
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/test"
37	"cmd/go/internal/tool"
38	"cmd/go/internal/trace"
39	"cmd/go/internal/version"
40	"cmd/go/internal/vet"
41	"cmd/go/internal/work"
42)
43
44func init() {
45	base.Go.Commands = []*base.Command{
46		bug.CmdBug,
47		work.CmdBuild,
48		clean.CmdClean,
49		doc.CmdDoc,
50		envcmd.CmdEnv,
51		fix.CmdFix,
52		fmtcmd.CmdFmt,
53		generate.CmdGenerate,
54		modget.CmdGet,
55		work.CmdInstall,
56		list.CmdList,
57		modcmd.CmdMod,
58		run.CmdRun,
59		test.CmdTest,
60		tool.CmdTool,
61		version.CmdVersion,
62		vet.CmdVet,
63
64		help.HelpBuildConstraint,
65		help.HelpBuildmode,
66		help.HelpC,
67		help.HelpCache,
68		help.HelpEnvironment,
69		help.HelpFileType,
70		modload.HelpGoMod,
71		help.HelpGopath,
72		get.HelpGopathGet,
73		modfetch.HelpGoproxy,
74		help.HelpImportPath,
75		modload.HelpModules,
76		modget.HelpModuleGet,
77		modfetch.HelpModuleAuth,
78		help.HelpPackages,
79		modfetch.HelpPrivate,
80		test.HelpTestflag,
81		test.HelpTestfunc,
82		modget.HelpVCS,
83	}
84}
85
86func main() {
87	_ = go11tag
88	flag.Usage = base.Usage
89	flag.Parse()
90	log.SetFlags(0)
91
92	args := flag.Args()
93	if len(args) < 1 {
94		base.Usage()
95	}
96
97	if args[0] == "get" || args[0] == "help" {
98		if !modload.WillBeEnabled() {
99			// Replace module-aware get with GOPATH get if appropriate.
100			*modget.CmdGet = *get.CmdGet
101		}
102	}
103
104	cfg.CmdName = args[0] // for error messages
105	if args[0] == "help" {
106		help.Help(os.Stdout, args[1:])
107		return
108	}
109
110	// Diagnose common mistake: GOPATH==GOROOT.
111	// This setting is equivalent to not setting GOPATH at all,
112	// which is not what most people want when they do it.
113	if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.GOROOT()) {
114		fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
115	} else {
116		for _, p := range filepath.SplitList(gopath) {
117			// Some GOPATHs have empty directory elements - ignore them.
118			// See issue 21928 for details.
119			if p == "" {
120				continue
121			}
122			// Note: using HasPrefix instead of Contains because a ~ can appear
123			// in the middle of directory elements, such as /tmp/git-1.8.2~rc3
124			// or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell.
125			if strings.HasPrefix(p, "~") {
126				fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p)
127				os.Exit(2)
128			}
129			if !filepath.IsAbs(p) {
130				if cfg.Getenv("GOPATH") == "" {
131					// We inferred $GOPATH from $HOME and did a bad job at it.
132					// Instead of dying, uninfer it.
133					cfg.BuildContext.GOPATH = ""
134				} else {
135					fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
136					os.Exit(2)
137				}
138			}
139		}
140	}
141
142	// For gccgo this is fine, carry on.
143	// Note that this check is imperfect as we have not yet parsed
144	// the -compiler flag.
145	if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() && runtime.Compiler != "gccgo" {
146		fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT)
147		os.Exit(2)
148	}
149
150	// Set environment (GOOS, GOARCH, etc) explicitly.
151	// In theory all the commands we invoke should have
152	// the same default computation of these as we do,
153	// but in practice there might be skew
154	// This makes sure we all agree.
155	cfg.OrigEnv = os.Environ()
156	cfg.CmdEnv = envcmd.MkEnv()
157	for _, env := range cfg.CmdEnv {
158		if os.Getenv(env.Name) != env.Value {
159			os.Setenv(env.Name, env.Value)
160		}
161	}
162
163BigCmdLoop:
164	for bigCmd := base.Go; ; {
165		for _, cmd := range bigCmd.Commands {
166			if cmd.Name() != args[0] {
167				continue
168			}
169			if len(cmd.Commands) > 0 {
170				bigCmd = cmd
171				args = args[1:]
172				if len(args) == 0 {
173					help.PrintUsage(os.Stderr, bigCmd)
174					base.SetExitStatus(2)
175					base.Exit()
176				}
177				if args[0] == "help" {
178					// Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'.
179					help.Help(os.Stdout, append(strings.Split(cfg.CmdName, " "), args[1:]...))
180					return
181				}
182				cfg.CmdName += " " + args[0]
183				continue BigCmdLoop
184			}
185			if !cmd.Runnable() {
186				continue
187			}
188			cmd.Flag.Usage = func() { cmd.Usage() }
189			if cmd.CustomFlags {
190				args = args[1:]
191			} else {
192				base.SetFromGOFLAGS(&cmd.Flag)
193				cmd.Flag.Parse(args[1:])
194				args = cmd.Flag.Args()
195			}
196			ctx := maybeStartTrace(context.Background())
197			ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
198			cmd.Run(ctx, cmd, args)
199			span.Done()
200			base.Exit()
201			return
202		}
203		helpArg := ""
204		if i := strings.LastIndex(cfg.CmdName, " "); i >= 0 {
205			helpArg = " " + cfg.CmdName[:i]
206		}
207		fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cfg.CmdName, helpArg)
208		base.SetExitStatus(2)
209		base.Exit()
210	}
211}
212
213func init() {
214	base.Usage = mainUsage
215}
216
217func mainUsage() {
218	help.PrintUsage(os.Stderr, base.Go)
219	os.Exit(2)
220}
221
222func maybeStartTrace(pctx context.Context) context.Context {
223	if cfg.DebugTrace == "" {
224		return pctx
225	}
226
227	ctx, close, err := trace.Start(pctx, cfg.DebugTrace)
228	if err != nil {
229		base.Fatalf("failed to start trace: %v", err)
230	}
231	base.AtExit(func() {
232		if err := close(); err != nil {
233			base.Fatalf("failed to stop trace: %v", err)
234		}
235	})
236
237	return ctx
238}
239