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