]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/main.go
cmd/go: additional doc-inspired tests and bug fixes
[gostls13.git] / src / cmd / go / main.go
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 go test cmd/go -v -run=TestDocsUpToDate -fixdocs
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         "runtime"
20         rtrace "runtime/trace"
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/get"
33         "cmd/go/internal/help"
34         "cmd/go/internal/list"
35         "cmd/go/internal/modcmd"
36         "cmd/go/internal/modfetch"
37         "cmd/go/internal/modget"
38         "cmd/go/internal/modload"
39         "cmd/go/internal/run"
40         "cmd/go/internal/test"
41         "cmd/go/internal/tool"
42         "cmd/go/internal/trace"
43         "cmd/go/internal/version"
44         "cmd/go/internal/vet"
45         "cmd/go/internal/work"
46 )
47
48 func init() {
49         base.Go.Commands = []*base.Command{
50                 bug.CmdBug,
51                 work.CmdBuild,
52                 clean.CmdClean,
53                 doc.CmdDoc,
54                 envcmd.CmdEnv,
55                 fix.CmdFix,
56                 fmtcmd.CmdFmt,
57                 generate.CmdGenerate,
58                 modget.CmdGet,
59                 work.CmdInstall,
60                 list.CmdList,
61                 modcmd.CmdMod,
62                 workcmd.CmdWork,
63                 run.CmdRun,
64                 test.CmdTest,
65                 tool.CmdTool,
66                 version.CmdVersion,
67                 vet.CmdVet,
68
69                 help.HelpBuildConstraint,
70                 help.HelpBuildmode,
71                 help.HelpC,
72                 help.HelpCache,
73                 help.HelpEnvironment,
74                 help.HelpFileType,
75                 modload.HelpGoMod,
76                 help.HelpGopath,
77                 get.HelpGopathGet,
78                 modfetch.HelpGoproxy,
79                 help.HelpImportPath,
80                 modload.HelpModules,
81                 modget.HelpModuleGet,
82                 modfetch.HelpModuleAuth,
83                 help.HelpPackages,
84                 modfetch.HelpPrivate,
85                 test.HelpTestflag,
86                 test.HelpTestfunc,
87                 modget.HelpVCS,
88         }
89 }
90
91 var _ = go11tag
92
93 func main() {
94         log.SetFlags(0)
95         toolchain.Select()
96
97         flag.Usage = base.Usage
98         flag.Parse()
99
100         args := flag.Args()
101         if len(args) < 1 {
102                 base.Usage()
103         }
104
105         if args[0] == "get" || args[0] == "help" {
106                 if !modload.WillBeEnabled() {
107                         // Replace module-aware get with GOPATH get if appropriate.
108                         *modget.CmdGet = *get.CmdGet
109                 }
110         }
111
112         cfg.CmdName = args[0] // for error messages
113         if args[0] == "help" {
114                 help.Help(os.Stdout, args[1:])
115                 return
116         }
117
118         // Diagnose common mistake: GOPATH==GOROOT.
119         // This setting is equivalent to not setting GOPATH at all,
120         // which is not what most people want when they do it.
121         if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.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                         // Some GOPATHs have empty directory elements - ignore them.
126                         // See issue 21928 for details.
127                         if p == "" {
128                                 continue
129                         }
130                         // Note: using HasPrefix instead of Contains because a ~ can appear
131                         // in the middle of directory elements, such as /tmp/git-1.8.2~rc3
132                         // or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell.
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                                         // We inferred $GOPATH from $HOME and did a bad job at it.
140                                         // Instead of dying, uninfer it.
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         if cfg.GOROOT == "" {
151                 fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n")
152                 os.Exit(2)
153         }
154         if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() {
155                 fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT)
156                 os.Exit(2)
157         }
158
159 BigCmdLoop:
160         for bigCmd := base.Go; ; {
161                 for _, cmd := range bigCmd.Commands {
162                         if cmd.Name() != args[0] {
163                                 continue
164                         }
165                         if len(cmd.Commands) > 0 {
166                                 bigCmd = cmd
167                                 args = args[1:]
168                                 if len(args) == 0 {
169                                         help.PrintUsage(os.Stderr, bigCmd)
170                                         base.SetExitStatus(2)
171                                         base.Exit()
172                                 }
173                                 if args[0] == "help" {
174                                         // Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'.
175                                         help.Help(os.Stdout, append(strings.Split(cfg.CmdName, " "), args[1:]...))
176                                         return
177                                 }
178                                 cfg.CmdName += " " + args[0]
179                                 continue BigCmdLoop
180                         }
181                         if !cmd.Runnable() {
182                                 continue
183                         }
184                         invoke(cmd, args)
185                         base.Exit()
186                         return
187                 }
188                 helpArg := ""
189                 if i := strings.LastIndex(cfg.CmdName, " "); i >= 0 {
190                         helpArg = " " + cfg.CmdName[:i]
191                 }
192                 fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cfg.CmdName, helpArg)
193                 base.SetExitStatus(2)
194                 base.Exit()
195         }
196 }
197
198 func invoke(cmd *base.Command, args []string) {
199         // 'go env' handles checking the build config
200         if cmd != envcmd.CmdEnv {
201                 buildcfg.Check()
202                 if cfg.ExperimentErr != nil {
203                         base.Fatal(cfg.ExperimentErr)
204                 }
205         }
206
207         // Set environment (GOOS, GOARCH, etc) explicitly.
208         // In theory all the commands we invoke should have
209         // the same default computation of these as we do,
210         // but in practice there might be skew
211         // This makes sure we all agree.
212         cfg.OrigEnv = toolchain.FilterEnv(os.Environ())
213         cfg.CmdEnv = envcmd.MkEnv()
214         for _, env := range cfg.CmdEnv {
215                 if os.Getenv(env.Name) != env.Value {
216                         os.Setenv(env.Name, env.Value)
217                 }
218         }
219
220         cmd.Flag.Usage = func() { cmd.Usage() }
221         if cmd.CustomFlags {
222                 args = args[1:]
223         } else {
224                 base.SetFromGOFLAGS(&cmd.Flag)
225                 cmd.Flag.Parse(args[1:])
226                 args = cmd.Flag.Args()
227         }
228
229         if cfg.DebugRuntimeTrace != "" {
230                 f, err := os.Create(cfg.DebugRuntimeTrace)
231                 if err != nil {
232                         base.Fatalf("creating trace file: %v", err)
233                 }
234                 if err := rtrace.Start(f); err != nil {
235                         base.Fatalf("starting event trace: %v", err)
236                 }
237                 defer func() {
238                         rtrace.Stop()
239                 }()
240         }
241
242         ctx := maybeStartTrace(context.Background())
243         ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
244         cmd.Run(ctx, cmd, args)
245         span.Done()
246 }
247
248 func init() {
249         base.Usage = mainUsage
250 }
251
252 func mainUsage() {
253         help.PrintUsage(os.Stderr, base.Go)
254         os.Exit(2)
255 }
256
257 func maybeStartTrace(pctx context.Context) context.Context {
258         if cfg.DebugTrace == "" {
259                 return pctx
260         }
261
262         ctx, close, err := trace.Start(pctx, cfg.DebugTrace)
263         if err != nil {
264                 base.Fatalf("failed to start trace: %v", err)
265         }
266         base.AtExit(func() {
267                 if err := close(); err != nil {
268                         base.Fatalf("failed to stop trace: %v", err)
269                 }
270         })
271
272         return ctx
273 }