github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/main.go (about) 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 gocmd 8 9 import ( 10 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/toolchain" 11 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/workcmd" 12 "context" 13 "github.com/bir3/gocompiler/src/cmd/gocmd/flag" 14 "fmt" 15 "github.com/bir3/gocompiler/src/internal/buildcfg" 16 "log" 17 "os" 18 "path/filepath" 19 rtrace "runtime/trace" 20 "slices" 21 "strings" 22 23 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 24 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/bug" 25 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 26 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/clean" 27 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/doc" 28 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/envcmd" 29 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fix" 30 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fmtcmd" 31 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/generate" 32 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/help" 33 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/list" 34 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modcmd" 35 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch" 36 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modget" 37 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modload" 38 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/run" 39 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/test" 40 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/tool" 41 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/trace" 42 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/version" 43 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/vet" 44 "github.com/bir3/gocompiler/src/cmd/gocmd/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] // for error messages 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 // 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(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 // 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 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 // Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'. 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 // lookupCmd interprets the initial elements of args 180 // to find a command to run (cmd.Runnable() == true) 181 // or else a command group that ran out of arguments 182 // or had an unknown subcommand (len(cmd.Commands) > 0). 183 // It returns that command and the number of elements of args 184 // that it took to arrive at that command. 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 // len(c.Commands) == 0 && !c.Runnable() => help text; stop at "help" 206 break 207 } 208 return cmd, used 209 } 210 211 func invoke(cmd *base.Command, args []string) { 212 // 'go env' handles checking the build config 213 if cmd != envcmd.CmdEnv { 214 buildcfg.Check() 215 if cfg.ExperimentErr != nil { 216 base.Fatal(cfg.ExperimentErr) 217 } 218 } 219 220 // Set environment (GOOS, GOARCH, etc) explicitly. 221 // In theory all the commands we invoke should have 222 // the same default computation of these as we do, 223 // but in practice there might be skew 224 // This makes sure we all agree. 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 // handleChdirFlag handles the -C flag before doing anything else. 289 // The -C flag must be the first flag on the command line, to make it easy to find 290 // even with commands that have custom flag parsing. 291 // handleChdirFlag handles the flag by chdir'ing to the directory 292 // and then removing that flag from the command line entirely. 293 // 294 // We have to handle the -C flag this way for two reasons: 295 // 296 // 1. Toolchain selection needs to be in the right directory to look for go.mod and go.work. 297 // 298 // 2. A toolchain switch later on reinvokes the new go command with the same arguments. 299 // The parent toolchain has already done the chdir; the child must not try to do it again. 300 func handleChdirFlag() { 301 _, used := lookupCmd(os.Args[1:]) 302 used++ // because of [1:] 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 }