github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/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 "context" 11 "github.com/bir3/gocompiler/src/cmd/gocmd/flag" 12 "fmt" 13 "log" 14 "os" 15 "os/exec" 16 "strings" 17 "sync" 18 19 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 21 ) 22 23 // A Command is an implementation of a go command 24 // like go build or go fix. 25 type Command struct { 26 // Run runs the command. 27 // The args are the arguments after the command name. 28 Run func(ctx context.Context, cmd *Command, args []string) 29 30 // UsageLine is the one-line usage message. 31 // The words between "go" and the first flag or argument in the line are taken to be the command name. 32 UsageLine string 33 34 // Short is the short description shown in the 'go help' output. 35 Short string 36 37 // Long is the long message shown in the 'go help <this-command>' output. 38 Long string 39 40 // Flag is a set of flags specific to this command. 41 Flag flag.FlagSet 42 43 // CustomFlags indicates that the command will do its own 44 // flag parsing. 45 CustomFlags bool 46 47 // Commands lists the available commands and help topics. 48 // The order here is the order in which they are printed by 'go help'. 49 // Note that subcommands are in general best avoided. 50 Commands []*Command 51 } 52 53 var Go = &Command{ 54 UsageLine: "go", 55 Long: `Go is a tool for managing Go source code.`, 56 // Commands initialized in package main 57 } 58 59 // hasFlag reports whether a command or any of its subcommands contain the given 60 // flag. 61 func hasFlag(c *Command, name string) bool { 62 if f := c.Flag.Lookup(name); f != nil { 63 return true 64 } 65 for _, sub := range c.Commands { 66 if hasFlag(sub, name) { 67 return true 68 } 69 } 70 return false 71 } 72 73 // LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument, 74 func (c *Command) LongName() string { 75 name := c.UsageLine 76 if i := strings.Index(name, " ["); i >= 0 { 77 name = name[:i] 78 } 79 if name == "go" { 80 return "" 81 } 82 return strings.TrimPrefix(name, "go ") 83 } 84 85 // Name returns the command's short name: the last word in the usage line before a flag or argument. 86 func (c *Command) Name() string { 87 name := c.LongName() 88 if i := strings.LastIndex(name, " "); i >= 0 { 89 name = name[i+1:] 90 } 91 return name 92 } 93 94 func (c *Command) Usage() { 95 fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine) 96 fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName()) 97 SetExitStatus(2) 98 Exit() 99 } 100 101 // Runnable reports whether the command can be run; otherwise 102 // it is a documentation pseudo-command such as importpath. 103 func (c *Command) Runnable() bool { 104 return c.Run != nil 105 } 106 107 var atExitFuncs []func() 108 109 func AtExit(f func()) { 110 atExitFuncs = append(atExitFuncs, f) 111 } 112 113 func Exit() { 114 for _, f := range atExitFuncs { 115 f() 116 } 117 os.Exit(exitStatus) 118 } 119 120 func Fatalf(format string, args ...any) { 121 Errorf(format, args...) 122 Exit() 123 } 124 125 func Errorf(format string, args ...any) { 126 log.Printf(format, args...) 127 SetExitStatus(1) 128 } 129 130 func ExitIfErrors() { 131 if exitStatus != 0 { 132 Exit() 133 } 134 } 135 136 var exitStatus = 0 137 var exitMu sync.Mutex 138 139 func SetExitStatus(n int) { 140 exitMu.Lock() 141 if exitStatus < n { 142 exitStatus = n 143 } 144 exitMu.Unlock() 145 } 146 147 func GetExitStatus() int { 148 return exitStatus 149 } 150 151 // Run runs the command, with stdout and stderr 152 // connected to the go command's own stdout and stderr. 153 // If the command fails, Run reports the error using Errorf. 154 func Run(cmdargs ...any) { 155 cmdline := str.StringList(cmdargs...) 156 if cfg.BuildN || cfg.BuildX { 157 fmt.Printf("%s\n", strings.Join(cmdline, " ")) 158 if cfg.BuildN { 159 return 160 } 161 } 162 163 cmd := exec.Command(cmdline[0], cmdline[1:]...) 164 cmd.Stdout = os.Stdout 165 cmd.Stderr = os.Stderr 166 if err := cmd.Run(); err != nil { 167 Errorf("%v", err) 168 } 169 } 170 171 // RunStdin is like run but connects Stdin. 172 func RunStdin(cmdline []string) { 173 cmd := exec.Command(cmdline[0], cmdline[1:]...) 174 cmd.Stdin = os.Stdin 175 cmd.Stdout = os.Stdout 176 cmd.Stderr = os.Stderr 177 cmd.Env = cfg.OrigEnv 178 StartSigHandlers() 179 if err := cmd.Run(); err != nil { 180 Errorf("%v", err) 181 } 182 } 183 184 // Usage is the usage-reporting function, filled in by package main 185 // but here for reference by other packages. 186 var Usage func()