diff options
Diffstat (limited to 'libgo/go/cmd/go/internal/load/pkg.go')
-rw-r--r-- | libgo/go/cmd/go/internal/load/pkg.go | 684 |
1 files changed, 405 insertions, 279 deletions
diff --git a/libgo/go/cmd/go/internal/load/pkg.go b/libgo/go/cmd/go/internal/load/pkg.go index dfb1ff63afe..0579fd5ca53 100644 --- a/libgo/go/cmd/go/internal/load/pkg.go +++ b/libgo/go/cmd/go/internal/load/pkg.go @@ -22,9 +22,26 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" + "cmd/go/internal/modinfo" + "cmd/go/internal/search" "cmd/go/internal/str" ) +var ( + // module initialization hook; never nil, no-op if module use is disabled + ModInit func() + + // module hooks; nil if module use is disabled + ModBinDir func() string // return effective bin directory + ModLookup func(path string) (dir, realPath string, err error) // lookup effective meaning of import + ModPackageModuleInfo func(path string) *modinfo.ModulePublic // return module info for Package struct + ModImportPaths func(args []string) []*search.Match // expand import paths + ModPackageBuildInfo func(main string, deps []string) string // return module info to embed in binary + ModInfoProg func(info string) []byte // wrap module info in .go code for binary + ModImportFromFiles func([]string) // update go.mod to add modules for imports in these files + ModDirImportPath func(string) string // return effective import path for directory +) + var IgnoreImports bool // control whether we ignore imports in packages // A Package describes a single package found in a directory. @@ -37,18 +54,24 @@ 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) - 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? - Root string `json:",omitempty"` // Go root or Go path dir containing this package - ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory - BinaryOnly bool `json:",omitempty"` // package cannot be recompiled + 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 or Go path 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) + 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. @@ -59,18 +82,19 @@ type PackagePublic struct { // 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 sources files that import "C" - IgnoredGoFiles []string `json:",omitempty"` // .go sources 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 + 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 + 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 // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler @@ -81,11 +105,12 @@ type PackagePublic struct { CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names // Dependency information - Imports []string `json:",omitempty"` // import paths used by this package - Deps []string `json:",omitempty"` // all (recursively) imported dependencies + 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 bool `json:",omitempty"` // was there an error loading this package or dependencies? + // 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 @@ -107,6 +132,7 @@ func (p *Package) AllFiles() []string { return str.StringList( p.GoFiles, p.CgoFiles, + // no p.CompiledGoFiles, because they are from GoFiles or generated by us p.IgnoredGoFiles, p.CFiles, p.CXXFiles, @@ -122,21 +148,33 @@ func (p *Package) AllFiles() []string { ) } +// 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 +} + type PackageInternal struct { // Unexported fields are not part of the public API. - Build *build.Package - Imports []*Package // this package's direct imports - RawImports []string // this package's original imports as they appear in the text of the program - 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 - Local bool // imported via local path (./ or ../) - LocalPrefix string // interpret ./ and ../ imports relative to this prefix - ExeName string // desired name for temporary executable - CoverMode string // preprocess Go source files with the coverage tool in this mode - CoverVars map[string]*CoverVar // variables created by coverage analysis - OmitDebug bool // tell linker not to write debug information - GobinSubdir bool // install target would be subdir of GOBIN + Build *build.Package + Imports []*Package // this package's direct imports + CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library) + RawImports []string // this package's original imports as they appear in the text of the program + 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 + CoverMode string // preprocess Go source files with the coverage tool in this mode + CoverVars map[string]*CoverVar // variables created by coverage analysis + 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 Asmflags []string // -asmflags for this package Gcflags []string // -gcflags for this package @@ -224,7 +262,7 @@ func (p *Package) copyBuild(pp *build.Package) { // TODO? Target p.Goroot = pp.Goroot - p.Standard = p.Goroot && p.ImportPath != "" && isStandardImportPath(p.ImportPath) + p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath) p.GoFiles = pp.GoFiles p.CgoFiles = pp.CgoFiles p.IgnoredGoFiles = pp.IgnoredGoFiles @@ -253,24 +291,12 @@ func (p *Package) copyBuild(pp *build.Package) { p.XTestImports = pp.XTestImports if IgnoreImports { p.Imports = nil + p.Internal.RawImports = nil p.TestImports = nil p.XTestImports = nil } } -// isStandardImportPath reports whether $GOROOT/src/path should be considered -// part of the standard distribution. For historical reasons we allow people to add -// their own code to $GOROOT instead of using $GOPATH, but we assume that -// code will start with a domain name (dot in the first element). -func isStandardImportPath(path string) bool { - i := strings.Index(path, "/") - if i < 0 { - i = len(path) - } - elem := path[:i] - return !strings.Contains(elem, ".") -} - // A PackageError describes an error loading information about a package. type PackageError struct { ImportStack []string // shortest path from package named on command line to this one @@ -296,7 +322,9 @@ func (p *PackageError) Error() string { return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err } -// An ImportStack is a stack of import paths. +// 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) { @@ -349,15 +377,17 @@ func ClearPackageCachePartial(args []string) { } } -// reloadPackage is like loadPackage but makes sure +// ReloadPackageNoFlags is like LoadPackageNoFlags but makes sure // not to use the package cache. -func ReloadPackage(arg string, stk *ImportStack) *Package { +// 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, p.Dir) delete(packageCache, p.ImportPath) } - return LoadPackage(arg, stk) + return LoadPackageNoFlags(arg, stk) } // dirToImportPath returns the pseudo-import path we use for a package @@ -406,10 +436,53 @@ const ( // 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(path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package { stk.Push(path) defer stk.Pop() + 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 &Package{ + PackagePublic: PackagePublic{ + ImportPath: path, + Error: &PackageError{ + ImportStack: stk.Copy(), + Err: fmt.Sprintf("disallowed import path %q", path), + }, + }, + } + } + + if strings.Contains(path, "@") { + var text string + if cfg.ModulesEnabled { + text = "can only use path@version syntax with 'go get'" + } else { + text = "cannot use path@version syntax in GOPATH mode" + } + return &Package{ + PackagePublic: PackagePublic{ + ImportPath: path, + Error: &PackageError{ + ImportStack: stk.Copy(), + Err: text, + }, + }, + } + } + + parentPath := "" + if parent != nil { + parentPath = parent.ImportPath + } + // Determine canonical identifier for this package. // For a local import the identifier is the pseudo-import path // we create from the full directory to the package. @@ -418,8 +491,16 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo importPath := path origPath := path isLocal := build.IsLocalImport(path) + var modDir string + var modErr error if isLocal { importPath = dirToImportPath(filepath.Join(srcDir, path)) + } else if cfg.ModulesEnabled { + var p string + modDir, p, modErr = ModLookup(path) + if modErr == nil { + importPath = p + } } 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 @@ -444,17 +525,31 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo // Load package. // Import always returns bp != nil, even if an error occurs, // in order to return partial information. - buildMode := build.ImportComment - if mode&ResolveImport == 0 || path != origPath { - // Not vendoring, or we already found the vendored path. - buildMode |= build.IgnoreVendor + var bp *build.Package + var err error + if modDir != "" { + bp, err = cfg.BuildContext.ImportDir(modDir, 0) + } else if modErr != nil { + bp = new(build.Package) + err = fmt.Errorf("unknown import path %q: %v", importPath, modErr) + } else if cfg.ModulesEnabled && path != "unsafe" { + bp = new(build.Package) + err = fmt.Errorf("unknown import path %q: internal error: module loader did not resolve import", importPath) + } else { + buildMode := build.ImportComment + if mode&ResolveImport == 0 || path != origPath { + // Not vendoring, or we already found the vendored path. + buildMode |= build.IgnoreVendor + } + bp, err = cfg.BuildContext.Import(path, srcDir, buildMode) } - bp, err := cfg.BuildContext.Import(path, srcDir, buildMode) bp.ImportPath = importPath if cfg.GOBIN != "" { bp.BinDir = cfg.GOBIN + } else if cfg.ModulesEnabled { + bp.BinDir = ModBinDir() } - if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && + if modDir == "" && err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && !strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") { err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment) } @@ -463,7 +558,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo p = setErrorPos(p, importPos) } - if origPath != cleanImport(origPath) { + if modDir == "" && origPath != cleanImport(origPath) { p.Error = &PackageError{ ImportStack: stk.Copy(), Err: fmt.Sprintf("non-canonical import path: %q should be %q", origPath, pathpkg.Clean(origPath)), @@ -473,11 +568,11 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo } // Checked on every import because the rules depend on the code doing the importing. - if perr := disallowInternal(srcDir, p, stk); perr != p { + if perr := disallowInternal(srcDir, parent, parentPath, p, stk); perr != p { return setErrorPos(perr, importPos) } if mode&ResolveImport != 0 { - if perr := disallowVendor(srcDir, origPath, p, stk); perr != p { + if perr := disallowVendor(srcDir, parent, parentPath, origPath, p, stk); perr != p { return setErrorPos(perr, importPos) } } @@ -539,8 +634,14 @@ func isDir(path string) bool { // 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 vgo legacy conversion (golang.org/issue/25069). +// Go 1.11 module legacy conversion (golang.org/issue/25069). func ResolveImportPath(parent *Package, path string) (found string) { + if cfg.ModulesEnabled { + if _, p, e := ModLookup(path); e == nil { + return p + } + return path + } found = VendoredImportPath(parent, path) if found != path { return found @@ -828,10 +929,11 @@ func reusePackage(p *Package, stk *ImportStack) *Package { return p } -// disallowInternal checks that srcDir is allowed to import 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(srcDir string, p *Package, stk *ImportStack) *Package { +func disallowInternal(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 @@ -874,23 +976,40 @@ func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package { if i > 0 { i-- // rewind over slash in ".../internal" } - 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 + 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 importerPath == "." { + // The importer is a list of command-line files. + // Pretend that the import path is the import path of the + // directory containing them. + importerPath = ModDirImportPath(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{ ImportStack: stk.Copy(), - Err: "use of internal package not allowed", + Err: "use of internal package " + p.ImportPath + " not allowed", } perr.Incomplete = true return &perr @@ -915,10 +1034,11 @@ func findInternal(path string) (index int, ok bool) { return 0, false } -// disallowVendor checks that srcDir is allowed to import p as path. +// disallowVendor checks that srcDir (containing package importerPath, if non-empty) +// 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, path string, p *Package, stk *ImportStack) *Package { +func disallowVendor(srcDir string, importer *Package, importerPath, path string, p *Package, stk *ImportStack) *Package { // The stack includes p.ImportPath. // If that's the only thing on the stack, we started // with a name given on the command line, not an @@ -927,6 +1047,20 @@ func disallowVendor(srcDir, path string, p *Package, stk *ImportStack) *Package return p } + // Modules must not import vendor packages in the standard library, + // but the usual vendor visibility check will not catch them + // because the module loader presents them with an ImportPath starting + // with "golang_org/" instead of "vendor/". + if p.Standard && !importer.Standard && strings.HasPrefix(p.ImportPath, "golang_org") { + perr := *p + perr.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: "use of vendored package " + path + " not allowed", + } + perr.Incomplete = true + return &perr + } + if perr := disallowVendorVisibility(srcDir, p, stk); perr != p { return perr } @@ -1057,26 +1191,6 @@ var foldPath = make(map[string]string) func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { p.copyBuild(bp) - // Decide whether p was listed on the command line. - // Given that load is called while processing the command line, - // you might think we could simply pass a flag down into load - // saying whether we are loading something named on the command - // line or something to satisfy an import. But the first load of a - // package named on the command line may be as a dependency - // of an earlier package named on the command line, not when we - // get to that package during command line processing. - // For example "go test fmt reflect" will load reflect as a dependency - // of fmt before it attempts to load as a command-line argument. - // Because loads are cached, the later load will be a no-op, - // so it is important that the first load can fill in CmdlinePkg correctly. - // Hence the call to an explicit matching check here. - p.Internal.CmdlinePkg = isCmdlinePkg(p) - - p.Internal.Asmflags = BuildAsmflags.For(p) - p.Internal.Gcflags = BuildGcflags.For(p) - p.Internal.Ldflags = BuildLdflags.For(p) - p.Internal.Gccgoflags = BuildGccgoflags.For(p) - // The localPrefix is the path we interpret ./ imports relative to. // Synthesized main packages sometimes override this. if p.Internal.Local { @@ -1113,11 +1227,45 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { return } _, elem := filepath.Split(p.Dir) + if cfg.ModulesEnabled { + // NOTE(rsc): Using p.ImportPath instead of p.Dir + // makes sure we install a package in the root of a + // cached module directory as that package name + // not name@v1.2.3. + // Using p.ImportPath instead of p.Dir + // is probably correct all the time, + // even for non-module-enabled code, + // but I'm not brave enough to change the + // non-module behavior this late in the + // release cycle. Maybe for Go 1.12. + // See golang.org/issue/26869. + _, elem = pathpkg.Split(p.ImportPath) + + // If this is example.com/mycmd/v2, it's more useful to install it as mycmd than as v2. + // See golang.org/issue/24667. + isVersion := func(v string) bool { + if len(v) < 2 || v[0] != 'v' || v[1] < '1' || '9' < v[1] { + return false + } + for i := 2; i < len(v); i++ { + if c := v[i]; c < '0' || '9' < c { + return false + } + } + return true + } + if isVersion(elem) { + _, elem = pathpkg.Split(pathpkg.Dir(p.ImportPath)) + } + } full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + "/" + elem if cfg.BuildContext.GOOS != base.ToolGOOS || cfg.BuildContext.GOARCH != base.ToolGOARCH { // Install cross-compiled binaries to subdirectories of bin. elem = full } + if p.Internal.Build.BinDir == "" && cfg.ModulesEnabled { + p.Internal.Build.BinDir = ModBinDir() + } if p.Internal.Build.BinDir != "" { // Install to GOBIN or bin of GOPATH entry. p.Target = filepath.Join(p.Internal.Build.BinDir, elem) @@ -1165,31 +1313,37 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { // 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) { + 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) + } } - // Cgo translation adds imports of "runtime/cgo" and "syscall", + // 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") + addImport("runtime/cgo", true) } if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { - addImport("syscall") + addImport("syscall", true) } // SWIG adds imports of some standard packages. if p.UsesSwig() { if cfg.BuildContext.Compiler != "gccgo" { - addImport("runtime/cgo") + addImport("runtime/cgo", true) } - addImport("syscall") - addImport("sync") + addImport("syscall", true) + addImport("sync", true) // TODO: The .swig and .swigcxx files can use // %go_import directives to import other packages. @@ -1198,7 +1352,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { // The linker loads implicit dependencies. if p.Name == "main" && !p.Internal.ForceLibrary { for _, dep := range LinkerDeps(p) { - addImport(dep) + addImport(dep, false) } } @@ -1368,6 +1522,13 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { setError(fmt.Sprintf("case-insensitive import collision: %q and %q", p.ImportPath, other)) return } + + if cfg.ModulesEnabled { + p.Module = ModPackageModuleInfo(p.ImportPath) + if p.Name == "main" { + p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps) + } + } } // SafeArg reports whether arg is a "safe" command-line argument, @@ -1466,7 +1627,13 @@ func (p *Package) mkAbs(list []string) []string { // 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, p.XTestGoFiles)) + 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, @@ -1492,7 +1659,7 @@ func (p *Package) UsesCgo() bool { return len(p.CgoFiles) > 0 } -// packageList returns the list of packages in the dag rooted at roots +// 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{} @@ -1514,6 +1681,42 @@ func PackageList(roots []*Package) []*Package { 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 GetTestPackageList(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(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 +} + var cmdCache = map[string]*Package{} func ClearCmdCache() { @@ -1522,11 +1725,31 @@ func ClearCmdCache() { } } +// LoadPackage loads the package named by arg. +func LoadPackage(arg string, stk *ImportStack) *Package { + p := loadPackage(arg, stk) + setToolFlags(p) + return p +} + +// LoadPackageNoFlags is like LoadPackage +// but does not guarantee that the build tool flags are set in the result. +// It is only for use by GOPATH-based "go get" +// and is only appropriate for preliminary loading of packages. +// A real load using LoadPackage or (more likely) +// Packages, PackageAndErrors, or PackagesForBuild +// must be done before passing the package to any build +// steps, so that the tool flags can be set properly. +// TODO(rsc): When GOPATH-based "go get" is removed, delete this function. +func LoadPackageNoFlags(arg string, stk *ImportStack) *Package { + return loadPackage(arg, stk) +} + // loadPackage is like loadImport but is used for command-line arguments, // not for paths found in import statements. In addition to ordinary import paths, // loadPackage accepts pseudo-paths beginning with cmd/ to denote commands // in the Go command directory, as well as paths to those directories. -func LoadPackage(arg string, stk *ImportStack) *Package { +func loadPackage(arg string, stk *ImportStack) *Package { if build.IsLocalImport(arg) { dir := arg if !filepath.IsAbs(dir) { @@ -1573,8 +1796,12 @@ func LoadPackage(arg string, stk *ImportStack) *Package { // This lets you run go test ./ioutil in package io and be // referring to io/ioutil rather than a hypothetical import of // "./ioutil". - if build.IsLocalImport(arg) { - bp, _ := cfg.BuildContext.ImportDir(filepath.Join(base.Cwd, arg), build.FindOnly) + if build.IsLocalImport(arg) || filepath.IsAbs(arg) { + dir := arg + if !filepath.IsAbs(arg) { + dir = filepath.Join(base.Cwd, arg) + } + bp, _ := cfg.BuildContext.ImportDir(dir, build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } @@ -1583,7 +1810,7 @@ func LoadPackage(arg string, stk *ImportStack) *Package { return LoadImport(arg, base.Cwd, nil, stk, nil, 0) } -// packages returns the packages named by the +// Packages returns the packages named by the // command line arguments 'args'. If a named package // cannot be loaded at all (for example, if the directory does not exist), // then packages prints an error and does not include that @@ -1603,41 +1830,68 @@ func Packages(args []string) []*Package { return pkgs } -// packagesAndErrors is like 'packages' but returns a +// PackagesAndErrors is like 'packages' but returns a // *Package for every argument, even the ones that // cannot be loaded at all. // The packages that fail to load will have p.Error != nil. -func PackagesAndErrors(args []string) []*Package { - if len(args) > 0 && strings.HasSuffix(args[0], ".go") { - return []*Package{GoFilesPackage(args)} +func PackagesAndErrors(patterns []string) []*Package { + if len(patterns) > 0 && strings.HasSuffix(patterns[0], ".go") { + return []*Package{GoFilesPackage(patterns)} } - args = ImportPaths(args) + matches := ImportPaths(patterns) var ( pkgs []*Package stk ImportStack - seenArg = make(map[string]bool) seenPkg = make(map[*Package]bool) ) - for _, arg := range args { - if seenArg[arg] { - continue - } - seenArg[arg] = true - pkg := LoadPackage(arg, &stk) - if seenPkg[pkg] { - continue + for _, m := range matches { + for _, pkg := range m.Pkgs { + p := loadPackage(pkg, &stk) + p.Match = append(p.Match, m.Pattern) + p.Internal.CmdlinePkg = true + if m.Literal { + // Note: do not set = m.Literal 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) } - seenPkg[pkg] = true - pkgs = append(pkgs, pkg) } + // 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...) + return pkgs } -// packagesForBuild is like 'packages' but fails if any of -// the packages or their dependencies have errors +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) + } +} + +func ImportPaths(args []string) []*search.Match { + if ModInit(); cfg.ModulesEnabled { + return ModImportPaths(args) + } + return search.ImportPaths(args) +} + +// PackagesForBuild is like Packages but exits +// if any of the packages or their dependencies have errors // (cannot be built). func PackagesForBuild(args []string) []*Package { pkgs := PackagesAndErrors(args) @@ -1645,6 +1899,7 @@ func PackagesForBuild(args []string) []*Package { for _, pkg := range pkgs { if pkg.Error != nil { base.Errorf("can't load package: %s", pkg.Error) + printed[pkg.Error] = true } for _, err := range pkg.DepsErrors { // Since these are errors in dependencies, @@ -1682,7 +1937,8 @@ func PackagesForBuild(args []string) []*Package { // (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(gofiles []string) *Package { - // TODO: Remove this restriction. + ModInit() + for _, f := range gofiles { if !strings.HasSuffix(f, ".go") { base.Fatalf("named files must be .go files") @@ -1720,6 +1976,10 @@ func GoFilesPackage(gofiles []string) *Package { } ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } + if cfg.ModulesEnabled { + ModImportFromFiles(gofiles) + } + var err error if dir == "" { dir = base.Cwd @@ -1730,6 +1990,11 @@ func GoFilesPackage(gofiles []string) *Package { } bp, err := ctxt.ImportDir(dir, 0) + if ModDirImportPath != nil { + // Use the effective import path of the directory + // for deciding visibility during pkg.load. + bp.ImportPath = ModDirImportPath(dir) + } pkg := new(Package) pkg.Internal.Local = true pkg.Internal.CmdlineFiles = true @@ -1739,6 +2004,7 @@ func GoFilesPackage(gofiles []string) *Package { pkg.Internal.LocalPrefix = dirToImportPath(dir) pkg.ImportPath = "command-line-arguments" pkg.Target = "" + pkg.Match = gofiles if pkg.Name == "main" { _, elem := filepath.Split(gofiles[0]) @@ -1748,152 +2014,12 @@ func GoFilesPackage(gofiles []string) *Package { } if cfg.GOBIN != "" { pkg.Target = filepath.Join(cfg.GOBIN, exe) + } else if cfg.ModulesEnabled { + pkg.Target = filepath.Join(ModBinDir(), exe) } } - return pkg -} - -// GetTestPackagesFor returns package structs ptest, the package p plus -// its test files, and pxtest, the external tests of package p. -// pxtest may be nil. If there are no test files, forceTest decides -// whether this returns a new package struct or just returns p. -func GetTestPackagesFor(p *Package, forceTest bool) (ptest, pxtest *Package, err error) { - var imports, ximports []*Package - var stk ImportStack - stk.Push(p.ImportPath + " (test)") - rawTestImports := str.StringList(p.TestImports) - for i, path := range p.TestImports { - p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport) - if p1.Error != nil { - return nil, nil, p1.Error - } - if len(p1.DepsErrors) > 0 { - err := p1.DepsErrors[0] - err.Pos = "" // show full import stack - return nil, nil, err - } - 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. - err := &PackageError{ - ImportStack: testImportStack(stk[0], p1, p.ImportPath), - Err: "import cycle not allowed in test", - IsImportCycle: true, - } - return nil, nil, err - } - p.TestImports[i] = p1.ImportPath - imports = append(imports, p1) - } - stk.Pop() - stk.Push(p.ImportPath + "_test") - pxtestNeedsPtest := false - rawXTestImports := str.StringList(p.XTestImports) - for i, path := range p.XTestImports { - p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport) - if p1.Error != nil { - return nil, nil, p1.Error - } - if len(p1.DepsErrors) > 0 { - err := p1.DepsErrors[0] - err.Pos = "" // show full import stack - return nil, nil, err - } - if p1.ImportPath == p.ImportPath { - pxtestNeedsPtest = true - } else { - ximports = append(ximports, p1) - } - p.XTestImports[i] = p1.ImportPath - } - stk.Pop() - - // Test package. - if len(p.TestGoFiles) > 0 || forceTest { - ptest = new(Package) - *ptest = *p - 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, ptest.Internal.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, - // Imports[i] is the expanded import string (vendoring applied or relative path expanded away), - // and Internal.Imports[i] is the corresponding *Package. - // 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. - 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.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 - } 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, - GoFiles: p.XTestGoFiles, - Imports: p.XTestImports, - }, - 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, - }, - } - if pxtestNeedsPtest { - pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest) - } - } - - return ptest, pxtest, nil -} + setToolFlags(pkg) -func testImportStack(top string, p *Package, target string) []string { - stk := []string{top, p.ImportPath} -Search: - for p.ImportPath != target { - for _, p1 := range p.Internal.Imports { - if p1.ImportPath == target || str.Contains(p1.Deps, target) { - stk = append(stk, p1.ImportPath) - p = p1 - continue Search - } - } - // Can't happen, but in case it does... - stk = append(stk, "<lost path to cycle>") - break - } - return stk + return pkg } |