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.ReplaceAll(string(slurp), "\r", "")), "\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.Contains(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.SplitSeq(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.ReplaceAll(help, "\n", nl))
   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