flag.go raw

   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