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 "" }
163