Source file src/cmd/go/internal/base/goflags.go

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

View as plain text