github.com/bir3/gocompiler@v0.3.205/src/cmd/gofmt/flag_objabi/flag.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/gofmt/flag" 9 "fmt" 10 "github.com/bir3/gocompiler/src/internal/buildcfg" 11 "io" 12 "log" 13 "os" 14 "reflect" 15 "sort" 16 "strconv" 17 "strings" 18 ) 19 20 func Flagcount(name, usage string, val *int) { 21 flag.Var((*count)(val), name, usage) 22 } 23 24 func Flagfn1(name, usage string, f func(string)) { 25 flag.Var(fn1(f), name, usage) 26 } 27 28 func Flagprint(w io.Writer) { 29 flag.CommandLine.SetOutput(w) 30 flag.PrintDefaults() 31 } 32 33 func Flagparse(usage func()) { 34 flag.Usage = usage 35 os.Args = expandArgs(os.Args) 36 flag.Parse() 37 } 38 39 // expandArgs expands "response files" arguments in the provided slice. 40 // 41 // A "response file" argument starts with '@' and the rest of that 42 // argument is a filename with CR-or-CRLF-separated arguments. Each 43 // argument in the named files can also contain response file 44 // arguments. See Issue 18468. 45 // 46 // The returned slice 'out' aliases 'in' iff the input did not contain 47 // any response file arguments. 48 // 49 // TODO: handle relative paths of recursive expansions in different directories? 50 // Is there a spec for this? Are relative paths allowed? 51 func expandArgs(in []string) (out []string) { 52 // out is nil until we see a "@" argument. 53 for i, s := range in { 54 if strings.HasPrefix(s, "@") { 55 if out == nil { 56 out = make([]string, 0, len(in)*2) 57 out = append(out, in[:i]...) 58 } 59 slurp, err := os.ReadFile(s[1:]) 60 if err != nil { 61 log.Fatal(err) 62 } 63 args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n") 64 for i, arg := range args { 65 args[i] = DecodeArg(arg) 66 } 67 out = append(out, expandArgs(args)...) 68 } else if out != nil { 69 out = append(out, s) 70 } 71 } 72 if out == nil { 73 return in 74 } 75 return 76 } 77 78 func AddVersionFlag() { 79 flag.Var(versionFlag{}, "V", "print version and exit") 80 } 81 82 var buildID string // filled in by linker 83 84 type versionFlag struct{} 85 86 func (versionFlag) IsBoolFlag() bool { return true } 87 func (versionFlag) Get() interface{} { return nil } 88 func (versionFlag) String() string { return "" } 89 func (versionFlag) Set(s string) error { 90 91 name := os.Getenv("GOCOMPILER_TOOL") 92 if name == "" { 93 panic("empty GOCOMPILER_TOOL env var") 94 } 95 96 97 p := "" 98 99 if s == "goexperiment" { 100 // test/run.go uses this to discover the full set of 101 // experiment tags. Report everything. 102 p = " X:" + strings.Join(buildcfg.Experiment.All(), ",") 103 } else { 104 // If the enabled experiments differ from the baseline, 105 // include that difference. 106 if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { 107 p = " X:" + goexperiment 108 } 109 } 110 111 // The go command invokes -V=full to get a unique identifier 112 // for this tool. It is assumed that the release version is sufficient 113 // for releases, but during development we include the full 114 // build ID of the binary, so that if the compiler is changed and 115 // rebuilt, we notice and rebuild all packages. 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 // count is a flag.Value that is like a flag.Bool and a flag.Int. 128 // If used as -name, it increments the count, but -name=x sets the count. 129 // Used for verbose flag -v. 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 // DecodeArg decodes an argument. 174 // 175 // This function is public for testing with the parallel encoder. 176 func DecodeArg(arg string) string { 177 // If no encoding, fastpath out. 178 if !strings.ContainsAny(arg, "\\\n") { 179 return arg 180 } 181 182 var b strings.Builder 183 var wasBS bool 184 for _, r := range arg { 185 if wasBS { 186 switch r { 187 case '\\': 188 b.WriteByte('\\') 189 case 'n': 190 b.WriteByte('\n') 191 default: 192 // This shouldn't happen. The only backslashes that reach here 193 // should encode '\n' and '\\' exclusively. 194 panic("badly formatted input") 195 } 196 } else if r == '\\' { 197 wasBS = true 198 continue 199 } else { 200 b.WriteRune(r) 201 } 202 wasBS = false 203 } 204 return b.String() 205 } 206 207 type debugField struct { 208 name string 209 help string 210 concurrentOk bool // true if this field/flag is compatible with concurrent compilation 211 val interface{} // *int or *string 212 } 213 214 type DebugFlag struct { 215 tab map[string]debugField 216 concurrentOk *bool // this is non-nil only for compiler's DebugFlags, but only compiler has concurrent:ok fields 217 debugSSA DebugSSA // this is non-nil only for compiler's DebugFlags. 218 } 219 220 // A DebugSSA function is called to set a -d ssa/... option. 221 // If nil, those options are reported as invalid options. 222 // If DebugSSA returns a non-empty string, that text is reported as a compiler error. 223 // If phase is "help", it should print usage information and terminate the process. 224 type DebugSSA func(phase, flag string, val int, valString string) string 225 226 // NewDebugFlag constructs a DebugFlag for the fields of debug, which 227 // must be a pointer to a struct. 228 // 229 // Each field of *debug is a different value, named for the lower-case of the field name. 230 // Each field must be an int or string and must have a `help` struct tag. 231 // There may be an "Any bool" field, which will be set if any debug flags are set. 232 // 233 // The returned flag takes a comma-separated list of settings. 234 // Each setting is name=value; for ints, name is short for name=1. 235 // 236 // If debugSSA is non-nil, any debug flags of the form ssa/... will be 237 // passed to debugSSA for processing. 238 func NewDebugFlag(debug interface{}, debugSSA DebugSSA) *DebugFlag { 239 flag := &DebugFlag{ 240 tab: make(map[string]debugField), 241 debugSSA: debugSSA, 242 } 243 244 v := reflect.ValueOf(debug).Elem() 245 t := v.Type() 246 for i := 0; i < t.NumField(); i++ { 247 f := t.Field(i) 248 ptr := v.Field(i).Addr().Interface() 249 if f.Name == "ConcurrentOk" { 250 switch ptr := ptr.(type) { 251 default: 252 panic("debug.ConcurrentOk must have type bool") 253 case *bool: 254 flag.concurrentOk = ptr 255 } 256 continue 257 } 258 name := strings.ToLower(f.Name) 259 help := f.Tag.Get("help") 260 if help == "" { 261 panic(fmt.Sprintf("debug.%s is missing help text", f.Name)) 262 } 263 concurrent := f.Tag.Get("concurrent") 264 265 switch ptr.(type) { 266 default: 267 panic(fmt.Sprintf("debug.%s has invalid type %v (must be int or string)", f.Name, f.Type)) 268 case *int, *string: 269 // ok 270 } 271 flag.tab[name] = debugField{name, help, concurrent == "ok", ptr} 272 } 273 274 return flag 275 } 276 277 func (f *DebugFlag) Set(debugstr string) error { 278 if debugstr == "" { 279 return nil 280 } 281 for _, name := range strings.Split(debugstr, ",") { 282 if name == "" { 283 continue 284 } 285 // display help about the debug option itself and quit 286 if name == "help" { 287 fmt.Print(debugHelpHeader) 288 maxLen, names := 0, []string{} 289 if f.debugSSA != nil { 290 maxLen = len("ssa/help") 291 } 292 for name := range f.tab { 293 if len(name) > maxLen { 294 maxLen = len(name) 295 } 296 names = append(names, name) 297 } 298 sort.Strings(names) 299 // Indent multi-line help messages. 300 nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "") 301 for _, name := range names { 302 help := f.tab[name].help 303 fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1)) 304 } 305 if f.debugSSA != nil { 306 // ssa options have their own help 307 fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") 308 } 309 os.Exit(0) 310 } 311 312 val, valstring, haveInt := 1, "", true 313 if i := strings.IndexAny(name, "=:"); i >= 0 { 314 var err error 315 name, valstring = name[:i], name[i+1:] 316 val, err = strconv.Atoi(valstring) 317 if err != nil { 318 val, haveInt = 1, false 319 } 320 } 321 322 if t, ok := f.tab[name]; ok { 323 switch vp := t.val.(type) { 324 case nil: 325 // Ignore 326 case *string: 327 *vp = valstring 328 case *int: 329 if !haveInt { 330 log.Fatalf("invalid debug value %v", name) 331 } 332 *vp = val 333 default: 334 panic("bad debugtab type") 335 } 336 // assembler DebugFlags don't have a ConcurrentOk field to reset, so check against that. 337 if !t.concurrentOk && f.concurrentOk != nil { 338 *f.concurrentOk = false 339 } 340 } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") { 341 // expect form ssa/phase/flag 342 // e.g. -d=ssa/generic_cse/time 343 // _ in phase name also matches space 344 phase := name[4:] 345 flag := "debug" // default flag is debug 346 if i := strings.Index(phase, "/"); i >= 0 { 347 flag = phase[i+1:] 348 phase = phase[:i] 349 } 350 err := f.debugSSA(phase, flag, val, valstring) 351 if err != "" { 352 log.Fatalf(err) 353 } 354 // Setting this false for -d=ssa/... preserves old behavior 355 // of turning off concurrency for any debug flags. 356 // It's not known for sure if this is necessary, but it is safe. 357 *f.concurrentOk = false 358 359 } else { 360 return fmt.Errorf("unknown debug key %s\n", name) 361 } 362 } 363 364 return nil 365 } 366 367 const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] 368 369 <key> is one of: 370 371 ` 372 373 func (f *DebugFlag) String() string { 374 return "" 375 }