| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package load |
| |
| import ( |
| "cmd/go/internal/base" |
| "cmd/go/internal/search" |
| "cmd/go/internal/str" |
| "fmt" |
| "strings" |
| ) |
| |
| var ( |
| BuildAsmflags PerPackageFlag // -asmflags |
| BuildGcflags PerPackageFlag // -gcflags |
| BuildLdflags PerPackageFlag // -ldflags |
| BuildGccgoflags PerPackageFlag // -gccgoflags |
| ) |
| |
| // A PerPackageFlag is a command-line flag implementation (a flag.Value) |
| // that allows specifying different effective flags for different packages. |
| // See 'go help build' for more details about per-package flags. |
| type PerPackageFlag struct { |
| present bool |
| values []ppfValue |
| } |
| |
| // A ppfValue is a single <pattern>=<flags> per-package flag value. |
| type ppfValue struct { |
| match func(*Package) bool // compiled pattern |
| flags []string |
| } |
| |
| // Set is called each time the flag is encountered on the command line. |
| func (f *PerPackageFlag) Set(v string) error { |
| return f.set(v, base.Cwd) |
| } |
| |
| // set is the implementation of Set, taking a cwd (current working directory) for easier testing. |
| func (f *PerPackageFlag) set(v, cwd string) error { |
| f.present = true |
| match := func(p *Package) bool { return p.Internal.CmdlinePkg || p.Internal.CmdlineFiles } // default predicate with no pattern |
| // For backwards compatibility with earlier flag splitting, ignore spaces around flags. |
| v = strings.TrimSpace(v) |
| if v == "" { |
| // Special case: -gcflags="" means no flags for command-line arguments |
| // (overrides previous -gcflags="-whatever"). |
| f.values = append(f.values, ppfValue{match, []string{}}) |
| return nil |
| } |
| if !strings.HasPrefix(v, "-") { |
| i := strings.Index(v, "=") |
| if i < 0 { |
| return fmt.Errorf("missing =<value> in <pattern>=<value>") |
| } |
| if i == 0 { |
| return fmt.Errorf("missing <pattern> in <pattern>=<value>") |
| } |
| pattern := strings.TrimSpace(v[:i]) |
| match = MatchPackage(pattern, cwd) |
| v = v[i+1:] |
| } |
| flags, err := str.SplitQuotedFields(v) |
| if err != nil { |
| return err |
| } |
| if flags == nil { |
| flags = []string{} |
| } |
| f.values = append(f.values, ppfValue{match, flags}) |
| return nil |
| } |
| |
| // String is required to implement flag.Value. |
| // It is not used, because cmd/go never calls flag.PrintDefaults. |
| func (f *PerPackageFlag) String() string { return "<PerPackageFlag>" } |
| |
| // Present reports whether the flag appeared on the command line. |
| func (f *PerPackageFlag) Present() bool { |
| return f.present |
| } |
| |
| // For returns the flags to use for the given package. |
| func (f *PerPackageFlag) For(p *Package) []string { |
| flags := []string{} |
| for _, v := range f.values { |
| if v.match(p) { |
| flags = v.flags |
| } |
| } |
| return flags |
| } |
| |
| var ( |
| cmdlineMatchers []func(*Package) bool |
| cmdlineMatcherLiterals []func(*Package) bool |
| ) |
| |
| // SetCmdlinePatterns records the set of patterns given on the command line, |
| // for use by the PerPackageFlags. |
| func SetCmdlinePatterns(args []string) { |
| setCmdlinePatterns(args, base.Cwd) |
| } |
| |
| func setCmdlinePatterns(args []string, cwd string) { |
| if len(args) == 0 { |
| args = []string{"."} |
| } |
| cmdlineMatchers = nil // allow reset for testing |
| cmdlineMatcherLiterals = nil |
| for _, arg := range args { |
| cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd)) |
| } |
| for _, arg := range args { |
| if !strings.Contains(arg, "...") && !search.IsMetaPackage(arg) { |
| cmdlineMatcherLiterals = append(cmdlineMatcherLiterals, MatchPackage(arg, cwd)) |
| } |
| } |
| } |
| |
| // isCmdlinePkg reports whether p is a package listed on the command line. |
| func isCmdlinePkg(p *Package) bool { |
| for _, m := range cmdlineMatchers { |
| if m(p) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // isCmdlinePkgLiteral reports whether p is a package listed as |
| // a literal package argument on the command line |
| // (as opposed to being the result of expanding a wildcard). |
| func isCmdlinePkgLiteral(p *Package) bool { |
| for _, m := range cmdlineMatcherLiterals { |
| if m(p) { |
| return true |
| } |
| } |
| return false |
| } |