diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
commit | f6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch) | |
tree | 7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/cmd/go/main.go | |
parent | Initial commit. (diff) | |
download | golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip |
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/main.go')
-rw-r--r-- | src/cmd/go/main.go | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go new file mode 100644 index 0000000..d380aae --- /dev/null +++ b/src/cmd/go/main.go @@ -0,0 +1,327 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go test cmd/go -v -run=^TestDocsUpToDate$ -fixdocs + +package main + +import ( + "cmd/go/internal/toolchain" + "cmd/go/internal/workcmd" + "context" + "flag" + "fmt" + "internal/buildcfg" + "log" + "os" + "path/filepath" + rtrace "runtime/trace" + "slices" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/bug" + "cmd/go/internal/cfg" + "cmd/go/internal/clean" + "cmd/go/internal/doc" + "cmd/go/internal/envcmd" + "cmd/go/internal/fix" + "cmd/go/internal/fmtcmd" + "cmd/go/internal/generate" + "cmd/go/internal/help" + "cmd/go/internal/list" + "cmd/go/internal/modcmd" + "cmd/go/internal/modfetch" + "cmd/go/internal/modget" + "cmd/go/internal/modload" + "cmd/go/internal/run" + "cmd/go/internal/test" + "cmd/go/internal/tool" + "cmd/go/internal/trace" + "cmd/go/internal/version" + "cmd/go/internal/vet" + "cmd/go/internal/work" +) + +func init() { + base.Go.Commands = []*base.Command{ + bug.CmdBug, + work.CmdBuild, + clean.CmdClean, + doc.CmdDoc, + envcmd.CmdEnv, + fix.CmdFix, + fmtcmd.CmdFmt, + generate.CmdGenerate, + modget.CmdGet, + work.CmdInstall, + list.CmdList, + modcmd.CmdMod, + workcmd.CmdWork, + run.CmdRun, + test.CmdTest, + tool.CmdTool, + version.CmdVersion, + vet.CmdVet, + + help.HelpBuildConstraint, + help.HelpBuildmode, + help.HelpC, + help.HelpCache, + help.HelpEnvironment, + help.HelpFileType, + modload.HelpGoMod, + help.HelpGopath, + modfetch.HelpGoproxy, + help.HelpImportPath, + modload.HelpModules, + modfetch.HelpModuleAuth, + help.HelpPackages, + modfetch.HelpPrivate, + test.HelpTestflag, + test.HelpTestfunc, + modget.HelpVCS, + } +} + +var _ = go11tag + +func main() { + log.SetFlags(0) + handleChdirFlag() + toolchain.Select() + + flag.Usage = base.Usage + flag.Parse() + + args := flag.Args() + if len(args) < 1 { + base.Usage() + } + + cfg.CmdName = args[0] // for error messages + if args[0] == "help" { + help.Help(os.Stdout, args[1:]) + return + } + + if cfg.GOROOT == "" { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n") + os.Exit(2) + } + if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT) + os.Exit(2) + } + + // Diagnose common mistake: GOPATH==GOROOT. + // This setting is equivalent to not setting GOPATH at all, + // which is not what most people want when they do it. + if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(cfg.GOROOT) { + fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) + } else { + for _, p := range filepath.SplitList(gopath) { + // Some GOPATHs have empty directory elements - ignore them. + // See issue 21928 for details. + if p == "" { + continue + } + // Note: using HasPrefix instead of Contains because a ~ can appear + // in the middle of directory elements, such as /tmp/git-1.8.2~rc3 + // or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell. + if strings.HasPrefix(p, "~") { + fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p) + os.Exit(2) + } + if !filepath.IsAbs(p) { + if cfg.Getenv("GOPATH") == "" { + // We inferred $GOPATH from $HOME and did a bad job at it. + // Instead of dying, uninfer it. + cfg.BuildContext.GOPATH = "" + } else { + fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p) + os.Exit(2) + } + } + } + } + + cmd, used := lookupCmd(args) + cfg.CmdName = strings.Join(args[:used], " ") + if len(cmd.Commands) > 0 { + if used >= len(args) { + help.PrintUsage(os.Stderr, cmd) + base.SetExitStatus(2) + base.Exit() + } + if args[used] == "help" { + // Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'. + help.Help(os.Stdout, append(slices.Clip(args[:used]), args[used+1:]...)) + base.Exit() + } + helpArg := "" + if used > 0 { + helpArg += " " + strings.Join(args[:used], " ") + } + cmdName := cfg.CmdName + if cmdName == "" { + cmdName = args[0] + } + fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cmdName, helpArg) + base.SetExitStatus(2) + base.Exit() + } + invoke(cmd, args[used-1:]) + base.Exit() +} + +// lookupCmd interprets the initial elements of args +// to find a command to run (cmd.Runnable() == true) +// or else a command group that ran out of arguments +// or had an unknown subcommand (len(cmd.Commands) > 0). +// It returns that command and the number of elements of args +// that it took to arrive at that command. +func lookupCmd(args []string) (cmd *base.Command, used int) { + cmd = base.Go + for used < len(args) { + c := cmd.Lookup(args[used]) + if c == nil { + break + } + if c.Runnable() { + cmd = c + used++ + break + } + if len(c.Commands) > 0 { + cmd = c + used++ + if used >= len(args) || args[0] == "help" { + break + } + continue + } + // len(c.Commands) == 0 && !c.Runnable() => help text; stop at "help" + break + } + return cmd, used +} + +func invoke(cmd *base.Command, args []string) { + // 'go env' handles checking the build config + if cmd != envcmd.CmdEnv { + buildcfg.Check() + if cfg.ExperimentErr != nil { + base.Fatal(cfg.ExperimentErr) + } + } + + // Set environment (GOOS, GOARCH, etc) explicitly. + // In theory all the commands we invoke should have + // the same default computation of these as we do, + // but in practice there might be skew + // This makes sure we all agree. + cfg.OrigEnv = toolchain.FilterEnv(os.Environ()) + cfg.CmdEnv = envcmd.MkEnv() + for _, env := range cfg.CmdEnv { + if os.Getenv(env.Name) != env.Value { + os.Setenv(env.Name, env.Value) + } + } + + cmd.Flag.Usage = func() { cmd.Usage() } + if cmd.CustomFlags { + args = args[1:] + } else { + base.SetFromGOFLAGS(&cmd.Flag) + cmd.Flag.Parse(args[1:]) + args = cmd.Flag.Args() + } + + if cfg.DebugRuntimeTrace != "" { + f, err := os.Create(cfg.DebugRuntimeTrace) + if err != nil { + base.Fatalf("creating trace file: %v", err) + } + if err := rtrace.Start(f); err != nil { + base.Fatalf("starting event trace: %v", err) + } + defer func() { + rtrace.Stop() + }() + } + + ctx := maybeStartTrace(context.Background()) + ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command")) + cmd.Run(ctx, cmd, args) + span.Done() +} + +func init() { + base.Usage = mainUsage +} + +func mainUsage() { + help.PrintUsage(os.Stderr, base.Go) + os.Exit(2) +} + +func maybeStartTrace(pctx context.Context) context.Context { + if cfg.DebugTrace == "" { + return pctx + } + + ctx, close, err := trace.Start(pctx, cfg.DebugTrace) + if err != nil { + base.Fatalf("failed to start trace: %v", err) + } + base.AtExit(func() { + if err := close(); err != nil { + base.Fatalf("failed to stop trace: %v", err) + } + }) + + return ctx +} + +// handleChdirFlag handles the -C flag before doing anything else. +// The -C flag must be the first flag on the command line, to make it easy to find +// even with commands that have custom flag parsing. +// handleChdirFlag handles the flag by chdir'ing to the directory +// and then removing that flag from the command line entirely. +// +// We have to handle the -C flag this way for two reasons: +// +// 1. Toolchain selection needs to be in the right directory to look for go.mod and go.work. +// +// 2. A toolchain switch later on reinvokes the new go command with the same arguments. +// The parent toolchain has already done the chdir; the child must not try to do it again. +func handleChdirFlag() { + _, used := lookupCmd(os.Args[1:]) + used++ // because of [1:] + if used >= len(os.Args) { + return + } + + var dir string + switch a := os.Args[used]; { + default: + return + + case a == "-C", a == "--C": + if used+1 >= len(os.Args) { + return + } + dir = os.Args[used+1] + os.Args = slices.Delete(os.Args, used, used+2) + + case strings.HasPrefix(a, "-C="), strings.HasPrefix(a, "--C="): + _, dir, _ = strings.Cut(a, "=") + os.Args = slices.Delete(os.Args, used, used+1) + } + + if err := os.Chdir(dir); err != nil { + base.Fatalf("go: %v", err) + } +} |