diff options
Diffstat (limited to 'src/cmd/go/internal/load')
-rw-r--r-- | src/cmd/go/internal/load/flag.go | 96 | ||||
-rw-r--r-- | src/cmd/go/internal/load/flag_test.go | 135 | ||||
-rw-r--r-- | src/cmd/go/internal/load/path.go | 18 | ||||
-rw-r--r-- | src/cmd/go/internal/load/pkg.go | 3477 | ||||
-rw-r--r-- | src/cmd/go/internal/load/pkg_test.go | 82 | ||||
-rw-r--r-- | src/cmd/go/internal/load/search.go | 57 | ||||
-rw-r--r-- | src/cmd/go/internal/load/test.go | 921 |
7 files changed, 4786 insertions, 0 deletions
diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go new file mode 100644 index 0000000..55bdab0 --- /dev/null +++ b/src/cmd/go/internal/load/flag.go @@ -0,0 +1,96 @@ +// 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/internal/quoted" + "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 { + raw string + 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.raw = v + 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>") + } + if v[0] == '\'' || v[0] == '"' { + return fmt.Errorf("parameter may not start with quote character %c", v[0]) + } + pattern := strings.TrimSpace(v[:i]) + match = MatchPackage(pattern, cwd) + v = v[i+1:] + } + flags, err := quoted.Split(v) + if err != nil { + return err + } + if flags == nil { + flags = []string{} + } + f.values = append(f.values, ppfValue{match, flags}) + return nil +} + +func (f *PerPackageFlag) String() string { return f.raw } + +// 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 +} diff --git a/src/cmd/go/internal/load/flag_test.go b/src/cmd/go/internal/load/flag_test.go new file mode 100644 index 0000000..d3223e1 --- /dev/null +++ b/src/cmd/go/internal/load/flag_test.go @@ -0,0 +1,135 @@ +// 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 ( + "fmt" + "path/filepath" + "reflect" + "testing" +) + +type ppfTestPackage struct { + path string + dir string + cmdline bool + flags []string +} + +type ppfTest struct { + args []string + pkgs []ppfTestPackage +} + +var ppfTests = []ppfTest{ + // -gcflags=-S applies only to packages on command line. + { + args: []string{"-S"}, + pkgs: []ppfTestPackage{ + {cmdline: true, flags: []string{"-S"}}, + {cmdline: false, flags: []string{}}, + }, + }, + + // -gcflags=-S -gcflags= overrides the earlier -S. + { + args: []string{"-S", ""}, + pkgs: []ppfTestPackage{ + {cmdline: true, flags: []string{}}, + }, + }, + + // -gcflags=net=-S applies only to package net + { + args: []string{"net=-S"}, + pkgs: []ppfTestPackage{ + {path: "math", cmdline: true, flags: []string{}}, + {path: "net", flags: []string{"-S"}}, + }, + }, + + // -gcflags=net=-S -gcflags=net= also overrides the earlier -S + { + args: []string{"net=-S", "net="}, + pkgs: []ppfTestPackage{ + {path: "net", flags: []string{}}, + }, + }, + + // -gcflags=net/...=-S net math + // applies -S to net and net/http but not math + { + args: []string{"net/...=-S"}, + pkgs: []ppfTestPackage{ + {path: "net", flags: []string{"-S"}}, + {path: "net/http", flags: []string{"-S"}}, + {path: "math", flags: []string{}}, + }, + }, + + // -gcflags=net/...=-S -gcflags=-m net math + // applies -m to net and math and -S to other packages matching net/... + // (net matches too, but it was grabbed by the later -gcflags). + { + args: []string{"net/...=-S", "-m"}, + pkgs: []ppfTestPackage{ + {path: "net", cmdline: true, flags: []string{"-m"}}, + {path: "math", cmdline: true, flags: []string{"-m"}}, + {path: "net", cmdline: false, flags: []string{"-S"}}, + {path: "net/http", flags: []string{"-S"}}, + {path: "math", flags: []string{}}, + }, + }, + + // relative path patterns + // ppfDirTest(pattern, n, dirs...) says the first n dirs should match and the others should not. + ppfDirTest(".", 1, "/my/test/dir", "/my/test", "/my/test/other", "/my/test/dir/sub"), + ppfDirTest("..", 1, "/my/test", "/my/test/dir", "/my/test/other", "/my/test/dir/sub"), + ppfDirTest("./sub", 1, "/my/test/dir/sub", "/my/test", "/my/test/dir", "/my/test/other", "/my/test/dir/sub/sub"), + ppfDirTest("../other", 1, "/my/test/other", "/my/test", "/my/test/dir", "/my/test/other/sub", "/my/test/dir/other", "/my/test/dir/sub"), + ppfDirTest("./...", 3, "/my/test/dir", "/my/test/dir/sub", "/my/test/dir/sub/sub", "/my/test/other", "/my/test/other/sub"), + ppfDirTest("../...", 4, "/my/test/dir", "/my/test/other", "/my/test/dir/sub", "/my/test/other/sub", "/my/other/test"), + ppfDirTest("../...sub...", 3, "/my/test/dir/sub", "/my/test/othersub", "/my/test/yellowsubmarine", "/my/other/test"), +} + +func ppfDirTest(pattern string, nmatch int, dirs ...string) ppfTest { + var pkgs []ppfTestPackage + for i, d := range dirs { + flags := []string{} + if i < nmatch { + flags = []string{"-S"} + } + pkgs = append(pkgs, ppfTestPackage{path: "p", dir: d, flags: flags}) + } + return ppfTest{args: []string{pattern + "=-S"}, pkgs: pkgs} +} + +func TestPerPackageFlag(t *testing.T) { + nativeDir := func(d string) string { + if filepath.Separator == '\\' { + return `C:` + filepath.FromSlash(d) + } + return d + } + + for i, tt := range ppfTests { + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + ppFlags := new(PerPackageFlag) + for _, arg := range tt.args { + t.Logf("set(%s)", arg) + if err := ppFlags.set(arg, nativeDir("/my/test/dir")); err != nil { + t.Fatal(err) + } + } + for _, p := range tt.pkgs { + dir := nativeDir(p.dir) + flags := ppFlags.For(&Package{PackagePublic: PackagePublic{ImportPath: p.path, Dir: dir}, Internal: PackageInternal{CmdlinePkg: p.cmdline}}) + if !reflect.DeepEqual(flags, p.flags) { + t.Errorf("For(%v, %v, %v) = %v, want %v", p.path, dir, p.cmdline, flags, p.flags) + } + } + }) + } +} diff --git a/src/cmd/go/internal/load/path.go b/src/cmd/go/internal/load/path.go new file mode 100644 index 0000000..584cdff --- /dev/null +++ b/src/cmd/go/internal/load/path.go @@ -0,0 +1,18 @@ +// 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 ( + "path/filepath" +) + +// expandPath returns the symlink-expanded form of path. +func expandPath(p string) string { + x, err := filepath.EvalSymlinks(p) + if err == nil { + return x + } + return p +} diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go new file mode 100644 index 0000000..f427e29 --- /dev/null +++ b/src/cmd/go/internal/load/pkg.go @@ -0,0 +1,3477 @@ +// Copyright 2011 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 loads packages. +package load + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "go/build" + "go/scanner" + "go/token" + "internal/platform" + "io/fs" + "os" + "os/exec" + pathpkg "path" + "path/filepath" + "runtime" + "runtime/debug" + "sort" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/fsys" + "cmd/go/internal/imports" + "cmd/go/internal/modfetch" + "cmd/go/internal/modindex" + "cmd/go/internal/modinfo" + "cmd/go/internal/modload" + "cmd/go/internal/par" + "cmd/go/internal/search" + "cmd/go/internal/str" + "cmd/go/internal/trace" + "cmd/go/internal/vcs" + "cmd/internal/pkgpattern" + + "golang.org/x/mod/modfile" + "golang.org/x/mod/module" +) + +// A Package describes a single package found in a directory. +type Package struct { + PackagePublic // visible in 'go list' + Internal PackageInternal // for use inside go command only +} + +type PackagePublic struct { + // Note: These fields are part of the go command's public API. + // See list.go. It is okay to add fields, but not to change or + // remove existing ones. Keep in sync with list.go + Dir string `json:",omitempty"` // directory containing package sources + ImportPath string `json:",omitempty"` // import path of package in dir + ImportComment string `json:",omitempty"` // path in import comment on package statement + Name string `json:",omitempty"` // package name + Doc string `json:",omitempty"` // package documentation string + Target string `json:",omitempty"` // installed target for this package (may be executable) + Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared) + Root string `json:",omitempty"` // Go root, Go path dir, or module root dir containing this package + ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory + ForTest string `json:",omitempty"` // package is only for use in named test + Export string `json:",omitempty"` // file containing export data (set by go list -export) + BuildID string `json:",omitempty"` // build ID of the compiled package (set by go list -export) + Module *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any + Match []string `json:",omitempty"` // command-line patterns matching this package + Goroot bool `json:",omitempty"` // is this package found in the Go root? + Standard bool `json:",omitempty"` // is this package part of the standard Go library? + DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed + BinaryOnly bool `json:",omitempty"` // package cannot be recompiled + Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? + + // Stale and StaleReason remain here *only* for the list command. + // They are only initialized in preparation for list execution. + // The regular build determines staleness on the fly during action execution. + Stale bool `json:",omitempty"` // would 'go install' do anything for this package? + StaleReason string `json:",omitempty"` // why is Stale true? + + // Source files + // If you add to this list you MUST add to p.AllFiles (below) too. + // Otherwise file name security lists will not apply to any new additions. + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string `json:",omitempty"` // .go source files that import "C" + CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles + IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints + InvalidGoFiles []string `json:",omitempty"` // .go source files with detected problems (parse error, wrong package name, and so on) + IgnoredOtherFiles []string `json:",omitempty"` // non-.go source files ignored due to build constraints + CFiles []string `json:",omitempty"` // .c source files + CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files + MFiles []string `json:",omitempty"` // .m source files + HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files + FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files + SFiles []string `json:",omitempty"` // .s source files + SwigFiles []string `json:",omitempty"` // .swig files + SwigCXXFiles []string `json:",omitempty"` // .swigcxx files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package + + // Embedded files + EmbedPatterns []string `json:",omitempty"` // //go:embed patterns + EmbedFiles []string `json:",omitempty"` // files matched by EmbedPatterns + + // Cgo directives + CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler + CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor + CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler + CgoFFLAGS []string `json:",omitempty"` // cgo: flags for Fortran compiler + CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker + CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names + + // Dependency information + Imports []string `json:",omitempty"` // import paths used by this package + ImportMap map[string]string `json:",omitempty"` // map from source import to ImportPath (identity entries omitted) + Deps []string `json:",omitempty"` // all (recursively) imported dependencies + + // Error information + // Incomplete is above, packed into the other bools + Error *PackageError `json:",omitempty"` // error loading this package (not dependencies) + DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies + + // Test information + // If you add to this list you MUST add to p.AllFiles (below) too. + // Otherwise file name security lists will not apply to any new additions. + TestGoFiles []string `json:",omitempty"` // _test.go files in package + TestImports []string `json:",omitempty"` // imports from TestGoFiles + TestEmbedPatterns []string `json:",omitempty"` // //go:embed patterns + TestEmbedFiles []string `json:",omitempty"` // files matched by TestEmbedPatterns + XTestGoFiles []string `json:",omitempty"` // _test.go files outside package + XTestImports []string `json:",omitempty"` // imports from XTestGoFiles + XTestEmbedPatterns []string `json:",omitempty"` // //go:embed patterns + XTestEmbedFiles []string `json:",omitempty"` // files matched by XTestEmbedPatterns +} + +// AllFiles returns the names of all the files considered for the package. +// This is used for sanity and security checks, so we include all files, +// even IgnoredGoFiles, because some subcommands consider them. +// The go/build package filtered others out (like foo_wrongGOARCH.s) +// and that's OK. +func (p *Package) AllFiles() []string { + files := str.StringList( + p.GoFiles, + p.CgoFiles, + // no p.CompiledGoFiles, because they are from GoFiles or generated by us + p.IgnoredGoFiles, + // no p.InvalidGoFiles, because they are from GoFiles + p.IgnoredOtherFiles, + p.CFiles, + p.CXXFiles, + p.MFiles, + p.HFiles, + p.FFiles, + p.SFiles, + p.SwigFiles, + p.SwigCXXFiles, + p.SysoFiles, + p.TestGoFiles, + p.XTestGoFiles, + ) + + // EmbedFiles may overlap with the other files. + // Dedup, but delay building the map as long as possible. + // Only files in the current directory (no slash in name) + // need to be checked against the files variable above. + var have map[string]bool + for _, file := range p.EmbedFiles { + if !strings.Contains(file, "/") { + if have == nil { + have = make(map[string]bool) + for _, file := range files { + have[file] = true + } + } + if have[file] { + continue + } + } + files = append(files, file) + } + return files +} + +// Desc returns the package "description", for use in b.showOutput. +func (p *Package) Desc() string { + if p.ForTest != "" { + return p.ImportPath + " [" + p.ForTest + ".test]" + } + return p.ImportPath +} + +// IsTestOnly reports whether p is a test-only package. +// +// A “test-only” package is one that: +// - is a test-only variant of an ordinary package, or +// - is a synthesized "main" package for a test binary, or +// - contains only _test.go files. +func (p *Package) IsTestOnly() bool { + return p.ForTest != "" || + p.Internal.TestmainGo != nil || + len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 && len(p.GoFiles)+len(p.CgoFiles) == 0 +} + +type PackageInternal struct { + // Unexported fields are not part of the public API. + Build *build.Package + Imports []*Package // this package's direct imports + CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports + RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports + ForceLibrary bool // this package is a library (even if named "main") + CmdlineFiles bool // package built from files listed on command line + CmdlinePkg bool // package listed on command line + CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard) + Local bool // imported via local path (./ or ../) + LocalPrefix string // interpret ./ and ../ imports relative to this prefix + ExeName string // desired name for temporary executable + FuzzInstrument bool // package should be instrumented for fuzzing + CoverMode string // preprocess Go source files with the coverage tool in this mode + CoverVars map[string]*CoverVar // variables created by coverage analysis + CoverageCfg string // coverage info config file path (passed to compiler) + OmitDebug bool // tell linker not to write debug information + GobinSubdir bool // install target would be subdir of GOBIN + BuildInfo string // add this info to package main + TestmainGo *[]byte // content for _testmain.go + Embed map[string][]string // //go:embed comment mapping + OrigImportPath string // original import path before adding '_test' suffix + + Asmflags []string // -asmflags for this package + Gcflags []string // -gcflags for this package + Ldflags []string // -ldflags for this package + Gccgoflags []string // -gccgoflags for this package +} + +// A NoGoError indicates that no Go files for the package were applicable to the +// build for that package. +// +// That may be because there were no files whatsoever, or because all files were +// excluded, or because all non-excluded files were test sources. +type NoGoError struct { + Package *Package +} + +func (e *NoGoError) Error() string { + if len(e.Package.IgnoredGoFiles) > 0 { + // Go files exist, but they were ignored due to build constraints. + return "build constraints exclude all Go files in " + e.Package.Dir + } + if len(e.Package.TestGoFiles)+len(e.Package.XTestGoFiles) > 0 { + // Test Go files exist, but we're not interested in them. + // The double-negative is unfortunate but we want e.Package.Dir + // to appear at the end of error message. + return "no non-test Go files in " + e.Package.Dir + } + return "no Go files in " + e.Package.Dir +} + +// setLoadPackageDataError presents an error found when loading package data +// as a *PackageError. It has special cases for some common errors to improve +// messages shown to users and reduce redundancy. +// +// setLoadPackageDataError returns true if it's safe to load information about +// imported packages, for example, if there was a parse error loading imports +// in one file, but other files are okay. +func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportStack, importPos []token.Position) { + matchErr, isMatchErr := err.(*search.MatchError) + if isMatchErr && matchErr.Match.Pattern() == path { + if matchErr.Match.IsLiteral() { + // The error has a pattern has a pattern similar to the import path. + // It may be slightly different (./foo matching example.com/foo), + // but close enough to seem redundant. + // Unwrap the error so we don't show the pattern. + err = matchErr.Err + } + } + + // Replace (possibly wrapped) *build.NoGoError with *load.NoGoError. + // The latter is more specific about the cause. + var nogoErr *build.NoGoError + if errors.As(err, &nogoErr) { + if p.Dir == "" && nogoErr.Dir != "" { + p.Dir = nogoErr.Dir + } + err = &NoGoError{Package: p} + } + + // Take only the first error from a scanner.ErrorList. PackageError only + // has room for one position, so we report the first error with a position + // instead of all of the errors without a position. + var pos string + var isScanErr bool + if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 { + isScanErr = true // For stack push/pop below. + + scanPos := scanErr[0].Pos + scanPos.Filename = base.ShortPath(scanPos.Filename) + pos = scanPos.String() + err = errors.New(scanErr[0].Msg) + } + + // Report the error on the importing package if the problem is with the import declaration + // for example, if the package doesn't exist or if the import path is malformed. + // On the other hand, don't include a position if the problem is with the imported package, + // for example there are no Go files (NoGoError), or there's a problem in the imported + // package's source files themselves (scanner errors). + // + // TODO(matloob): Perhaps make each of those the errors in the first group + // (including modload.ImportMissingError, ImportMissingSumError, and the + // corresponding "cannot find package %q in any of" GOPATH-mode error + // produced in build.(*Context).Import; modload.AmbiguousImportError, + // and modload.PackageNotInModuleError; and the malformed module path errors + // produced in golang.org/x/mod/module.CheckMod) implement an interface + // to make it easier to check for them? That would save us from having to + // move the modload errors into this package to avoid a package import cycle, + // and from having to export an error type for the errors produced in build. + if !isMatchErr && (nogoErr != nil || isScanErr) { + stk.Push(path) + defer stk.Pop() + } + + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Pos: pos, + Err: err, + } + + if path != stk.Top() { + p.Error.setPos(importPos) + } +} + +// Resolve returns the resolved version of imports, +// which should be p.TestImports or p.XTestImports, NOT p.Imports. +// The imports in p.TestImports and p.XTestImports are not recursively +// loaded during the initial load of p, so they list the imports found in +// the source file, but most processing should be over the vendor-resolved +// import paths. We do this resolution lazily both to avoid file system work +// and because the eventual real load of the test imports (during 'go test') +// can produce better error messages if it starts with the original paths. +// The initial load of p loads all the non-test imports and rewrites +// the vendored paths, so nothing should ever call p.vendored(p.Imports). +func (p *Package) Resolve(imports []string) []string { + if len(imports) > 0 && len(p.Imports) > 0 && &imports[0] == &p.Imports[0] { + panic("internal error: p.Resolve(p.Imports) called") + } + seen := make(map[string]bool) + var all []string + for _, path := range imports { + path = ResolveImportPath(p, path) + if !seen[path] { + seen[path] = true + all = append(all, path) + } + } + sort.Strings(all) + return all +} + +// CoverVar holds the name of the generated coverage variables targeting the named file. +type CoverVar struct { + File string // local file name + Var string // name of count struct +} + +func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) { + p.Internal.Build = pp + + if pp.PkgTargetRoot != "" && cfg.BuildPkgdir != "" { + old := pp.PkgTargetRoot + pp.PkgRoot = cfg.BuildPkgdir + pp.PkgTargetRoot = cfg.BuildPkgdir + if pp.PkgObj != "" { + pp.PkgObj = filepath.Join(cfg.BuildPkgdir, strings.TrimPrefix(pp.PkgObj, old)) + } + } + + p.Dir = pp.Dir + p.ImportPath = pp.ImportPath + p.ImportComment = pp.ImportComment + p.Name = pp.Name + p.Doc = pp.Doc + p.Root = pp.Root + p.ConflictDir = pp.ConflictDir + p.BinaryOnly = pp.BinaryOnly + + // TODO? Target + p.Goroot = pp.Goroot + p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath) + p.GoFiles = pp.GoFiles + p.CgoFiles = pp.CgoFiles + p.IgnoredGoFiles = pp.IgnoredGoFiles + p.InvalidGoFiles = pp.InvalidGoFiles + p.IgnoredOtherFiles = pp.IgnoredOtherFiles + p.CFiles = pp.CFiles + p.CXXFiles = pp.CXXFiles + p.MFiles = pp.MFiles + p.HFiles = pp.HFiles + p.FFiles = pp.FFiles + p.SFiles = pp.SFiles + p.SwigFiles = pp.SwigFiles + p.SwigCXXFiles = pp.SwigCXXFiles + p.SysoFiles = pp.SysoFiles + if cfg.BuildMSan { + // There's no way for .syso files to be built both with and without + // support for memory sanitizer. Assume they are built without, + // and drop them. + p.SysoFiles = nil + } + p.CgoCFLAGS = pp.CgoCFLAGS + p.CgoCPPFLAGS = pp.CgoCPPFLAGS + p.CgoCXXFLAGS = pp.CgoCXXFLAGS + p.CgoFFLAGS = pp.CgoFFLAGS + p.CgoLDFLAGS = pp.CgoLDFLAGS + p.CgoPkgConfig = pp.CgoPkgConfig + // We modify p.Imports in place, so make copy now. + p.Imports = make([]string, len(pp.Imports)) + copy(p.Imports, pp.Imports) + p.Internal.RawImports = pp.Imports + p.TestGoFiles = pp.TestGoFiles + p.TestImports = pp.TestImports + p.XTestGoFiles = pp.XTestGoFiles + p.XTestImports = pp.XTestImports + if opts.IgnoreImports { + p.Imports = nil + p.Internal.RawImports = nil + p.TestImports = nil + p.XTestImports = nil + } + p.EmbedPatterns = pp.EmbedPatterns + p.TestEmbedPatterns = pp.TestEmbedPatterns + p.XTestEmbedPatterns = pp.XTestEmbedPatterns + p.Internal.OrigImportPath = pp.ImportPath +} + +// 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 + Pos string // position of error + Err error // the error itself + IsImportCycle bool // the error is an import cycle + Hard bool // whether the error is soft or hard; soft errors are ignored in some places + alwaysPrintStack bool // whether to always print the ImportStack +} + +func (p *PackageError) Error() string { + // TODO(#43696): decide when to print the stack or the position based on + // the error type and whether the package is in the main module. + // Document the rationale. + if p.Pos != "" && (len(p.ImportStack) == 0 || !p.alwaysPrintStack) { + // Omit import stack. The full path to the file where the error + // is the most important thing. + return p.Pos + ": " + p.Err.Error() + } + + // If the error is an ImportPathError, and the last path on the stack appears + // in the error message, omit that path from the stack to avoid repetition. + // If an ImportPathError wraps another ImportPathError that matches the + // last path on the stack, we don't omit the path. An error like + // "package A imports B: error loading C caused by B" would not be clearer + // if "imports B" were omitted. + if len(p.ImportStack) == 0 { + return p.Err.Error() + } + var optpos string + if p.Pos != "" { + optpos = "\n\t" + p.Pos + } + return "package " + strings.Join(p.ImportStack, "\n\timports ") + optpos + ": " + p.Err.Error() +} + +func (p *PackageError) Unwrap() error { return p.Err } + +// PackageError implements MarshalJSON so that Err is marshaled as a string +// and non-essential fields are omitted. +func (p *PackageError) MarshalJSON() ([]byte, error) { + perr := struct { + ImportStack []string + Pos string + Err string + }{p.ImportStack, p.Pos, p.Err.Error()} + return json.Marshal(perr) +} + +func (p *PackageError) setPos(posList []token.Position) { + if len(posList) == 0 { + return + } + pos := posList[0] + pos.Filename = base.ShortPath(pos.Filename) + p.Pos = pos.String() +} + +// ImportPathError is a type of error that prevents a package from being loaded +// for a given import path. When such a package is loaded, a *Package is +// returned with Err wrapping an ImportPathError: the error is attached to +// the imported package, not the importing package. +// +// The string returned by ImportPath must appear in the string returned by +// Error. Errors that wrap ImportPathError (such as PackageError) may omit +// the import path. +type ImportPathError interface { + error + ImportPath() string +} + +var ( + _ ImportPathError = (*importError)(nil) + _ ImportPathError = (*mainPackageError)(nil) + _ ImportPathError = (*modload.ImportMissingError)(nil) + _ ImportPathError = (*modload.ImportMissingSumError)(nil) + _ ImportPathError = (*modload.DirectImportFromImplicitDependencyError)(nil) +) + +type importError struct { + importPath string + err error // created with fmt.Errorf +} + +func ImportErrorf(path, format string, args ...any) ImportPathError { + err := &importError{importPath: path, err: fmt.Errorf(format, args...)} + if errStr := err.Error(); !strings.Contains(errStr, path) { + panic(fmt.Sprintf("path %q not in error %q", path, errStr)) + } + return err +} + +func (e *importError) Error() string { + return e.err.Error() +} + +func (e *importError) Unwrap() error { + // Don't return e.err directly, since we're only wrapping an error if %w + // was passed to ImportErrorf. + return errors.Unwrap(e.err) +} + +func (e *importError) ImportPath() string { + return e.importPath +} + +// An ImportStack is a stack of import paths, possibly with the suffix " (test)" appended. +// The import path of a test package is the import path of the corresponding +// non-test package with the suffix "_test" added. +type ImportStack []string + +func (s *ImportStack) Push(p string) { + *s = append(*s, p) +} + +func (s *ImportStack) Pop() { + *s = (*s)[0 : len(*s)-1] +} + +func (s *ImportStack) Copy() []string { + return append([]string{}, *s...) +} + +func (s *ImportStack) Top() string { + if len(*s) == 0 { + return "" + } + return (*s)[len(*s)-1] +} + +// shorterThan reports whether sp is shorter than t. +// We use this to record the shortest import sequence +// that leads to a particular package. +func (sp *ImportStack) shorterThan(t []string) bool { + s := *sp + if len(s) != len(t) { + return len(s) < len(t) + } + // If they are the same length, settle ties using string ordering. + for i := range s { + if s[i] != t[i] { + return s[i] < t[i] + } + } + return false // they are equal +} + +// packageCache is a lookup cache for LoadImport, +// so that if we look up a package multiple times +// we return the same pointer each time. +var packageCache = map[string]*Package{} + +// ClearPackageCache clears the in-memory package cache and the preload caches. +// It is only for use by GOPATH-based "go get". +// TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function. +func ClearPackageCache() { + for name := range packageCache { + delete(packageCache, name) + } + resolvedImportCache.Clear() + packageDataCache.Clear() +} + +// ClearPackageCachePartial clears packages with the given import paths from the +// in-memory package cache and the preload caches. It is only for use by +// GOPATH-based "go get". +// TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function. +func ClearPackageCachePartial(args []string) { + shouldDelete := make(map[string]bool) + for _, arg := range args { + shouldDelete[arg] = true + if p := packageCache[arg]; p != nil { + delete(packageCache, arg) + } + } + resolvedImportCache.DeleteIf(func(key any) bool { + return shouldDelete[key.(importSpec).path] + }) + packageDataCache.DeleteIf(func(key any) bool { + return shouldDelete[key.(string)] + }) +} + +// ReloadPackageNoFlags is like LoadImport but makes sure +// not to use the package cache. +// It is only for use by GOPATH-based "go get". +// TODO(rsc): When GOPATH-based "go get" is removed, delete this function. +func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package { + p := packageCache[arg] + if p != nil { + delete(packageCache, arg) + resolvedImportCache.DeleteIf(func(key any) bool { + return key.(importSpec).path == p.ImportPath + }) + packageDataCache.Delete(p.ImportPath) + } + return LoadImport(context.TODO(), PackageOpts{}, arg, base.Cwd(), nil, stk, nil, 0) +} + +// dirToImportPath returns the pseudo-import path we use for a package +// outside the Go path. It begins with _/ and then contains the full path +// to the directory. If the package lives in c:\home\gopher\my\pkg then +// the pseudo-import path is _/c_/home/gopher/my/pkg. +// Using a pseudo-import path like this makes the ./ imports no longer +// a special case, so that all the code to deal with ordinary imports works +// automatically. +func dirToImportPath(dir string) string { + return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir))) +} + +func makeImportValid(r rune) rune { + // Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport. + const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" + if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { + return '_' + } + return r +} + +// Mode flags for loadImport and download (in get.go). +const ( + // ResolveImport means that loadImport should do import path expansion. + // That is, ResolveImport means that the import path came from + // a source file and has not been expanded yet to account for + // vendoring or possible module adjustment. + // Every import path should be loaded initially with ResolveImport, + // and then the expanded version (for example with the /vendor/ in it) + // gets recorded as the canonical import path. At that point, future loads + // of that package must not pass ResolveImport, because + // disallowVendor will reject direct use of paths containing /vendor/. + ResolveImport = 1 << iota + + // ResolveModule is for download (part of "go get") and indicates + // that the module adjustment should be done, but not vendor adjustment. + ResolveModule + + // GetTestDeps is for download (part of "go get") and indicates + // that test dependencies should be fetched too. + GetTestDeps +) + +// LoadImport scans the directory named by path, which must be an import path, +// but possibly a local import path (an absolute file system path or one beginning +// with ./ or ../). A local relative path is interpreted relative to srcDir. +// It returns a *Package describing the package found in that directory. +// LoadImport does not set tool flags and should only be used by +// this package, as part of a bigger load operation, and by GOPATH-based "go get". +// TODO(rsc): When GOPATH-based "go get" is removed, unexport this function. +func LoadImport(ctx context.Context, opts PackageOpts, path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package { + return loadImport(ctx, opts, nil, path, srcDir, parent, stk, importPos, mode) +} + +func loadImport(ctx context.Context, opts PackageOpts, pre *preload, path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package { + ctx, span := trace.StartSpan(ctx, "modload.loadImport "+path) + defer span.Done() + + if path == "" { + panic("LoadImport called with empty package path") + } + + var parentPath, parentRoot string + parentIsStd := false + if parent != nil { + parentPath = parent.ImportPath + parentRoot = parent.Root + parentIsStd = parent.Standard + } + bp, loaded, err := loadPackageData(ctx, path, parentPath, srcDir, parentRoot, parentIsStd, mode) + if loaded && pre != nil && !opts.IgnoreImports { + pre.preloadImports(ctx, opts, bp.Imports, bp) + } + if bp == nil { + p := &Package{ + PackagePublic: PackagePublic{ + ImportPath: path, + Incomplete: true, + }, + } + if importErr, ok := err.(ImportPathError); !ok || importErr.ImportPath() != path { + // Only add path to the error's import stack if it's not already present + // in the error. + // + // TODO(bcmills): setLoadPackageDataError itself has a similar Push / Pop + // sequence that empirically doesn't trigger for these errors, guarded by + // a somewhat complex condition. Figure out how to generalize that + // condition and eliminate the explicit calls here. + stk.Push(path) + defer stk.Pop() + } + p.setLoadPackageDataError(err, path, stk, nil) + return p + } + + importPath := bp.ImportPath + p := packageCache[importPath] + if p != nil { + stk.Push(path) + p = reusePackage(p, stk) + stk.Pop() + } else { + p = new(Package) + p.Internal.Local = build.IsLocalImport(path) + p.ImportPath = importPath + packageCache[importPath] = p + + // Load package. + // loadPackageData may return bp != nil even if an error occurs, + // in order to return partial information. + p.load(ctx, opts, path, stk, importPos, bp, err) + + if !cfg.ModulesEnabled && path != cleanImport(path) { + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: ImportErrorf(path, "non-canonical import path %q: should be %q", path, pathpkg.Clean(path)), + } + p.Incomplete = true + p.Error.setPos(importPos) + } + } + + // Checked on every import because the rules depend on the code doing the importing. + if perr := disallowInternal(ctx, srcDir, parent, parentPath, p, stk); perr != p { + perr.Error.setPos(importPos) + return perr + } + if mode&ResolveImport != 0 { + if perr := disallowVendor(srcDir, path, parentPath, p, stk); perr != p { + perr.Error.setPos(importPos) + return perr + } + } + + if p.Name == "main" && parent != nil && parent.Dir != p.Dir { + perr := *p + perr.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: ImportErrorf(path, "import %q is a program, not an importable package", path), + } + perr.Error.setPos(importPos) + return &perr + } + + if p.Internal.Local && parent != nil && !parent.Internal.Local { + perr := *p + var err error + if path == "." { + err = ImportErrorf(path, "%s: cannot import current directory", path) + } else { + err = ImportErrorf(path, "local import %q in non-local package", path) + } + perr.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: err, + } + perr.Error.setPos(importPos) + return &perr + } + + return p +} + +// loadPackageData loads information needed to construct a *Package. The result +// is cached, and later calls to loadPackageData for the same package will return +// the same data. +// +// loadPackageData returns a non-nil package even if err is non-nil unless +// the package path is malformed (for example, the path contains "mod/" or "@"). +// +// loadPackageData returns a boolean, loaded, which is true if this is the +// first time the package was loaded. Callers may preload imports in this case. +func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoot string, parentIsStd bool, mode int) (bp *build.Package, loaded bool, err error) { + ctx, span := trace.StartSpan(ctx, "load.loadPackageData "+path) + defer span.Done() + + if path == "" { + panic("loadPackageData called with empty package path") + } + + if strings.HasPrefix(path, "mod/") { + // Paths beginning with "mod/" might accidentally + // look in the module cache directory tree in $GOPATH/pkg/mod/. + // This prefix is owned by the Go core for possible use in the + // standard library (since it does not begin with a domain name), + // so it's OK to disallow entirely. + return nil, false, fmt.Errorf("disallowed import path %q", path) + } + + if strings.Contains(path, "@") { + return nil, false, errors.New("can only use path@version syntax with 'go get' and 'go install' in module-aware mode") + } + + // Determine canonical package path and directory. + // For a local import the identifier is the pseudo-import path + // we create from the full directory to the package. + // Otherwise it is the usual import path. + // For vendored imports, it is the expanded form. + // + // Note that when modules are enabled, local import paths are normally + // canonicalized by modload.LoadPackages before now. However, if there's an + // error resolving a local path, it will be returned untransformed + // so that 'go list -e' reports something useful. + importKey := importSpec{ + path: path, + parentPath: parentPath, + parentDir: parentDir, + parentRoot: parentRoot, + parentIsStd: parentIsStd, + mode: mode, + } + r := resolvedImportCache.Do(importKey, func() any { + var r resolvedImport + if cfg.ModulesEnabled { + r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path) + } else if build.IsLocalImport(path) { + r.dir = filepath.Join(parentDir, path) + r.path = dirToImportPath(r.dir) + } else if mode&ResolveImport != 0 { + // We do our own path resolution, because we want to + // find out the key to use in packageCache without the + // overhead of repeated calls to buildContext.Import. + // The code is also needed in a few other places anyway. + r.path = resolveImportPath(path, parentPath, parentDir, parentRoot, parentIsStd) + } else if mode&ResolveModule != 0 { + r.path = moduleImportPath(path, parentPath, parentDir, parentRoot) + } + if r.path == "" { + r.path = path + } + return r + }).(resolvedImport) + // Invariant: r.path is set to the resolved import path. If the path cannot + // be resolved, r.path is set to path, the source import path. + // r.path is never empty. + + // Load the package from its directory. If we already found the package's + // directory when resolving its import path, use that. + data := packageDataCache.Do(r.path, func() any { + loaded = true + var data packageData + if r.dir != "" { + var buildMode build.ImportMode + buildContext := cfg.BuildContext + if !cfg.ModulesEnabled { + buildMode = build.ImportComment + } else { + buildContext.GOPATH = "" // Clear GOPATH so packages are imported as pure module packages + } + modroot := modload.PackageModRoot(ctx, r.path) + if modroot == "" && str.HasPathPrefix(r.dir, cfg.GOROOTsrc) { + modroot = cfg.GOROOTsrc + if str.HasPathPrefix(r.dir, cfg.GOROOTsrc+string(filepath.Separator)+"cmd") { + modroot += string(filepath.Separator) + "cmd" + } + } + if modroot != "" { + if rp, err := modindex.GetPackage(modroot, r.dir); err == nil { + data.p, data.err = rp.Import(cfg.BuildContext, buildMode) + goto Happy + } else if !errors.Is(err, modindex.ErrNotIndexed) { + base.Fatalf("go: %v", err) + } + } + data.p, data.err = buildContext.ImportDir(r.dir, buildMode) + Happy: + if cfg.ModulesEnabled { + // Override data.p.Root, since ImportDir sets it to $GOPATH, if + // the module is inside $GOPATH/src. + if info := modload.PackageModuleInfo(ctx, path); info != nil { + data.p.Root = info.Dir + } + } + if r.err != nil { + if data.err != nil { + // ImportDir gave us one error, and the module loader gave us another. + // We arbitrarily choose to keep the error from ImportDir because + // that's what our tests already expect, and it seems to provide a bit + // more detail in most cases. + } else if errors.Is(r.err, imports.ErrNoGo) { + // ImportDir said there were files in the package, but the module + // loader said there weren't. Which one is right? + // Without this special-case hack, the TestScript/test_vet case fails + // on the vetfail/p1 package (added in CL 83955). + // Apparently, imports.ShouldBuild biases toward rejecting files + // with invalid build constraints, whereas ImportDir biases toward + // accepting them. + // + // TODO(#41410: Figure out how this actually ought to work and fix + // this mess. + } else { + data.err = r.err + } + } + } else if r.err != nil { + data.p = new(build.Package) + data.err = r.err + } else if cfg.ModulesEnabled && path != "unsafe" { + data.p = new(build.Package) + data.err = fmt.Errorf("unknown import path %q: internal error: module loader did not resolve import", r.path) + } else { + buildMode := build.ImportComment + if mode&ResolveImport == 0 || r.path != path { + // Not vendoring, or we already found the vendored path. + buildMode |= build.IgnoreVendor + } + data.p, data.err = cfg.BuildContext.Import(r.path, parentDir, buildMode) + } + data.p.ImportPath = r.path + + // Set data.p.BinDir in cases where go/build.Context.Import + // may give us a path we don't want. + if !data.p.Goroot { + if cfg.GOBIN != "" { + data.p.BinDir = cfg.GOBIN + } else if cfg.ModulesEnabled { + data.p.BinDir = modload.BinDir() + } + } + + if !cfg.ModulesEnabled && data.err == nil && + data.p.ImportComment != "" && data.p.ImportComment != path && + !strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") { + data.err = fmt.Errorf("code in directory %s expects import %q", data.p.Dir, data.p.ImportComment) + } + return data + }).(packageData) + + return data.p, loaded, data.err +} + +// importSpec describes an import declaration in source code. It is used as a +// cache key for resolvedImportCache. +type importSpec struct { + path string + parentPath, parentDir, parentRoot string + parentIsStd bool + mode int +} + +// resolvedImport holds a canonical identifier for a package. It may also contain +// a path to the package's directory and an error if one occurred. resolvedImport +// is the value type in resolvedImportCache. +type resolvedImport struct { + path, dir string + err error +} + +// packageData holds information loaded from a package. It is the value type +// in packageDataCache. +type packageData struct { + p *build.Package + err error +} + +// resolvedImportCache maps import strings (importSpec) to canonical package names +// (resolvedImport). +var resolvedImportCache par.Cache + +// packageDataCache maps canonical package names (string) to package metadata +// (packageData). +var packageDataCache par.Cache + +// preloadWorkerCount is the number of concurrent goroutines that can load +// packages. Experimentally, there are diminishing returns with more than +// 4 workers. This was measured on the following machines. +// +// * MacBookPro with a 4-core Intel Core i7 CPU +// * Linux workstation with 6-core Intel Xeon CPU +// * Linux workstation with 24-core Intel Xeon CPU +// +// It is very likely (though not confirmed) that this workload is limited +// by memory bandwidth. We don't have a good way to determine the number of +// workers that would saturate the bus though, so runtime.GOMAXPROCS +// seems like a reasonable default. +var preloadWorkerCount = runtime.GOMAXPROCS(0) + +// preload holds state for managing concurrent preloading of package data. +// +// A preload should be created with newPreload before loading a large +// package graph. flush must be called when package loading is complete +// to ensure preload goroutines are no longer active. This is necessary +// because of global mutable state that cannot safely be read and written +// concurrently. In particular, packageDataCache may be cleared by "go get" +// in GOPATH mode, and modload.loaded (accessed via modload.Lookup) may be +// modified by modload.LoadPackages. +type preload struct { + cancel chan struct{} + sema chan struct{} +} + +// newPreload creates a new preloader. flush must be called later to avoid +// accessing global state while it is being modified. +func newPreload() *preload { + pre := &preload{ + cancel: make(chan struct{}), + sema: make(chan struct{}, preloadWorkerCount), + } + return pre +} + +// preloadMatches loads data for package paths matched by patterns. +// When preloadMatches returns, some packages may not be loaded yet, but +// loadPackageData and loadImport are always safe to call. +func (pre *preload) preloadMatches(ctx context.Context, opts PackageOpts, matches []*search.Match) { + for _, m := range matches { + for _, pkg := range m.Pkgs { + select { + case <-pre.cancel: + return + case pre.sema <- struct{}{}: + go func(pkg string) { + mode := 0 // don't use vendoring or module import resolution + bp, loaded, err := loadPackageData(ctx, pkg, "", base.Cwd(), "", false, mode) + <-pre.sema + if bp != nil && loaded && err == nil && !opts.IgnoreImports { + pre.preloadImports(ctx, opts, bp.Imports, bp) + } + }(pkg) + } + } + } +} + +// preloadImports queues a list of imports for preloading. +// When preloadImports returns, some packages may not be loaded yet, +// but loadPackageData and loadImport are always safe to call. +func (pre *preload) preloadImports(ctx context.Context, opts PackageOpts, imports []string, parent *build.Package) { + parentIsStd := parent.Goroot && parent.ImportPath != "" && search.IsStandardImportPath(parent.ImportPath) + for _, path := range imports { + if path == "C" || path == "unsafe" { + continue + } + select { + case <-pre.cancel: + return + case pre.sema <- struct{}{}: + go func(path string) { + bp, loaded, err := loadPackageData(ctx, path, parent.ImportPath, parent.Dir, parent.Root, parentIsStd, ResolveImport) + <-pre.sema + if bp != nil && loaded && err == nil && !opts.IgnoreImports { + pre.preloadImports(ctx, opts, bp.Imports, bp) + } + }(path) + } + } +} + +// flush stops pending preload operations. flush blocks until preload calls to +// loadPackageData have completed. The preloader will not make any new calls +// to loadPackageData. +func (pre *preload) flush() { + // flush is usually deferred. + // Don't hang program waiting for workers on panic. + if v := recover(); v != nil { + panic(v) + } + + close(pre.cancel) + for i := 0; i < preloadWorkerCount; i++ { + pre.sema <- struct{}{} + } +} + +func cleanImport(path string) string { + orig := path + path = pathpkg.Clean(path) + if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") { + path = "./" + path + } + return path +} + +var isDirCache par.Cache + +func isDir(path string) bool { + return isDirCache.Do(path, func() any { + fi, err := fsys.Stat(path) + return err == nil && fi.IsDir() + }).(bool) +} + +// ResolveImportPath returns the true meaning of path when it appears in parent. +// There are two different resolutions applied. +// First, there is Go 1.5 vendoring (golang.org/s/go15vendor). +// If vendor expansion doesn't trigger, then the path is also subject to +// Go 1.11 module legacy conversion (golang.org/issue/25069). +func ResolveImportPath(parent *Package, path string) (found string) { + var parentPath, parentDir, parentRoot string + parentIsStd := false + if parent != nil { + parentPath = parent.ImportPath + parentDir = parent.Dir + parentRoot = parent.Root + parentIsStd = parent.Standard + } + return resolveImportPath(path, parentPath, parentDir, parentRoot, parentIsStd) +} + +func resolveImportPath(path, parentPath, parentDir, parentRoot string, parentIsStd bool) (found string) { + if cfg.ModulesEnabled { + if _, p, e := modload.Lookup(parentPath, parentIsStd, path); e == nil { + return p + } + return path + } + found = vendoredImportPath(path, parentPath, parentDir, parentRoot) + if found != path { + return found + } + return moduleImportPath(path, parentPath, parentDir, parentRoot) +} + +// dirAndRoot returns the source directory and workspace root +// for the package p, guaranteeing that root is a path prefix of dir. +func dirAndRoot(path string, dir, root string) (string, string) { + origDir, origRoot := dir, root + dir = filepath.Clean(dir) + root = filepath.Join(root, "src") + if !str.HasFilePathPrefix(dir, root) || path != "command-line-arguments" && filepath.Join(root, path) != dir { + // Look for symlinks before reporting error. + dir = expandPath(dir) + root = expandPath(root) + } + + if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || path != "command-line-arguments" && !build.IsLocalImport(path) && filepath.Join(root, path) != dir { + debug.PrintStack() + base.Fatalf("unexpected directory layout:\n"+ + " import path: %s\n"+ + " root: %s\n"+ + " dir: %s\n"+ + " expand root: %s\n"+ + " expand dir: %s\n"+ + " separator: %s", + path, + filepath.Join(origRoot, "src"), + filepath.Clean(origDir), + origRoot, + origDir, + string(filepath.Separator)) + } + + return dir, root +} + +// vendoredImportPath returns the vendor-expansion of path when it appears in parent. +// If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path, +// x/vendor/path, vendor/path, or else stay path if none of those exist. +// vendoredImportPath returns the expanded path or, if no expansion is found, the original. +func vendoredImportPath(path, parentPath, parentDir, parentRoot string) (found string) { + if parentRoot == "" { + return path + } + + dir, root := dirAndRoot(parentPath, parentDir, parentRoot) + + vpath := "vendor/" + path + for i := len(dir); i >= len(root); i-- { + if i < len(dir) && dir[i] != filepath.Separator { + continue + } + // Note: checking for the vendor directory before checking + // for the vendor/path directory helps us hit the + // isDir cache more often. It also helps us prepare a more useful + // list of places we looked, to report when an import is not found. + if !isDir(filepath.Join(dir[:i], "vendor")) { + continue + } + targ := filepath.Join(dir[:i], vpath) + if isDir(targ) && hasGoFiles(targ) { + importPath := parentPath + if importPath == "command-line-arguments" { + // If parent.ImportPath is 'command-line-arguments'. + // set to relative directory to root (also chopped root directory) + importPath = dir[len(root)+1:] + } + // We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy. + // We know the import path for parent's dir. + // We chopped off some number of path elements and + // added vendor\path to produce c:\gopath\src\foo\bar\baz\vendor\path. + // Now we want to know the import path for that directory. + // Construct it by chopping the same number of path elements + // (actually the same number of bytes) from parent's import path + // and then append /vendor/path. + chopped := len(dir) - i + if chopped == len(importPath)+1 { + // We walked up from c:\gopath\src\foo\bar + // and found c:\gopath\src\vendor\path. + // We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7). + // Use "vendor/path" without any prefix. + return vpath + } + return importPath[:len(importPath)-chopped] + "/" + vpath + } + } + return path +} + +var ( + modulePrefix = []byte("\nmodule ") + goModPathCache par.Cache +) + +// goModPath returns the module path in the go.mod in dir, if any. +func goModPath(dir string) (path string) { + return goModPathCache.Do(dir, func() any { + data, err := os.ReadFile(filepath.Join(dir, "go.mod")) + if err != nil { + return "" + } + var i int + if bytes.HasPrefix(data, modulePrefix[1:]) { + i = 0 + } else { + i = bytes.Index(data, modulePrefix) + if i < 0 { + return "" + } + i++ + } + line := data[i:] + + // Cut line at \n, drop trailing \r if present. + if j := bytes.IndexByte(line, '\n'); j >= 0 { + line = line[:j] + } + if line[len(line)-1] == '\r' { + line = line[:len(line)-1] + } + line = line[len("module "):] + + // If quoted, unquote. + path = strings.TrimSpace(string(line)) + if path != "" && path[0] == '"' { + s, err := strconv.Unquote(path) + if err != nil { + return "" + } + path = s + } + return path + }).(string) +} + +// findVersionElement returns the slice indices of the final version element /vN in path. +// If there is no such element, it returns -1, -1. +func findVersionElement(path string) (i, j int) { + j = len(path) + for i = len(path) - 1; i >= 0; i-- { + if path[i] == '/' { + if isVersionElement(path[i+1 : j]) { + return i, j + } + j = i + } + } + return -1, -1 +} + +// isVersionElement reports whether s is a well-formed path version element: +// v2, v3, v10, etc, but not v0, v05, v1. +func isVersionElement(s string) bool { + if len(s) < 2 || s[0] != 'v' || s[1] == '0' || s[1] == '1' && len(s) == 2 { + return false + } + for i := 1; i < len(s); i++ { + if s[i] < '0' || '9' < s[i] { + return false + } + } + return true +} + +// moduleImportPath translates import paths found in go modules +// back down to paths that can be resolved in ordinary builds. +// +// Define “new” code as code with a go.mod file in the same directory +// or a parent directory. If an import in new code says x/y/v2/z but +// x/y/v2/z does not exist and x/y/go.mod says “module x/y/v2”, +// then go build will read the import as x/y/z instead. +// See golang.org/issue/25069. +func moduleImportPath(path, parentPath, parentDir, parentRoot string) (found string) { + if parentRoot == "" { + return path + } + + // If there are no vN elements in path, leave it alone. + // (The code below would do the same, but only after + // some other file system accesses that we can avoid + // here by returning early.) + if i, _ := findVersionElement(path); i < 0 { + return path + } + + dir, root := dirAndRoot(parentPath, parentDir, parentRoot) + + // Consider dir and parents, up to and including root. + for i := len(dir); i >= len(root); i-- { + if i < len(dir) && dir[i] != filepath.Separator { + continue + } + if goModPath(dir[:i]) != "" { + goto HaveGoMod + } + } + // This code is not in a tree with a go.mod, + // so apply no changes to the path. + return path + +HaveGoMod: + // This import is in a tree with a go.mod. + // Allow it to refer to code in GOPATH/src/x/y/z as x/y/v2/z + // if GOPATH/src/x/y/go.mod says module "x/y/v2", + + // If x/y/v2/z exists, use it unmodified. + if bp, _ := cfg.BuildContext.Import(path, "", build.IgnoreVendor); bp.Dir != "" { + return path + } + + // Otherwise look for a go.mod supplying a version element. + // Some version-like elements may appear in paths but not + // be module versions; we skip over those to look for module + // versions. For example the module m/v2 might have a + // package m/v2/api/v1/foo. + limit := len(path) + for limit > 0 { + i, j := findVersionElement(path[:limit]) + if i < 0 { + return path + } + if bp, _ := cfg.BuildContext.Import(path[:i], "", build.IgnoreVendor); bp.Dir != "" { + if mpath := goModPath(bp.Dir); mpath != "" { + // Found a valid go.mod file, so we're stopping the search. + // If the path is m/v2/p and we found m/go.mod that says + // "module m/v2", then we return "m/p". + if mpath == path[:j] { + return path[:i] + path[j:] + } + // Otherwise just return the original path. + // We didn't find anything worth rewriting, + // and the go.mod indicates that we should + // not consider parent directories. + return path + } + } + limit = i + } + return path +} + +// hasGoFiles reports whether dir contains any files with names ending in .go. +// For a vendor check we must exclude directories that contain no .go files. +// Otherwise it is not possible to vendor just a/b/c and still import the +// non-vendored a/b. See golang.org/issue/13832. +func hasGoFiles(dir string) bool { + files, _ := os.ReadDir(dir) + for _, f := range files { + if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") { + return true + } + } + return false +} + +// reusePackage reuses package p to satisfy the import at the top +// of the import stack stk. If this use causes an import loop, +// reusePackage updates p's error information to record the loop. +func reusePackage(p *Package, stk *ImportStack) *Package { + // We use p.Internal.Imports==nil to detect a package that + // is in the midst of its own loadPackage call + // (all the recursion below happens before p.Internal.Imports gets set). + if p.Internal.Imports == nil { + if p.Error == nil { + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: errors.New("import cycle not allowed"), + IsImportCycle: true, + } + } else if !p.Error.IsImportCycle { + // If the error is already set, but it does not indicate that + // we are in an import cycle, set IsImportCycle so that we don't + // end up stuck in a loop down the road. + p.Error.IsImportCycle = true + } + p.Incomplete = true + } + // Don't rewrite the import stack in the error if we have an import cycle. + // If we do, we'll lose the path that describes the cycle. + if p.Error != nil && !p.Error.IsImportCycle && stk.shorterThan(p.Error.ImportStack) { + p.Error.ImportStack = stk.Copy() + } + return p +} + +// disallowInternal checks that srcDir (containing package importerPath, if non-empty) +// is allowed to import p. +// If the import is allowed, disallowInternal returns the original package p. +// If not, it returns a new package containing just an appropriate error. +func disallowInternal(ctx context.Context, srcDir string, importer *Package, importerPath string, p *Package, stk *ImportStack) *Package { + // golang.org/s/go14internal: + // An import of a path containing the element “internal” + // is disallowed if the importing code is outside the tree + // rooted at the parent of the “internal” directory. + + // There was an error loading the package; stop here. + if p.Error != nil { + return p + } + + // The generated 'testmain' package is allowed to access testing/internal/..., + // as if it were generated into the testing directory tree + // (it's actually in a temporary directory outside any Go tree). + // This cleans up a former kludge in passing functionality to the testing package. + if str.HasPathPrefix(p.ImportPath, "testing/internal") && importerPath == "testmain" { + return p + } + + // We can't check standard packages with gccgo. + if cfg.BuildContext.Compiler == "gccgo" && p.Standard { + return p + } + + // The sort package depends on internal/reflectlite, but during bootstrap + // the path rewriting causes the normal internal checks to fail. + // Instead, just ignore the internal rules during bootstrap. + if p.Standard && strings.HasPrefix(importerPath, "bootstrap/") { + return p + } + + // importerPath is empty: we started + // with a name given on the command line, not an + // import. Anything listed on the command line is fine. + if importerPath == "" { + return p + } + + // Check for "internal" element: three cases depending on begin of string and/or end of string. + i, ok := findInternal(p.ImportPath) + if !ok { + return p + } + + // Internal is present. + // Map import path back to directory corresponding to parent of internal. + if i > 0 { + i-- // rewind over slash in ".../internal" + } + + if p.Module == nil { + parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)] + + if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { + return p + } + + // Look for symlinks before reporting error. + srcDir = expandPath(srcDir) + parent = expandPath(parent) + if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { + return p + } + } else { + // p is in a module, so make it available based on the importer's import path instead + // of the file path (https://golang.org/issue/23970). + if importer.Internal.CmdlineFiles { + // The importer is a list of command-line files. + // Pretend that the import path is the import path of the + // directory containing them. + // If the directory is outside the main modules, this will resolve to ".", + // which is not a prefix of any valid module. + importerPath, _ = modload.MainModules.DirImportPath(ctx, importer.Dir) + } + parentOfInternal := p.ImportPath[:i] + if str.HasPathPrefix(importerPath, parentOfInternal) { + return p + } + } + + // Internal is present, and srcDir is outside parent's tree. Not allowed. + perr := *p + perr.Error = &PackageError{ + alwaysPrintStack: true, + ImportStack: stk.Copy(), + Err: ImportErrorf(p.ImportPath, "use of internal package "+p.ImportPath+" not allowed"), + } + perr.Incomplete = true + return &perr +} + +// findInternal looks for the final "internal" path element in the given import path. +// If there isn't one, findInternal returns ok=false. +// Otherwise, findInternal returns ok=true and the index of the "internal". +func findInternal(path string) (index int, ok bool) { + // Three cases, depending on internal at start/end of string or not. + // The order matters: we must return the index of the final element, + // because the final one produces the most restrictive requirement + // on the importer. + switch { + case strings.HasSuffix(path, "/internal"): + return len(path) - len("internal"), true + case strings.Contains(path, "/internal/"): + return strings.LastIndex(path, "/internal/") + 1, true + case path == "internal", strings.HasPrefix(path, "internal/"): + return 0, true + } + return 0, false +} + +// disallowVendor checks that srcDir is allowed to import p as path. +// If the import is allowed, disallowVendor returns the original package p. +// If not, it returns a new package containing just an appropriate error. +func disallowVendor(srcDir string, path string, importerPath string, p *Package, stk *ImportStack) *Package { + // If the importerPath is empty, we started + // with a name given on the command line, not an + // import. Anything listed on the command line is fine. + if importerPath == "" { + return p + } + + if perr := disallowVendorVisibility(srcDir, p, importerPath, stk); perr != p { + return perr + } + + // Paths like x/vendor/y must be imported as y, never as x/vendor/y. + if i, ok := FindVendor(path); ok { + perr := *p + perr.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: ImportErrorf(path, "%s must be imported as %s", path, path[i+len("vendor/"):]), + } + perr.Incomplete = true + return &perr + } + + return p +} + +// disallowVendorVisibility checks that srcDir is allowed to import p. +// The rules are the same as for /internal/ except that a path ending in /vendor +// is not subject to the rules, only subdirectories of vendor. +// This allows people to have packages and commands named vendor, +// for maximal compatibility with existing source trees. +func disallowVendorVisibility(srcDir string, p *Package, importerPath string, stk *ImportStack) *Package { + // The stack does not include p.ImportPath. + // If there's nothing on the stack, we started + // with a name given on the command line, not an + // import. Anything listed on the command line is fine. + if importerPath == "" { + return p + } + + // Check for "vendor" element. + i, ok := FindVendor(p.ImportPath) + if !ok { + return p + } + + // Vendor is present. + // Map import path back to directory corresponding to parent of vendor. + if i > 0 { + i-- // rewind over slash in ".../vendor" + } + truncateTo := i + len(p.Dir) - len(p.ImportPath) + if truncateTo < 0 || len(p.Dir) < truncateTo { + return p + } + parent := p.Dir[:truncateTo] + if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { + return p + } + + // Look for symlinks before reporting error. + srcDir = expandPath(srcDir) + parent = expandPath(parent) + if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { + return p + } + + // Vendor is present, and srcDir is outside parent's tree. Not allowed. + perr := *p + perr.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: errors.New("use of vendored package not allowed"), + } + perr.Incomplete = true + return &perr +} + +// FindVendor looks for the last non-terminating "vendor" path element in the given import path. +// If there isn't one, FindVendor returns ok=false. +// Otherwise, FindVendor returns ok=true and the index of the "vendor". +// +// Note that terminating "vendor" elements don't count: "x/vendor" is its own package, +// not the vendored copy of an import "" (the empty import path). +// This will allow people to have packages or commands named vendor. +// This may help reduce breakage, or it may just be confusing. We'll see. +func FindVendor(path string) (index int, ok bool) { + // Two cases, depending on internal at start of string or not. + // The order matters: we must return the index of the final element, + // because the final one is where the effective import path starts. + switch { + case strings.Contains(path, "/vendor/"): + return strings.LastIndex(path, "/vendor/") + 1, true + case strings.HasPrefix(path, "vendor/"): + return 0, true + } + return 0, false +} + +type TargetDir int + +const ( + ToTool TargetDir = iota // to GOROOT/pkg/tool (default for cmd/*) + ToBin // to bin dir inside package root (default for non-cmd/*) + StalePath // an old import path; fail to build +) + +// InstallTargetDir reports the target directory for installing the command p. +func InstallTargetDir(p *Package) TargetDir { + if strings.HasPrefix(p.ImportPath, "code.google.com/p/go.tools/cmd/") { + return StalePath + } + if p.Goroot && strings.HasPrefix(p.ImportPath, "cmd/") && p.Name == "main" { + switch p.ImportPath { + case "cmd/go", "cmd/gofmt": + return ToBin + } + return ToTool + } + return ToBin +} + +var cgoExclude = map[string]bool{ + "runtime/cgo": true, +} + +var cgoSyscallExclude = map[string]bool{ + "runtime/cgo": true, + "runtime/race": true, + "runtime/msan": true, + "runtime/asan": true, +} + +var foldPath = make(map[string]string) + +// exeFromImportPath returns an executable name +// for a package using the import path. +// +// The executable name is the last element of the import path. +// In module-aware mode, an additional rule is used on import paths +// consisting of two or more path elements. If the last element is +// a vN path element specifying the major version, then the +// second last element of the import path is used instead. +func (p *Package) exeFromImportPath() string { + _, elem := pathpkg.Split(p.ImportPath) + if cfg.ModulesEnabled { + // If this is example.com/mycmd/v2, it's more useful to + // install it as mycmd than as v2. See golang.org/issue/24667. + if elem != p.ImportPath && isVersionElement(elem) { + _, elem = pathpkg.Split(pathpkg.Dir(p.ImportPath)) + } + } + return elem +} + +// exeFromFiles returns an executable name for a package +// using the first element in GoFiles or CgoFiles collections without the prefix. +// +// Returns empty string in case of empty collection. +func (p *Package) exeFromFiles() string { + var src string + if len(p.GoFiles) > 0 { + src = p.GoFiles[0] + } else if len(p.CgoFiles) > 0 { + src = p.CgoFiles[0] + } else { + return "" + } + _, elem := filepath.Split(src) + return elem[:len(elem)-len(".go")] +} + +// DefaultExecName returns the default executable name for a package +func (p *Package) DefaultExecName() string { + if p.Internal.CmdlineFiles { + return p.exeFromFiles() + } + return p.exeFromImportPath() +} + +// load populates p using information from bp, err, which should +// be the result of calling build.Context.Import. +// stk contains the import stack, not including path itself. +func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *ImportStack, importPos []token.Position, bp *build.Package, err error) { + p.copyBuild(opts, bp) + + // The localPrefix is the path we interpret ./ imports relative to, + // if we support them at all (not in module mode!). + // Synthesized main packages sometimes override this. + if p.Internal.Local && !cfg.ModulesEnabled { + p.Internal.LocalPrefix = dirToImportPath(p.Dir) + } + + // setError sets p.Error if it hasn't already been set. We may proceed + // after encountering some errors so that 'go list -e' has more complete + // output. If there's more than one error, we should report the first. + setError := func(err error) { + if p.Error == nil { + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: err, + } + + // Add the importer's position information if the import position exists, and + // the current package being examined is the importer. + // If we have not yet accepted package p onto the import stack, + // then the cause of the error is not within p itself: the error + // must be either in an explicit command-line argument, + // or on the importer side (indicated by a non-empty importPos). + if path != stk.Top() && len(importPos) > 0 { + p.Error.setPos(importPos) + } + } + } + + if err != nil { + p.Incomplete = true + p.setLoadPackageDataError(err, path, stk, importPos) + } + + useBindir := p.Name == "main" + if !p.Standard { + switch cfg.BuildBuildmode { + case "c-archive", "c-shared", "plugin": + useBindir = false + } + } + + if useBindir { + // Report an error when the old code.google.com/p/go.tools paths are used. + if InstallTargetDir(p) == StalePath { + // TODO(matloob): remove this branch, and StalePath itself. code.google.com/p/go is so + // old, even this code checking for it is stale now! + newPath := strings.Replace(p.ImportPath, "code.google.com/p/go.", "golang.org/x/", 1) + e := ImportErrorf(p.ImportPath, "the %v command has moved; use %v instead.", p.ImportPath, newPath) + setError(e) + return + } + elem := p.DefaultExecName() + cfg.ExeSuffix + full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + string(filepath.Separator) + elem + if cfg.BuildContext.GOOS != runtime.GOOS || cfg.BuildContext.GOARCH != runtime.GOARCH { + // Install cross-compiled binaries to subdirectories of bin. + elem = full + } + if p.Internal.Build.BinDir == "" && cfg.ModulesEnabled { + p.Internal.Build.BinDir = modload.BinDir() + } + if p.Internal.Build.BinDir != "" { + // Install to GOBIN or bin of GOPATH entry. + p.Target = filepath.Join(p.Internal.Build.BinDir, elem) + if !p.Goroot && strings.Contains(elem, string(filepath.Separator)) && cfg.GOBIN != "" { + // Do not create $GOBIN/goos_goarch/elem. + p.Target = "" + p.Internal.GobinSubdir = true + } + } + if InstallTargetDir(p) == ToTool { + // This is for 'go tool'. + // Override all the usual logic and force it into the tool directory. + if cfg.BuildToolchainName == "gccgo" { + p.Target = filepath.Join(build.ToolDir, elem) + } else { + p.Target = filepath.Join(cfg.GOROOTpkg, "tool", full) + } + } + } else if p.Internal.Local { + // Local import turned into absolute path. + // No permanent install target. + p.Target = "" + } else if p.Standard && cfg.BuildContext.Compiler == "gccgo" { + // gccgo has a preinstalled standard library that cmd/go cannot rebuild. + p.Target = "" + } else { + p.Target = p.Internal.Build.PkgObj + if cfg.BuildBuildmode == "shared" && p.Internal.Build.PkgTargetRoot != "" { + // TODO(matloob): This shouldn't be necessary, but the misc/cgo/testshared + // test fails without Target set for this condition. Figure out why and + // fix it. + p.Target = filepath.Join(p.Internal.Build.PkgTargetRoot, p.ImportPath+".a") + } + if cfg.BuildLinkshared && p.Internal.Build.PkgTargetRoot != "" { + // TODO(bcmills): The reliance on PkgTargetRoot implies that -linkshared does + // not work for any package that lacks a PkgTargetRoot — such as a non-main + // package in module mode. We should probably fix that. + targetPrefix := filepath.Join(p.Internal.Build.PkgTargetRoot, p.ImportPath) + p.Target = targetPrefix + ".a" + shlibnamefile := targetPrefix + ".shlibname" + shlib, err := os.ReadFile(shlibnamefile) + if err != nil && !os.IsNotExist(err) { + base.Fatalf("reading shlibname: %v", err) + } + if err == nil { + libname := strings.TrimSpace(string(shlib)) + if cfg.BuildContext.Compiler == "gccgo" { + p.Shlib = filepath.Join(p.Internal.Build.PkgTargetRoot, "shlibs", libname) + } else { + p.Shlib = filepath.Join(p.Internal.Build.PkgTargetRoot, libname) + } + } + } + } + + // Build augmented import list to add implicit dependencies. + // Be careful not to add imports twice, just to avoid confusion. + importPaths := p.Imports + addImport := func(path string, forCompiler bool) { + for _, p := range importPaths { + if path == p { + return + } + } + importPaths = append(importPaths, path) + if forCompiler { + p.Internal.CompiledImports = append(p.Internal.CompiledImports, path) + } + } + + if !opts.IgnoreImports { + // Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall", + // except for certain packages, to avoid circular dependencies. + if p.UsesCgo() { + addImport("unsafe", true) + } + if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" { + addImport("runtime/cgo", true) + } + if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { + addImport("syscall", true) + } + + // SWIG adds imports of some standard packages. + if p.UsesSwig() { + addImport("unsafe", true) + if cfg.BuildContext.Compiler != "gccgo" { + addImport("runtime/cgo", true) + } + addImport("syscall", true) + addImport("sync", true) + + // TODO: The .swig and .swigcxx files can use + // %go_import directives to import other packages. + } + + // The linker loads implicit dependencies. + if p.Name == "main" && !p.Internal.ForceLibrary { + for _, dep := range LinkerDeps(p) { + addImport(dep, false) + } + } + } + + // Check for case-insensitive collisions of import paths. + fold := str.ToFold(p.ImportPath) + if other := foldPath[fold]; other == "" { + foldPath[fold] = p.ImportPath + } else if other != p.ImportPath { + setError(ImportErrorf(p.ImportPath, "case-insensitive import collision: %q and %q", p.ImportPath, other)) + return + } + + if !SafeArg(p.ImportPath) { + setError(ImportErrorf(p.ImportPath, "invalid import path %q", p.ImportPath)) + return + } + + // Errors after this point are caused by this package, not the importing + // package. Pushing the path here prevents us from reporting the error + // with the position of the import declaration. + stk.Push(path) + defer stk.Pop() + + pkgPath := p.ImportPath + if p.Internal.CmdlineFiles { + pkgPath = "command-line-arguments" + } + if cfg.ModulesEnabled { + p.Module = modload.PackageModuleInfo(ctx, pkgPath) + } + + p.EmbedFiles, p.Internal.Embed, err = resolveEmbed(p.Dir, p.EmbedPatterns) + if err != nil { + p.Incomplete = true + setError(err) + embedErr := err.(*EmbedError) + p.Error.setPos(p.Internal.Build.EmbedPatternPos[embedErr.Pattern]) + } + + // Check for case-insensitive collision of input files. + // To avoid problems on case-insensitive files, we reject any package + // where two different input files have equal names under a case-insensitive + // comparison. + inputs := p.AllFiles() + f1, f2 := str.FoldDup(inputs) + if f1 != "" { + setError(fmt.Errorf("case-insensitive file name collision: %q and %q", f1, f2)) + return + } + + // If first letter of input file is ASCII, it must be alphanumeric. + // This avoids files turning into flags when invoking commands, + // and other problems we haven't thought of yet. + // Also, _cgo_ files must be generated by us, not supplied. + // They are allowed to have //go:cgo_ldflag directives. + // The directory scan ignores files beginning with _, + // so we shouldn't see any _cgo_ files anyway, but just be safe. + for _, file := range inputs { + if !SafeArg(file) || strings.HasPrefix(file, "_cgo_") { + setError(fmt.Errorf("invalid input file name %q", file)) + return + } + } + if name := pathpkg.Base(p.ImportPath); !SafeArg(name) { + setError(fmt.Errorf("invalid input directory name %q", name)) + return + } + if strings.ContainsAny(p.Dir, "\r\n") { + setError(fmt.Errorf("invalid package directory %q", p.Dir)) + return + } + + // Build list of imported packages and full dependency list. + imports := make([]*Package, 0, len(p.Imports)) + for i, path := range importPaths { + if path == "C" { + continue + } + p1 := LoadImport(ctx, opts, path, p.Dir, p, stk, p.Internal.Build.ImportPos[path], ResolveImport) + + path = p1.ImportPath + importPaths[i] = path + if i < len(p.Imports) { + p.Imports[i] = path + } + + imports = append(imports, p1) + if p1.Incomplete { + p.Incomplete = true + } + } + p.Internal.Imports = imports + if !opts.SuppressDeps { + p.collectDeps() + } + if p.Error == nil && p.Name == "main" && !p.Internal.ForceLibrary && len(p.DepsErrors) == 0 && !opts.SuppressBuildInfo { + // TODO(bcmills): loading VCS metadata can be fairly slow. + // Consider starting this as a background goroutine and retrieving the result + // asynchronously when we're actually ready to build the package, or when we + // actually need to evaluate whether the package's metadata is stale. + p.setBuildInfo(opts.AutoVCS) + } + + // If cgo is not enabled, ignore cgo supporting sources + // just as we ignore go files containing import "C". + if !cfg.BuildContext.CgoEnabled { + p.CFiles = nil + p.CXXFiles = nil + p.MFiles = nil + p.SwigFiles = nil + p.SwigCXXFiles = nil + // Note that SFiles are okay (they go to the Go assembler) + // and HFiles are okay (they might be used by the SFiles). + // Also Sysofiles are okay (they might not contain object + // code; see issue #16050). + } + + // The gc toolchain only permits C source files with cgo or SWIG. + if len(p.CFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() && cfg.BuildContext.Compiler == "gc" { + setError(fmt.Errorf("C source files not allowed when not using cgo or SWIG: %s", strings.Join(p.CFiles, " "))) + return + } + + // C++, Objective-C, and Fortran source files are permitted only with cgo or SWIG, + // regardless of toolchain. + if len(p.CXXFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() { + setError(fmt.Errorf("C++ source files not allowed when not using cgo or SWIG: %s", strings.Join(p.CXXFiles, " "))) + return + } + if len(p.MFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() { + setError(fmt.Errorf("Objective-C source files not allowed when not using cgo or SWIG: %s", strings.Join(p.MFiles, " "))) + return + } + if len(p.FFiles) > 0 && !p.UsesCgo() && !p.UsesSwig() { + setError(fmt.Errorf("Fortran source files not allowed when not using cgo or SWIG: %s", strings.Join(p.FFiles, " "))) + return + } +} + +// An EmbedError indicates a problem with a go:embed directive. +type EmbedError struct { + Pattern string + Err error +} + +func (e *EmbedError) Error() string { + return fmt.Sprintf("pattern %s: %v", e.Pattern, e.Err) +} + +func (e *EmbedError) Unwrap() error { + return e.Err +} + +// ResolveEmbed resolves //go:embed patterns and returns only the file list. +// For use by go mod vendor to find embedded files it should copy into the +// vendor directory. +// TODO(#42504): Once go mod vendor uses load.PackagesAndErrors, just +// call (*Package).ResolveEmbed +func ResolveEmbed(dir string, patterns []string) ([]string, error) { + files, _, err := resolveEmbed(dir, patterns) + return files, err +} + +// resolveEmbed resolves //go:embed patterns to precise file lists. +// It sets files to the list of unique files matched (for go list), +// and it sets pmap to the more precise mapping from +// patterns to files. +func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[string][]string, err error) { + var pattern string + defer func() { + if err != nil { + err = &EmbedError{ + Pattern: pattern, + Err: err, + } + } + }() + + // TODO(rsc): All these messages need position information for better error reports. + pmap = make(map[string][]string) + have := make(map[string]int) + dirOK := make(map[string]bool) + pid := 0 // pattern ID, to allow reuse of have map + for _, pattern = range patterns { + pid++ + + glob := pattern + all := strings.HasPrefix(pattern, "all:") + if all { + glob = pattern[len("all:"):] + } + // Check pattern is valid for //go:embed. + if _, err := pathpkg.Match(glob, ""); err != nil || !validEmbedPattern(glob) { + return nil, nil, fmt.Errorf("invalid pattern syntax") + } + + // Glob to find matches. + match, err := fsys.Glob(str.QuoteGlob(pkgdir) + string(filepath.Separator) + filepath.FromSlash(glob)) + if err != nil { + return nil, nil, err + } + + // Filter list of matches down to the ones that will still exist when + // the directory is packaged up as a module. (If p.Dir is in the module cache, + // only those files exist already, but if p.Dir is in the current module, + // then there may be other things lying around, like symbolic links or .git directories.) + var list []string + for _, file := range match { + // relative path to p.Dir which begins without prefix slash + rel := filepath.ToSlash(str.TrimFilePathPrefix(file, pkgdir)) + + what := "file" + info, err := fsys.Lstat(file) + if err != nil { + return nil, nil, err + } + if info.IsDir() { + what = "directory" + } + + // Check that directories along path do not begin a new module + // (do not contain a go.mod). + for dir := file; len(dir) > len(pkgdir)+1 && !dirOK[dir]; dir = filepath.Dir(dir) { + if _, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil { + return nil, nil, fmt.Errorf("cannot embed %s %s: in different module", what, rel) + } + if dir != file { + if info, err := fsys.Lstat(dir); err == nil && !info.IsDir() { + return nil, nil, fmt.Errorf("cannot embed %s %s: in non-directory %s", what, rel, dir[len(pkgdir)+1:]) + } + } + dirOK[dir] = true + if elem := filepath.Base(dir); isBadEmbedName(elem) { + if dir == file { + return nil, nil, fmt.Errorf("cannot embed %s %s: invalid name %s", what, rel, elem) + } else { + return nil, nil, fmt.Errorf("cannot embed %s %s: in invalid directory %s", what, rel, elem) + } + } + } + + switch { + default: + return nil, nil, fmt.Errorf("cannot embed irregular file %s", rel) + + case info.Mode().IsRegular(): + if have[rel] != pid { + have[rel] = pid + list = append(list, rel) + } + + case info.IsDir(): + // Gather all files in the named directory, stopping at module boundaries + // and ignoring files that wouldn't be packaged into a module. + count := 0 + err := fsys.Walk(file, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + rel := filepath.ToSlash(str.TrimFilePathPrefix(path, pkgdir)) + name := info.Name() + if path != file && (isBadEmbedName(name) || ((name[0] == '.' || name[0] == '_') && !all)) { + // Ignore bad names, assuming they won't go into modules. + // Also avoid hidden files that user may not know about. + // See golang.org/issue/42328. + if info.IsDir() { + return fs.SkipDir + } + return nil + } + if info.IsDir() { + if _, err := fsys.Stat(filepath.Join(path, "go.mod")); err == nil { + return filepath.SkipDir + } + return nil + } + if !info.Mode().IsRegular() { + return nil + } + count++ + if have[rel] != pid { + have[rel] = pid + list = append(list, rel) + } + return nil + }) + if err != nil { + return nil, nil, err + } + if count == 0 { + return nil, nil, fmt.Errorf("cannot embed directory %s: contains no embeddable files", rel) + } + } + } + + if len(list) == 0 { + return nil, nil, fmt.Errorf("no matching files found") + } + sort.Strings(list) + pmap[pattern] = list + } + + for file := range have { + files = append(files, file) + } + sort.Strings(files) + return files, pmap, nil +} + +func validEmbedPattern(pattern string) bool { + return pattern != "." && fs.ValidPath(pattern) +} + +// isBadEmbedName reports whether name is the base name of a file that +// can't or won't be included in modules and therefore shouldn't be treated +// as existing for embedding. +func isBadEmbedName(name string) bool { + if err := module.CheckFilePath(name); err != nil { + return true + } + switch name { + // Empty string should be impossible but make it bad. + case "": + return true + // Version control directories won't be present in module. + case ".bzr", ".hg", ".git", ".svn": + return true + } + return false +} + +// collectDeps populates p.Deps and p.DepsErrors by iterating over +// p.Internal.Imports. +// +// TODO(jayconrod): collectDeps iterates over transitive imports for every +// package. We should only need to visit direct imports. +func (p *Package) collectDeps() { + deps := make(map[string]*Package) + var q []*Package + q = append(q, p.Internal.Imports...) + for i := 0; i < len(q); i++ { + p1 := q[i] + path := p1.ImportPath + // The same import path could produce an error or not, + // depending on what tries to import it. + // Prefer to record entries with errors, so we can report them. + p0 := deps[path] + if p0 == nil || p1.Error != nil && (p0.Error == nil || len(p0.Error.ImportStack) > len(p1.Error.ImportStack)) { + deps[path] = p1 + for _, p2 := range p1.Internal.Imports { + if deps[p2.ImportPath] != p2 { + q = append(q, p2) + } + } + } + } + + p.Deps = make([]string, 0, len(deps)) + for dep := range deps { + p.Deps = append(p.Deps, dep) + } + sort.Strings(p.Deps) + for _, dep := range p.Deps { + p1 := deps[dep] + if p1 == nil { + panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath) + } + if p1.Error != nil { + p.DepsErrors = append(p.DepsErrors, p1.Error) + } + } +} + +// vcsStatusCache maps repository directories (string) +// to their VCS information (vcsStatusError). +var vcsStatusCache par.Cache + +// setBuildInfo gathers build information, formats it as a string to be +// embedded in the binary, then sets p.Internal.BuildInfo to that string. +// setBuildInfo should only be called on a main package with no errors. +// +// This information can be retrieved using debug.ReadBuildInfo. +// +// Note that the GoVersion field is not set here to avoid encoding it twice. +// It is stored separately in the binary, mostly for historical reasons. +func (p *Package) setBuildInfo(autoVCS bool) { + setPkgErrorf := func(format string, args ...any) { + if p.Error == nil { + p.Error = &PackageError{Err: fmt.Errorf(format, args...)} + } + } + + var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module + debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module { + version := mi.Version + if version == "" { + version = "(devel)" + } + dm := &debug.Module{ + Path: mi.Path, + Version: version, + } + if mi.Replace != nil { + dm.Replace = debugModFromModinfo(mi.Replace) + } else if mi.Version != "" { + dm.Sum = modfetch.Sum(module.Version{Path: mi.Path, Version: mi.Version}) + } + return dm + } + + var main debug.Module + if p.Module != nil { + main = *debugModFromModinfo(p.Module) + } + + visited := make(map[*Package]bool) + mdeps := make(map[module.Version]*debug.Module) + var q []*Package + q = append(q, p.Internal.Imports...) + for len(q) > 0 { + p1 := q[0] + q = q[1:] + if visited[p1] { + continue + } + visited[p1] = true + if p1.Module != nil { + m := module.Version{Path: p1.Module.Path, Version: p1.Module.Version} + if p1.Module.Path != main.Path && mdeps[m] == nil { + mdeps[m] = debugModFromModinfo(p1.Module) + } + } + q = append(q, p1.Internal.Imports...) + } + sortedMods := make([]module.Version, 0, len(mdeps)) + for mod := range mdeps { + sortedMods = append(sortedMods, mod) + } + module.Sort(sortedMods) + deps := make([]*debug.Module, len(sortedMods)) + for i, mod := range sortedMods { + deps[i] = mdeps[mod] + } + + pkgPath := p.ImportPath + if p.Internal.CmdlineFiles { + pkgPath = "command-line-arguments" + } + info := &debug.BuildInfo{ + Path: pkgPath, + Main: main, + Deps: deps, + } + appendSetting := func(key, value string) { + value = strings.ReplaceAll(value, "\n", " ") // make value safe + info.Settings = append(info.Settings, debug.BuildSetting{Key: key, Value: value}) + } + + // Add command-line flags relevant to the build. + // This is informational, not an exhaustive list. + // Please keep the list sorted. + if cfg.BuildASan { + appendSetting("-asan", "true") + } + if BuildAsmflags.present { + appendSetting("-asmflags", BuildAsmflags.String()) + } + buildmode := cfg.BuildBuildmode + if buildmode == "default" { + if p.Name == "main" { + buildmode = "exe" + } else { + buildmode = "archive" + } + } + appendSetting("-buildmode", buildmode) + appendSetting("-compiler", cfg.BuildContext.Compiler) + if gccgoflags := BuildGccgoflags.String(); gccgoflags != "" && cfg.BuildContext.Compiler == "gccgo" { + appendSetting("-gccgoflags", gccgoflags) + } + if gcflags := BuildGcflags.String(); gcflags != "" && cfg.BuildContext.Compiler == "gc" { + appendSetting("-gcflags", gcflags) + } + if ldflags := BuildLdflags.String(); ldflags != "" { + // https://go.dev/issue/52372: only include ldflags if -trimpath is not set, + // since it can include system paths through various linker flags (notably + // -extar, -extld, and -extldflags). + // + // TODO: since we control cmd/link, in theory we can parse ldflags to + // determine whether they may refer to system paths. If we do that, we can + // redact only those paths from the recorded -ldflags setting and still + // record the system-independent parts of the flags. + if !cfg.BuildTrimpath { + appendSetting("-ldflags", ldflags) + } + } + if cfg.BuildPGOFile != "" { + if cfg.BuildTrimpath { + appendSetting("-pgo", filepath.Base(cfg.BuildPGOFile)) + } else { + appendSetting("-pgo", cfg.BuildPGOFile) + } + } + if cfg.BuildMSan { + appendSetting("-msan", "true") + } + if cfg.BuildRace { + appendSetting("-race", "true") + } + if tags := cfg.BuildContext.BuildTags; len(tags) > 0 { + appendSetting("-tags", strings.Join(tags, ",")) + } + if cfg.BuildTrimpath { + appendSetting("-trimpath", "true") + } + cgo := "0" + if cfg.BuildContext.CgoEnabled { + cgo = "1" + } + appendSetting("CGO_ENABLED", cgo) + // https://go.dev/issue/52372: only include CGO flags if -trimpath is not set. + // (If -trimpath is set, it is possible that these flags include system paths.) + // If cgo is involved, reproducibility is already pretty well ruined anyway, + // given that we aren't stamping header or library versions. + // + // TODO(bcmills): perhaps we could at least parse the flags and stamp the + // subset of flags that are known not to be paths? + if cfg.BuildContext.CgoEnabled && !cfg.BuildTrimpath { + for _, name := range []string{"CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} { + appendSetting(name, cfg.Getenv(name)) + } + } + appendSetting("GOARCH", cfg.BuildContext.GOARCH) + if cfg.RawGOEXPERIMENT != "" { + appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT) + } + appendSetting("GOOS", cfg.BuildContext.GOOS) + if key, val := cfg.GetArchEnv(); key != "" && val != "" { + appendSetting(key, val) + } + + // Add VCS status if all conditions are true: + // + // - -buildvcs is enabled. + // - p is a non-test contained within a main module (there may be multiple + // main modules in a workspace, but local replacements don't count). + // - Both the current directory and p's module's root directory are contained + // in the same local repository. + // - We know the VCS commands needed to get the status. + setVCSError := func(err error) { + setPkgErrorf("error obtaining VCS status: %v\n\tUse -buildvcs=false to disable VCS stamping.", err) + } + + var repoDir string + var vcsCmd *vcs.Cmd + var err error + const allowNesting = true + + wantVCS := false + switch cfg.BuildBuildvcs { + case "true": + wantVCS = true // Include VCS metadata even for tests if requested explicitly; see https://go.dev/issue/52648. + case "auto": + wantVCS = autoVCS && !p.IsTestOnly() + case "false": + default: + panic(fmt.Sprintf("unexpected value for cfg.BuildBuildvcs: %q", cfg.BuildBuildvcs)) + } + + if wantVCS && p.Module != nil && p.Module.Version == "" && !p.Standard { + if p.Module.Path == "bootstrap" && cfg.GOROOT == os.Getenv("GOROOT_BOOTSTRAP") { + // During bootstrapping, the bootstrap toolchain is built in module + // "bootstrap" (instead of "std"), with GOROOT set to GOROOT_BOOTSTRAP + // (so the bootstrap toolchain packages don't even appear to be in GOROOT). + goto omitVCS + } + repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting) + if err != nil && !errors.Is(err, os.ErrNotExist) { + setVCSError(err) + return + } + if !str.HasFilePathPrefix(p.Module.Dir, repoDir) && + !str.HasFilePathPrefix(repoDir, p.Module.Dir) { + // The module containing the main package does not overlap with the + // repository containing the working directory. Don't include VCS info. + // If the repo contains the module or vice versa, but they are not + // the same directory, it's likely an error (see below). + goto omitVCS + } + if cfg.BuildBuildvcs == "auto" && vcsCmd != nil && vcsCmd.Cmd != "" { + if _, err := exec.LookPath(vcsCmd.Cmd); err != nil { + // We fould a repository, but the required VCS tool is not present. + // "-buildvcs=auto" means that we should silently drop the VCS metadata. + goto omitVCS + } + } + } + if repoDir != "" && vcsCmd.Status != nil { + // Check that the current directory, package, and module are in the same + // repository. vcs.FromDir allows nested Git repositories, but nesting + // is not allowed for other VCS tools. The current directory may be outside + // p.Module.Dir when a workspace is used. + pkgRepoDir, _, err := vcs.FromDir(p.Dir, "", allowNesting) + if err != nil { + setVCSError(err) + return + } + if pkgRepoDir != repoDir { + if cfg.BuildBuildvcs != "auto" { + setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir)) + return + } + goto omitVCS + } + modRepoDir, _, err := vcs.FromDir(p.Module.Dir, "", allowNesting) + if err != nil { + setVCSError(err) + return + } + if modRepoDir != repoDir { + if cfg.BuildBuildvcs != "auto" { + setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir)) + return + } + goto omitVCS + } + + type vcsStatusError struct { + Status vcs.Status + Err error + } + cached := vcsStatusCache.Do(repoDir, func() any { + st, err := vcsCmd.Status(vcsCmd, repoDir) + return vcsStatusError{st, err} + }).(vcsStatusError) + if err := cached.Err; err != nil { + setVCSError(err) + return + } + st := cached.Status + + appendSetting("vcs", vcsCmd.Cmd) + if st.Revision != "" { + appendSetting("vcs.revision", st.Revision) + } + if !st.CommitTime.IsZero() { + stamp := st.CommitTime.UTC().Format(time.RFC3339Nano) + appendSetting("vcs.time", stamp) + } + appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted)) + } +omitVCS: + + p.Internal.BuildInfo = info.String() +} + +// SafeArg reports whether arg is a "safe" command-line argument, +// meaning that when it appears in a command-line, it probably +// doesn't have some special meaning other than its own name. +// Obviously args beginning with - are not safe (they look like flags). +// Less obviously, args beginning with @ are not safe (they look like +// GNU binutils flagfile specifiers, sometimes called "response files"). +// To be conservative, we reject almost any arg beginning with non-alphanumeric ASCII. +// We accept leading . _ and / as likely in file system paths. +// There is a copy of this function in cmd/compile/internal/gc/noder.go. +func SafeArg(name string) bool { + if name == "" { + return false + } + c := name[0] + return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf +} + +// LinkerDeps returns the list of linker-induced dependencies for main package p. +func LinkerDeps(p *Package) []string { + // Everything links runtime. + deps := []string{"runtime"} + + // External linking mode forces an import of runtime/cgo. + if externalLinkingForced(p) && cfg.BuildContext.Compiler != "gccgo" { + deps = append(deps, "runtime/cgo") + } + // On ARM with GOARM=5, it forces an import of math, for soft floating point. + if cfg.Goarch == "arm" { + deps = append(deps, "math") + } + // Using the race detector forces an import of runtime/race. + if cfg.BuildRace { + deps = append(deps, "runtime/race") + } + // Using memory sanitizer forces an import of runtime/msan. + if cfg.BuildMSan { + deps = append(deps, "runtime/msan") + } + // Using address sanitizer forces an import of runtime/asan. + if cfg.BuildASan { + deps = append(deps, "runtime/asan") + } + // Building for coverage forces an import of runtime/coverage. + if cfg.BuildCover && cfg.Experiment.CoverageRedesign { + deps = append(deps, "runtime/coverage") + } + + return deps +} + +// externalLinkingForced reports whether external linking is being +// forced even for programs that do not use cgo. +func externalLinkingForced(p *Package) bool { + if !cfg.BuildContext.CgoEnabled { + return false + } + + // Some targets must use external linking even inside GOROOT. + switch cfg.BuildContext.GOOS { + case "android": + if cfg.BuildContext.GOARCH != "arm64" { + return true + } + case "ios": + return true + } + + // Currently build modes c-shared, pie (on systems that do not + // support PIE with internal linking mode (currently all + // systems: issue #18968)), plugin, and -linkshared force + // external linking mode, as of course does + // -ldflags=-linkmode=external. External linking mode forces + // an import of runtime/cgo. + // If there are multiple -linkmode options, the last one wins. + pieCgo := cfg.BuildBuildmode == "pie" && !platform.InternalLinkPIESupported(cfg.BuildContext.GOOS, cfg.BuildContext.GOARCH) + linkmodeExternal := false + if p != nil { + ldflags := BuildLdflags.For(p) + for i := len(ldflags) - 1; i >= 0; i-- { + a := ldflags[i] + if a == "-linkmode=external" || + a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "external" { + linkmodeExternal = true + break + } else if a == "-linkmode=internal" || + a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "internal" { + break + } + } + } + + return cfg.BuildBuildmode == "c-shared" || cfg.BuildBuildmode == "plugin" || pieCgo || cfg.BuildLinkshared || linkmodeExternal +} + +// mkAbs rewrites list, which must be paths relative to p.Dir, +// into a sorted list of absolute paths. It edits list in place but for +// convenience also returns list back to its caller. +func (p *Package) mkAbs(list []string) []string { + for i, f := range list { + list[i] = filepath.Join(p.Dir, f) + } + sort.Strings(list) + return list +} + +// InternalGoFiles returns the list of Go files being built for the package, +// using absolute paths. +func (p *Package) InternalGoFiles() []string { + return p.mkAbs(str.StringList(p.GoFiles, p.CgoFiles, p.TestGoFiles)) +} + +// InternalXGoFiles returns the list of Go files being built for the XTest package, +// using absolute paths. +func (p *Package) InternalXGoFiles() []string { + return p.mkAbs(p.XTestGoFiles) +} + +// InternalGoFiles returns the list of all Go files possibly relevant for the package, +// using absolute paths. "Possibly relevant" means that files are not excluded +// due to build tags, but files with names beginning with . or _ are still excluded. +func (p *Package) InternalAllGoFiles() []string { + return p.mkAbs(str.StringList(p.IgnoredGoFiles, p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles)) +} + +// usesSwig reports whether the package needs to run SWIG. +func (p *Package) UsesSwig() bool { + return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 +} + +// usesCgo reports whether the package needs to run cgo +func (p *Package) UsesCgo() bool { + return len(p.CgoFiles) > 0 +} + +// PackageList returns the list of packages in the dag rooted at roots +// as visited in a depth-first post-order traversal. +func PackageList(roots []*Package) []*Package { + seen := map[*Package]bool{} + all := []*Package{} + var walk func(*Package) + walk = func(p *Package) { + if seen[p] { + return + } + seen[p] = true + for _, p1 := range p.Internal.Imports { + walk(p1) + } + all = append(all, p) + } + for _, root := range roots { + walk(root) + } + return all +} + +// TestPackageList returns the list of packages in the dag rooted at roots +// as visited in a depth-first post-order traversal, including the test +// imports of the roots. This ignores errors in test packages. +func TestPackageList(ctx context.Context, opts PackageOpts, roots []*Package) []*Package { + seen := map[*Package]bool{} + all := []*Package{} + var walk func(*Package) + walk = func(p *Package) { + if seen[p] { + return + } + seen[p] = true + for _, p1 := range p.Internal.Imports { + walk(p1) + } + all = append(all, p) + } + walkTest := func(root *Package, path string) { + var stk ImportStack + p1 := LoadImport(ctx, opts, path, root.Dir, root, &stk, root.Internal.Build.TestImportPos[path], ResolveImport) + if p1.Error == nil { + walk(p1) + } + } + for _, root := range roots { + walk(root) + for _, path := range root.TestImports { + walkTest(root, path) + } + for _, path := range root.XTestImports { + walkTest(root, path) + } + } + return all +} + +// LoadImportWithFlags loads the package with the given import path and +// sets tool flags on that package. This function is useful loading implicit +// dependencies (like sync/atomic for coverage). +// TODO(jayconrod): delete this function and set flags automatically +// in LoadImport instead. +func LoadImportWithFlags(path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package { + p := LoadImport(context.TODO(), PackageOpts{}, path, srcDir, parent, stk, importPos, mode) + setToolFlags(p) + return p +} + +// PackageOpts control the behavior of PackagesAndErrors and other package +// loading functions. +type PackageOpts struct { + // IgnoreImports controls whether we ignore explicit and implicit imports + // when loading packages. Implicit imports are added when supporting Cgo + // or SWIG and when linking main packages. + IgnoreImports bool + + // ModResolveTests indicates whether calls to the module loader should also + // resolve test dependencies of the requested packages. + // + // If ModResolveTests is true, then the module loader needs to resolve test + // dependencies at the same time as packages; otherwise, the test dependencies + // of those packages could be missing, and resolving those missing dependencies + // could change the selected versions of modules that provide other packages. + ModResolveTests bool + + // MainOnly is true if the caller only wants to load main packages. + // For a literal argument matching a non-main package, a stub may be returned + // with an error. For a non-literal argument (with "..."), non-main packages + // are not be matched, and their dependencies may not be loaded. A warning + // may be printed for non-literal arguments that match no main packages. + MainOnly bool + + // AutoVCS controls whether we also load version-control metadata for main packages + // when -buildvcs=auto (the default). + AutoVCS bool + + // SuppressDeps is true if the caller does not need Deps and DepsErrors to be populated + // on the package. TestPackagesAndErrors examines the Deps field to determine if the test + // variant has an import cycle, so SuppressDeps should not be set if TestPackagesAndErrors + // will be called on the package. + SuppressDeps bool + + // SuppressBuildInfo is true if the caller does not need p.Stale, p.StaleReason, or p.Internal.BuildInfo + // to be populated on the package. + SuppressBuildInfo bool +} + +// PackagesAndErrors returns the packages named by the command line arguments +// 'patterns'. If a named package cannot be loaded, PackagesAndErrors returns +// a *Package with the Error field describing the failure. If errors are found +// loading imported packages, the DepsErrors field is set. The Incomplete field +// may be set as well. +// +// To obtain a flat list of packages, use PackageList. +// To report errors loading packages, use ReportPackageErrors. +func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string) []*Package { + ctx, span := trace.StartSpan(ctx, "load.PackagesAndErrors") + defer span.Done() + + for _, p := range patterns { + // Listing is only supported with all patterns referring to either: + // - Files that are part of the same directory. + // - Explicit package paths or patterns. + if strings.HasSuffix(p, ".go") { + // We need to test whether the path is an actual Go file and not a + // package path or pattern ending in '.go' (see golang.org/issue/34653). + if fi, err := fsys.Stat(p); err == nil && !fi.IsDir() { + pkgs := []*Package{GoFilesPackage(ctx, opts, patterns)} + setPGOProfilePath(pkgs) + return pkgs + } + } + } + + var matches []*search.Match + if modload.Init(); cfg.ModulesEnabled { + modOpts := modload.PackageOpts{ + ResolveMissingImports: true, + LoadTests: opts.ModResolveTests, + SilencePackageErrors: true, + } + matches, _ = modload.LoadPackages(ctx, modOpts, patterns...) + } else { + noModRoots := []string{} + matches = search.ImportPaths(patterns, noModRoots) + } + + var ( + pkgs []*Package + stk ImportStack + seenPkg = make(map[*Package]bool) + ) + + pre := newPreload() + defer pre.flush() + pre.preloadMatches(ctx, opts, matches) + + for _, m := range matches { + for _, pkg := range m.Pkgs { + if pkg == "" { + panic(fmt.Sprintf("ImportPaths returned empty package for pattern %s", m.Pattern())) + } + p := loadImport(ctx, opts, pre, pkg, base.Cwd(), nil, &stk, nil, 0) + p.Match = append(p.Match, m.Pattern()) + p.Internal.CmdlinePkg = true + if m.IsLiteral() { + // Note: do not set = m.IsLiteral unconditionally + // because maybe we'll see p matching both + // a literal and also a non-literal pattern. + p.Internal.CmdlinePkgLiteral = true + } + if seenPkg[p] { + continue + } + seenPkg[p] = true + pkgs = append(pkgs, p) + } + + if len(m.Errs) > 0 { + // In addition to any packages that were actually resolved from the + // pattern, there was some error in resolving the pattern itself. + // Report it as a synthetic package. + p := new(Package) + p.ImportPath = m.Pattern() + // Pass an empty ImportStack and nil importPos: the error arose from a pattern, not an import. + var stk ImportStack + var importPos []token.Position + p.setLoadPackageDataError(m.Errs[0], m.Pattern(), &stk, importPos) + p.Incomplete = true + p.Match = append(p.Match, m.Pattern()) + p.Internal.CmdlinePkg = true + if m.IsLiteral() { + p.Internal.CmdlinePkgLiteral = true + } + pkgs = append(pkgs, p) + } + } + + if opts.MainOnly { + pkgs = mainPackagesOnly(pkgs, matches) + } + + // Now that CmdlinePkg is set correctly, + // compute the effective flags for all loaded packages + // (not just the ones matching the patterns but also + // their dependencies). + setToolFlags(pkgs...) + + setPGOProfilePath(pkgs) + + return pkgs +} + +// setPGOProfilePath sets cfg.BuildPGOFile to the PGO profile path. +// In -pgo=auto mode, it finds the default PGO profile. +func setPGOProfilePath(pkgs []*Package) { + switch cfg.BuildPGO { + case "": + fallthrough // default to "off" + case "off": + return + + case "auto": + // Locate PGO profile from the main package. + + setError := func(p *Package) { + if p.Error == nil { + p.Error = &PackageError{Err: errors.New("-pgo=auto requires exactly one main package")} + } + } + + var mainpkg *Package + for _, p := range pkgs { + if p.Name == "main" { + if mainpkg != nil { + setError(p) + setError(mainpkg) + continue + } + mainpkg = p + } + } + if mainpkg == nil { + // No main package, no default.pgo to look for. + return + } + file := filepath.Join(mainpkg.Dir, "default.pgo") + if fi, err := os.Stat(file); err == nil && !fi.IsDir() { + cfg.BuildPGOFile = file + } + + default: + // Profile specified from the command line. + // Make it absolute path, as the compiler runs on various directories. + if p, err := filepath.Abs(cfg.BuildPGO); err != nil { + base.Fatalf("fail to get absolute path of PGO file %s: %v", cfg.BuildPGO, err) + } else { + cfg.BuildPGOFile = p + } + } +} + +// CheckPackageErrors prints errors encountered loading pkgs and their +// dependencies, then exits with a non-zero status if any errors were found. +func CheckPackageErrors(pkgs []*Package) { + printed := map[*PackageError]bool{} + for _, pkg := range pkgs { + if pkg.Error != nil { + base.Errorf("%v", pkg.Error) + printed[pkg.Error] = true + } + for _, err := range pkg.DepsErrors { + // Since these are errors in dependencies, + // the same error might show up multiple times, + // once in each package that depends on it. + // Only print each once. + if !printed[err] { + printed[err] = true + base.Errorf("%v", err) + } + } + } + base.ExitIfErrors() + + // Check for duplicate loads of the same package. + // That should be impossible, but if it does happen then + // we end up trying to build the same package twice, + // usually in parallel overwriting the same files, + // which doesn't work very well. + seen := map[string]bool{} + reported := map[string]bool{} + for _, pkg := range PackageList(pkgs) { + if seen[pkg.ImportPath] && !reported[pkg.ImportPath] { + reported[pkg.ImportPath] = true + base.Errorf("internal error: duplicate loads of %s", pkg.ImportPath) + } + seen[pkg.ImportPath] = true + } + base.ExitIfErrors() +} + +// mainPackagesOnly filters out non-main packages matched only by arguments +// containing "..." and returns the remaining main packages. +// +// Packages with missing, invalid, or ambiguous names may be treated as +// possibly-main packages. +// +// mainPackagesOnly sets a non-main package's Error field and returns it if it +// is named by a literal argument. +// +// mainPackagesOnly prints warnings for non-literal arguments that only match +// non-main packages. +func mainPackagesOnly(pkgs []*Package, matches []*search.Match) []*Package { + treatAsMain := map[string]bool{} + for _, m := range matches { + if m.IsLiteral() { + for _, path := range m.Pkgs { + treatAsMain[path] = true + } + } + } + + var mains []*Package + for _, pkg := range pkgs { + if pkg.Name == "main" || (pkg.Name == "" && pkg.Error != nil) { + treatAsMain[pkg.ImportPath] = true + mains = append(mains, pkg) + continue + } + + if len(pkg.InvalidGoFiles) > 0 { // TODO(#45999): && pkg.Name == "", but currently go/build sets pkg.Name arbitrarily if it is ambiguous. + // The package has (or may have) conflicting names, and we can't easily + // tell whether one of them is "main". So assume that it could be, and + // report an error for the package. + treatAsMain[pkg.ImportPath] = true + } + if treatAsMain[pkg.ImportPath] { + if pkg.Error == nil { + pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}} + } + mains = append(mains, pkg) + } + } + + for _, m := range matches { + if m.IsLiteral() || len(m.Pkgs) == 0 { + continue + } + foundMain := false + for _, path := range m.Pkgs { + if treatAsMain[path] { + foundMain = true + break + } + } + if !foundMain { + fmt.Fprintf(os.Stderr, "go: warning: %q matched only non-main packages\n", m.Pattern()) + } + } + + return mains +} + +type mainPackageError struct { + importPath string +} + +func (e *mainPackageError) Error() string { + return fmt.Sprintf("package %s is not a main package", e.importPath) +} + +func (e *mainPackageError) ImportPath() string { + return e.importPath +} + +func setToolFlags(pkgs ...*Package) { + for _, p := range PackageList(pkgs) { + p.Internal.Asmflags = BuildAsmflags.For(p) + p.Internal.Gcflags = BuildGcflags.For(p) + p.Internal.Ldflags = BuildLdflags.For(p) + p.Internal.Gccgoflags = BuildGccgoflags.For(p) + } +} + +// GoFilesPackage creates a package for building a collection of Go files +// (typically named on the command line). The target is named p.a for +// package p or named after the first Go file for package main. +func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Package { + modload.Init() + + for _, f := range gofiles { + if !strings.HasSuffix(f, ".go") { + pkg := new(Package) + pkg.Internal.Local = true + pkg.Internal.CmdlineFiles = true + pkg.Name = f + pkg.Error = &PackageError{ + Err: fmt.Errorf("named files must be .go files: %s", pkg.Name), + } + return pkg + } + } + + var stk ImportStack + ctxt := cfg.BuildContext + ctxt.UseAllFiles = true + + // Synthesize fake "directory" that only shows the named files, + // to make it look like this is a standard package or + // command directory. So that local imports resolve + // consistently, the files must all be in the same directory. + var dirent []fs.FileInfo + var dir string + for _, file := range gofiles { + fi, err := fsys.Stat(file) + if err != nil { + base.Fatalf("%s", err) + } + if fi.IsDir() { + base.Fatalf("%s is a directory, should be a Go file", file) + } + dir1 := filepath.Dir(file) + if dir == "" { + dir = dir1 + } else if dir != dir1 { + base.Fatalf("named files must all be in one directory; have %s and %s", dir, dir1) + } + dirent = append(dirent, fi) + } + ctxt.ReadDir = func(string) ([]fs.FileInfo, error) { return dirent, nil } + + if cfg.ModulesEnabled { + modload.ImportFromFiles(ctx, gofiles) + } + + var err error + if dir == "" { + dir = base.Cwd() + } + dir, err = filepath.Abs(dir) + if err != nil { + base.Fatalf("%s", err) + } + + bp, err := ctxt.ImportDir(dir, 0) + pkg := new(Package) + pkg.Internal.Local = true + pkg.Internal.CmdlineFiles = true + pkg.load(ctx, opts, "command-line-arguments", &stk, nil, bp, err) + if !cfg.ModulesEnabled { + pkg.Internal.LocalPrefix = dirToImportPath(dir) + } + pkg.ImportPath = "command-line-arguments" + pkg.Target = "" + pkg.Match = gofiles + + if pkg.Name == "main" { + exe := pkg.DefaultExecName() + cfg.ExeSuffix + + if cfg.GOBIN != "" { + pkg.Target = filepath.Join(cfg.GOBIN, exe) + } else if cfg.ModulesEnabled { + pkg.Target = filepath.Join(modload.BinDir(), exe) + } + } + + if opts.MainOnly && pkg.Name != "main" && pkg.Error == nil { + pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}} + } + setToolFlags(pkg) + + return pkg +} + +// PackagesAndErrorsOutsideModule is like PackagesAndErrors but runs in +// module-aware mode and ignores the go.mod file in the current directory or any +// parent directory, if there is one. This is used in the implementation of 'go +// install pkg@version' and other commands that support similar forms. +// +// modload.ForceUseModules must be true, and modload.RootMode must be NoRoot +// before calling this function. +// +// PackagesAndErrorsOutsideModule imposes several constraints to avoid +// ambiguity. All arguments must have the same version suffix (not just a suffix +// that resolves to the same version). They must refer to packages in the same +// module, which must not be std or cmd. That module is not considered the main +// module, but its go.mod file (if it has one) must not contain directives that +// would cause it to be interpreted differently if it were the main module +// (replace, exclude). +func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args []string) ([]*Package, error) { + if !modload.ForceUseModules { + panic("modload.ForceUseModules must be true") + } + if modload.RootMode != modload.NoRoot { + panic("modload.RootMode must be NoRoot") + } + + // Check that the arguments satisfy syntactic constraints. + var version string + for _, arg := range args { + if i := strings.Index(arg, "@"); i >= 0 { + version = arg[i+1:] + if version == "" { + return nil, fmt.Errorf("%s: version must not be empty", arg) + } + break + } + } + patterns := make([]string, len(args)) + for i, arg := range args { + p, found := strings.CutSuffix(arg, "@"+version) + if !found { + return nil, fmt.Errorf("%s: all arguments must refer to packages in the same module at the same version (@%s)", arg, version) + } + switch { + case build.IsLocalImport(p): + return nil, fmt.Errorf("%s: argument must be a package path, not a relative path", arg) + case filepath.IsAbs(p): + return nil, fmt.Errorf("%s: argument must be a package path, not an absolute path", arg) + case search.IsMetaPackage(p): + return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg) + case pathpkg.Clean(p) != p: + return nil, fmt.Errorf("%s: argument must be a clean package path", arg) + case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): + return nil, fmt.Errorf("%s: argument must not be a package in the standard library", arg) + default: + patterns[i] = p + } + } + + // Query the module providing the first argument, load its go.mod file, and + // check that it doesn't contain directives that would cause it to be + // interpreted differently if it were the main module. + // + // If multiple modules match the first argument, accept the longest match + // (first result). It's possible this module won't provide packages named by + // later arguments, and other modules would. Let's not try to be too + // magical though. + allowed := modload.CheckAllowed + if modload.IsRevisionQuery(version) { + // Don't check for retractions if a specific revision is requested. + allowed = nil + } + noneSelected := func(path string) (version string) { return "none" } + qrs, err := modload.QueryPackages(ctx, patterns[0], version, noneSelected, allowed) + if err != nil { + return nil, fmt.Errorf("%s: %w", args[0], err) + } + rootMod := qrs[0].Mod + data, err := modfetch.GoMod(rootMod.Path, rootMod.Version) + if err != nil { + return nil, fmt.Errorf("%s: %w", args[0], err) + } + f, err := modfile.Parse("go.mod", data, nil) + if err != nil { + return nil, fmt.Errorf("%s (in %s): %w", args[0], rootMod, err) + } + directiveFmt := "%s (in %s):\n" + + "\tThe go.mod file for the module providing named packages contains one or\n" + + "\tmore %s directives. It must not contain directives that would cause\n" + + "\tit to be interpreted differently than if it were the main module." + if len(f.Replace) > 0 { + return nil, fmt.Errorf(directiveFmt, args[0], rootMod, "replace") + } + if len(f.Exclude) > 0 { + return nil, fmt.Errorf(directiveFmt, args[0], rootMod, "exclude") + } + + // Since we are in NoRoot mode, the build list initially contains only + // the dummy command-line-arguments module. Add a requirement on the + // module that provides the packages named on the command line. + if _, err := modload.EditBuildList(ctx, nil, []module.Version{rootMod}); err != nil { + return nil, fmt.Errorf("%s: %w", args[0], err) + } + + // Load packages for all arguments. + pkgs := PackagesAndErrors(ctx, opts, patterns) + + // Check that named packages are all provided by the same module. + for _, pkg := range pkgs { + var pkgErr error + if pkg.Module == nil { + // Packages in std, cmd, and their vendored dependencies + // don't have this field set. + pkgErr = fmt.Errorf("package %s not provided by module %s", pkg.ImportPath, rootMod) + } else if pkg.Module.Path != rootMod.Path || pkg.Module.Version != rootMod.Version { + pkgErr = fmt.Errorf("package %s provided by module %s@%s\n\tAll packages must be provided by the same module (%s).", pkg.ImportPath, pkg.Module.Path, pkg.Module.Version, rootMod) + } + if pkgErr != nil && pkg.Error == nil { + pkg.Error = &PackageError{Err: pkgErr} + } + } + + matchers := make([]func(string) bool, len(patterns)) + for i, p := range patterns { + if strings.Contains(p, "...") { + matchers[i] = pkgpattern.MatchPattern(p) + } + } + return pkgs, nil +} + +// EnsureImport ensures that package p imports the named package. +func EnsureImport(p *Package, pkg string) { + for _, d := range p.Internal.Imports { + if d.Name == pkg { + return + } + } + + p1 := LoadImportWithFlags(pkg, p.Dir, p, &ImportStack{}, nil, 0) + if p1.Error != nil { + base.Fatalf("load %s: %v", pkg, p1.Error) + } + + p.Internal.Imports = append(p.Internal.Imports, p1) +} + +// PrepareForCoverageBuild is a helper invoked for "go install +// -cover", "go run -cover", and "go build -cover" (but not used by +// "go test -cover"). It walks through the packages being built (and +// dependencies) and marks them for coverage instrumentation when +// appropriate, and possibly adding additional deps where needed. +func PrepareForCoverageBuild(pkgs []*Package) { + var match []func(*Package) bool + + matchMainModAndCommandLine := func(p *Package) bool { + // note that p.Standard implies p.Module == nil below. + return p.Internal.CmdlineFiles || p.Internal.CmdlinePkg || (p.Module != nil && p.Module.Main) + } + + if len(cfg.BuildCoverPkg) != 0 { + // If -coverpkg has been specified, then we instrument only + // the specific packages selected by the user-specified pattern(s). + match = make([]func(*Package) bool, len(cfg.BuildCoverPkg)) + for i := range cfg.BuildCoverPkg { + match[i] = MatchPackage(cfg.BuildCoverPkg[i], base.Cwd()) + } + } else { + // Without -coverpkg, instrument only packages in the main module + // (if any), as well as packages/files specifically named on the + // command line. + match = []func(*Package) bool{matchMainModAndCommandLine} + } + + // Visit the packages being built or installed, along with all of + // their dependencies, and mark them to be instrumented, taking + // into account the matchers we've set up in the sequence above. + SelectCoverPackages(PackageList(pkgs), match, "build") +} + +func SelectCoverPackages(roots []*Package, match []func(*Package) bool, op string) []*Package { + var warntag string + var includeMain bool + switch op { + case "build": + warntag = "built" + includeMain = true + case "test": + warntag = "tested" + default: + panic("internal error, bad mode passed to SelectCoverPackages") + } + + covered := []*Package{} + matched := make([]bool, len(match)) + for _, p := range roots { + haveMatch := false + for i := range match { + if match[i](p) { + matched[i] = true + haveMatch = true + } + } + if !haveMatch { + continue + } + + // There is nothing to cover in package unsafe; it comes from + // the compiler. + if p.ImportPath == "unsafe" { + continue + } + + // A package which only has test files can't be imported as a + // dependency, and at the moment we don't try to instrument it + // for coverage. There isn't any technical reason why + // *_test.go files couldn't be instrumented, but it probably + // doesn't make much sense to lump together coverage metrics + // (ex: percent stmts covered) of *_test.go files with + // non-test Go code. + if len(p.GoFiles)+len(p.CgoFiles) == 0 { + continue + } + + // Silently ignore attempts to run coverage on sync/atomic + // and/or runtime/internal/atomic when using atomic coverage + // mode. Atomic coverage mode uses sync/atomic, so we can't + // also do coverage on it. + if cfg.BuildCoverMode == "atomic" && p.Standard && + (p.ImportPath == "sync/atomic" || p.ImportPath == "runtime/internal/atomic") { + continue + } + + // If using the race detector, silently ignore attempts to run + // coverage on the runtime packages. It will cause the race + // detector to be invoked before it has been initialized. Note + // the use of "regonly" instead of just ignoring the package + // completely-- we do this due to the requirements of the + // package ID numbering scheme. See the comment in + // $GOROOT/src/internal/coverage/pkid.go dealing with + // hard-coding of runtime package IDs. + cmode := cfg.BuildCoverMode + if cfg.BuildRace && p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal")) { + cmode = "regonly" + } + + // If -coverpkg is in effect and for some reason we don't want + // coverage data for the main package, make sure that we at + // least process it for registration hooks. + if includeMain && p.Name == "main" && !haveMatch { + haveMatch = true + cmode = "regonly" + } + + // Mark package for instrumentation. + p.Internal.CoverMode = cmode + covered = append(covered, p) + + // Force import of sync/atomic into package if atomic mode. + if cfg.BuildCoverMode == "atomic" { + EnsureImport(p, "sync/atomic") + } + + // Generate covervars if using legacy coverage design. + if !cfg.Experiment.CoverageRedesign { + var coverFiles []string + coverFiles = append(coverFiles, p.GoFiles...) + coverFiles = append(coverFiles, p.CgoFiles...) + p.Internal.CoverVars = DeclareCoverVars(p, coverFiles...) + } + } + + // Warn about -coverpkg arguments that are not actually used. + for i := range cfg.BuildCoverPkg { + if !matched[i] { + fmt.Fprintf(os.Stderr, "warning: no packages being %s depend on matches for pattern %s\n", warntag, cfg.BuildCoverPkg[i]) + } + } + + return covered +} + +// declareCoverVars attaches the required cover variables names +// to the files, to be used when annotating the files. This +// function only called when using legacy coverage test/build +// (e.g. GOEXPERIMENT=coverageredesign is off). +func DeclareCoverVars(p *Package, files ...string) map[string]*CoverVar { + coverVars := make(map[string]*CoverVar) + coverIndex := 0 + // We create the cover counters as new top-level variables in the package. + // We need to avoid collisions with user variables (GoCover_0 is unlikely but still) + // and more importantly with dot imports of other covered packages, + // so we append 12 hex digits from the SHA-256 of the import path. + // The point is only to avoid accidents, not to defeat users determined to + // break things. + sum := sha256.Sum256([]byte(p.ImportPath)) + h := fmt.Sprintf("%x", sum[:6]) + for _, file := range files { + if base.IsTestFile(file) { + continue + } + // For a package that is "local" (imported via ./ import or command line, outside GOPATH), + // we record the full path to the file name. + // Otherwise we record the import path, then a forward slash, then the file name. + // This makes profiles within GOPATH file system-independent. + // These names appear in the cmd/cover HTML interface. + var longFile string + if p.Internal.Local { + longFile = filepath.Join(p.Dir, file) + } else { + longFile = pathpkg.Join(p.ImportPath, file) + } + coverVars[file] = &CoverVar{ + File: longFile, + Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h), + } + coverIndex++ + } + return coverVars +} diff --git a/src/cmd/go/internal/load/pkg_test.go b/src/cmd/go/internal/load/pkg_test.go new file mode 100644 index 0000000..3bcddee --- /dev/null +++ b/src/cmd/go/internal/load/pkg_test.go @@ -0,0 +1,82 @@ +// Copyright 2019 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/cfg" + "testing" +) + +func TestPkgDefaultExecName(t *testing.T) { + oldModulesEnabled := cfg.ModulesEnabled + defer func() { cfg.ModulesEnabled = oldModulesEnabled }() + for _, tt := range []struct { + in string + files []string + wantMod string + wantGopath string + }{ + {"example.com/mycmd", []string{}, "mycmd", "mycmd"}, + {"example.com/mycmd/v0", []string{}, "v0", "v0"}, + {"example.com/mycmd/v1", []string{}, "v1", "v1"}, + {"example.com/mycmd/v2", []string{}, "mycmd", "v2"}, // Semantic import versioning, use second last element in module mode. + {"example.com/mycmd/v3", []string{}, "mycmd", "v3"}, // Semantic import versioning, use second last element in module mode. + {"mycmd", []string{}, "mycmd", "mycmd"}, + {"mycmd/v0", []string{}, "v0", "v0"}, + {"mycmd/v1", []string{}, "v1", "v1"}, + {"mycmd/v2", []string{}, "mycmd", "v2"}, // Semantic import versioning, use second last element in module mode. + {"v0", []string{}, "v0", "v0"}, + {"v1", []string{}, "v1", "v1"}, + {"v2", []string{}, "v2", "v2"}, + {"command-line-arguments", []string{"output.go", "foo.go"}, "output", "output"}, + } { + { + cfg.ModulesEnabled = true + pkg := new(Package) + pkg.ImportPath = tt.in + pkg.GoFiles = tt.files + pkg.Internal.CmdlineFiles = len(tt.files) > 0 + gotMod := pkg.DefaultExecName() + if gotMod != tt.wantMod { + t.Errorf("pkg.DefaultExecName with ImportPath = %q in module mode = %v; want %v", tt.in, gotMod, tt.wantMod) + } + } + { + cfg.ModulesEnabled = false + pkg := new(Package) + pkg.ImportPath = tt.in + pkg.GoFiles = tt.files + pkg.Internal.CmdlineFiles = len(tt.files) > 0 + gotGopath := pkg.DefaultExecName() + if gotGopath != tt.wantGopath { + t.Errorf("pkg.DefaultExecName with ImportPath = %q in gopath mode = %v; want %v", tt.in, gotGopath, tt.wantGopath) + } + } + } +} + +func TestIsVersionElement(t *testing.T) { + t.Parallel() + for _, tt := range []struct { + in string + want bool + }{ + {"v0", false}, + {"v05", false}, + {"v1", false}, + {"v2", true}, + {"v3", true}, + {"v9", true}, + {"v10", true}, + {"v11", true}, + {"v", false}, + {"vx", false}, + } { + got := isVersionElement(tt.in) + if got != tt.want { + t.Errorf("isVersionElement(%q) = %v; want %v", tt.in, got, tt.want) + } + } +} diff --git a/src/cmd/go/internal/load/search.go b/src/cmd/go/internal/load/search.go new file mode 100644 index 0000000..565996a --- /dev/null +++ b/src/cmd/go/internal/load/search.go @@ -0,0 +1,57 @@ +// 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 ( + "path/filepath" + "strings" + + "cmd/go/internal/search" + "cmd/internal/pkgpattern" +) + +// MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd. +func MatchPackage(pattern, cwd string) func(*Package) bool { + switch { + case search.IsRelativePath(pattern): + // Split pattern into leading pattern-free directory path + // (including all . and .. elements) and the final pattern. + var dir string + i := strings.Index(pattern, "...") + if i < 0 { + dir, pattern = pattern, "" + } else { + j := strings.LastIndex(pattern[:i], "/") + dir, pattern = pattern[:j], pattern[j+1:] + } + dir = filepath.Join(cwd, dir) + if pattern == "" { + return func(p *Package) bool { return p.Dir == dir } + } + matchPath := pkgpattern.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) + if err != nil { + // Cannot make relative - e.g. different drive letters on Windows. + return false + } + rel = filepath.ToSlash(rel) + if rel == ".." || strings.HasPrefix(rel, "../") { + return false + } + return matchPath(rel) + } + case pattern == "all": + return func(p *Package) bool { return true } + case pattern == "std": + return func(p *Package) bool { return p.Standard } + case pattern == "cmd": + return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") } + default: + matchPath := pkgpattern.MatchPattern(pattern) + return func(p *Package) bool { return matchPath(p.ImportPath) } + } +} diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go new file mode 100644 index 0000000..0c20a23 --- /dev/null +++ b/src/cmd/go/internal/load/test.go @@ -0,0 +1,921 @@ +// 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 load + +import ( + "bytes" + "context" + "errors" + "fmt" + "go/ast" + "go/build" + "go/doc" + "go/parser" + "go/token" + "internal/lazytemplate" + "path/filepath" + "sort" + "strings" + "unicode" + "unicode/utf8" + + "cmd/go/internal/cfg" + "cmd/go/internal/fsys" + "cmd/go/internal/str" + "cmd/go/internal/trace" +) + +var TestMainDeps = []string{ + // Dependencies for testmain. + "os", + "reflect", + "testing", + "testing/internal/testdeps", +} + +type TestCover struct { + Mode string + Local bool + Pkgs []*Package + Paths []string + Vars []coverInfo +} + +// TestPackagesFor is like TestPackagesAndErrors but it returns +// an error if the test packages or their dependencies have errors. +// Only test packages without errors are returned. +func TestPackagesFor(ctx context.Context, opts PackageOpts, p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) { + pmain, ptest, pxtest = TestPackagesAndErrors(ctx, opts, p, cover) + for _, p1 := range []*Package{ptest, pxtest, pmain} { + if p1 == nil { + // pxtest may be nil + continue + } + if p1.Error != nil { + err = p1.Error + break + } + if len(p1.DepsErrors) > 0 { + perr := p1.DepsErrors[0] + err = perr + break + } + } + if pmain.Error != nil || len(pmain.DepsErrors) > 0 { + pmain = nil + } + if ptest.Error != nil || len(ptest.DepsErrors) > 0 { + ptest = nil + } + if pxtest != nil && (pxtest.Error != nil || len(pxtest.DepsErrors) > 0) { + pxtest = nil + } + return pmain, ptest, pxtest, err +} + +// TestPackagesAndErrors returns three packages: +// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest). +// - ptest, the package p compiled with added "package p" test files. +// - pxtest, the result of compiling any "package p_test" (external) test files. +// +// If the package has no "package p_test" test files, pxtest will be nil. +// If the non-test compilation of package p can be reused +// (for example, if there are no "package p" test files and +// package p need not be instrumented for coverage or any other reason), +// then the returned ptest == p. +// +// An error is returned if the testmain source cannot be completely generated +// (for example, due to a syntax error in a test file). No error will be +// returned for errors loading packages, but the Error or DepsError fields +// of the returned packages may be set. +// +// The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0, +// or else there's no point in any of this. +func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, cover *TestCover) (pmain, ptest, pxtest *Package) { + ctx, span := trace.StartSpan(ctx, "load.TestPackagesAndErrors") + defer span.Done() + + pre := newPreload() + defer pre.flush() + allImports := append([]string{}, p.TestImports...) + allImports = append(allImports, p.XTestImports...) + pre.preloadImports(ctx, opts, allImports, p.Internal.Build) + + var ptestErr, pxtestErr *PackageError + var imports, ximports []*Package + var stk ImportStack + var testEmbed, xtestEmbed map[string][]string + stk.Push(p.ImportPath + " (test)") + rawTestImports := str.StringList(p.TestImports) + for i, path := range p.TestImports { + p1 := loadImport(ctx, opts, pre, path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport) + if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath { + // Same error that loadPackage returns (via reusePackage) in pkg.go. + // Can't change that code, because that code is only for loading the + // non-test copy of a package. + ptestErr = &PackageError{ + ImportStack: importCycleStack(p1, p.ImportPath), + Err: errors.New("import cycle not allowed in test"), + IsImportCycle: true, + } + } + p.TestImports[i] = p1.ImportPath + imports = append(imports, p1) + } + var err error + p.TestEmbedFiles, testEmbed, err = resolveEmbed(p.Dir, p.TestEmbedPatterns) + if err != nil && ptestErr == nil { + ptestErr = &PackageError{ + ImportStack: stk.Copy(), + Err: err, + } + embedErr := err.(*EmbedError) + ptestErr.setPos(p.Internal.Build.TestEmbedPatternPos[embedErr.Pattern]) + } + stk.Pop() + + stk.Push(p.ImportPath + "_test") + pxtestNeedsPtest := false + rawXTestImports := str.StringList(p.XTestImports) + for i, path := range p.XTestImports { + p1 := loadImport(ctx, opts, pre, path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport) + if p1.ImportPath == p.ImportPath { + pxtestNeedsPtest = true + } else { + ximports = append(ximports, p1) + } + p.XTestImports[i] = p1.ImportPath + } + p.XTestEmbedFiles, xtestEmbed, err = resolveEmbed(p.Dir, p.XTestEmbedPatterns) + if err != nil && pxtestErr == nil { + pxtestErr = &PackageError{ + ImportStack: stk.Copy(), + Err: err, + } + embedErr := err.(*EmbedError) + pxtestErr.setPos(p.Internal.Build.XTestEmbedPatternPos[embedErr.Pattern]) + } + stk.Pop() + + // Test package. + if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local { + ptest = new(Package) + *ptest = *p + ptest.Error = ptestErr + ptest.ForTest = p.ImportPath + ptest.GoFiles = nil + ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) + ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) + ptest.Target = "" + // Note: The preparation of the vet config requires that common + // indexes in ptest.Imports and ptest.Internal.RawImports + // all line up (but RawImports can be shorter than the others). + // That is, for 0 ≤ i < len(RawImports), + // RawImports[i] is the import string in the program text, and + // Imports[i] is the expanded import string (vendoring applied or relative path expanded away). + // Any implicitly added imports appear in Imports and Internal.Imports + // but not RawImports (because they were not in the source code). + // We insert TestImports, imports, and rawTestImports at the start of + // these lists to preserve the alignment. + // Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports, + // but we insert at the beginning there too just for consistency. + ptest.Imports = str.StringList(p.TestImports, p.Imports) + ptest.Internal.Imports = append(imports, p.Internal.Imports...) + ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports) + ptest.Internal.ForceLibrary = true + ptest.Internal.BuildInfo = "" + ptest.Internal.Build = new(build.Package) + *ptest.Internal.Build = *p.Internal.Build + m := map[string][]token.Position{} + for k, v := range p.Internal.Build.ImportPos { + m[k] = append(m[k], v...) + } + for k, v := range p.Internal.Build.TestImportPos { + m[k] = append(m[k], v...) + } + ptest.Internal.Build.ImportPos = m + if testEmbed == nil && len(p.Internal.Embed) > 0 { + testEmbed = map[string][]string{} + } + for k, v := range p.Internal.Embed { + testEmbed[k] = v + } + ptest.Internal.Embed = testEmbed + ptest.EmbedFiles = str.StringList(p.EmbedFiles, p.TestEmbedFiles) + ptest.Internal.OrigImportPath = p.Internal.OrigImportPath + ptest.collectDeps() + } else { + ptest = p + } + + // External test package. + if len(p.XTestGoFiles) > 0 { + pxtest = &Package{ + PackagePublic: PackagePublic{ + Name: p.Name + "_test", + ImportPath: p.ImportPath + "_test", + Root: p.Root, + Dir: p.Dir, + Goroot: p.Goroot, + GoFiles: p.XTestGoFiles, + Imports: p.XTestImports, + ForTest: p.ImportPath, + Module: p.Module, + Error: pxtestErr, + EmbedFiles: p.XTestEmbedFiles, + }, + Internal: PackageInternal{ + LocalPrefix: p.Internal.LocalPrefix, + Build: &build.Package{ + ImportPos: p.Internal.Build.XTestImportPos, + }, + Imports: ximports, + RawImports: rawXTestImports, + + Asmflags: p.Internal.Asmflags, + Gcflags: p.Internal.Gcflags, + Ldflags: p.Internal.Ldflags, + Gccgoflags: p.Internal.Gccgoflags, + Embed: xtestEmbed, + OrigImportPath: p.Internal.OrigImportPath, + }, + } + if pxtestNeedsPtest { + pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest) + } + pxtest.collectDeps() + } + + // Build main package. + pmain = &Package{ + PackagePublic: PackagePublic{ + Name: "main", + Dir: p.Dir, + GoFiles: []string{"_testmain.go"}, + ImportPath: p.ImportPath + ".test", + Root: p.Root, + Imports: str.StringList(TestMainDeps), + Module: p.Module, + }, + Internal: PackageInternal{ + Build: &build.Package{Name: "main"}, + BuildInfo: p.Internal.BuildInfo, + Asmflags: p.Internal.Asmflags, + Gcflags: p.Internal.Gcflags, + Ldflags: p.Internal.Ldflags, + Gccgoflags: p.Internal.Gccgoflags, + OrigImportPath: p.Internal.OrigImportPath, + }, + } + + // The generated main also imports testing, regexp, and os. + // Also the linker introduces implicit dependencies reported by LinkerDeps. + stk.Push("testmain") + deps := TestMainDeps // cap==len, so safe for append + for _, d := range LinkerDeps(p) { + deps = append(deps, d) + } + for _, dep := range deps { + if dep == ptest.ImportPath { + pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) + } else { + p1 := loadImport(ctx, opts, pre, dep, "", nil, &stk, nil, 0) + pmain.Internal.Imports = append(pmain.Internal.Imports, p1) + } + } + stk.Pop() + + if cover != nil && cover.Pkgs != nil && !cfg.Experiment.CoverageRedesign { + // Add imports, but avoid duplicates. + seen := map[*Package]bool{p: true, ptest: true} + for _, p1 := range pmain.Internal.Imports { + seen[p1] = true + } + for _, p1 := range cover.Pkgs { + if seen[p1] { + // Don't add duplicate imports. + continue + } + seen[p1] = true + pmain.Internal.Imports = append(pmain.Internal.Imports, p1) + } + } + + allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports)) + allTestImports = append(allTestImports, pmain.Internal.Imports...) + allTestImports = append(allTestImports, imports...) + allTestImports = append(allTestImports, ximports...) + setToolFlags(allTestImports...) + + // Do initial scan for metadata needed for writing _testmain.go + // Use that metadata to update the list of imports for package main. + // The list of imports is used by recompileForTest and by the loop + // afterward that gathers t.Cover information. + t, err := loadTestFuncs(ptest) + if err != nil && pmain.Error == nil { + pmain.setLoadPackageDataError(err, p.ImportPath, &stk, nil) + } + t.Cover = cover + if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { + pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) + pmain.Imports = append(pmain.Imports, ptest.ImportPath) + t.ImportTest = true + } + if pxtest != nil { + pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest) + pmain.Imports = append(pmain.Imports, pxtest.ImportPath) + t.ImportXtest = true + } + pmain.collectDeps() + + // Sort and dedup pmain.Imports. + // Only matters for go list -test output. + sort.Strings(pmain.Imports) + w := 0 + for _, path := range pmain.Imports { + if w == 0 || path != pmain.Imports[w-1] { + pmain.Imports[w] = path + w++ + } + } + pmain.Imports = pmain.Imports[:w] + pmain.Internal.RawImports = str.StringList(pmain.Imports) + + // Replace pmain's transitive dependencies with test copies, as necessary. + recompileForTest(pmain, p, ptest, pxtest) + + if cover != nil { + if cfg.Experiment.CoverageRedesign { + // Here ptest needs to inherit the proper coverage mode (since + // it contains p's Go files), whereas pmain contains only + // test harness code (don't want to instrument it, and + // we don't want coverage hooks in the pkg init). + ptest.Internal.CoverMode = p.Internal.CoverMode + pmain.Internal.CoverMode = "testmain" + } + // Should we apply coverage analysis locally, only for this + // package and only for this test? Yes, if -cover is on but + // -coverpkg has not specified a list of packages for global + // coverage. + if cover.Local { + ptest.Internal.CoverMode = cover.Mode + + if !cfg.Experiment.CoverageRedesign { + var coverFiles []string + coverFiles = append(coverFiles, ptest.GoFiles...) + coverFiles = append(coverFiles, ptest.CgoFiles...) + ptest.Internal.CoverVars = DeclareCoverVars(ptest, coverFiles...) + } + } + + if !cfg.Experiment.CoverageRedesign { + for _, cp := range pmain.Internal.Imports { + if len(cp.Internal.CoverVars) > 0 { + t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars}) + } + } + } + } + + data, err := formatTestmain(t) + if err != nil && pmain.Error == nil { + pmain.Error = &PackageError{Err: err} + } + // Set TestmainGo even if it is empty: the presence of a TestmainGo + // indicates that this package is, in fact, a test main. + pmain.Internal.TestmainGo = &data + + return pmain, ptest, pxtest +} + +// importCycleStack returns an import stack from p to the package whose import +// path is target. +func importCycleStack(p *Package, target string) []string { + // importerOf maps each import path to its importer nearest to p. + importerOf := map[string]string{p.ImportPath: ""} + + // q is a breadth-first queue of packages to search for target. + // Every package added to q has a corresponding entry in pathTo. + // + // We search breadth-first for two reasons: + // + // 1. We want to report the shortest cycle. + // + // 2. If p contains multiple cycles, the first cycle we encounter might not + // contain target. To ensure termination, we have to break all cycles + // other than the first. + q := []*Package{p} + + for len(q) > 0 { + p := q[0] + q = q[1:] + if path := p.ImportPath; path == target { + var stk []string + for path != "" { + stk = append(stk, path) + path = importerOf[path] + } + return stk + } + for _, dep := range p.Internal.Imports { + if _, ok := importerOf[dep.ImportPath]; !ok { + importerOf[dep.ImportPath] = p.ImportPath + q = append(q, dep) + } + } + } + + panic("lost path to cycle") +} + +// recompileForTest copies and replaces certain packages in pmain's dependency +// graph. This is necessary for two reasons. First, if ptest is different than +// preal, packages that import the package under test should get ptest instead +// of preal. This is particularly important if pxtest depends on functionality +// exposed in test sources in ptest. Second, if there is a main package +// (other than pmain) anywhere, we need to set p.Internal.ForceLibrary and +// clear p.Internal.BuildInfo in the test copy to prevent link conflicts. +// This may happen if both -coverpkg and the command line patterns include +// multiple main packages. +func recompileForTest(pmain, preal, ptest, pxtest *Package) { + // The "test copy" of preal is ptest. + // For each package that depends on preal, make a "test copy" + // that depends on ptest. And so on, up the dependency tree. + testCopy := map[*Package]*Package{preal: ptest} + for _, p := range PackageList([]*Package{pmain}) { + if p == preal { + continue + } + // Copy on write. + didSplit := p == pmain || p == pxtest + split := func() { + if didSplit { + return + } + didSplit = true + if testCopy[p] != nil { + panic("recompileForTest loop") + } + p1 := new(Package) + testCopy[p] = p1 + *p1 = *p + p1.ForTest = preal.ImportPath + p1.Internal.Imports = make([]*Package, len(p.Internal.Imports)) + copy(p1.Internal.Imports, p.Internal.Imports) + p1.Imports = make([]string, len(p.Imports)) + copy(p1.Imports, p.Imports) + p = p1 + p.Target = "" + p.Internal.BuildInfo = "" + p.Internal.ForceLibrary = true + } + + // Update p.Internal.Imports to use test copies. + for i, imp := range p.Internal.Imports { + if p1 := testCopy[imp]; p1 != nil && p1 != imp { + split() + p.Internal.Imports[i] = p1 + } + } + + // Force main packages the test imports to be built as libraries. + // Normal imports of main packages are forbidden by the package loader, + // but this can still happen if -coverpkg patterns include main packages: + // covered packages are imported by pmain. Linking multiple packages + // compiled with '-p main' causes duplicate symbol errors. + // See golang.org/issue/30907, golang.org/issue/34114. + if p.Name == "main" && p != pmain && p != ptest { + split() + } + } +} + +// isTestFunc tells whether fn has the type of a testing function. arg +// specifies the parameter type we look for: B, M or T. +func isTestFunc(fn *ast.FuncDecl, arg string) bool { + if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params.List == nil || + len(fn.Type.Params.List) != 1 || + len(fn.Type.Params.List[0].Names) > 1 { + return false + } + ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) + if !ok { + return false + } + // We can't easily check that the type is *testing.M + // because we don't know how testing has been imported, + // but at least check that it's *M or *something.M. + // Same applies for B and T. + if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { + return true + } + if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { + return true + } + return false +} + +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +type coverInfo struct { + Package *Package + Vars map[string]*CoverVar +} + +// loadTestFuncs returns the testFuncs describing the tests that will be run. +// The returned testFuncs is always non-nil, even if an error occurred while +// processing test files. +func loadTestFuncs(ptest *Package) (*testFuncs, error) { + t := &testFuncs{ + Package: ptest, + } + var err error + for _, file := range ptest.TestGoFiles { + if lerr := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); lerr != nil && err == nil { + err = lerr + } + } + for _, file := range ptest.XTestGoFiles { + if lerr := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); lerr != nil && err == nil { + err = lerr + } + } + return t, err +} + +// formatTestmain returns the content of the _testmain.go file for t. +func formatTestmain(t *testFuncs) ([]byte, error) { + var buf bytes.Buffer + tmpl := testmainTmpl + if cfg.Experiment.CoverageRedesign { + tmpl = testmainTmplNewCoverage + } + if err := tmpl.Execute(&buf, t); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +type testFuncs struct { + Tests []testFunc + Benchmarks []testFunc + FuzzTargets []testFunc + Examples []testFunc + TestMain *testFunc + Package *Package + ImportTest bool + NeedTest bool + ImportXtest bool + NeedXtest bool + Cover *TestCover +} + +// ImportPath returns the import path of the package being tested, if it is within GOPATH. +// This is printed by the testing package when running benchmarks. +func (t *testFuncs) ImportPath() string { + pkg := t.Package.ImportPath + if strings.HasPrefix(pkg, "_/") { + return "" + } + if pkg == "command-line-arguments" { + return "" + } + return pkg +} + +// Covered returns a string describing which packages are being tested for coverage. +// If the covered package is the same as the tested package, it returns the empty string. +// Otherwise it is a comma-separated human-readable list of packages beginning with +// " in", ready for use in the coverage message. +func (t *testFuncs) Covered() string { + if t.Cover == nil || t.Cover.Paths == nil { + return "" + } + return " in " + strings.Join(t.Cover.Paths, ", ") +} + +// Tested returns the name of the package being tested. +func (t *testFuncs) Tested() string { + return t.Package.Name +} + +type testFunc struct { + Package string // imported package name (_test or _xtest) + Name string // function name + Output string // output, for examples + Unordered bool // output is allowed to be unordered. +} + +var testFileSet = token.NewFileSet() + +func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { + // Pass in the overlaid source if we have an overlay for this file. + src, err := fsys.Open(filename) + if err != nil { + return err + } + defer src.Close() + f, err := parser.ParseFile(testFileSet, filename, src, parser.ParseComments|parser.SkipObjectResolution) + if err != nil { + return err + } + for _, d := range f.Decls { + n, ok := d.(*ast.FuncDecl) + if !ok { + continue + } + if n.Recv != nil { + continue + } + name := n.Name.String() + switch { + case name == "TestMain": + if isTestFunc(n, "T") { + t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + continue + } + err := checkTestFunc(n, "M") + if err != nil { + return err + } + if t.TestMain != nil { + return errors.New("multiple definitions of TestMain") + } + t.TestMain = &testFunc{pkg, name, "", false} + *doImport, *seen = true, true + case isTest(name, "Test"): + err := checkTestFunc(n, "T") + if err != nil { + return err + } + t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + case isTest(name, "Benchmark"): + err := checkTestFunc(n, "B") + if err != nil { + return err + } + t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + case isTest(name, "Fuzz"): + err := checkTestFunc(n, "F") + if err != nil { + return err + } + t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + } + } + ex := doc.Examples(f) + sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) + for _, e := range ex { + *doImport = true // import test file whether executed or not + if e.Output == "" && !e.EmptyOutput { + // Don't run examples with no output. + continue + } + t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) + *seen = true + } + return nil +} + +func checkTestFunc(fn *ast.FuncDecl, arg string) error { + var why string + if !isTestFunc(fn, arg) { + why = fmt.Sprintf("must be: func %s(%s *testing.%s)", fn.Name.String(), strings.ToLower(arg), arg) + } + if fn.Type.TypeParams.NumFields() > 0 { + why = "test functions cannot have type parameters" + } + if why != "" { + pos := testFileSet.Position(fn.Pos()) + return fmt.Errorf("%s: wrong signature for %s, %s", pos, fn.Name.String(), why) + } + return nil +} + +var testmainTmpl = lazytemplate.New("main", ` +// Code generated by 'go test'. DO NOT EDIT. + +package main + +import ( + "os" +{{if .TestMain}} + "reflect" +{{end}} + "testing" + "testing/internal/testdeps" + +{{if .ImportTest}} + {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} +{{end}} +{{if .ImportXtest}} + {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} +{{end}} +{{if .Cover}} +{{range $i, $p := .Cover.Vars}} + _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} +{{end}} +{{end}} +) + +var tests = []testing.InternalTest{ +{{range .Tests}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var benchmarks = []testing.InternalBenchmark{ +{{range .Benchmarks}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var fuzzTargets = []testing.InternalFuzzTarget{ +{{range .FuzzTargets}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var examples = []testing.InternalExample{ +{{range .Examples}} + {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, +{{end}} +} + +func init() { + testdeps.ImportPath = {{.ImportPath | printf "%q"}} +} + +{{if .Cover}} + +// Only updated by init functions, so no need for atomicity. +var ( + coverCounters = make(map[string][]uint32) + coverBlocks = make(map[string][]testing.CoverBlock) +) + +func init() { + {{range $i, $p := .Cover.Vars}} + {{range $file, $cover := $p.Vars}} + coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) + {{end}} + {{end}} +} + +func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { + if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { + panic("coverage: mismatched sizes") + } + if coverCounters[fileName] != nil { + // Already registered. + return + } + coverCounters[fileName] = counter + block := make([]testing.CoverBlock, len(counter)) + for i := range counter { + block[i] = testing.CoverBlock{ + Line0: pos[3*i+0], + Col0: uint16(pos[3*i+2]), + Line1: pos[3*i+1], + Col1: uint16(pos[3*i+2]>>16), + Stmts: numStmts[i], + } + } + coverBlocks[fileName] = block +} +{{end}} + +func main() { +{{if .Cover}} + testing.RegisterCover(testing.Cover{ + Mode: {{printf "%q" .Cover.Mode}}, + Counters: coverCounters, + Blocks: coverBlocks, + CoveredPackages: {{printf "%q" .Covered}}, + }) +{{end}} + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) +{{with .TestMain}} + {{.Package}}.{{.Name}}(m) + os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) +{{else}} + os.Exit(m.Run()) +{{end}} +} + +`) + +var testmainTmplNewCoverage = lazytemplate.New("main", ` +// Code generated by 'go test'. DO NOT EDIT. + +package main + +import ( + "os" +{{if .Cover}} + _ "unsafe" +{{end}} +{{if .TestMain}} + "reflect" +{{end}} + "testing" + "testing/internal/testdeps" + +{{if .ImportTest}} + {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} +{{end}} +{{if .ImportXtest}} + {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} +{{end}} +) + +var tests = []testing.InternalTest{ +{{range .Tests}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var benchmarks = []testing.InternalBenchmark{ +{{range .Benchmarks}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var fuzzTargets = []testing.InternalFuzzTarget{ +{{range .FuzzTargets}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var examples = []testing.InternalExample{ +{{range .Examples}} + {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, +{{end}} +} + +func init() { + testdeps.ImportPath = {{.ImportPath | printf "%q"}} +} + +{{if .Cover}} + +//go:linkname runtime_coverage_processCoverTestDir runtime/coverage.processCoverTestDir +func runtime_coverage_processCoverTestDir(dir string, cfile string, cmode string, cpkgs string) error + +//go:linkname testing_registerCover2 testing.registerCover2 +func testing_registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error)) + +//go:linkname runtime_coverage_markProfileEmitted runtime/coverage.markProfileEmitted +func runtime_coverage_markProfileEmitted(val bool) + +func coverTearDown(coverprofile string, gocoverdir string) (string, error) { + var err error + if gocoverdir == "" { + gocoverdir, err = os.MkdirTemp("", "gocoverdir") + if err != nil { + return "error setting GOCOVERDIR: bad os.MkdirTemp return", err + } + defer os.RemoveAll(gocoverdir) + } + runtime_coverage_markProfileEmitted(true) + cmode := {{printf "%q" .Cover.Mode}} + if err := runtime_coverage_processCoverTestDir(gocoverdir, coverprofile, cmode, {{printf "%q" .Covered}}); err != nil { + return "error generating coverage report", err + } + return "", nil +} +{{end}} + +func main() { +{{if .Cover}} + testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown) +{{end}} + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) +{{with .TestMain}} + {{.Package}}.{{.Name}}(m) + os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) +{{else}} + os.Exit(m.Run()) +{{end}} +} + +`) |