1
2
3
4
5 package objabi
6
7 import (
8 "flag"
9 "fmt"
10 "internal/bisect"
11 "internal/buildcfg"
12 "io"
13 "log"
14 "os"
15 "reflect"
16 "sort"
17 "strconv"
18 "strings"
19 )
20
21 func Flagcount(name, usage string, val *int) {
22 flag.Var((*count)(val), name, usage)
23 }
24
25 func Flagfn1(name, usage string, f func(string)) {
26 flag.Var(fn1(f), name, usage)
27 }
28
29 func Flagprint(w io.Writer) {
30 flag.CommandLine.SetOutput(w)
31 flag.PrintDefaults()
32 }
33
34 func Flagparse(usage func()) {
35 flag.Usage = usage
36 os.Args = expandArgs(os.Args)
37 flag.Parse()
38 }
39
40
41
42
43
44
45
46
47
48
49
50
51
52 func expandArgs(in []string) (out []string) {
53
54 for i, s := range in {
55 if strings.HasPrefix(s, "@") {
56 if out == nil {
57 out = make([]string, 0, len(in)*2)
58 out = append(out, in[:i]...)
59 }
60 slurp, err := os.ReadFile(s[1:])
61 if err != nil {
62 log.Fatal(err)
63 }
64 args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n")
65 for i, arg := range args {
66 args[i] = DecodeArg(arg)
67 }
68 out = append(out, expandArgs(args)...)
69 } else if out != nil {
70 out = append(out, s)
71 }
72 }
73 if out == nil {
74 return in
75 }
76 return
77 }
78
79 func AddVersionFlag() {
80 flag.Var(versionFlag{}, "V", "print version and exit")
81 }
82
83 var buildID string
84
85 type versionFlag struct{}
86
87 func (versionFlag) IsBoolFlag() bool { return true }
88 func (versionFlag) Get() interface{} { return nil }
89 func (versionFlag) String() string { return "" }
90 func (versionFlag) Set(s string) error {
91 name := os.Args[0]
92 name = name[strings.LastIndex(name, `/`)+1:]
93 name = name[strings.LastIndex(name, `\`)+1:]
94 name = strings.TrimSuffix(name, ".exe")
95
96 p := ""
97
98 if s == "goexperiment" {
99
100
101 p = " X:" + strings.Join(buildcfg.Experiment.All(), ",")
102 } else {
103
104
105 if goexperiment := buildcfg.Experiment.String(); goexperiment != "" {
106 p = " X:" + goexperiment
107 }
108 }
109
110
111
112
113
114
115 if s == "full" {
116 if strings.HasPrefix(buildcfg.Version, "devel") {
117 p += " buildID=" + buildID
118 }
119 }
120
121 fmt.Printf("%s version %s%s\n", name, buildcfg.Version, p)
122 os.Exit(0)
123 return nil
124 }
125
126
127
128
129 type count int
130
131 func (c *count) String() string {
132 return fmt.Sprint(int(*c))
133 }
134
135 func (c *count) Set(s string) error {
136 switch s {
137 case "true":
138 *c++
139 case "false":
140 *c = 0
141 default:
142 n, err := strconv.Atoi(s)
143 if err != nil {
144 return fmt.Errorf("invalid count %q", s)
145 }
146 *c = count(n)
147 }
148 return nil
149 }
150
151 func (c *count) Get() interface{} {
152 return int(*c)
153 }
154
155 func (c *count) IsBoolFlag() bool {
156 return true
157 }
158
159 func (c *count) IsCountFlag() bool {
160 return true
161 }
162
163 type fn1 func(string)
164
165 func (f fn1) Set(s string) error {
166 f(s)
167 return nil
168 }
169
170 func (f fn1) String() string { return "" }
171
172
173
174
175 func DecodeArg(arg string) string {
176
177 if !strings.ContainsAny(arg, "\\\n") {
178 return arg
179 }
180
181 var b strings.Builder
182 var wasBS bool
183 for _, r := range arg {
184 if wasBS {
185 switch r {
186 case '\\':
187 b.WriteByte('\\')
188 case 'n':
189 b.WriteByte('\n')
190 default:
191
192
193 panic("badly formatted input")
194 }
195 } else if r == '\\' {
196 wasBS = true
197 continue
198 } else {
199 b.WriteRune(r)
200 }
201 wasBS = false
202 }
203 return b.String()
204 }
205
206 type debugField struct {
207 name string
208 help string
209 concurrentOk bool
210 val interface{}
211 }
212
213 type DebugFlag struct {
214 tab map[string]debugField
215 concurrentOk *bool
216 debugSSA DebugSSA
217 }
218
219
220
221
222
223 type DebugSSA func(phase, flag string, val int, valString string) string
224
225
226
227
228
229
230
231
232
233
234
235
236
237 func NewDebugFlag(debug interface{}, debugSSA DebugSSA) *DebugFlag {
238 flag := &DebugFlag{
239 tab: make(map[string]debugField),
240 debugSSA: debugSSA,
241 }
242
243 v := reflect.ValueOf(debug).Elem()
244 t := v.Type()
245 for i := 0; i < t.NumField(); i++ {
246 f := t.Field(i)
247 ptr := v.Field(i).Addr().Interface()
248 if f.Name == "ConcurrentOk" {
249 switch ptr := ptr.(type) {
250 default:
251 panic("debug.ConcurrentOk must have type bool")
252 case *bool:
253 flag.concurrentOk = ptr
254 }
255 continue
256 }
257 name := strings.ToLower(f.Name)
258 help := f.Tag.Get("help")
259 if help == "" {
260 panic(fmt.Sprintf("debug.%s is missing help text", f.Name))
261 }
262 concurrent := f.Tag.Get("concurrent")
263
264 switch ptr.(type) {
265 default:
266 panic(fmt.Sprintf("debug.%s has invalid type %v (must be int, string, or *bisect.Matcher)", f.Name, f.Type))
267 case *int, *string, **bisect.Matcher:
268
269 }
270 flag.tab[name] = debugField{name, help, concurrent == "ok", ptr}
271 }
272
273 return flag
274 }
275
276 func (f *DebugFlag) Set(debugstr string) error {
277 if debugstr == "" {
278 return nil
279 }
280 for _, name := range strings.Split(debugstr, ",") {
281 if name == "" {
282 continue
283 }
284
285 if name == "help" {
286 fmt.Print(debugHelpHeader)
287 maxLen, names := 0, []string{}
288 if f.debugSSA != nil {
289 maxLen = len("ssa/help")
290 }
291 for name := range f.tab {
292 if len(name) > maxLen {
293 maxLen = len(name)
294 }
295 names = append(names, name)
296 }
297 sort.Strings(names)
298
299 nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "")
300 for _, name := range names {
301 help := f.tab[name].help
302 fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1))
303 }
304 if f.debugSSA != nil {
305
306 fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
307 }
308 os.Exit(0)
309 }
310
311 val, valstring, haveInt := 1, "", true
312 if i := strings.IndexAny(name, "=:"); i >= 0 {
313 var err error
314 name, valstring = name[:i], name[i+1:]
315 val, err = strconv.Atoi(valstring)
316 if err != nil {
317 val, haveInt = 1, false
318 }
319 }
320
321 if t, ok := f.tab[name]; ok {
322 switch vp := t.val.(type) {
323 case nil:
324
325 case *string:
326 *vp = valstring
327 case *int:
328 if !haveInt {
329 log.Fatalf("invalid debug value %v", name)
330 }
331 *vp = val
332 case **bisect.Matcher:
333 var err error
334 *vp, err = bisect.New(valstring)
335 if err != nil {
336 log.Fatalf("debug flag %v: %v", name, err)
337 }
338 default:
339 panic("bad debugtab type")
340 }
341
342 if !t.concurrentOk && f.concurrentOk != nil {
343 *f.concurrentOk = false
344 }
345 } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") {
346
347
348
349 phase := name[4:]
350 flag := "debug"
351 if i := strings.Index(phase, "/"); i >= 0 {
352 flag = phase[i+1:]
353 phase = phase[:i]
354 }
355 err := f.debugSSA(phase, flag, val, valstring)
356 if err != "" {
357 log.Fatal(err)
358 }
359
360
361
362 *f.concurrentOk = false
363
364 } else {
365 return fmt.Errorf("unknown debug key %s\n", name)
366 }
367 }
368
369 return nil
370 }
371
372 const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
373
374 <key> is one of:
375
376 `
377
378 func (f *DebugFlag) String() string {
379 return ""
380 }
381
View as plain text