...

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	)
    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	}
   152	

View as plain text