...

Source file src/pkg/cmd/go/internal/cmdflag/flag.go

     1	// Copyright 2017 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 cmdflag handles flag processing common to several go tools.
     6	package cmdflag
     7	
     8	import (
     9		"flag"
    10		"fmt"
    11		"os"
    12		"strconv"
    13		"strings"
    14	
    15		"cmd/go/internal/base"
    16	)
    17	
    18	// The flag handling part of go commands such as test is large and distracting.
    19	// We can't use the standard flag package because some of the flags from
    20	// our command line are for us, and some are for the binary we're running,
    21	// and some are for both.
    22	
    23	// Defn defines a flag we know about.
    24	type Defn struct {
    25		Name       string     // Name on command line.
    26		BoolVar    *bool      // If it's a boolean flag, this points to it.
    27		Value      flag.Value // The flag.Value represented.
    28		PassToTest bool       // Pass to the test binary? Used only by go test.
    29		Present    bool       // Flag has been seen.
    30	}
    31	
    32	// IsBool reports whether v is a bool flag.
    33	func IsBool(v flag.Value) bool {
    34		vv, ok := v.(interface {
    35			IsBoolFlag() bool
    36		})
    37		if ok {
    38			return vv.IsBoolFlag()
    39		}
    40		return false
    41	}
    42	
    43	// SetBool sets the addressed boolean to the value.
    44	func SetBool(cmd string, flag *bool, value string) {
    45		x, err := strconv.ParseBool(value)
    46		if err != nil {
    47			SyntaxError(cmd, "illegal bool flag value "+value)
    48		}
    49		*flag = x
    50	}
    51	
    52	// SetInt sets the addressed integer to the value.
    53	func SetInt(cmd string, flag *int, value string) {
    54		x, err := strconv.Atoi(value)
    55		if err != nil {
    56			SyntaxError(cmd, "illegal int flag value "+value)
    57		}
    58		*flag = x
    59	}
    60	
    61	// SyntaxError reports an argument syntax error and exits the program.
    62	func SyntaxError(cmd, msg string) {
    63		fmt.Fprintf(os.Stderr, "go %s: %s\n", cmd, msg)
    64		if cmd == "test" {
    65			fmt.Fprintf(os.Stderr, `run "go help %s" or "go help testflag" for more information`+"\n", cmd)
    66		} else {
    67			fmt.Fprintf(os.Stderr, `run "go help %s" for more information`+"\n", cmd)
    68		}
    69		base.SetExitStatus(2)
    70		base.Exit()
    71	}
    72	
    73	// AddKnownFlags registers the flags in defns with base.AddKnownFlag.
    74	func AddKnownFlags(cmd string, defns []*Defn) {
    75		for _, f := range defns {
    76			base.AddKnownFlag(f.Name)
    77			base.AddKnownFlag(cmd + "." + f.Name)
    78		}
    79	}
    80	
    81	// Parse sees if argument i is present in the definitions and if so,
    82	// returns its definition, value, and whether it consumed an extra word.
    83	// If the flag begins (cmd.Name()+".") it is ignored for the purpose of this function.
    84	func Parse(cmd string, usage func(), defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) {
    85		arg := args[i]
    86		if strings.HasPrefix(arg, "--") { // reduce two minuses to one
    87			arg = arg[1:]
    88		}
    89		switch arg {
    90		case "-?", "-h", "-help":
    91			usage()
    92		}
    93		if arg == "" || arg[0] != '-' {
    94			return
    95		}
    96		name := arg[1:]
    97		// If there's already a prefix such as "test.", drop it for now.
    98		name = strings.TrimPrefix(name, cmd+".")
    99		equals := strings.Index(name, "=")
   100		if equals >= 0 {
   101			value = name[equals+1:]
   102			name = name[:equals]
   103		}
   104		for _, f = range defns {
   105			if name == f.Name {
   106				// Booleans are special because they have modes -x, -x=true, -x=false.
   107				if f.BoolVar != nil || IsBool(f.Value) {
   108					if equals < 0 { // Otherwise, it's been set and will be verified in SetBool.
   109						value = "true"
   110					} else {
   111						// verify it parses
   112						SetBool(cmd, new(bool), value)
   113					}
   114				} else { // Non-booleans must have a value.
   115					extra = equals < 0
   116					if extra {
   117						if i+1 >= len(args) {
   118							SyntaxError(cmd, "missing argument for flag "+f.Name)
   119						}
   120						value = args[i+1]
   121					}
   122				}
   123				if f.Present {
   124					SyntaxError(cmd, f.Name+" flag may be set only once")
   125				}
   126				f.Present = true
   127				return
   128			}
   129		}
   130		f = nil
   131		return
   132	}
   133	
   134	// FindGOFLAGS extracts and returns the flags matching defns from GOFLAGS.
   135	// Ideally the caller would mention that the flags were from GOFLAGS
   136	// when reporting errors, but that's too hard for now.
   137	func FindGOFLAGS(defns []*Defn) []string {
   138		var flags []string
   139		for _, flag := range base.GOFLAGS() {
   140			// Flags returned by base.GOFLAGS are well-formed, one of:
   141			//	-x
   142			//	--x
   143			//	-x=value
   144			//	--x=value
   145			if strings.HasPrefix(flag, "--") {
   146				flag = flag[1:]
   147			}
   148			name := flag[1:]
   149			if i := strings.Index(name, "="); i >= 0 {
   150				name = name[:i]
   151			}
   152			for _, f := range defns {
   153				if name == f.Name {
   154					flags = append(flags, flag)
   155					break
   156				}
   157			}
   158		}
   159		return flags
   160	}
   161	

View as plain text