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 }