github.com/kdevb0x/go@v0.0.0-20180115030120-39687051e9e7/src/cmd/go/internal/load/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 load 6 7 import ( 8 "cmd/go/internal/base" 9 "cmd/go/internal/str" 10 "fmt" 11 "strings" 12 ) 13 14 var ( 15 BuildAsmflags PerPackageFlag // -asmflags 16 BuildGcflags PerPackageFlag // -gcflags 17 BuildLdflags PerPackageFlag // -ldflags 18 BuildGccgoflags PerPackageFlag // -gccgoflags 19 ) 20 21 // A PerPackageFlag is a command-line flag implementation (a flag.Value) 22 // that allows specifying different effective flags for different packages. 23 // See 'go help build' for more details about per-package flags. 24 type PerPackageFlag struct { 25 present bool 26 values []ppfValue 27 } 28 29 // A ppfValue is a single <pattern>=<flags> per-package flag value. 30 type ppfValue struct { 31 match func(*Package) bool // compiled pattern 32 flags []string 33 } 34 35 // Set is called each time the flag is encountered on the command line. 36 func (f *PerPackageFlag) Set(v string) error { 37 return f.set(v, base.Cwd) 38 } 39 40 // set is the implementation of Set, taking a cwd (current working directory) for easier testing. 41 func (f *PerPackageFlag) set(v, cwd string) error { 42 f.present = true 43 match := func(p *Package) bool { return p.Internal.CmdlinePkg || p.Internal.CmdlineFiles } // default predicate with no pattern 44 // For backwards compatibility with earlier flag splitting, ignore spaces around flags. 45 v = strings.TrimSpace(v) 46 if v == "" { 47 // Special case: -gcflags="" means no flags for command-line arguments 48 // (overrides previous -gcflags="-whatever"). 49 f.values = append(f.values, ppfValue{match, []string{}}) 50 return nil 51 } 52 if !strings.HasPrefix(v, "-") { 53 i := strings.Index(v, "=") 54 if i < 0 { 55 return fmt.Errorf("missing =<value> in <pattern>=<value>") 56 } 57 if i == 0 { 58 return fmt.Errorf("missing <pattern> in <pattern>=<value>") 59 } 60 pattern := strings.TrimSpace(v[:i]) 61 match = MatchPackage(pattern, cwd) 62 v = v[i+1:] 63 } 64 flags, err := str.SplitQuotedFields(v) 65 if err != nil { 66 return err 67 } 68 if flags == nil { 69 flags = []string{} 70 } 71 f.values = append(f.values, ppfValue{match, flags}) 72 return nil 73 } 74 75 // String is required to implement flag.Value. 76 // It is not used, because cmd/go never calls flag.PrintDefaults. 77 func (f *PerPackageFlag) String() string { return "<PerPackageFlag>" } 78 79 // Present reports whether the flag appeared on the command line. 80 func (f *PerPackageFlag) Present() bool { 81 return f.present 82 } 83 84 // For returns the flags to use for the given package. 85 func (f *PerPackageFlag) For(p *Package) []string { 86 flags := []string{} 87 for _, v := range f.values { 88 if v.match(p) { 89 flags = v.flags 90 } 91 } 92 return flags 93 } 94 95 var cmdlineMatchers []func(*Package) bool 96 97 // SetCmdlinePatterns records the set of patterns given on the command line, 98 // for use by the PerPackageFlags. 99 func SetCmdlinePatterns(args []string) { 100 setCmdlinePatterns(args, base.Cwd) 101 } 102 103 func setCmdlinePatterns(args []string, cwd string) { 104 if len(args) == 0 { 105 args = []string{"."} 106 } 107 cmdlineMatchers = nil // allow reset for testing 108 for _, arg := range args { 109 cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd)) 110 } 111 } 112 113 // isCmdlinePkg reports whether p is a package listed on the command line. 114 func isCmdlinePkg(p *Package) bool { 115 for _, m := range cmdlineMatchers { 116 if m(p) { 117 return true 118 } 119 } 120 return false 121 }