github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/base/base.go (about)

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