blob: d9177b0de3e94edd53349aac5e872f32e73e886a [file] [log] [blame]
// 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
}