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