cmd/go: index standard library packages

Change-Id: I07594303a1e9833723522d5ff94577a5510ca6f0
Reviewed-on: https://go-review.googlesource.com/c/go/+/404714
Run-TryBot: Michael Matloob <matloob@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Michael Matloob 2022-04-29 14:45:46 -04:00
parent 9d3dbd78c7
commit 21f05284c7
4 changed files with 120 additions and 38 deletions

View file

@ -14,7 +14,6 @@ import (
"go/build"
"go/scanner"
"go/token"
"internal/goroot"
"io/fs"
"os"
"os/exec"
@ -3072,7 +3071,7 @@ func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args
return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg)
case path.Clean(p) != p:
return nil, fmt.Errorf("%s: argument must be a clean package path", arg)
case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p):
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

View file

@ -1,7 +1,32 @@
// Copyright 2022 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 modindex
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"go/build"
"go/build/constraint"
"go/token"
"internal/goroot"
"internal/unsafeheader"
"io/fs"
"math"
"os"
"path"
"path/filepath"
"runtime"
"runtime/debug"
"sort"
"strconv"
"strings"
"sync"
"unsafe"
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
@ -9,23 +34,6 @@ import (
"cmd/go/internal/imports"
"cmd/go/internal/par"
"cmd/go/internal/str"
"encoding/binary"
"errors"
"fmt"
"go/build"
"go/build/constraint"
"go/token"
"internal/unsafeheader"
"io/fs"
"math"
"os"
"path/filepath"
"runtime/debug"
"sort"
"strconv"
"strings"
"sync"
"unsafe"
)
// enabled is used to flag off the behavior of the module index on tip.
@ -48,8 +56,8 @@ var fcache par.Cache
func moduleHash(modroot string, ismodcache bool) (cache.ActionID, error) {
h := cache.NewHash("moduleIndex")
fmt.Fprintf(h, "module index %s %v", indexVersion, modroot)
if ismodcache {
fmt.Fprintf(h, "module index %s %s %v\n", runtime.Version(), indexVersion, modroot)
if ismodcache || str.HasFilePathPrefix(modroot, cfg.GOROOT) {
return h.Sum(), nil
}
// walkdir happens in deterministic order.
@ -97,10 +105,6 @@ func Get(modroot string) (*ModuleIndex, error) {
if modroot == "" {
panic("modindex.Get called with empty modroot")
}
if str.HasFilePathPrefix(modroot, cfg.GOROOT) {
// TODO(matloob): add a case for stdlib here.
return nil, ErrNotIndexed
}
isModCache := str.HasFilePathPrefix(modroot, cfg.GOMODCACHE)
return openIndex(modroot, isModCache)
}
@ -225,9 +229,6 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im
p.ImportPath = "."
p.Dir = filepath.Join(mi.modroot, rp.dir)
if rp.error != "" {
return p, errors.New(rp.error)
}
var pkgerr error
switch ctxt.Compiler {
@ -241,6 +242,62 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im
return p, fmt.Errorf("import %q: import of unknown directory", p.Dir)
}
// goroot
inTestdata := func(sub string) bool {
return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || str.HasPathPrefix(sub, "testdata")
}
if ctxt.GOROOT != "" && str.HasFilePathPrefix(mi.modroot, cfg.GOROOTsrc) && !inTestdata(relpath) {
modprefix := str.TrimFilePathPrefix(mi.modroot, cfg.GOROOTsrc)
p.Goroot = true
p.ImportPath = relpath
if modprefix != "" {
p.ImportPath = filepath.Join(modprefix, p.ImportPath)
}
// In build.go, p.Root should only be set in the non-local-import case, or in
// GOROOT or GOPATH. Since module mode only calls Import with path set to "."
// and the module index doesn't apply outside modules, the GOROOT case is
// the only case where GOROOT needs to be set.
// TODO(#37015): p.Root actually might be set in the local-import case outside
// GOROOT, if the directory is contained in GOPATH/src, even in module
// mode, but that's a bug.
p.Root = ctxt.GOROOT
// Set GOROOT-specific fields
// The fields set below (SrcRoot, PkgRoot, BinDir, PkgTargetRoot, and PkgObj)
// are only set in build.Import if p.Root != "". As noted in the comment
// on setting p.Root above, p.Root should only be set in the GOROOT case for the
// set of packages we care about.
var pkgtargetroot string
var pkga string
suffix := ""
if ctxt.InstallSuffix != "" {
suffix = "_" + ctxt.InstallSuffix
}
switch ctxt.Compiler {
case "gccgo":
pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
dir, elem := path.Split(p.ImportPath)
pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
case "gc":
pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
}
p.SrcRoot = ctxt.joinPath(p.Root, "src")
p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
p.BinDir = ctxt.joinPath(p.Root, "bin")
if pkga != "" {
p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
p.PkgObj = ctxt.joinPath(p.Root, pkga)
}
}
if rp.error != nil {
if errors.Is(rp.error, errCannotFindPackage) && ctxt.Compiler == "gccgo" && p.Goroot {
return p, nil
}
return p, rp.error
}
if mode&build.FindOnly != 0 {
return p, pkgerr
}
@ -444,8 +501,31 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im
return p, pkgerr
}
// IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the
// RawPackage.
// IsStandardPackage reports whether path is a standard package
// for the goroot and compiler using the module index if possible,
// and otherwise falling back to internal/goroot.IsStandardPackage
func IsStandardPackage(goroot_, compiler, path string) bool {
if !enabled || compiler != "gc" {
return goroot.IsStandardPackage(goroot_, compiler, path)
}
reldir := filepath.FromSlash(path) // relative dir path in module index for package
modroot := filepath.Join(goroot_, "src")
if str.HasFilePathPrefix(reldir, "cmd") {
reldir = str.TrimFilePathPrefix(reldir, "cmd")
modroot = filepath.Join(modroot, "cmd")
}
mod, err := Get(modroot)
if err != nil {
return goroot.IsStandardPackage(goroot_, compiler, path)
}
pkgs := mod.Packages()
i := sort.SearchStrings(pkgs, reldir)
return i != len(pkgs) && pkgs[i] == reldir
}
// IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the index.
func (mi *ModuleIndex) IsDirWithGoFiles(relpath string) (_ bool, err error) {
rp := mi.indexPackage(relpath)
@ -462,7 +542,7 @@ func (mi *ModuleIndex) IsDirWithGoFiles(relpath string) (_ bool, err error) {
return false, nil
}
// ScanDir implements imports.ScanDir using the information in the RawPackage.
// ScanDir implements imports.ScanDir using the information in the index.
func (mi *ModuleIndex) ScanDir(path string, tags map[string]bool) (sortedImports []string, sortedTestImports []string, err error) {
rp := mi.indexPackage(path)
@ -556,13 +636,15 @@ func shouldBuild(sf *sourceFile, tags map[string]bool) bool {
// index package holds the information needed to access information in the
// index about a package.
type indexPackage struct {
error string
error error
dir string // directory of the package relative to the modroot
// Source files
sourceFiles []*sourceFile
}
var errCannotFindPackage = errors.New("cannot find package")
// indexPackage returns an indexPackage constructed using the information in the ModuleIndex.
func (mi *ModuleIndex) indexPackage(path string) *indexPackage {
defer func() {
@ -572,13 +654,15 @@ func (mi *ModuleIndex) indexPackage(path string) *indexPackage {
}()
offset, ok := mi.packages[path]
if !ok {
return &indexPackage{error: fmt.Sprintf("cannot find package %q in:\n\t%s", path, filepath.Join(mi.modroot, path))}
return &indexPackage{error: fmt.Errorf("%w %q in:\n\t%s", errCannotFindPackage, path, filepath.Join(mi.modroot, path))}
}
// TODO(matloob): do we want to lock on the module index?
d := mi.od.decoderAt(offset)
rp := new(indexPackage)
rp.error = d.string()
if errstr := d.string(); errstr != "" {
rp.error = errors.New(errstr)
}
rp.dir = d.string()
numSourceFiles := d.uint32()
rp.sourceFiles = make([]*sourceFile, numSourceFiles)

View file

@ -9,7 +9,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"internal/goroot"
"io/fs"
"os"
"path/filepath"
@ -18,6 +17,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modindex"
"cmd/go/internal/modinfo"
"cmd/go/internal/search"
@ -39,7 +39,7 @@ func findStandardImportPath(path string) string {
panic("findStandardImportPath called with empty path")
}
if search.IsStandardImportPath(path) {
if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
if modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
return filepath.Join(cfg.GOROOT, "src", path)
}
}

View file

@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"go/build"
"internal/goroot"
"io/fs"
"os"
pathpkg "path"
@ -281,7 +280,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
// Is the package in the standard library?
pathIsStd := search.IsStandardImportPath(path)
if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
if pathIsStd && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
for _, mainModule := range MainModules.Versions() {
if MainModules.InGorootSrc(mainModule) {
if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil {