| | |
| | |
| | |
| |
|
| | package objabi |
| |
|
| | import ( |
| | "flag" |
| | "fmt" |
| | "internal/bisect" |
| | "internal/buildcfg" |
| | "io" |
| | "log" |
| | "os" |
| | "reflect" |
| | "sort" |
| | "strconv" |
| | "strings" |
| | ) |
| |
|
| | func Flagcount(name, usage string, val *int) { |
| | flag.Var((*count)(val), name, usage) |
| | } |
| |
|
| | func Flagfn1(name, usage string, f func(string)) { |
| | flag.Var(fn1(f), name, usage) |
| | } |
| |
|
| | func Flagprint(w io.Writer) { |
| | flag.CommandLine.SetOutput(w) |
| | flag.PrintDefaults() |
| | } |
| |
|
| | func Flagparse(usage func()) { |
| | flag.Usage = usage |
| | os.Args = expandArgs(os.Args) |
| | flag.Parse() |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func expandArgs(in []string) (out []string) { |
| | |
| | for i, s := range in { |
| | if strings.HasPrefix(s, "@") { |
| | if out == nil { |
| | out = make([]string, 0, len(in)*2) |
| | out = append(out, in[:i]...) |
| | } |
| | slurp, err := os.ReadFile(s[1:]) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | args := strings.Split(strings.TrimSpace(strings.ReplaceAll(string(slurp), "\r", "")), "\n") |
| | for i, arg := range args { |
| | args[i] = DecodeArg(arg) |
| | } |
| | out = append(out, expandArgs(args)...) |
| | } else if out != nil { |
| | out = append(out, s) |
| | } |
| | } |
| | if out == nil { |
| | return in |
| | } |
| | return |
| | } |
| |
|
| | func AddVersionFlag() { |
| | flag.Var(versionFlag{}, "V", "print version and exit") |
| | } |
| |
|
| | var buildID string |
| |
|
| | type versionFlag struct{} |
| |
|
| | func (versionFlag) IsBoolFlag() bool { return true } |
| | func (versionFlag) Get() any { return nil } |
| | func (versionFlag) String() string { return "" } |
| | func (versionFlag) Set(s string) error { |
| | name := os.Args[0] |
| | name = name[strings.LastIndex(name, `/`)+1:] |
| | name = name[strings.LastIndex(name, `\`)+1:] |
| | name = strings.TrimSuffix(name, ".exe") |
| |
|
| | p := "" |
| |
|
| | |
| | |
| | if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { |
| | p = " X:" + goexperiment |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | if s == "full" { |
| | if strings.Contains(buildcfg.Version, "devel") { |
| | p += " buildID=" + buildID |
| | } |
| | } |
| |
|
| | fmt.Printf("%s version %s%s\n", name, buildcfg.Version, p) |
| | os.Exit(0) |
| | return nil |
| | } |
| |
|
| | |
| | |
| | |
| | type count int |
| |
|
| | func (c *count) String() string { |
| | return fmt.Sprint(int(*c)) |
| | } |
| |
|
| | func (c *count) Set(s string) error { |
| | switch s { |
| | case "true": |
| | *c++ |
| | case "false": |
| | *c = 0 |
| | default: |
| | n, err := strconv.Atoi(s) |
| | if err != nil { |
| | return fmt.Errorf("invalid count %q", s) |
| | } |
| | *c = count(n) |
| | } |
| | return nil |
| | } |
| |
|
| | func (c *count) Get() any { |
| | return int(*c) |
| | } |
| |
|
| | func (c *count) IsBoolFlag() bool { |
| | return true |
| | } |
| |
|
| | func (c *count) IsCountFlag() bool { |
| | return true |
| | } |
| |
|
| | type fn1 func(string) |
| |
|
| | func (f fn1) Set(s string) error { |
| | f(s) |
| | return nil |
| | } |
| |
|
| | func (f fn1) String() string { return "" } |
| |
|
| | |
| | |
| | |
| | func DecodeArg(arg string) string { |
| | |
| | if !strings.ContainsAny(arg, "\\\n") { |
| | return arg |
| | } |
| |
|
| | var b strings.Builder |
| | var wasBS bool |
| | for _, r := range arg { |
| | if wasBS { |
| | switch r { |
| | case '\\': |
| | b.WriteByte('\\') |
| | case 'n': |
| | b.WriteByte('\n') |
| | default: |
| | |
| | |
| | panic("badly formatted input") |
| | } |
| | } else if r == '\\' { |
| | wasBS = true |
| | continue |
| | } else { |
| | b.WriteRune(r) |
| | } |
| | wasBS = false |
| | } |
| | return b.String() |
| | } |
| |
|
| | type debugField struct { |
| | name string |
| | help string |
| | concurrentOk bool |
| | val any |
| | } |
| |
|
| | type DebugFlag struct { |
| | tab map[string]debugField |
| | concurrentOk *bool |
| | debugSSA DebugSSA |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | type DebugSSA func(phase, flag string, val int, valString string) string |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func NewDebugFlag(debug any, debugSSA DebugSSA) *DebugFlag { |
| | flag := &DebugFlag{ |
| | tab: make(map[string]debugField), |
| | debugSSA: debugSSA, |
| | } |
| |
|
| | v := reflect.ValueOf(debug).Elem() |
| | t := v.Type() |
| | for i := 0; i < t.NumField(); i++ { |
| | f := t.Field(i) |
| | ptr := v.Field(i).Addr().Interface() |
| | if f.Name == "ConcurrentOk" { |
| | switch ptr := ptr.(type) { |
| | default: |
| | panic("debug.ConcurrentOk must have type bool") |
| | case *bool: |
| | flag.concurrentOk = ptr |
| | } |
| | continue |
| | } |
| | name := strings.ToLower(f.Name) |
| | help := f.Tag.Get("help") |
| | if help == "" { |
| | panic(fmt.Sprintf("debug.%s is missing help text", f.Name)) |
| | } |
| | concurrent := f.Tag.Get("concurrent") |
| |
|
| | switch ptr.(type) { |
| | default: |
| | panic(fmt.Sprintf("debug.%s has invalid type %v (must be int, string, or *bisect.Matcher)", f.Name, f.Type)) |
| | case *int, *string, **bisect.Matcher: |
| | |
| | } |
| | flag.tab[name] = debugField{name, help, concurrent == "ok", ptr} |
| | } |
| |
|
| | return flag |
| | } |
| |
|
| | func (f *DebugFlag) Set(debugstr string) error { |
| | if debugstr == "" { |
| | return nil |
| | } |
| | for name := range strings.SplitSeq(debugstr, ",") { |
| | if name == "" { |
| | continue |
| | } |
| | |
| | if name == "help" { |
| | fmt.Print(debugHelpHeader) |
| | maxLen, names := 0, []string{} |
| | if f.debugSSA != nil { |
| | maxLen = len("ssa/help") |
| | } |
| | for name := range f.tab { |
| | if len(name) > maxLen { |
| | maxLen = len(name) |
| | } |
| | names = append(names, name) |
| | } |
| | sort.Strings(names) |
| | |
| | nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "") |
| | for _, name := range names { |
| | help := f.tab[name].help |
| | fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.ReplaceAll(help, "\n", nl)) |
| | } |
| | if f.debugSSA != nil { |
| | |
| | fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") |
| | } |
| | os.Exit(0) |
| | } |
| |
|
| | val, valstring, haveInt := 1, "", true |
| | if i := strings.IndexAny(name, "=:"); i >= 0 { |
| | var err error |
| | name, valstring = name[:i], name[i+1:] |
| | val, err = strconv.Atoi(valstring) |
| | if err != nil { |
| | val, haveInt = 1, false |
| | } |
| | } |
| |
|
| | if t, ok := f.tab[name]; ok { |
| | switch vp := t.val.(type) { |
| | case nil: |
| | |
| | case *string: |
| | *vp = valstring |
| | case *int: |
| | if !haveInt { |
| | log.Fatalf("invalid debug value %v", name) |
| | } |
| | *vp = val |
| | case **bisect.Matcher: |
| | var err error |
| | *vp, err = bisect.New(valstring) |
| | if err != nil { |
| | log.Fatalf("debug flag %v: %v", name, err) |
| | } |
| | default: |
| | panic("bad debugtab type") |
| | } |
| | |
| | if !t.concurrentOk && f.concurrentOk != nil { |
| | *f.concurrentOk = false |
| | } |
| | } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") { |
| | |
| | |
| | |
| | phase := name[4:] |
| | flag := "debug" |
| | if i := strings.Index(phase, "/"); i >= 0 { |
| | flag = phase[i+1:] |
| | phase = phase[:i] |
| | } |
| | err := f.debugSSA(phase, flag, val, valstring) |
| | if err != "" { |
| | log.Fatal(err) |
| | } |
| | |
| | |
| | |
| | *f.concurrentOk = false |
| |
|
| | } else { |
| | return fmt.Errorf("unknown debug key %s\n", name) |
| | } |
| | } |
| |
|
| | return nil |
| | } |
| |
|
| | const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] |
| | |
| | <key> is one of: |
| | |
| | ` |
| |
|
| | func (f *DebugFlag) String() string { |
| | return "" |
| | } |
| |
|