github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/flag_objabi/flag_objabi.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package flag_objabi 6 7 import ( 8 flag "github.com/bir3/gocompiler/src/cmd/compile/flag" 9 "fmt" 10 "github.com/bir3/gocompiler/src/internal/bisect" 11 "github.com/bir3/gocompiler/src/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 // expandArgs expands "response files" arguments in the provided slice. 41 // 42 // A "response file" argument starts with '@' and the rest of that 43 // argument is a filename with CR-or-CRLF-separated arguments. Each 44 // argument in the named files can also contain response file 45 // arguments. See Issue 18468. 46 // 47 // The returned slice 'out' aliases 'in' iff the input did not contain 48 // any response file arguments. 49 // 50 // TODO: handle relative paths of recursive expansions in different directories? 51 // Is there a spec for this? Are relative paths allowed? 52 func expandArgs(in []string) (out []string) { 53 // out is nil until we see a "@" argument. 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 // filled in by linker 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 92 name := os.Getenv("BIR3_GOCOMPILER_TOOL") 93 if name == "" { 94 panic("empty BIR3_GOCOMPILER_TOOL env var") 95 } 96 97 98 p := "" 99 100 if s == "goexperiment" { 101 // test/run.go uses this to discover the full set of 102 // experiment tags. Report everything. 103 p = " X:" + strings.Join(buildcfg.Experiment.All(), ",") 104 } else { 105 // If the enabled experiments differ from the baseline, 106 // include that difference. 107 if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { 108 p = " X:" + goexperiment 109 } 110 } 111 112 // The go command invokes -V=full to get a unique identifier 113 // for this tool. It is assumed that the release version is sufficient 114 // for releases, but during development we include the full 115 // build ID of the binary, so that if the compiler is changed and 116 // rebuilt, we notice and rebuild all packages. 117 if s == "full" { 118 if strings.HasPrefix(buildcfg.Version, "devel") { 119 p += " buildID=" + buildID 120 } 121 } 122 123 fmt.Printf("%s version %s%s\n", name, buildcfg.Version, p) 124 os.Exit(0) 125 return nil 126 } 127 128 // count is a flag.Value that is like a flag.Bool and a flag.Int. 129 // If used as -name, it increments the count, but -name=x sets the count. 130 // Used for verbose flag -v. 131 type count int 132 133 func (c *count) String() string { 134 return fmt.Sprint(int(*c)) 135 } 136 137 func (c *count) Set(s string) error { 138 switch s { 139 case "true": 140 *c++ 141 case "false": 142 *c = 0 143 default: 144 n, err := strconv.Atoi(s) 145 if err != nil { 146 return fmt.Errorf("invalid count %q", s) 147 } 148 *c = count(n) 149 } 150 return nil 151 } 152 153 func (c *count) Get() interface{} { 154 return int(*c) 155 } 156 157 func (c *count) IsBoolFlag() bool { 158 return true 159 } 160 161 func (c *count) IsCountFlag() bool { 162 return true 163 } 164 165 type fn1 func(string) 166 167 func (f fn1) Set(s string) error { 168 f(s) 169 return nil 170 } 171 172 func (f fn1) String() string { return "" } 173 174 // DecodeArg decodes an argument. 175 // 176 // This function is public for testing with the parallel encoder. 177 func DecodeArg(arg string) string { 178 // If no encoding, fastpath out. 179 if !strings.ContainsAny(arg, "\\\n") { 180 return arg 181 } 182 183 var b strings.Builder 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 // This shouldn't happen. The only backslashes that reach here 194 // should encode '\n' and '\\' exclusively. 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 concurrentOk bool // true if this field/flag is compatible with concurrent compilation 212 val interface{} // *int or *string 213 } 214 215 type DebugFlag struct { 216 tab map[string]debugField 217 concurrentOk *bool // this is non-nil only for compiler's DebugFlags, but only compiler has concurrent:ok fields 218 debugSSA DebugSSA // this is non-nil only for compiler's DebugFlags. 219 } 220 221 // A DebugSSA function is called to set a -d ssa/... option. 222 // If nil, those options are reported as invalid options. 223 // If DebugSSA returns a non-empty string, that text is reported as a compiler error. 224 // If phase is "help", it should print usage information and terminate the process. 225 type DebugSSA func(phase, flag string, val int, valString string) string 226 227 // NewDebugFlag constructs a DebugFlag for the fields of debug, which 228 // must be a pointer to a struct. 229 // 230 // Each field of *debug is a different value, named for the lower-case of the field name. 231 // Each field must be an int or string and must have a `help` struct tag. 232 // There may be an "Any bool" field, which will be set if any debug flags are set. 233 // 234 // The returned flag takes a comma-separated list of settings. 235 // Each setting is name=value; for ints, name is short for name=1. 236 // 237 // If debugSSA is non-nil, any debug flags of the form ssa/... will be 238 // passed to debugSSA for processing. 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 == "ConcurrentOk" { 251 switch ptr := ptr.(type) { 252 default: 253 panic("debug.ConcurrentOk must have type bool") 254 case *bool: 255 flag.concurrentOk = 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 concurrent := f.Tag.Get("concurrent") 265 266 switch ptr.(type) { 267 default: 268 panic(fmt.Sprintf("debug.%s has invalid type %v (must be int, string, or *bisect.Matcher)", f.Name, f.Type)) 269 case *int, *string, **bisect.Matcher: 270 // ok 271 } 272 flag.tab[name] = debugField{name, help, concurrent == "ok", ptr} 273 } 274 275 return flag 276 } 277 278 func (f *DebugFlag) Set(debugstr string) error { 279 if debugstr == "" { 280 return nil 281 } 282 for _, name := range strings.Split(debugstr, ",") { 283 if name == "" { 284 continue 285 } 286 // display help about the debug option itself and quit 287 if name == "help" { 288 fmt.Print(debugHelpHeader) 289 maxLen, names := 0, []string{} 290 if f.debugSSA != nil { 291 maxLen = len("ssa/help") 292 } 293 for name := range f.tab { 294 if len(name) > maxLen { 295 maxLen = len(name) 296 } 297 names = append(names, name) 298 } 299 sort.Strings(names) 300 // Indent multi-line help messages. 301 nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "") 302 for _, name := range names { 303 help := f.tab[name].help 304 fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1)) 305 } 306 if f.debugSSA != nil { 307 // ssa options have their own help 308 fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") 309 } 310 os.Exit(0) 311 } 312 313 val, valstring, haveInt := 1, "", true 314 if i := strings.IndexAny(name, "=:"); i >= 0 { 315 var err error 316 name, valstring = name[:i], name[i+1:] 317 val, err = strconv.Atoi(valstring) 318 if err != nil { 319 val, haveInt = 1, false 320 } 321 } 322 323 if t, ok := f.tab[name]; ok { 324 switch vp := t.val.(type) { 325 case nil: 326 // Ignore 327 case *string: 328 *vp = valstring 329 case *int: 330 if !haveInt { 331 log.Fatalf("invalid debug value %v", name) 332 } 333 *vp = val 334 case **bisect.Matcher: 335 var err error 336 *vp, err = bisect.New(valstring) 337 if err != nil { 338 log.Fatalf("debug flag %v: %v", name, err) 339 } 340 default: 341 panic("bad debugtab type") 342 } 343 // assembler DebugFlags don't have a ConcurrentOk field to reset, so check against that. 344 if !t.concurrentOk && f.concurrentOk != nil { 345 *f.concurrentOk = false 346 } 347 } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") { 348 // expect form ssa/phase/flag 349 // e.g. -d=ssa/generic_cse/time 350 // _ in phase name also matches space 351 phase := name[4:] 352 flag := "debug" // default flag is debug 353 if i := strings.Index(phase, "/"); i >= 0 { 354 flag = phase[i+1:] 355 phase = phase[:i] 356 } 357 err := f.debugSSA(phase, flag, val, valstring) 358 if err != "" { 359 log.Fatalf(err) 360 } 361 // Setting this false for -d=ssa/... preserves old behavior 362 // of turning off concurrency for any debug flags. 363 // It's not known for sure if this is necessary, but it is safe. 364 *f.concurrentOk = false 365 366 } else { 367 return fmt.Errorf("unknown debug key %s\n", name) 368 } 369 } 370 371 return nil 372 } 373 374 const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] 375 376 <key> is one of: 377 378 ` 379 380 func (f *DebugFlag) String() string { 381 return "" 382 }