github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/go/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  	"cmd/go/internal/cfg"
    22  	"cmd/go/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 first word in the line is 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  
    50  // Commands lists the available commands and help topics.
    51  // The order here is the order in which they are printed by 'go help'.
    52  var Commands []*Command
    53  
    54  // Name returns the command's name: the first word in the usage line.
    55  func (c *Command) Name() string {
    56  	name := c.UsageLine
    57  	i := strings.Index(name, " ")
    58  	if i >= 0 {
    59  		name = name[:i]
    60  	}
    61  	return name
    62  }
    63  
    64  func (c *Command) Usage() {
    65  	fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
    66  	fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.Name())
    67  	os.Exit(2)
    68  }
    69  
    70  // Runnable reports whether the command can be run; otherwise
    71  // it is a documentation pseudo-command such as importpath.
    72  func (c *Command) Runnable() bool {
    73  	return c.Run != nil
    74  }
    75  
    76  var atExitFuncs []func()
    77  
    78  func AtExit(f func()) {
    79  	atExitFuncs = append(atExitFuncs, f)
    80  }
    81  
    82  func Exit() {
    83  	for _, f := range atExitFuncs {
    84  		f()
    85  	}
    86  	os.Exit(exitStatus)
    87  }
    88  
    89  func Fatalf(format string, args ...interface{}) {
    90  	Errorf(format, args...)
    91  	Exit()
    92  }
    93  
    94  func Errorf(format string, args ...interface{}) {
    95  	log.Printf(format, args...)
    96  	SetExitStatus(1)
    97  }
    98  
    99  func ExitIfErrors() {
   100  	if exitStatus != 0 {
   101  		Exit()
   102  	}
   103  }
   104  
   105  var exitStatus = 0
   106  var exitMu sync.Mutex
   107  
   108  func SetExitStatus(n int) {
   109  	exitMu.Lock()
   110  	if exitStatus < n {
   111  		exitStatus = n
   112  	}
   113  	exitMu.Unlock()
   114  }
   115  
   116  // Run runs the command, with stdout and stderr
   117  // connected to the go command's own stdout and stderr.
   118  // If the command fails, Run reports the error using Errorf.
   119  func Run(cmdargs ...interface{}) {
   120  	cmdline := str.StringList(cmdargs...)
   121  	if cfg.BuildN || cfg.BuildX {
   122  		fmt.Printf("%s\n", strings.Join(cmdline, " "))
   123  		if cfg.BuildN {
   124  			return
   125  		}
   126  	}
   127  
   128  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   129  	cmd.Stdout = os.Stdout
   130  	cmd.Stderr = os.Stderr
   131  	if err := cmd.Run(); err != nil {
   132  		Errorf("%v", err)
   133  	}
   134  }
   135  
   136  // RunStdin is like run but connects Stdin.
   137  func RunStdin(cmdline []string) {
   138  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   139  	cmd.Stdin = os.Stdin
   140  	cmd.Stdout = os.Stdout
   141  	cmd.Stderr = os.Stderr
   142  	cmd.Env = cfg.OrigEnv
   143  	StartSigHandlers()
   144  	if err := cmd.Run(); err != nil {
   145  		Errorf("%v", err)
   146  	}
   147  }
   148  
   149  // Usage is the usage-reporting function, filled in by package main
   150  // but here for reference by other packages.
   151  var Usage func()
   152  
   153  // ExpandScanner expands a scanner.List error into all the errors in the list.
   154  // The default Error method only shows the first error
   155  // and does not shorten paths.
   156  func ExpandScanner(err error) error {
   157  	// Look for parser errors.
   158  	if err, ok := err.(scanner.ErrorList); ok {
   159  		// Prepare error with \n before each message.
   160  		// When printed in something like context: %v
   161  		// this will put the leading file positions each on
   162  		// its own line. It will also show all the errors
   163  		// instead of just the first, as err.Error does.
   164  		var buf bytes.Buffer
   165  		for _, e := range err {
   166  			e.Pos.Filename = ShortPath(e.Pos.Filename)
   167  			buf.WriteString("\n")
   168  			buf.WriteString(e.Error())
   169  		}
   170  		return errors.New(buf.String())
   171  	}
   172  	return err
   173  }