github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/cmd/go/internal/cmdflag/flag.go (about) 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 os.Exit(2) 70 } 71 72 // Parse sees if argument i is present in the definitions and if so, 73 // returns its definition, value, and whether it consumed an extra word. 74 // If the flag begins (cmd+".") it is ignored for the purpose of this function. 75 func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) { 76 arg := args[i] 77 if strings.HasPrefix(arg, "--") { // reduce two minuses to one 78 arg = arg[1:] 79 } 80 switch arg { 81 case "-?", "-h", "-help": 82 base.Usage() 83 } 84 if arg == "" || arg[0] != '-' { 85 return 86 } 87 name := arg[1:] 88 // If there's already a prefix such as "test.", drop it for now. 89 name = strings.TrimPrefix(name, cmd+".") 90 equals := strings.Index(name, "=") 91 if equals >= 0 { 92 value = name[equals+1:] 93 name = name[:equals] 94 } 95 for _, f = range defns { 96 if name == f.Name { 97 // Booleans are special because they have modes -x, -x=true, -x=false. 98 if f.BoolVar != nil || IsBool(f.Value) { 99 if equals < 0 { // Otherwise, it's been set and will be verified in SetBool. 100 value = "true" 101 } else { 102 // verify it parses 103 SetBool(cmd, new(bool), value) 104 } 105 } else { // Non-booleans must have a value. 106 extra = equals < 0 107 if extra { 108 if i+1 >= len(args) { 109 SyntaxError(cmd, "missing argument for flag "+f.Name) 110 } 111 value = args[i+1] 112 } 113 } 114 if f.Present { 115 SyntaxError(cmd, f.Name+" flag may be set only once") 116 } 117 f.Present = true 118 return 119 } 120 } 121 f = nil 122 return 123 }