1
2
3
4
5
6
7 package base
8
9 import (
10 "context"
11 "flag"
12 "fmt"
13 "log"
14 "os"
15 "os/exec"
16 "reflect"
17 "strings"
18 "sync"
19
20 "cmd/go/internal/cfg"
21 "cmd/go/internal/str"
22 )
23
24
25
26 type Command struct {
27
28
29 Run func(ctx context.Context, cmd *Command, args []string)
30
31
32
33 UsageLine string
34
35
36 Short string
37
38
39 Long string
40
41
42 Flag flag.FlagSet
43
44
45
46 CustomFlags bool
47
48
49
50
51 Commands []*Command
52 }
53
54 var Go = &Command{
55 UsageLine: "go",
56 Long: `Go is a tool for managing Go source code.`,
57
58 }
59
60
61
62
63
64
65 func (c *Command) Lookup(name string) *Command {
66 for _, sub := range c.Commands {
67 if sub.Name() == name && (len(c.Commands) > 0 || c.Runnable()) {
68 return sub
69 }
70 }
71 return nil
72 }
73
74
75
76 func hasFlag(c *Command, name string) bool {
77 if f := c.Flag.Lookup(name); f != nil {
78 return true
79 }
80 for _, sub := range c.Commands {
81 if hasFlag(sub, name) {
82 return true
83 }
84 }
85 return false
86 }
87
88
89 func (c *Command) LongName() string {
90 name := c.UsageLine
91 if i := strings.Index(name, " ["); i >= 0 {
92 name = name[:i]
93 }
94 if name == "go" {
95 return ""
96 }
97 return strings.TrimPrefix(name, "go ")
98 }
99
100
101 func (c *Command) Name() string {
102 name := c.LongName()
103 if i := strings.LastIndex(name, " "); i >= 0 {
104 name = name[i+1:]
105 }
106 return name
107 }
108
109 func (c *Command) Usage() {
110 fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
111 fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName())
112 SetExitStatus(2)
113 Exit()
114 }
115
116
117
118 func (c *Command) Runnable() bool {
119 return c.Run != nil
120 }
121
122 var atExitFuncs []func()
123
124 func AtExit(f func()) {
125 atExitFuncs = append(atExitFuncs, f)
126 }
127
128 func Exit() {
129 for _, f := range atExitFuncs {
130 f()
131 }
132 os.Exit(exitStatus)
133 }
134
135 func Fatalf(format string, args ...any) {
136 Errorf(format, args...)
137 Exit()
138 }
139
140 func Errorf(format string, args ...any) {
141 log.Printf(format, args...)
142 SetExitStatus(1)
143 }
144
145 func ExitIfErrors() {
146 if exitStatus != 0 {
147 Exit()
148 }
149 }
150
151 func Error(err error) {
152
153
154
155
156
157
158
159 if err != nil && reflect.TypeOf(err).String() == "*errors.joinError" {
160 for _, e := range err.(interface{ Unwrap() []error }).Unwrap() {
161 Error(e)
162 }
163 return
164 }
165 Errorf("go: %v", err)
166 }
167
168 func Fatal(err error) {
169 Error(err)
170 Exit()
171 }
172
173 var exitStatus = 0
174 var exitMu sync.Mutex
175
176 func SetExitStatus(n int) {
177 exitMu.Lock()
178 if exitStatus < n {
179 exitStatus = n
180 }
181 exitMu.Unlock()
182 }
183
184 func GetExitStatus() int {
185 return exitStatus
186 }
187
188
189
190
191 func Run(cmdargs ...any) {
192 cmdline := str.StringList(cmdargs...)
193 if cfg.BuildN || cfg.BuildX {
194 fmt.Printf("%s\n", strings.Join(cmdline, " "))
195 if cfg.BuildN {
196 return
197 }
198 }
199
200 cmd := exec.Command(cmdline[0], cmdline[1:]...)
201 cmd.Stdout = os.Stdout
202 cmd.Stderr = os.Stderr
203 if err := cmd.Run(); err != nil {
204 Errorf("%v", err)
205 }
206 }
207
208
209 func RunStdin(cmdline []string) {
210 cmd := exec.Command(cmdline[0], cmdline[1:]...)
211 cmd.Stdin = os.Stdin
212 cmd.Stdout = os.Stdout
213 cmd.Stderr = os.Stderr
214 cmd.Env = cfg.OrigEnv
215 StartSigHandlers()
216 if err := cmd.Run(); err != nil {
217 Errorf("%v", err)
218 }
219 }
220
221
222
223 var Usage func()
224
View as plain text