// packages. The build tags should typically be imports.Tags() or
// imports.AnyTags(); a nil map has no special meaning.
func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
- var fsDirs [][]string
updateMatches := func(matches []*search.Match, iterating bool) {
- for i, m := range matches {
+ for _, m := range matches {
switch {
case m.IsLocal():
// Evaluate list of file system directories on first iteration.
- if fsDirs == nil {
- fsDirs = make([][]string, len(matches))
- }
- if fsDirs[i] == nil {
- if m.IsLiteral() {
- fsDirs[i] = []string{m.Pattern()}
- } else {
- m.MatchPackagesInFS()
- // Pull out the matching directories: we are going to resolve them
- // to package paths below.
- fsDirs[i], m.Pkgs = m.Pkgs, nil
- }
+ if m.Dirs == nil {
+ matchLocalDirs(m)
}
// Make a copy of the directory list and translate to import paths.
// from not being in the build list to being in it and back as
// the exact version of a particular module increases during
// the loader iterations.
- pkgs := str.StringList(fsDirs[i])
- m.Pkgs = pkgs[:0]
- for _, pkg := range pkgs {
- pkg, err := resolveLocalPackage(pkg)
+ m.Pkgs = m.Pkgs[:0]
+ for _, dir := range m.Dirs {
+ pkg, err := resolveLocalPackage(dir)
if err != nil {
if !m.IsLiteral() && (err == errPkgIsBuiltin || err == errPkgIsGorootSrc) {
continue // Don't include "builtin" or GOROOT/src in wildcard patterns.
}
case m.Pattern() == "std" || m.Pattern() == "cmd":
- if len(m.Pkgs) == 0 {
+ if m.Pkgs == nil {
m.MatchPackages() // Locate the packages within GOROOT/src.
}
base.ExitIfErrors()
}
+// matchLocalDirs is like m.MatchDirs, but tries to avoid scanning directories
+// outside of the standard library and active modules.
+func matchLocalDirs(m *search.Match) {
+ if !m.IsLocal() {
+ panic(fmt.Sprintf("internal error: resolveLocalDirs on non-local pattern %s", m.Pattern()))
+ }
+
+ if i := strings.Index(m.Pattern(), "..."); i >= 0 {
+ // The pattern is local, but it is a wildcard. Its packages will
+ // only resolve to paths if they are inside of the standard
+ // library, the main module, or some dependency of the main
+ // module. Verify that before we walk the filesystem: a filesystem
+ // walk in a directory like /var or /etc can be very expensive!
+ dir := filepath.Dir(filepath.Clean(m.Pattern()[:i+3]))
+ absDir := dir
+ if !filepath.IsAbs(dir) {
+ absDir = filepath.Join(base.Cwd, dir)
+ }
+ if search.InDir(absDir, cfg.GOROOTsrc) == "" && search.InDir(absDir, ModRoot()) == "" && pathInModuleCache(absDir) == "" {
+ m.Dirs = []string{}
+ m.AddError(fmt.Errorf("directory prefix %s outside available modules", base.ShortPath(absDir)))
+ return
+ }
+ }
+
+ m.MatchDirs()
+}
+
// resolveLocalPackage resolves a filesystem path to a package path.
func resolveLocalPackage(dir string) (string, error) {
var absDir string
}
if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
- return filepath.ToSlash(sub), nil
+ pkg := filepath.ToSlash(sub)
+ if pkg == "builtin" {
+ return "", errPkgIsBuiltin
+ }
+ return pkg, nil
}
pkg := pathInModuleCache(absDir)
// A Match represents the result of matching a single package pattern.
type Match struct {
pattern string // the pattern itself
- Pkgs []string // matching packages (dirs or import paths)
+ Dirs []string // if the pattern is local, directories that potentially contain matching packages
+ Pkgs []string // matching packages (import paths)
Errs []error // errors matching the patterns to packages, NOT errors loading those packages
// Errs may be non-empty even if len(Pkgs) > 0, indicating that some matching
return e.Err
}
-// MatchPackages sets m.Pkgs to contain 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 "...".
+// MatchPackages sets m.Pkgs to a non-nil slice containing all the packages that
+// can be found under the $GOPATH directories and $GOROOT that match the
+// pattern. The pattern must be either "all" (all packages), "std" (standard
+// packages), "cmd" (standard commands), or a path including "...".
//
-// MatchPackages sets m.Errs to contain any errors encountered while processing
-// the match.
+// If any errors may have caused the set of packages to be incomplete,
+// MatchPackages appends those errors to m.Errs.
func (m *Match) MatchPackages() {
- m.Pkgs, m.Errs = nil, nil
+ m.Pkgs = []string{}
if m.IsLocal() {
m.AddError(fmt.Errorf("internal error: MatchPackages: %s is not a valid package pattern", m.pattern))
return
}
+ if m.IsLiteral() {
+ m.Pkgs = []string{m.pattern}
+ return
+ }
+
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !m.IsMeta() {
modRoot = dir
}
-// MatchPackagesInFS is like MatchPackages but is passed a pattern that
-// begins with an absolute path or "./" or "../". On Windows, the pattern may
-// use slash or backslash separators or a mix of both.
+// MatchDirs sets m.Dirs to a non-nil slice containing all directories that
+// potentially match a local pattern. The pattern must begin with an absolute
+// path, or "./", or "../". On Windows, the pattern may use slash or backslash
+// separators or a mix of both.
//
-// MatchPackagesInFS scans the tree rooted at the directory that contains the
-// first "..." wildcard.
-func (m *Match) MatchPackagesInFS() {
- m.Pkgs, m.Errs = nil, nil
+// If any errors may have caused the set of directories to be incomplete,
+// MatchDirs appends those errors to m.Errs.
+func (m *Match) MatchDirs() {
+ m.Dirs = []string{}
if !m.IsLocal() {
- m.AddError(fmt.Errorf("internal error: MatchPackagesInFS: %s is not a valid filesystem pattern", m.pattern))
+ m.AddError(fmt.Errorf("internal error: MatchDirs: %s is not a valid filesystem pattern", m.pattern))
+ return
+ }
+
+ if m.IsLiteral() {
+ m.Dirs = []string{m.pattern}
return
}
// which is all that Match promises to do.
// Ignore the import error.
}
- m.Pkgs = append(m.Pkgs, name)
+ m.Dirs = append(m.Dirs, name)
return nil
})
if err != nil {
for _, a := range CleanPatterns(patterns) {
m := NewMatch(a)
if m.IsLocal() {
- if m.IsLiteral() {
- m.Pkgs = []string{a}
- } else {
- m.MatchPackagesInFS()
- }
+ m.MatchDirs()
// Change the file import path to a regular import path if the package
// is in GOPATH or GOROOT. We don't report errors here; LoadImport
// (or something similar) will report them later.
- for i, dir := range m.Pkgs {
+ m.Pkgs = make([]string, len(m.Dirs))
+ for i, dir := range m.Dirs {
+ absDir := dir
if !filepath.IsAbs(dir) {
- dir = filepath.Join(base.Cwd, dir)
+ absDir = filepath.Join(base.Cwd, dir)
}
- if bp, _ := cfg.BuildContext.ImportDir(dir, build.FindOnly); bp.ImportPath != "" && bp.ImportPath != "." {
+ if bp, _ := cfg.BuildContext.ImportDir(absDir, build.FindOnly); bp.ImportPath != "" && bp.ImportPath != "." {
m.Pkgs[i] = bp.ImportPath
+ } else {
+ m.Pkgs[i] = dir
}
}
- } else if m.IsLiteral() {
- m.Pkgs = []string{a}
} else {
m.MatchPackages()
}