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 }