github.com/gernest/nezuko@v0.1.2/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/gernest/nezuko/internal/cfg"
    22  	"github.com/gernest/nezuko/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  	// 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  	os.Exit(2)
    86  }
    87  
    88  // Runnable reports whether the command can be run; otherwise
    89  // it is a documentation pseudo-command such as importpath.
    90  func (c *Command) Runnable() bool {
    91  	return c.Run != nil
    92  }
    93  
    94  var atExitFuncs []func()
    95  
    96  func AtExit(f func()) {
    97  	atExitFuncs = append(atExitFuncs, f)
    98  }
    99  
   100  func Exit() {
   101  	for _, f := range atExitFuncs {
   102  		f()
   103  	}
   104  	os.Exit(exitStatus)
   105  }
   106  
   107  func Fatalf(format string, args ...interface{}) {
   108  	Errorf(format, args...)
   109  	Exit()
   110  }
   111  
   112  func Errorf(format string, args ...interface{}) {
   113  	log.Printf(format, args...)
   114  	SetExitStatus(1)
   115  }
   116  
   117  func ExitIfErrors() {
   118  	if exitStatus != 0 {
   119  		Exit()
   120  	}
   121  }
   122  
   123  var exitStatus = 0
   124  var exitMu sync.Mutex
   125  
   126  func SetExitStatus(n int) {
   127  	exitMu.Lock()
   128  	if exitStatus < n {
   129  		exitStatus = n
   130  	}
   131  	exitMu.Unlock()
   132  }
   133  
   134  // Run runs the command, with stdout and stderr
   135  // connected to the go command's own stdout and stderr.
   136  // If the command fails, Run reports the error using Errorf.
   137  func Run(cmdargs ...interface{}) {
   138  	cmdline := str.StringList(cmdargs...)
   139  	if cfg.BuildN || cfg.BuildX {
   140  		fmt.Printf("%s\n", strings.Join(cmdline, " "))
   141  		if cfg.BuildN {
   142  			return
   143  		}
   144  	}
   145  
   146  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   147  	cmd.Stdout = os.Stdout
   148  	cmd.Stderr = os.Stderr
   149  	if err := cmd.Run(); err != nil {
   150  		Errorf("%v", err)
   151  	}
   152  }
   153  
   154  // RunStdin is like run but connects Stdin.
   155  func RunStdin(cmdline []string) {
   156  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   157  	cmd.Stdin = os.Stdin
   158  	cmd.Stdout = os.Stdout
   159  	cmd.Stderr = os.Stderr
   160  	cmd.Env = cfg.OrigEnv
   161  	StartSigHandlers()
   162  	if err := cmd.Run(); err != nil {
   163  		Errorf("%v", err)
   164  	}
   165  }
   166  
   167  // Usage is the usage-reporting function, filled in by package main
   168  // but here for reference by other packages.
   169  var Usage func()
   170  
   171  // ExpandScanner expands a scanner.List error into all the errors in the list.
   172  // The default Error method only shows the first error
   173  // and does not shorten paths.
   174  func ExpandScanner(err error) error {
   175  	// Look for parser errors.
   176  	if err, ok := err.(scanner.ErrorList); ok {
   177  		// Prepare error with \n before each message.
   178  		// When printed in something like context: %v
   179  		// this will put the leading file positions each on
   180  		// its own line. It will also show all the errors
   181  		// instead of just the first, as err.Error does.
   182  		var buf bytes.Buffer
   183  		for _, e := range err {
   184  			e.Pos.Filename = ShortPath(e.Pos.Filename)
   185  			buf.WriteString("\n")
   186  			buf.WriteString(e.Error())
   187  		}
   188  		return errors.New(buf.String())
   189  	}
   190  	return err
   191  }