github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/base/goflags.go (about)

     1  // Copyright 2018 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 base
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"runtime"
    11  	"strings"
    12  
    13  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg"
    14  )
    15  
    16  var (
    17  	goflags   []string                // cached $GOFLAGS list; can be -x or --x form
    18  	knownFlag = make(map[string]bool) // flags allowed to appear in $GOFLAGS; no leading dashes
    19  )
    20  
    21  // AddKnownFlag adds name to the list of known flags for use in $GOFLAGS.
    22  func AddKnownFlag(name string) {
    23  	knownFlag[name] = true
    24  }
    25  
    26  // GOFLAGS returns the flags from $GOFLAGS.
    27  // The list can be assumed to contain one string per flag,
    28  // with each string either beginning with -name or --name.
    29  func GOFLAGS() []string {
    30  	InitGOFLAGS()
    31  	return goflags
    32  }
    33  
    34  // InitGOFLAGS initializes the goflags list from $GOFLAGS.
    35  // If goflags is already initialized, it does nothing.
    36  func InitGOFLAGS() {
    37  	if goflags != nil { // already initialized
    38  		return
    39  	}
    40  
    41  	// Build list of all flags for all commands.
    42  	// If no command has that flag, then we report the problem.
    43  	// This catches typos while still letting users record flags in GOFLAGS
    44  	// that only apply to a subset of go commands.
    45  	// Commands using CustomFlags can report their flag names
    46  	// by calling AddKnownFlag instead.
    47  	var walkFlags func(*Command)
    48  	walkFlags = func(cmd *Command) {
    49  		for _, sub := range cmd.Commands {
    50  			walkFlags(sub)
    51  		}
    52  		cmd.Flag.VisitAll(func(f *flag.Flag) {
    53  			knownFlag[f.Name] = true
    54  		})
    55  	}
    56  	walkFlags(Go)
    57  
    58  	// Ignore bad flag in go env and go bug, because
    59  	// they are what people reach for when debugging
    60  	// a problem, and maybe they're debugging GOFLAGS.
    61  	// (Both will show the GOFLAGS setting if let succeed.)
    62  	hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"
    63  
    64  	goflags = strings.Fields(cfg.Getenv("GOFLAGS"))
    65  	if goflags == nil {
    66  		goflags = []string{} // avoid work on later InitGOFLAGS call
    67  	}
    68  
    69  	// Each of the words returned by strings.Fields must be its own flag.
    70  	// To set flag arguments use -x=value instead of -x value.
    71  	// For boolean flags, -x is fine instead of -x=true.
    72  	for _, f := range goflags {
    73  		// Check that every flag looks like -x --x -x=value or --x=value.
    74  		if !strings.HasPrefix(f, "-") || f == "-" || f == "--" || strings.HasPrefix(f, "---") || strings.HasPrefix(f, "-=") || strings.HasPrefix(f, "--=") {
    75  			if hideErrors {
    76  				continue
    77  			}
    78  			Fatalf("go: parsing $GOFLAGS: non-flag %q", f)
    79  		}
    80  
    81  		name := f[1:]
    82  		if name[0] == '-' {
    83  			name = name[1:]
    84  		}
    85  		if i := strings.Index(name, "="); i >= 0 {
    86  			name = name[:i]
    87  		}
    88  		if !knownFlag[name] {
    89  			if hideErrors {
    90  				continue
    91  			}
    92  			Fatalf("go: parsing $GOFLAGS: unknown flag -%s", name)
    93  		}
    94  	}
    95  }
    96  
    97  // boolFlag is the optional interface for flag.Value known to the flag package.
    98  // (It is not clear why package flag does not export this interface.)
    99  type boolFlag interface {
   100  	flag.Value
   101  	IsBoolFlag() bool
   102  }
   103  
   104  // SetFromGOFLAGS sets the flags in the given flag set using settings in $GOFLAGS.
   105  func SetFromGOFLAGS(flags flag.FlagSet) {
   106  	InitGOFLAGS()
   107  
   108  	// This loop is similar to flag.Parse except that it ignores
   109  	// unknown flags found in goflags, so that setting, say, GOFLAGS=-ldflags=-w
   110  	// does not break commands that don't have a -ldflags.
   111  	// It also adjusts the output to be clear that the reported problem is from $GOFLAGS.
   112  	where := "$GOFLAGS"
   113  	if runtime.GOOS == "windows" {
   114  		where = "%GOFLAGS%"
   115  	}
   116  	for _, goflag := range goflags {
   117  		name, value, hasValue := goflag, "", false
   118  		if i := strings.Index(goflag, "="); i >= 0 {
   119  			name, value, hasValue = goflag[:i], goflag[i+1:], true
   120  		}
   121  		if strings.HasPrefix(name, "--") {
   122  			name = name[1:]
   123  		}
   124  		f := flags.Lookup(name[1:])
   125  		if f == nil {
   126  			continue
   127  		}
   128  		if fb, ok := f.Value.(boolFlag); ok && fb.IsBoolFlag() {
   129  			if hasValue {
   130  				if err := fb.Set(value); err != nil {
   131  					fmt.Fprintf(flags.Output(), "go: invalid boolean value %q for flag %s (from %s): %v\n", value, name, where, err)
   132  					flags.Usage()
   133  				}
   134  			} else {
   135  				if err := fb.Set("true"); err != nil {
   136  					fmt.Fprintf(flags.Output(), "go: invalid boolean flag %s (from %s): %v\n", name, where, err)
   137  					flags.Usage()
   138  				}
   139  			}
   140  		} else {
   141  			if !hasValue {
   142  				fmt.Fprintf(flags.Output(), "go: flag needs an argument: %s (from %s)\n", name, where)
   143  				flags.Usage()
   144  			}
   145  			if err := f.Value.Set(value); err != nil {
   146  				fmt.Fprintf(flags.Output(), "go: invalid value %q for flag %s (from %s): %v\n", value, name, where, err)
   147  				flags.Usage()
   148  			}
   149  		}
   150  	}
   151  }