blob: 2a37f1d8740c39306ab01c8d60b083a302f8ca90 [file] [log] [blame]
// 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 (
"bytes"
"context"
"errors"
"fmt"
"go/build"
"os"
"path"
pathpkg "path"
"path/filepath"
"sort"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/modfetch"
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"cmd/go/internal/search"
"cmd/go/internal/str"
"cmd/go/internal/trace"
"golang.org/x/mod/module"
)
// buildList is the list of modules to use for building packages.
// It is initialized by calling ImportPaths, ImportFromFiles,
// LoadALL, or LoadBuildList, each of which uses loaded.load.
//
// Ideally, exactly ONE of those functions would be called,
// and exactly once. Most of the time, that's true.
// During "go get" it may not be. TODO(rsc): Figure out if
// that restriction can be established, or else document why not.
//
var buildList []module.Version
// loaded is the most recently-used package loader.
// It holds details about individual packages.
//
// Note that loaded.buildList is only valid during a load operation;
// afterward, it is copied back into the global buildList,
// which should be used instead.
var loaded *loader
// ImportPaths returns the set of packages matching the args (patterns),
// on the target platform. Modules may be added to the build list
// to satisfy new imports.
func ImportPaths(ctx context.Context, patterns []string) []*search.Match {
matches := ImportPathsQuiet(ctx, patterns, imports.Tags())
search.WarnUnmatched(matches)
return matches
}
// ImportPathsQuiet is like ImportPaths but does not warn about patterns with
// no matches. It also lets the caller specify a set of build tags to match
// packages. The build tags should typically be imports.Tags() or
// imports.AnyTags(); a nil map has no special meaning.
func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bool) []*search.Match {
updateMatches := func(matches []*search.Match, iterating bool) {
for _, m := range matches {
switch {
case m.IsLocal():
// Evaluate list of file system directories on first iteration.
if m.Dirs == nil {
matchLocalDirs(m)
}
// Make a copy of the directory list and translate to import paths.
// Note that whether a directory corresponds to an import path
// changes as the build list is updated, and a directory can change
// 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.
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.
}
// If we're outside of a module, ensure that the failure mode
// indicates that.
ModRoot()
if !iterating {
m.AddError(err)
}
continue
}
m.Pkgs = append(m.Pkgs, pkg)
}
case m.IsLiteral():
m.Pkgs = []string{m.Pattern()}
case strings.Contains(m.Pattern(), "..."):
m.Errs = m.Errs[:0]
matchPackages(ctx, m, loaded.tags, includeStd, buildList)
case m.Pattern() == "all":
loaded.testAll = true
if iterating {
// Enumerate the packages in the main module.
// We'll load the dependencies as we find them.
m.Errs = m.Errs[:0]
matchPackages(ctx, m, loaded.tags, omitStd, []module.Version{Target})
} else {
// Starting with the packages in the main module,
// enumerate the full list of "all".
m.Pkgs = loaded.computePatternAll(m.Pkgs)
}
case m.Pattern() == "std" || m.Pattern() == "cmd":
if m.Pkgs == nil {
m.MatchPackages() // Locate the packages within GOROOT/src.
}
default:
panic(fmt.Sprintf("internal error: modload missing case for pattern %s", m.Pattern()))
}
}
}
InitMod(ctx)
var matches []*search.Match
for _, pattern := range search.CleanPatterns(patterns) {
matches = append(matches, search.NewMatch(pattern))
}
loaded = newLoader(tags)
loaded.load(func() []string {
var roots []string
updateMatches(matches, true)
for _, m := range matches {
roots = append(roots, m.Pkgs...)
}
return roots
})
// One last pass to finalize wildcards.
updateMatches(matches, false)
checkMultiplePaths()
WriteGoMod()
return matches
}
// checkMultiplePaths verifies that a given module path is used as itself
// or as a replacement for another module, but not both at the same time.
//
// (See https://golang.org/issue/26607 and https://golang.org/issue/34650.)
func checkMultiplePaths() {
firstPath := make(map[module.Version]string, len(buildList))
for _, mod := range buildList {
src := mod
if rep := Replacement(mod); rep.Path != "" {
src = rep
}
if prev, ok := firstPath[src]; !ok {
firstPath[src] = mod.Path
} else if prev != mod.Path {
base.Errorf("go: %s@%s used for two different module paths (%s and %s)", src.Path, src.Version, prev, mod.Path)
}
}
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 filepath.IsAbs(dir) {
absDir = filepath.Clean(dir)
} else {
absDir = filepath.Join(base.Cwd, dir)
}
bp, err := cfg.BuildContext.ImportDir(absDir, 0)
if err != nil && (bp == nil || len(bp.IgnoredGoFiles) == 0) {
// golang.org/issue/32917: We should resolve a relative path to a
// package path only if the relative path actually contains the code
// for that package.
//
// If the named directory does not exist or contains no Go files,
// the package does not exist.
// Other errors may affect package loading, but not resolution.
if _, err := os.Stat(absDir); err != nil {
if os.IsNotExist(err) {
// Canonicalize OS-specific errors to errDirectoryNotFound so that error
// messages will be easier for users to search for.
return "", &os.PathError{Op: "stat", Path: absDir, Err: errDirectoryNotFound}
}
return "", err
}
if _, noGo := err.(*build.NoGoError); noGo {
// A directory that does not contain any Go source files — even ignored
// ones! — is not a Go package, and we can't resolve it to a package
// path because that path could plausibly be provided by some other
// module.
//
// Any other error indicates that the package “exists” (at least in the
// sense that it cannot exist in any other module), but has some other
// problem (such as a syntax error).
return "", err
}
}
if modRoot != "" && absDir == modRoot {
if absDir == cfg.GOROOTsrc {
return "", errPkgIsGorootSrc
}
return targetPrefix, nil
}
// Note: The checks for @ here are just to avoid misinterpreting
// the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar).
// It's not strictly necessary but helpful to keep the checks.
if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") {
suffix := filepath.ToSlash(absDir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
if cfg.BuildMod != "vendor" {
return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir)
}
readVendorList()
pkg := strings.TrimPrefix(suffix, "/vendor/")
if _, ok := vendorPkgModule[pkg]; !ok {
return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir)
}
return pkg, nil
}
if targetPrefix == "" {
pkg := strings.TrimPrefix(suffix, "/")
if pkg == "builtin" {
// "builtin" is a pseudo-package with a real source file.
// It's not included in "std", so it shouldn't resolve from "."
// within module "std" either.
return "", errPkgIsBuiltin
}
return pkg, nil
}
pkg := targetPrefix + suffix
if _, ok, err := dirInModule(pkg, targetPrefix, modRoot, true); err != nil {
return "", err
} else if !ok {
return "", &PackageNotInModuleError{Mod: Target, Pattern: pkg}
}
return pkg, nil
}
if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
pkg := filepath.ToSlash(sub)
if pkg == "builtin" {
return "", errPkgIsBuiltin
}
return pkg, nil
}
pkg := pathInModuleCache(absDir)
if pkg == "" {
return "", fmt.Errorf("directory %s outside available modules", base.ShortPath(absDir))
}
return pkg, nil
}
var (
errDirectoryNotFound = errors.New("directory not found")
errPkgIsGorootSrc = errors.New("GOROOT/src is not an importable package")
errPkgIsBuiltin = errors.New(`"builtin" is a pseudo-package, not an importable package`)
)
// pathInModuleCache returns the import path of the directory dir,
// if dir is in the module cache copy of a module in our build list.
func pathInModuleCache(dir string) string {
for _, m := range buildList[1:] {
var root string
var err error
if repl := Replacement(m); repl.Path != "" && repl.Version == "" {
root = repl.Path
if !filepath.IsAbs(root) {
root = filepath.Join(ModRoot(), root)
}
} else if repl.Path != "" {
root, err = modfetch.DownloadDir(repl)
} else {
root, err = modfetch.DownloadDir(m)
}
if err != nil {
continue
}
if sub := search.InDir(dir, root); sub != "" {
sub = filepath.ToSlash(sub)
if !strings.Contains(sub, "/vendor/") && !strings.HasPrefix(sub, "vendor/") && !strings.Contains(sub, "@") {
return path.Join(m.Path, filepath.ToSlash(sub))
}
}
}
return ""
}
// ImportFromFiles adds modules to the build list as needed
// to satisfy the imports in the named Go source files.
func ImportFromFiles(ctx context.Context, gofiles []string) {
InitMod(ctx)
tags := imports.Tags()
imports, testImports, err := imports.ScanFiles(gofiles, tags)
if err != nil {
base.Fatalf("go: %v", err)
}
loaded = newLoader(tags)
loaded.load(func() []string {
var roots []string
roots = append(roots, imports...)
roots = append(roots, testImports...)
return roots
})
WriteGoMod()
}
// DirImportPath returns the effective import path for dir,
// provided it is within the main module, or else returns ".".
func DirImportPath(dir string) string {
if modRoot == "" {
return "."
}
if !filepath.IsAbs(dir) {
dir = filepath.Join(base.Cwd, dir)
} else {
dir = filepath.Clean(dir)
}
if dir == modRoot {
return targetPrefix
}
if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
return strings.TrimPrefix(suffix, "/vendor/")
}
return targetPrefix + suffix
}
return "."
}
// LoadBuildList loads and returns the build list from go.mod.
// The loading of the build list happens automatically in ImportPaths:
// LoadBuildList need only be called if ImportPaths is not
// (typically in commands that care about the module but
// no particular package).
func LoadBuildList(ctx context.Context) []module.Version {
ctx, span := trace.StartSpan(ctx, "LoadBuildList")
defer span.Done()
InitMod(ctx)
ReloadBuildList()
WriteGoMod()
return buildList
}
func ReloadBuildList() []module.Version {
loaded = newLoader(imports.Tags())
loaded.load(func() []string { return nil })
return buildList
}
// LoadALL returns the set of all packages in the current module
// and their dependencies in any other modules, without filtering
// due to build tags, except "+build ignore".
// It adds modules to the build list as needed to satisfy new imports.
// This set is useful for deciding whether a particular import is needed
// anywhere in a module.
func LoadALL(ctx context.Context) []string {
return loadAll(ctx, true)
}
// LoadVendor is like LoadALL but only follows test dependencies
// for tests in the main module. Tests in dependency modules are
// ignored completely.
// This set is useful for identifying the which packages to include in a vendor directory.
func LoadVendor(ctx context.Context) []string {
return loadAll(ctx, false)
}
func loadAll(ctx context.Context, testAll bool) []string {
InitMod(ctx)
loaded = newLoader(imports.AnyTags())
loaded.isALL = true
loaded.testAll = testAll
if !testAll {
loaded.testRoots = true
}
all := TargetPackages(ctx, "...")
loaded.load(func() []string { return all.Pkgs })
checkMultiplePaths()
WriteGoMod()
var paths []string
for _, pkg := range loaded.pkgs {
if pkg.err != nil {
base.Errorf("%s: %v", pkg.stackText(), pkg.err)
continue
}
paths = append(paths, pkg.path)
}
for _, err := range all.Errs {
base.Errorf("%v", err)
}
base.ExitIfErrors()
return paths
}
// TargetPackages returns the list of packages in the target (top-level) module
// matching pattern, which may be relative to the working directory, under all
// build tag settings.
func TargetPackages(ctx context.Context, pattern string) *search.Match {
// TargetPackages is relative to the main module, so ensure that the main
// module is a thing that can contain packages.
ModRoot()
m := search.NewMatch(pattern)
matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{Target})
return m
}
// BuildList returns the module build list,
// typically constructed by a previous call to
// LoadBuildList or ImportPaths.
// The caller must not modify the returned list.
func BuildList() []module.Version {
return buildList
}
// SetBuildList sets the module build list.
// The caller is responsible for ensuring that the list is valid.
// SetBuildList does not retain a reference to the original list.
func SetBuildList(list []module.Version) {
buildList = append([]module.Version{}, list...)
}
// TidyBuildList trims the build list to the minimal requirements needed to
// retain the same versions of all packages from the preceding Load* or
// ImportPaths* call.
func TidyBuildList() {
used := map[module.Version]bool{Target: true}
for _, pkg := range loaded.pkgs {
used[pkg.mod] = true
}
keep := []module.Version{Target}
var direct []string
for _, m := range buildList[1:] {
if used[m] {
keep = append(keep, m)
if loaded.direct[m.Path] {
direct = append(direct, m.Path)
}
} else if cfg.BuildV {
if _, ok := index.require[m]; ok {
fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
}
}
}
min, err := mvs.Req(Target, direct, &mvsReqs{buildList: keep})
if err != nil {
base.Fatalf("go: %v", err)
}
buildList = append([]module.Version{Target}, min...)
}
// ImportMap returns the actual package import path
// for an import path found in source code.
// If the given import path does not appear in the source code
// for the packages that have been loaded, ImportMap returns the empty string.
func ImportMap(path string) string {
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
return ""
}
return pkg.path
}
// PackageDir returns the directory containing the source code
// for the package named by the import path.
func PackageDir(path string) string {
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
return ""
}
return pkg.dir
}
// PackageModule returns the module providing the package named by the import path.
func PackageModule(path string) module.Version {
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
return module.Version{}
}
return pkg.mod
}
// PackageImports returns the imports for the package named by the import path.
// Test imports will be returned as well if tests were loaded for the package
// (i.e., if "all" was loaded or if LoadTests was set and the path was matched
// by a command line argument). PackageImports will return nil for
// unknown package paths.
func PackageImports(path string) (imports, testImports []string) {
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
return nil, nil
}
imports = make([]string, len(pkg.imports))
for i, p := range pkg.imports {
imports[i] = p.path
}
if pkg.test != nil {
testImports = make([]string, len(pkg.test.imports))
for i, p := range pkg.test.imports {
testImports[i] = p.path
}
}
return imports, testImports
}
// ModuleUsedDirectly reports whether the main module directly imports
// some package in the module with the given path.
func ModuleUsedDirectly(path string) bool {
return loaded.direct[path]
}
// Lookup returns the source directory, import path, and any loading error for
// the package at path as imported from the package in parentDir.
// Lookup requires that one of the Load functions in this package has already
// been called.
func Lookup(parentPath string, parentIsStd bool, path string) (dir, realPath string, err error) {
if path == "" {
panic("Lookup called with empty package path")
}
if parentIsStd {
path = loaded.stdVendor(parentPath, path)
}
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
// The loader should have found all the relevant paths.
// There are a few exceptions, though:
// - during go list without -test, the p.Resolve calls to process p.TestImports and p.XTestImports
// end up here to canonicalize the import paths.
// - during any load, non-loaded packages like "unsafe" end up here.
// - during any load, build-injected dependencies like "runtime/cgo" end up here.
// - because we ignore appengine/* in the module loader,
// the dependencies of any actual appengine/* library end up here.
dir := findStandardImportPath(path)
if dir != "" {
return dir, path, nil
}
return "", "", errMissing
}
return pkg.dir, pkg.path, pkg.err
}
// A loader manages the process of loading information about
// the required packages for a particular build,
// checking that the packages are available in the module set,
// and updating the module set if needed.
// Loading is an iterative process: try to load all the needed packages,
// but if imports are missing, try to resolve those imports, and repeat.
//
// Although most of the loading state is maintained in the loader struct,
// one key piece - the build list - is a global, so that it can be modified
// separate from the loading operation, such as during "go get"
// upgrades/downgrades or in "go mod" operations.
// TODO(rsc): It might be nice to make the loader take and return
// a buildList rather than hard-coding use of the global.
type loader struct {
tags map[string]bool // tags for scanDir
testRoots bool // include tests for roots
isALL bool // created with LoadALL
testAll bool // include tests for all packages
forceStdVendor bool // if true, load standard-library dependencies from the vendor subtree
// reset on each iteration
roots []*loadPkg
pkgs []*loadPkg
work *par.Work // current work queue
pkgCache *par.Cache // map from string to *loadPkg
// computed at end of iterations
direct map[string]bool // imported directly by main module
}
// LoadTests controls whether the loaders load tests of the root packages.
var LoadTests bool
func newLoader(tags map[string]bool) *loader {
ld := new(loader)
ld.tags = tags
ld.testRoots = LoadTests
// Inside the "std" and "cmd" modules, we prefer to use the vendor directory
// unless the command explicitly changes the module graph.
if !targetInGorootSrc || (cfg.CmdName != "get" && !strings.HasPrefix(cfg.CmdName, "mod ")) {
ld.forceStdVendor = true
}
return ld
}
func (ld *loader) reset() {
ld.roots = nil
ld.pkgs = nil
ld.work = new(par.Work)
ld.pkgCache = new(par.Cache)
}
// A loadPkg records information about a single loaded package.
type loadPkg struct {
path string // import path
mod module.Version // module providing package
dir string // directory containing source code
imports []*loadPkg // packages imported by this one
err error // error loading package
stack *loadPkg // package importing this one in minimal import stack for this pkg
test *loadPkg // package with test imports, if we need test
testOf *loadPkg
testImports []string // test-only imports, saved for use by pkg.test.
}
var errMissing = errors.New("cannot find package")
// load attempts to load the build graph needed to process a set of root packages.
// The set of root packages is defined by the addRoots function,
// which must call add(path) with the import path of each root package.
func (ld *loader) load(roots func() []string) {
var err error
reqs := Reqs()
buildList, err = mvs.BuildList(Target, reqs)
if err != nil {
base.Fatalf("go: %v", err)
}
added := make(map[string]bool)
for {
ld.reset()
if roots != nil {
// Note: the returned roots can change on each iteration,
// since the expansion of package patterns depends on the
// build list we're using.
for _, path := range roots() {
ld.work.Add(ld.pkg(path, true))
}
}
ld.work.Do(10, ld.doPkg)
ld.buildStacks()
numAdded := 0
haveMod := make(map[module.Version]bool)
for _, m := range buildList {
haveMod[m] = true
}
modAddedBy := make(map[module.Version]*loadPkg)
for _, pkg := range ld.pkgs {
if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" {
if err.newMissingVersion != "" {
base.Fatalf("go: %s: package provided by %s at latest version %s but not at required version %s", pkg.stackText(), err.Module.Path, err.Module.Version, err.newMissingVersion)
}
fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, err.Module.Path, err.Module.Version)
if added[pkg.path] {
base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
}
added[pkg.path] = true
numAdded++
if !haveMod[err.Module] {
haveMod[err.Module] = true
modAddedBy[err.Module] = pkg
buildList = append(buildList, err.Module)
}
continue
}
// Leave other errors for Import or load.Packages to report.
}
base.ExitIfErrors()
if numAdded == 0 {
break
}
// Recompute buildList with all our additions.
reqs = Reqs()
buildList, err = mvs.BuildList(Target, reqs)
if err != nil {
// If an error was found in a newly added module, report the package
// import stack instead of the module requirement stack. Packages
// are more descriptive.
if err, ok := err.(*mvs.BuildListError); ok {
if pkg := modAddedBy[err.Module()]; pkg != nil {
base.Fatalf("go: %s: %v", pkg.stackText(), err.Err)
}
}
base.Fatalf("go: %v", err)
}
}
base.ExitIfErrors()
// Compute directly referenced dependency modules.
ld.direct = make(map[string]bool)
for _, pkg := range ld.pkgs {
if pkg.mod == Target {
for _, dep := range pkg.imports {
if dep.mod.Path != "" {
ld.direct[dep.mod.Path] = true
}
}
}
}
// Mix in direct markings (really, lack of indirect markings)
// from go.mod, unless we scanned the whole module
// and can therefore be sure we know better than go.mod.
if !ld.isALL && modFile != nil {
for _, r := range modFile.Require {
if !r.Indirect {
ld.direct[r.Mod.Path] = true
}
}
}
}
// pkg returns the *loadPkg for path, creating and queuing it if needed.
// If the package should be tested, its test is created but not queued
// (the test is queued after processing pkg).
// If isRoot is true, the pkg is being queued as one of the roots of the work graph.
func (ld *loader) pkg(path string, isRoot bool) *loadPkg {
return ld.pkgCache.Do(path, func() interface{} {
pkg := &loadPkg{
path: path,
}
if ld.testRoots && isRoot || ld.testAll {
test := &loadPkg{
path: path,
testOf: pkg,
}
pkg.test = test
}
if isRoot {
ld.roots = append(ld.roots, pkg)
}
ld.work.Add(pkg)
return pkg
}).(*loadPkg)
}
// doPkg processes a package on the work queue.
func (ld *loader) doPkg(item interface{}) {
// TODO: what about replacements?
pkg := item.(*loadPkg)
var imports []string
if pkg.testOf != nil {
pkg.dir = pkg.testOf.dir
pkg.mod = pkg.testOf.mod
imports = pkg.testOf.testImports
} else {
if strings.Contains(pkg.path, "@") {
// Leave for error during load.
return
}
if build.IsLocalImport(pkg.path) || filepath.IsAbs(pkg.path) {
// Leave for error during load.
// (Module mode does not allow local imports.)
return
}
// TODO(matloob): Handle TODO context. This needs to be threaded through Do.
pkg.mod, pkg.dir, pkg.err = Import(context.TODO(), pkg.path)
if pkg.dir == "" {
return
}
var testImports []string
var err error
imports, testImports, err = scanDir(pkg.dir, ld.tags)
if err != nil {
pkg.err = err
return
}
if pkg.test != nil {
pkg.testImports = testImports
}
}
inStd := (search.IsStandardImportPath(pkg.path) && search.InDir(pkg.dir, cfg.GOROOTsrc) != "")
for _, path := range imports {
if inStd {
path = ld.stdVendor(pkg.path, path)
}
pkg.imports = append(pkg.imports, ld.pkg(path, false))
}
// Now that pkg.dir, pkg.mod, pkg.testImports are set, we can queue pkg.test.
// TODO: All that's left is creating new imports. Why not just do it now?
if pkg.test != nil {
ld.work.Add(pkg.test)
}
}
// stdVendor returns the canonical import path for the package with the given
// path when imported from the standard-library package at parentPath.
func (ld *loader) stdVendor(parentPath, path string) string {
if search.IsStandardImportPath(path) {
return path
}
if str.HasPathPrefix(parentPath, "cmd") {
if ld.forceStdVendor || Target.Path != "cmd" {
vendorPath := pathpkg.Join("cmd", "vendor", path)
if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
return vendorPath
}
}
} else if ld.forceStdVendor || Target.Path != "std" {
vendorPath := pathpkg.Join("vendor", path)
if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
return vendorPath
}
}
// Not vendored: resolve from modules.
return path
}
// computePatternAll returns the list of packages matching pattern "all",
// starting with a list of the import paths for the packages in the main module.
func (ld *loader) computePatternAll(paths []string) []string {
seen := make(map[*loadPkg]bool)
var all []string
var walk func(*loadPkg)
walk = func(pkg *loadPkg) {
if seen[pkg] {
return
}
seen[pkg] = true
if pkg.testOf == nil {
all = append(all, pkg.path)
}
for _, p := range pkg.imports {
walk(p)
}
if p := pkg.test; p != nil {
walk(p)
}
}
for _, path := range paths {
walk(ld.pkg(path, false))
}
sort.Strings(all)
return all
}
// scanDir is like imports.ScanDir but elides known magic imports from the list,
// so that we do not go looking for packages that don't really exist.
//
// The standard magic import is "C", for cgo.
//
// The only other known magic imports are appengine and appengine/*.
// These are so old that they predate "go get" and did not use URL-like paths.
// Most code today now uses google.golang.org/appengine instead,
// but not all code has been so updated. When we mostly ignore build tags
// during "go vendor", we look into "// +build appengine" files and
// may see these legacy imports. We drop them so that the module
// search does not look for modules to try to satisfy them.
func scanDir(dir string, tags map[string]bool) (imports_, testImports []string, err error) {
imports_, testImports, err = imports.ScanDir(dir, tags)
filter := func(x []string) []string {
w := 0
for _, pkg := range x {
if pkg != "C" && pkg != "appengine" && !strings.HasPrefix(pkg, "appengine/") &&
pkg != "appengine_internal" && !strings.HasPrefix(pkg, "appengine_internal/") {
x[w] = pkg
w++
}
}
return x[:w]
}
return filter(imports_), filter(testImports), err
}
// buildStacks computes minimal import stacks for each package,
// for use in error messages. When it completes, packages that
// are part of the original root set have pkg.stack == nil,
// and other packages have pkg.stack pointing at the next
// package up the import stack in their minimal chain.
// As a side effect, buildStacks also constructs ld.pkgs,
// the list of all packages loaded.
func (ld *loader) buildStacks() {
if len(ld.pkgs) > 0 {
panic("buildStacks")
}
for _, pkg := range ld.roots {
pkg.stack = pkg // sentinel to avoid processing in next loop
ld.pkgs = append(ld.pkgs, pkg)
}
for i := 0; i < len(ld.pkgs); i++ { // not range: appending to ld.pkgs in loop
pkg := ld.pkgs[i]
for _, next := range pkg.imports {
if next.stack == nil {
next.stack = pkg
ld.pkgs = append(ld.pkgs, next)
}
}
if next := pkg.test; next != nil && next.stack == nil {
next.stack = pkg
ld.pkgs = append(ld.pkgs, next)
}
}
for _, pkg := range ld.roots {
pkg.stack = nil
}
}
// stackText builds the import stack text to use when
// reporting an error in pkg. It has the general form
//
// root imports
// other imports
// other2 tested by
// other2.test imports
// pkg
//
func (pkg *loadPkg) stackText() string {
var stack []*loadPkg
for p := pkg; p != nil; p = p.stack {
stack = append(stack, p)
}
var buf bytes.Buffer
for i := len(stack) - 1; i >= 0; i-- {
p := stack[i]
fmt.Fprint(&buf, p.path)
if p.testOf != nil {
fmt.Fprint(&buf, ".test")
}
if i > 0 {
if stack[i-1].testOf == p {
fmt.Fprint(&buf, " tested by\n\t")
} else {
fmt.Fprint(&buf, " imports\n\t")
}
}
}
return buf.String()
}
// why returns the text to use in "go mod why" output about the given package.
// It is less ornate than the stackText but contains the same information.
func (pkg *loadPkg) why() string {
var buf strings.Builder
var stack []*loadPkg
for p := pkg; p != nil; p = p.stack {
stack = append(stack, p)
}
for i := len(stack) - 1; i >= 0; i-- {
p := stack[i]
if p.testOf != nil {
fmt.Fprintf(&buf, "%s.test\n", p.testOf.path)
} else {
fmt.Fprintf(&buf, "%s\n", p.path)
}
}
return buf.String()
}
// Why returns the "go mod why" output stanza for the given package,
// without the leading # comment.
// The package graph must have been loaded already, usually by LoadALL.
// If there is no reason for the package to be in the current build,
// Why returns an empty string.
func Why(path string) string {
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
return ""
}
return pkg.why()
}
// WhyDepth returns the number of steps in the Why listing.
// If there is no reason for the package to be in the current build,
// WhyDepth returns 0.
func WhyDepth(path string) int {
n := 0
pkg, _ := loaded.pkgCache.Get(path).(*loadPkg)
for p := pkg; p != nil; p = p.stack {
n++
}
return n
}