Home | History | Annotate | Download | only in load
      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 }
    122