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  }