summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/base/base.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/cmd/go/internal/base/base.go
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/internal/base/base.go')
-rw-r--r--src/cmd/go/internal/base/base.go223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go
new file mode 100644
index 0000000..2171d13
--- /dev/null
+++ b/src/cmd/go/internal/base/base.go
@@ -0,0 +1,223 @@
+// Copyright 2017 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.
+
+// Package base defines shared basic pieces of the go command,
+// in particular logging and the Command structure.
+package base
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+ "reflect"
+ "strings"
+ "sync"
+
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/str"
+)
+
+// A Command is an implementation of a go command
+// like go build or go fix.
+type Command struct {
+ // Run runs the command.
+ // The args are the arguments after the command name.
+ Run func(ctx context.Context, cmd *Command, args []string)
+
+ // UsageLine is the one-line usage message.
+ // The words between "go" and the first flag or argument in the line are taken to be the command name.
+ UsageLine string
+
+ // Short is the short description shown in the 'go help' output.
+ Short string
+
+ // Long is the long message shown in the 'go help <this-command>' output.
+ Long string
+
+ // Flag is a set of flags specific to this command.
+ Flag flag.FlagSet
+
+ // CustomFlags indicates that the command will do its own
+ // flag parsing.
+ CustomFlags bool
+
+ // Commands lists the available commands and help topics.
+ // The order here is the order in which they are printed by 'go help'.
+ // Note that subcommands are in general best avoided.
+ Commands []*Command
+}
+
+var Go = &Command{
+ UsageLine: "go",
+ Long: `Go is a tool for managing Go source code.`,
+ // Commands initialized in package main
+}
+
+// Lookup returns the subcommand with the given name, if any.
+// Otherwise it returns nil.
+//
+// Lookup ignores subcommands that have len(c.Commands) == 0 and c.Run == nil.
+// Such subcommands are only for use as arguments to "help".
+func (c *Command) Lookup(name string) *Command {
+ for _, sub := range c.Commands {
+ if sub.Name() == name && (len(c.Commands) > 0 || c.Runnable()) {
+ return sub
+ }
+ }
+ return nil
+}
+
+// hasFlag reports whether a command or any of its subcommands contain the given
+// flag.
+func hasFlag(c *Command, name string) bool {
+ if f := c.Flag.Lookup(name); f != nil {
+ return true
+ }
+ for _, sub := range c.Commands {
+ if hasFlag(sub, name) {
+ return true
+ }
+ }
+ return false
+}
+
+// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
+func (c *Command) LongName() string {
+ name := c.UsageLine
+ if i := strings.Index(name, " ["); i >= 0 {
+ name = name[:i]
+ }
+ if name == "go" {
+ return ""
+ }
+ return strings.TrimPrefix(name, "go ")
+}
+
+// Name returns the command's short name: the last word in the usage line before a flag or argument.
+func (c *Command) Name() string {
+ name := c.LongName()
+ if i := strings.LastIndex(name, " "); i >= 0 {
+ name = name[i+1:]
+ }
+ return name
+}
+
+func (c *Command) Usage() {
+ fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
+ fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName())
+ SetExitStatus(2)
+ Exit()
+}
+
+// Runnable reports whether the command can be run; otherwise
+// it is a documentation pseudo-command such as importpath.
+func (c *Command) Runnable() bool {
+ return c.Run != nil
+}
+
+var atExitFuncs []func()
+
+func AtExit(f func()) {
+ atExitFuncs = append(atExitFuncs, f)
+}
+
+func Exit() {
+ for _, f := range atExitFuncs {
+ f()
+ }
+ os.Exit(exitStatus)
+}
+
+func Fatalf(format string, args ...any) {
+ Errorf(format, args...)
+ Exit()
+}
+
+func Errorf(format string, args ...any) {
+ log.Printf(format, args...)
+ SetExitStatus(1)
+}
+
+func ExitIfErrors() {
+ if exitStatus != 0 {
+ Exit()
+ }
+}
+
+func Error(err error) {
+ // We use errors.Join to return multiple errors from various routines.
+ // If we receive multiple errors joined with a basic errors.Join,
+ // handle each one separately so that they all have the leading "go: " prefix.
+ // A plain interface check is not good enough because there might be
+ // other kinds of structured errors that are logically one unit and that
+ // add other context: only handling the wrapped errors would lose
+ // that context.
+ if err != nil && reflect.TypeOf(err).String() == "*errors.joinError" {
+ for _, e := range err.(interface{ Unwrap() []error }).Unwrap() {
+ Error(e)
+ }
+ return
+ }
+ Errorf("go: %v", err)
+}
+
+func Fatal(err error) {
+ Error(err)
+ Exit()
+}
+
+var exitStatus = 0
+var exitMu sync.Mutex
+
+func SetExitStatus(n int) {
+ exitMu.Lock()
+ if exitStatus < n {
+ exitStatus = n
+ }
+ exitMu.Unlock()
+}
+
+func GetExitStatus() int {
+ return exitStatus
+}
+
+// Run runs the command, with stdout and stderr
+// connected to the go command's own stdout and stderr.
+// If the command fails, Run reports the error using Errorf.
+func Run(cmdargs ...any) {
+ cmdline := str.StringList(cmdargs...)
+ if cfg.BuildN || cfg.BuildX {
+ fmt.Printf("%s\n", strings.Join(cmdline, " "))
+ if cfg.BuildN {
+ return
+ }
+ }
+
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ Errorf("%v", err)
+ }
+}
+
+// RunStdin is like run but connects Stdin.
+func RunStdin(cmdline []string) {
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Env = cfg.OrigEnv
+ StartSigHandlers()
+ if err := cmd.Run(); err != nil {
+ Errorf("%v", err)
+ }
+}
+
+// Usage is the usage-reporting function, filled in by package main
+// but here for reference by other packages.
+var Usage func()