flag.go (3732B)
1 // Copyright 2015 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 objabi 6 7 import ( 8 "flag" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "log" 13 "os" 14 "strconv" 15 "strings" 16 ) 17 18 func Flagcount(name, usage string, val *int) { 19 flag.Var((*count)(val), name, usage) 20 } 21 22 func Flagfn1(name, usage string, f func(string)) { 23 flag.Var(fn1(f), name, usage) 24 } 25 26 func Flagprint(w io.Writer) { 27 flag.CommandLine.SetOutput(w) 28 flag.PrintDefaults() 29 } 30 31 func Flagparse(usage func()) { 32 flag.Usage = usage 33 os.Args = expandArgs(os.Args) 34 flag.Parse() 35 } 36 37 // expandArgs expands "response files" arguments in the provided slice. 38 // 39 // A "response file" argument starts with '@' and the rest of that 40 // argument is a filename with CR-or-CRLF-separated arguments. Each 41 // argument in the named files can also contain response file 42 // arguments. See Issue 18468. 43 // 44 // The returned slice 'out' aliases 'in' iff the input did not contain 45 // any response file arguments. 46 // 47 // TODO: handle relative paths of recursive expansions in different directories? 48 // Is there a spec for this? Are relative paths allowed? 49 func expandArgs(in []string) (out []string) { 50 // out is nil until we see a "@" argument. 51 for i, s := range in { 52 if strings.HasPrefix(s, "@") { 53 if out == nil { 54 out = make([]string, 0, len(in)*2) 55 out = append(out, in[:i]...) 56 } 57 slurp, err := ioutil.ReadFile(s[1:]) 58 if err != nil { 59 log.Fatal(err) 60 } 61 args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n") 62 out = append(out, expandArgs(args)...) 63 } else if out != nil { 64 out = append(out, s) 65 } 66 } 67 if out == nil { 68 return in 69 } 70 return 71 } 72 73 func AddVersionFlag() { 74 flag.Var(versionFlag{}, "V", "print version and exit") 75 } 76 77 var buildID string // filled in by linker 78 79 type versionFlag struct{} 80 81 func (versionFlag) IsBoolFlag() bool { return true } 82 func (versionFlag) Get() interface{} { return nil } 83 func (versionFlag) String() string { return "" } 84 func (versionFlag) Set(s string) error { 85 name := os.Args[0] 86 name = name[strings.LastIndex(name, `/`)+1:] 87 name = name[strings.LastIndex(name, `\`)+1:] 88 name = strings.TrimSuffix(name, ".exe") 89 90 // If there's an active experiment, include that, 91 // to distinguish go1.10.2 with an experiment 92 // from go1.10.2 without an experiment. 93 p := Expstring() 94 if p == DefaultExpstring() { 95 p = "" 96 } 97 sep := "" 98 if p != "" { 99 sep = " " 100 } 101 102 // The go command invokes -V=full to get a unique identifier 103 // for this tool. It is assumed that the release version is sufficient 104 // for releases, but during development we include the full 105 // build ID of the binary, so that if the compiler is changed and 106 // rebuilt, we notice and rebuild all packages. 107 if s == "full" { 108 if strings.HasPrefix(Version, "devel") { 109 p += " buildID=" + buildID 110 } 111 } 112 113 fmt.Printf("%s version %s%s%s\n", name, Version, sep, p) 114 os.Exit(0) 115 return nil 116 } 117 118 // count is a flag.Value that is like a flag.Bool and a flag.Int. 119 // If used as -name, it increments the count, but -name=x sets the count. 120 // Used for verbose flag -v. 121 type count int 122 123 func (c *count) String() string { 124 return fmt.Sprint(int(*c)) 125 } 126 127 func (c *count) Set(s string) error { 128 switch s { 129 case "true": 130 *c++ 131 case "false": 132 *c = 0 133 default: 134 n, err := strconv.Atoi(s) 135 if err != nil { 136 return fmt.Errorf("invalid count %q", s) 137 } 138 *c = count(n) 139 } 140 return nil 141 } 142 143 func (c *count) Get() interface{} { 144 return int(*c) 145 } 146 147 func (c *count) IsBoolFlag() bool { 148 return true 149 } 150 151 func (c *count) IsCountFlag() bool { 152 return true 153 } 154 155 type fn1 func(string) 156 157 func (f fn1) Set(s string) error { 158 f(s) 159 return nil 160 } 161 162 func (f fn1) String() string { return "" }