cmd/go/internal/search: split pattern matching out of internal/load
diff --git a/vendor/cmd/go/internal/get/get.go b/vendor/cmd/go/internal/get/get.go
index 733116e..0fee5f0 100644
--- a/vendor/cmd/go/internal/get/get.go
+++ b/vendor/cmd/go/internal/get/get.go
@@ -16,6 +16,7 @@
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
+ "cmd/go/internal/search"
"cmd/go/internal/str"
"cmd/go/internal/web"
"cmd/go/internal/work"
@@ -170,7 +171,7 @@
// in the hope that we can figure out the repository from the
// initial ...-free prefix.
func downloadPaths(args []string) []string {
- args = load.ImportPathsNoDotExpansion(args)
+ args = load.ImportPathsForGoGet(args)
var out []string
for _, a := range args {
if strings.Contains(a, "...") {
@@ -179,9 +180,9 @@
// warnings. They will be printed by the
// eventual call to importPaths instead.
if build.IsLocalImport(a) {
- expand = load.MatchPackagesInFS(a)
+ expand = search.MatchPackagesInFS(a)
} else {
- expand = load.MatchPackages(a)
+ expand = search.MatchPackages(a)
}
if len(expand) > 0 {
out = append(out, expand...)
@@ -271,9 +272,9 @@
// for p has been replaced in the package cache.
if wildcardOkay && strings.Contains(arg, "...") {
if build.IsLocalImport(arg) {
- args = load.MatchPackagesInFS(arg)
+ args = search.MatchPackagesInFS(arg)
} else {
- args = load.MatchPackages(arg)
+ args = search.MatchPackages(arg)
}
isWildcard = true
}
diff --git a/vendor/cmd/go/internal/load/path.go b/vendor/cmd/go/internal/load/path.go
index 45a9e7b..8f3184e 100644
--- a/vendor/cmd/go/internal/load/path.go
+++ b/vendor/cmd/go/internal/load/path.go
@@ -32,21 +32,7 @@
return filepath.ToSlash(dir[len(root):]), true
}
-// hasPathPrefix reports whether the path s begins with the
-// elements in prefix.
-func hasPathPrefix(s, prefix string) bool {
- switch {
- default:
- return false
- case len(s) == len(prefix):
- return s == prefix
- case len(s) > len(prefix):
- if prefix != "" && prefix[len(prefix)-1] == '/' {
- return strings.HasPrefix(s, prefix)
- }
- return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
- }
-}
+// vgo: hasPathPrefix moved to ../search
// expandPath returns the symlink-expanded form of path.
func expandPath(p string) string {
diff --git a/vendor/cmd/go/internal/load/pkg.go b/vendor/cmd/go/internal/load/pkg.go
index b006d41..9f5f858 100644
--- a/vendor/cmd/go/internal/load/pkg.go
+++ b/vendor/cmd/go/internal/load/pkg.go
@@ -20,6 +20,7 @@
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/search"
"cmd/go/internal/str"
)
@@ -222,7 +223,7 @@
// TODO? Target
p.Goroot = pp.Goroot
- p.Standard = p.Goroot && p.ImportPath != "" && isStandardImportPath(p.ImportPath)
+ p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath)
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
@@ -256,19 +257,6 @@
}
}
-// isStandardImportPath reports whether $GOROOT/src/path should be considered
-// part of the standard distribution. For historical reasons we allow people to add
-// their own code to $GOROOT instead of using $GOPATH, but we assume that
-// code will start with a domain name (dot in the first element).
-func isStandardImportPath(path string) bool {
- i := strings.Index(path, "/")
- if i < 0 {
- i = len(path)
- }
- elem := path[:i]
- return !strings.Contains(elem, ".")
-}
-
// A PackageError describes an error loading information about a package.
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
@@ -1464,6 +1452,20 @@
return pkgs
}
+func ImportPaths(args []string) []string {
+ if cmdlineMatchers == nil {
+ SetCmdlinePatterns(search.CleanImportPaths(args))
+ }
+ return search.ImportPaths(args)
+}
+
+func ImportPathsForGoGet(args []string) []string {
+ if cmdlineMatchers == nil {
+ SetCmdlinePatterns(search.CleanImportPaths(args))
+ }
+ return search.ImportPathsNoDotExpansion(args)
+}
+
// packagesForBuild is like 'packages' but fails if any of
// the packages or their dependencies have errors
// (cannot be built).
diff --git a/vendor/cmd/go/internal/load/search.go b/vendor/cmd/go/internal/load/search.go
index 595de07..e3f54a6 100644
--- a/vendor/cmd/go/internal/load/search.go
+++ b/vendor/cmd/go/internal/load/search.go
@@ -5,267 +5,12 @@
package load
import (
- "cmd/go/internal/cfg"
- "fmt"
- "go/build"
- "log"
- "os"
- "path"
"path/filepath"
- "regexp"
"strings"
+
+ "cmd/go/internal/search"
)
-// allPackages returns all the packages that can be found
-// under the $GOPATH directories and $GOROOT matching pattern.
-// The pattern is either "all" (all packages), "std" (standard packages),
-// "cmd" (standard commands), or a path including "...".
-func allPackages(pattern string) []string {
- pkgs := MatchPackages(pattern)
- if len(pkgs) == 0 {
- fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
- }
- return pkgs
-}
-
-// allPackagesInFS is like allPackages but is passed a pattern
-// beginning ./ or ../, meaning it should scan the tree rooted
-// at the given directory. There are ... in the pattern too.
-func allPackagesInFS(pattern string) []string {
- pkgs := MatchPackagesInFS(pattern)
- if len(pkgs) == 0 {
- fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
- }
- return pkgs
-}
-
-// MatchPackages returns a list of package paths matching pattern
-// (see go help packages for pattern syntax).
-func MatchPackages(pattern string) []string {
- match := func(string) bool { return true }
- treeCanMatch := func(string) bool { return true }
- if !IsMetaPackage(pattern) {
- match = matchPattern(pattern)
- treeCanMatch = treeCanMatchPattern(pattern)
- }
-
- have := map[string]bool{
- "builtin": true, // ignore pseudo-package that exists only for documentation
- }
- if !cfg.BuildContext.CgoEnabled {
- have["runtime/cgo"] = true // ignore during walk
- }
- var pkgs []string
-
- for _, src := range cfg.BuildContext.SrcDirs() {
- if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
- continue
- }
- src = filepath.Clean(src) + string(filepath.Separator)
- root := src
- if pattern == "cmd" {
- root += "cmd" + string(filepath.Separator)
- }
- filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
- if err != nil || path == src {
- return nil
- }
-
- want := true
- // Avoid .foo, _foo, and testdata directory trees.
- _, elem := filepath.Split(path)
- if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
- want = false
- }
-
- name := filepath.ToSlash(path[len(src):])
- if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
- // The name "std" is only the standard library.
- // If the name is cmd, it's the root of the command tree.
- want = false
- }
- if !treeCanMatch(name) {
- want = false
- }
-
- if !fi.IsDir() {
- if fi.Mode()&os.ModeSymlink != 0 && want {
- if target, err := os.Stat(path); err == nil && target.IsDir() {
- fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
- }
- }
- return nil
- }
- if !want {
- return filepath.SkipDir
- }
-
- if have[name] {
- return nil
- }
- have[name] = true
- if !match(name) {
- return nil
- }
- pkg, err := cfg.BuildContext.ImportDir(path, 0)
- if err != nil {
- if _, noGo := err.(*build.NoGoError); noGo {
- return nil
- }
- }
-
- // If we are expanding "cmd", skip main
- // packages under cmd/vendor. At least as of
- // March, 2017, there is one there for the
- // vendored pprof tool.
- if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
- return nil
- }
-
- pkgs = append(pkgs, name)
- return nil
- })
- }
- return pkgs
-}
-
-// MatchPackagesInFS returns a list of package paths matching pattern,
-// which must begin with ./ or ../
-// (see go help packages for pattern syntax).
-func MatchPackagesInFS(pattern string) []string {
- // Find directory to begin the scan.
- // Could be smarter but this one optimization
- // is enough for now, since ... is usually at the
- // end of a path.
- i := strings.Index(pattern, "...")
- dir, _ := path.Split(pattern[:i])
-
- // pattern begins with ./ or ../.
- // path.Clean will discard the ./ but not the ../.
- // We need to preserve the ./ for pattern matching
- // and in the returned import paths.
- prefix := ""
- if strings.HasPrefix(pattern, "./") {
- prefix = "./"
- }
- match := matchPattern(pattern)
-
- var pkgs []string
- filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
- return nil
- }
- if path == dir {
- // filepath.Walk starts at dir and recurses. For the recursive case,
- // the path is the result of filepath.Join, which calls filepath.Clean.
- // The initial case is not Cleaned, though, so we do this explicitly.
- //
- // This converts a path like "./io/" to "io". Without this step, running
- // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
- // package, because prepending the prefix "./" to the unclean path would
- // result in "././io", and match("././io") returns false.
- path = filepath.Clean(path)
- }
-
- // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
- _, elem := filepath.Split(path)
- dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
- if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
- return filepath.SkipDir
- }
-
- name := prefix + filepath.ToSlash(path)
- if !match(name) {
- return nil
- }
-
- // We keep the directory if we can import it, or if we can't import it
- // due to invalid Go source files. This means that directories containing
- // parse errors will be built (and fail) instead of being silently skipped
- // as not matching the pattern. Go 1.5 and earlier skipped, but that
- // behavior means people miss serious mistakes.
- // See golang.org/issue/11407.
- if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
- if _, noGo := err.(*build.NoGoError); !noGo {
- log.Print(err)
- }
- return nil
- }
- pkgs = append(pkgs, name)
- return nil
- })
- return pkgs
-}
-
-// treeCanMatchPattern(pattern)(name) reports whether
-// name or children of name can possibly match pattern.
-// Pattern is the same limited glob accepted by matchPattern.
-func treeCanMatchPattern(pattern string) func(name string) bool {
- wildCard := false
- if i := strings.Index(pattern, "..."); i >= 0 {
- wildCard = true
- pattern = pattern[:i]
- }
- return func(name string) bool {
- return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
- wildCard && strings.HasPrefix(name, pattern)
- }
-}
-
-// matchPattern(pattern)(name) reports whether
-// name matches pattern. Pattern is a limited glob
-// pattern in which '...' means 'any string' and there
-// is no other special syntax.
-// Unfortunately, there are two special cases. Quoting "go help packages":
-//
-// First, /... at the end of the pattern can match an empty string,
-// so that net/... matches both net and packages in its subdirectories, like net/http.
-// Second, any slash-separted pattern element containing a wildcard never
-// participates in a match of the "vendor" element in the path of a vendored
-// package, so that ./... does not match packages in subdirectories of
-// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
-// Note, however, that a directory named vendor that itself contains code
-// is not a vendored package: cmd/vendor would be a command named vendor,
-// and the pattern cmd/... matches it.
-func matchPattern(pattern string) func(name string) bool {
- // Convert pattern to regular expression.
- // The strategy for the trailing /... is to nest it in an explicit ? expression.
- // The strategy for the vendor exclusion is to change the unmatchable
- // vendor strings to a disallowed code point (vendorChar) and to use
- // "(anything but that codepoint)*" as the implementation of the ... wildcard.
- // This is a bit complicated but the obvious alternative,
- // namely a hand-written search like in most shell glob matchers,
- // is too easy to make accidentally exponential.
- // Using package regexp guarantees linear-time matching.
-
- const vendorChar = "\x00"
-
- if strings.Contains(pattern, vendorChar) {
- return func(name string) bool { return false }
- }
-
- re := regexp.QuoteMeta(pattern)
- re = replaceVendor(re, vendorChar)
- switch {
- case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
- re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
- case re == vendorChar+`/\.\.\.`:
- re = `(/vendor|/` + vendorChar + `/\.\.\.)`
- case strings.HasSuffix(re, `/\.\.\.`):
- re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
- }
- re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
-
- reg := regexp.MustCompile(`^` + re + `$`)
-
- return func(name string) bool {
- if strings.Contains(name, vendorChar) {
- return false
- }
- return reg.MatchString(replaceVendor(name, vendorChar))
- }
-}
-
// MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
func MatchPackage(pattern, cwd string) func(*Package) bool {
switch {
@@ -284,7 +29,7 @@
if pattern == "" {
return func(p *Package) bool { return p.Dir == dir }
}
- matchPath := matchPattern(pattern)
+ matchPath := search.MatchPattern(pattern)
return func(p *Package) bool {
// Compute relative path to dir and see if it matches the pattern.
rel, err := filepath.Rel(dir, p.Dir)
@@ -305,81 +50,7 @@
case pattern == "cmd":
return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") }
default:
- matchPath := matchPattern(pattern)
+ matchPath := search.MatchPattern(pattern)
return func(p *Package) bool { return matchPath(p.ImportPath) }
}
}
-
-// replaceVendor returns the result of replacing
-// non-trailing vendor path elements in x with repl.
-func replaceVendor(x, repl string) string {
- if !strings.Contains(x, "vendor") {
- return x
- }
- elem := strings.Split(x, "/")
- for i := 0; i < len(elem)-1; i++ {
- if elem[i] == "vendor" {
- elem[i] = repl
- }
- }
- return strings.Join(elem, "/")
-}
-
-// ImportPaths returns the import paths to use for the given command line.
-func ImportPaths(args []string) []string {
- args = ImportPathsNoDotExpansion(args)
- var out []string
- for _, a := range args {
- if strings.Contains(a, "...") {
- if build.IsLocalImport(a) {
- out = append(out, allPackagesInFS(a)...)
- } else {
- out = append(out, allPackages(a)...)
- }
- continue
- }
- out = append(out, a)
- }
- return out
-}
-
-// ImportPathsNoDotExpansion returns the import paths to use for the given
-// command line, but it does no ... expansion.
-func ImportPathsNoDotExpansion(args []string) []string {
- if cmdlineMatchers == nil {
- SetCmdlinePatterns(args)
- }
- if len(args) == 0 {
- return []string{"."}
- }
- var out []string
- for _, a := range args {
- // Arguments are supposed to be import paths, but
- // as a courtesy to Windows developers, rewrite \ to /
- // in command-line arguments. Handles .\... and so on.
- if filepath.Separator == '\\' {
- a = strings.Replace(a, `\`, `/`, -1)
- }
-
- // Put argument in canonical form, but preserve leading ./.
- if strings.HasPrefix(a, "./") {
- a = "./" + path.Clean(a)
- if a == "./." {
- a = "."
- }
- } else {
- a = path.Clean(a)
- }
- if IsMetaPackage(a) {
- out = append(out, allPackages(a)...)
- continue
- }
- out = append(out, a)
- }
- return out
-}
-
-// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
-func IsMetaPackage(name string) bool {
- return name == "std" || name == "cmd" || name == "all"
-}
diff --git a/vendor/cmd/go/internal/search/search.go b/vendor/cmd/go/internal/search/search.go
new file mode 100644
index 0000000..ab31ab6
--- /dev/null
+++ b/vendor/cmd/go/internal/search/search.go
@@ -0,0 +1,383 @@
+// 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 search
+
+import (
+ "cmd/go/internal/cfg"
+ "fmt"
+ "go/build"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+// AllPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func AllPackages(pattern string) []string {
+ pkgs := MatchPackages(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// AllPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+func AllPackagesInFS(pattern string) []string {
+ pkgs := MatchPackagesInFS(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// MatchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func MatchPackages(pattern string) []string {
+ match := func(string) bool { return true }
+ treeCanMatch := func(string) bool { return true }
+ if !IsMetaPackage(pattern) {
+ match = MatchPattern(pattern)
+ treeCanMatch = TreeCanMatchPattern(pattern)
+ }
+
+ have := map[string]bool{
+ "builtin": true, // ignore pseudo-package that exists only for documentation
+ }
+ if !cfg.BuildContext.CgoEnabled {
+ have["runtime/cgo"] = true // ignore during walk
+ }
+ var pkgs []string
+
+ for _, src := range cfg.BuildContext.SrcDirs() {
+ if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
+ continue
+ }
+ src = filepath.Clean(src) + string(filepath.Separator)
+ root := src
+ if pattern == "cmd" {
+ root += "cmd" + string(filepath.Separator)
+ }
+ filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || path == src {
+ return nil
+ }
+
+ want := true
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ want = false
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") {
+ // The name "std" is only the standard library.
+ // If the name is cmd, it's the root of the command tree.
+ want = false
+ }
+ if !treeCanMatch(name) {
+ want = false
+ }
+
+ if !fi.IsDir() {
+ if fi.Mode()&os.ModeSymlink != 0 && want {
+ if target, err := os.Stat(path); err == nil && target.IsDir() {
+ fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+ }
+ }
+ return nil
+ }
+ if !want {
+ return filepath.SkipDir
+ }
+
+ if have[name] {
+ return nil
+ }
+ have[name] = true
+ if !match(name) {
+ return nil
+ }
+ pkg, err := cfg.BuildContext.ImportDir(path, 0)
+ if err != nil {
+ if _, noGo := err.(*build.NoGoError); noGo {
+ return nil
+ }
+ }
+
+ // If we are expanding "cmd", skip main
+ // packages under cmd/vendor. At least as of
+ // March, 2017, there is one there for the
+ // vendored pprof tool.
+ if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
+ return nil
+ }
+
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ }
+ return pkgs
+}
+
+// MatchPackagesInFS returns a list of package paths matching pattern,
+// which must begin with ./ or ../
+// (see go help packages for pattern syntax).
+func MatchPackagesInFS(pattern string) []string {
+ // Find directory to begin the scan.
+ // Could be smarter but this one optimization
+ // is enough for now, since ... is usually at the
+ // end of a path.
+ i := strings.Index(pattern, "...")
+ dir, _ := path.Split(pattern[:i])
+
+ // pattern begins with ./ or ../.
+ // path.Clean will discard the ./ but not the ../.
+ // We need to preserve the ./ for pattern matching
+ // and in the returned import paths.
+ prefix := ""
+ if strings.HasPrefix(pattern, "./") {
+ prefix = "./"
+ }
+ match := MatchPattern(pattern)
+
+ var pkgs []string
+ filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() {
+ return nil
+ }
+ if path == dir {
+ // filepath.Walk starts at dir and recurses. For the recursive case,
+ // the path is the result of filepath.Join, which calls filepath.Clean.
+ // The initial case is not Cleaned, though, so we do this explicitly.
+ //
+ // This converts a path like "./io/" to "io". Without this step, running
+ // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
+ // package, because prepending the prefix "./" to the unclean path would
+ // result in "././io", and match("././io") returns false.
+ path = filepath.Clean(path)
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
+ _, elem := filepath.Split(path)
+ dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
+ if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := prefix + filepath.ToSlash(path)
+ if !match(name) {
+ return nil
+ }
+
+ // We keep the directory if we can import it, or if we can't import it
+ // due to invalid Go source files. This means that directories containing
+ // parse errors will be built (and fail) instead of being silently skipped
+ // as not matching the pattern. Go 1.5 and earlier skipped, but that
+ // behavior means people miss serious mistakes.
+ // See golang.org/issue/11407.
+ if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
+ if _, noGo := err.(*build.NoGoError); !noGo {
+ log.Print(err)
+ }
+ return nil
+ }
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ return pkgs
+}
+
+// TreeCanMatchPattern(pattern)(name) reports whether
+// name or children of name can possibly match pattern.
+// Pattern is the same limited glob accepted by matchPattern.
+func TreeCanMatchPattern(pattern string) func(name string) bool {
+ wildCard := false
+ if i := strings.Index(pattern, "..."); i >= 0 {
+ wildCard = true
+ pattern = pattern[:i]
+ }
+ return func(name string) bool {
+ return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
+ wildCard && strings.HasPrefix(name, pattern)
+ }
+}
+
+// MatchPattern(pattern)(name) reports whether
+// name matches pattern. Pattern is a limited glob
+// pattern in which '...' means 'any string' and there
+// is no other special syntax.
+// Unfortunately, there are two special cases. Quoting "go help packages":
+//
+// First, /... at the end of the pattern can match an empty string,
+// so that net/... matches both net and packages in its subdirectories, like net/http.
+// Second, any slash-separted pattern element containing a wildcard never
+// participates in a match of the "vendor" element in the path of a vendored
+// package, so that ./... does not match packages in subdirectories of
+// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+// Note, however, that a directory named vendor that itself contains code
+// is not a vendored package: cmd/vendor would be a command named vendor,
+// and the pattern cmd/... matches it.
+func MatchPattern(pattern string) func(name string) bool {
+ // Convert pattern to regular expression.
+ // The strategy for the trailing /... is to nest it in an explicit ? expression.
+ // The strategy for the vendor exclusion is to change the unmatchable
+ // vendor strings to a disallowed code point (vendorChar) and to use
+ // "(anything but that codepoint)*" as the implementation of the ... wildcard.
+ // This is a bit complicated but the obvious alternative,
+ // namely a hand-written search like in most shell glob matchers,
+ // is too easy to make accidentally exponential.
+ // Using package regexp guarantees linear-time matching.
+
+ const vendorChar = "\x00"
+
+ if strings.Contains(pattern, vendorChar) {
+ return func(name string) bool { return false }
+ }
+
+ re := regexp.QuoteMeta(pattern)
+ re = replaceVendor(re, vendorChar)
+ switch {
+ case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
+ re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case re == vendorChar+`/\.\.\.`:
+ re = `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case strings.HasSuffix(re, `/\.\.\.`):
+ re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
+ }
+ re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
+
+ reg := regexp.MustCompile(`^` + re + `$`)
+
+ return func(name string) bool {
+ if strings.Contains(name, vendorChar) {
+ return false
+ }
+ return reg.MatchString(replaceVendor(name, vendorChar))
+ }
+}
+
+// replaceVendor returns the result of replacing
+// non-trailing vendor path elements in x with repl.
+func replaceVendor(x, repl string) string {
+ if !strings.Contains(x, "vendor") {
+ return x
+ }
+ elem := strings.Split(x, "/")
+ for i := 0; i < len(elem)-1; i++ {
+ if elem[i] == "vendor" {
+ elem[i] = repl
+ }
+ }
+ return strings.Join(elem, "/")
+}
+
+// ImportPaths returns the import paths to use for the given command line.
+func ImportPaths(args []string) []string {
+ args = CleanImportPaths(args)
+ var out []string
+ for _, a := range args {
+ if IsMetaPackage(a) {
+ out = append(out, AllPackages(a)...)
+ continue
+ }
+ if strings.Contains(a, "...") {
+ if build.IsLocalImport(a) {
+ out = append(out, AllPackagesInFS(a)...)
+ } else {
+ out = append(out, AllPackages(a)...)
+ }
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// CleanImportPaths returns the import paths to use for the given
+// command line, but it does no wildcard expansion.
+func CleanImportPaths(args []string) []string {
+ if len(args) == 0 {
+ return []string{"."}
+ }
+ var out []string
+ for _, a := range args {
+ // Arguments are supposed to be import paths, but
+ // as a courtesy to Windows developers, rewrite \ to /
+ // in command-line arguments. Handles .\... and so on.
+ if filepath.Separator == '\\' {
+ a = strings.Replace(a, `\`, `/`, -1)
+ }
+
+ // Put argument in canonical form, but preserve leading ./.
+ if strings.HasPrefix(a, "./") {
+ a = "./" + path.Clean(a)
+ if a == "./." {
+ a = "."
+ }
+ } else {
+ a = path.Clean(a)
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// ImportPathsNoDotExpansion returns the import paths to use for the given
+// command line, but it does no ... expansion.
+// TODO(vgo): Delete once old go get is gone.
+func ImportPathsNoDotExpansion(args []string) []string {
+ args = CleanImportPaths(args)
+ var out []string
+ for _, a := range args {
+ if IsMetaPackage(a) {
+ out = append(out, AllPackages(a)...)
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
+func IsMetaPackage(name string) bool {
+ return name == "std" || name == "cmd" || name == "all"
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
+
+// IsStandardImportPath reports whether $GOROOT/src/path should be considered
+// part of the standard distribution. For historical reasons we allow people to add
+// their own code to $GOROOT instead of using $GOPATH, but we assume that
+// code will start with a domain name (dot in the first element).
+func IsStandardImportPath(path string) bool {
+ i := strings.Index(path, "/")
+ if i < 0 {
+ i = len(path)
+ }
+ elem := path[:i]
+ return !strings.Contains(elem, ".")
+}
diff --git a/vendor/cmd/go/internal/load/match_test.go b/vendor/cmd/go/internal/search/search_test.go
similarity index 93%
rename from vendor/cmd/go/internal/load/match_test.go
rename to vendor/cmd/go/internal/search/search_test.go
index b8d67da..0bef765 100644
--- a/vendor/cmd/go/internal/load/match_test.go
+++ b/vendor/cmd/go/internal/search/search_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package load
+package search
import (
"strings"
@@ -65,8 +65,8 @@
`
func TestMatchPattern(t *testing.T) {
- testPatterns(t, "matchPattern", matchPatternTests, func(pattern, name string) bool {
- return matchPattern(pattern)(name)
+ testPatterns(t, "MatchPattern", matchPatternTests, func(pattern, name string) bool {
+ return MatchPattern(pattern)(name)
})
}
@@ -106,8 +106,8 @@
`
func TestTreeCanMatchPattern(t *testing.T) {
- testPatterns(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
- return treeCanMatchPattern(pattern)(name)
+ testPatterns(t, "TreeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
+ return TreeCanMatchPattern(pattern)(name)
})
}
diff --git a/vendor/cmd/go/internal/work/build.go b/vendor/cmd/go/internal/work/build.go
index 57b7b00..191e45c 100644
--- a/vendor/cmd/go/internal/work/build.go
+++ b/vendor/cmd/go/internal/work/build.go
@@ -18,6 +18,7 @@
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
+ "cmd/go/internal/search"
)
var CmdBuild = &base.Command{
@@ -377,7 +378,7 @@
}
var haveNonMeta bool
for _, arg := range args {
- if load.IsMetaPackage(arg) {
+ if search.IsMetaPackage(arg) {
appendName(arg)
} else {
haveNonMeta = true