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 }