// Copyright 2018 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 modload import ( "fmt" "os" "path/filepath" "strings" "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/imports" "cmd/go/internal/module" "cmd/go/internal/search" ) // matchPackages returns a list of packages in the list of modules // matching the pattern. Package loading assumes the given set of tags. func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []module.Version) []string { match := func(string) bool { return true } treeCanMatch := func(string) bool { return true } if !search.IsMetaPackage(pattern) { match = search.MatchPattern(pattern) treeCanMatch = search.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 walkPkgs := func(root, importPathRoot string) { root = filepath.Clean(root) var cmd string if root == cfg.GOROOTsrc { cmd = filepath.Join(root, "cmd") } filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil { return nil } // Don't use GOROOT/src but do walk down into it. if path == root && importPathRoot == "" { return nil } // GOROOT/src/cmd makes use of GOROOT/src/cmd/vendor, // which module mode can't deal with. Eventually we'll stop using // that vendor directory, and then we can remove this exclusion. // golang.org/issue/26924. if path == cmd { return filepath.SkipDir } 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 := importPathRoot + filepath.ToSlash(path[len(root):]) if importPathRoot == "" { name = name[1:] // cut leading slash } 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 path != root { if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil { return filepath.SkipDir } } if !have[name] { have[name] = true if match(name) { if _, _, err := scanDir(path, tags); err != imports.ErrNoGo { pkgs = append(pkgs, name) } } } if elem == "vendor" { return filepath.SkipDir } return nil }) } if useStd { walkPkgs(cfg.GOROOTsrc, "") } for _, mod := range modules { if !treeCanMatch(mod.Path) { continue } var root string if mod.Version == "" { root = ModRoot } else { var err error root, _, err = fetch(mod) if err != nil { base.Errorf("go: %v", err) continue } } walkPkgs(root, mod.Path) } return pkgs }