Source file src/cmd/go/internal/base/base.go

     1  // Copyright 2017 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  // Package base defines shared basic pieces of the go command,
     6  // in particular logging and the Command structure.
     7  package base
     8  
     9  import (
    10  	"context"
    11  	"flag"
    12  	"fmt"
    13  	exec "internal/execabs"
    14  	"log"
    15  	"os"
    16  	"strings"
    17  	"sync"
    18  
    19  	"cmd/go/internal/cfg"
    20  	"cmd/go/internal/str"
    21  )
    22  
    23  // A Command is an implementation of a go command
    24  // like go build or go fix.
    25  type Command struct {
    26  	// Run runs the command.
    27  	// The args are the arguments after the command name.
    28  	Run func(ctx context.Context, cmd *Command, args []string)
    29  
    30  	// UsageLine is the one-line usage message.
    31  	// The words between "go" and the first flag or argument in the line are taken to be the command name.
    32  	UsageLine string
    33  
    34  	// Short is the short description shown in the 'go help' output.
    35  	Short string
    36  
    37  	// Long is the long message shown in the 'go help <this-command>' output.
    38  	Long string
    39  
    40  	// Flag is a set of flags specific to this command.
    41  	Flag flag.FlagSet
    42  
    43  	// CustomFlags indicates that the command will do its own
    44  	// flag parsing.
    45  	CustomFlags bool
    46  
    47  	// Commands lists the available commands and help topics.
    48  	// The order here is the order in which they are printed by 'go help'.
    49  	// Note that subcommands are in general best avoided.
    50  	Commands []*Command
    51  }
    52  
    53  var Go = &Command{
    54  	UsageLine: "go",
    55  	Long:      `Go is a tool for managing Go source code.`,
    56  	// Commands initialized in package main
    57  }
    58  
    59  // hasFlag reports whether a command or any of its subcommands contain the given
    60  // flag.
    61  func hasFlag(c *Command, name string) bool {
    62  	if f := c.Flag.Lookup(name); f != nil {
    63  		return true
    64  	}
    65  	for _, sub := range c.Commands {
    66  		if hasFlag(sub, name) {
    67  			return true
    68  		}
    69  	}
    70  	return false
    71  }
    72  
    73  // LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
    74  func (c *Command) LongName() string {
    75  	name := c.UsageLine
    76  	if i := strings.Index(name, " ["); i >= 0 {
    77  		name = name[:i]
    78  	}
    79  	if name == "go" {
    80  		return ""
    81  	}
    82  	return strings.TrimPrefix(name, "go ")
    83  }
    84  
    85  // Name returns the command's short name: the last word in the usage line before a flag or argument.
    86  func (c *Command) Name() string {
    87  	name := c.LongName()
    88  	if i := strings.LastIndex(name, " "); i >= 0 {
    89  		name = name[i+1:]
    90  	}
    91  	return name
    92  }
    93  
    94  func (c *Command) Usage() {
    95  	fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
    96  	fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName())
    97  	SetExitStatus(2)
    98  	Exit()
    99  }
   100  
   101  // Runnable reports whether the command can be run; otherwise
   102  // it is a documentation pseudo-command such as importpath.
   103  func (c *Command) Runnable() bool {
   104  	return c.Run != nil
   105  }
   106  
   107  var atExitFuncs []func()
   108  
   109  func AtExit(f func()) {
   110  	atExitFuncs = append(atExitFuncs, f)
   111  }
   112  
   113  func Exit() {
   114  	for _, f := range atExitFuncs {
   115  		f()
   116  	}
   117  	os.Exit(exitStatus)
   118  }
   119  
   120  func Fatalf(format string, args ...any) {
   121  	Errorf(format, args...)
   122  	Exit()
   123  }
   124  
   125  func Errorf(format string, args ...any) {
   126  	log.Printf(format, args...)
   127  	SetExitStatus(1)
   128  }
   129  
   130  func ExitIfErrors() {
   131  	if exitStatus != 0 {
   132  		Exit()
   133  	}
   134  }
   135  
   136  var exitStatus = 0
   137  var exitMu sync.Mutex
   138  
   139  func SetExitStatus(n int) {
   140  	exitMu.Lock()
   141  	if exitStatus < n {
   142  		exitStatus = n
   143  	}
   144  	exitMu.Unlock()
   145  }
   146  
   147  func GetExitStatus() int {
   148  	return exitStatus
   149  }
   150  
   151  // Run runs the command, with stdout and stderr
   152  // connected to the go command's own stdout and stderr.
   153  // If the command fails, Run reports the error using Errorf.
   154  func Run(cmdargs ...any) {
   155  	cmdline := str.StringList(cmdargs...)
   156  	if cfg.BuildN || cfg.BuildX {
   157  		fmt.Printf("%s\n", strings.Join(cmdline, " "))
   158  		if cfg.BuildN {
   159  			return
   160  		}
   161  	}
   162  
   163  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   164  	cmd.Stdout = os.Stdout
   165  	cmd.Stderr = os.Stderr
   166  	if err := cmd.Run(); err != nil {
   167  		Errorf("%v", err)
   168  	}
   169  }
   170  
   171  // RunStdin is like run but connects Stdin.
   172  func RunStdin(cmdline []string) {
   173  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   174  	cmd.Stdin = os.Stdin
   175  	cmd.Stdout = os.Stdout
   176  	cmd.Stderr = os.Stderr
   177  	cmd.Env = cfg.OrigEnv
   178  	StartSigHandlers()
   179  	if err := cmd.Run(); err != nil {
   180  		Errorf("%v", err)
   181  	}
   182  }
   183  
   184  // Usage is the usage-reporting function, filled in by package main
   185  // but here for reference by other packages.
   186  var Usage func()
   187  

View as plain text