]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: copy files from go/build into new modindex package
authorMichael Matloob <matloob@golang.org>
Tue, 3 May 2022 21:51:14 +0000 (17:51 -0400)
committerMichael Matloob <matloob@golang.org>
Thu, 12 May 2022 20:17:26 +0000 (20:17 +0000)
These files are all copied as is from the go/build package, to files
with the same name in modindex (with the exception of build_read, which
was copied from go/build/read.go).

This is being done so that the next CL can show exactly the changes that
were made against the go/build versions.

Unfortunately, git doesn't recognize these as copies, which is annoying.

Change-Id: I27b05b23dc5ccefe5252956bf75025bd57b36c66
Reviewed-on: https://go-review.googlesource.com/c/go/+/403777
Reviewed-by: Michael Matloob <matloob@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Matloob <matloob@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
src/cmd/go/internal/modindex/build.go [new file with mode: 0644]
src/cmd/go/internal/modindex/build_read.go [new file with mode: 0644]
src/cmd/go/internal/modindex/stubs.go [new file with mode: 0644]
src/cmd/go/internal/modindex/syslist.go [new file with mode: 0644]
src/cmd/go/internal/modindex/syslist_test.go [new file with mode: 0644]

diff --git a/src/cmd/go/internal/modindex/build.go b/src/cmd/go/internal/modindex/build.go
new file mode 100644 (file)
index 0000000..039b422
--- /dev/null
@@ -0,0 +1,2007 @@
+// 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 build
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+       "go/ast"
+       "go/build/constraint"
+       "go/doc"
+       "go/token"
+       "internal/buildcfg"
+       "internal/goroot"
+       "internal/goversion"
+       "io"
+       "io/fs"
+       "os"
+       "os/exec"
+       pathpkg "path"
+       "path/filepath"
+       "runtime"
+       "sort"
+       "strconv"
+       "strings"
+       "unicode"
+       "unicode/utf8"
+)
+
+// A Context specifies the supporting context for a build.
+type Context struct {
+       GOARCH string // target architecture
+       GOOS   string // target operating system
+       GOROOT string // Go root
+       GOPATH string // Go paths
+
+       // Dir is the caller's working directory, or the empty string to use
+       // the current directory of the running process. In module mode, this is used
+       // to locate the main module.
+       //
+       // If Dir is non-empty, directories passed to Import and ImportDir must
+       // be absolute.
+       Dir string
+
+       CgoEnabled  bool   // whether cgo files are included
+       UseAllFiles bool   // use files regardless of +build lines, file names
+       Compiler    string // compiler to assume when computing target paths
+
+       // The build, tool, and release tags specify build constraints
+       // that should be considered satisfied when processing +build lines.
+       // Clients creating a new context may customize BuildTags, which
+       // defaults to empty, but it is usually an error to customize ToolTags or ReleaseTags.
+       // ToolTags defaults to build tags appropriate to the current Go toolchain configuration.
+       // ReleaseTags defaults to the list of Go releases the current release is compatible with.
+       // BuildTags is not set for the Default build Context.
+       // In addition to the BuildTags, ToolTags, and ReleaseTags, build constraints
+       // consider the values of GOARCH and GOOS as satisfied tags.
+       // The last element in ReleaseTags is assumed to be the current release.
+       BuildTags   []string
+       ToolTags    []string
+       ReleaseTags []string
+
+       // The install suffix specifies a suffix to use in the name of the installation
+       // directory. By default it is empty, but custom builds that need to keep
+       // their outputs separate can set InstallSuffix to do so. For example, when
+       // using the race detector, the go command uses InstallSuffix = "race", so
+       // that on a Linux/386 system, packages are written to a directory named
+       // "linux_386_race" instead of the usual "linux_386".
+       InstallSuffix string
+
+       // By default, Import uses the operating system's file system calls
+       // to read directories and files. To read from other sources,
+       // callers can set the following functions. They all have default
+       // behaviors that use the local file system, so clients need only set
+       // the functions whose behaviors they wish to change.
+
+       // JoinPath joins the sequence of path fragments into a single path.
+       // If JoinPath is nil, Import uses filepath.Join.
+       JoinPath func(elem ...string) string
+
+       // SplitPathList splits the path list into a slice of individual paths.
+       // If SplitPathList is nil, Import uses filepath.SplitList.
+       SplitPathList func(list string) []string
+
+       // IsAbsPath reports whether path is an absolute path.
+       // If IsAbsPath is nil, Import uses filepath.IsAbs.
+       IsAbsPath func(path string) bool
+
+       // IsDir reports whether the path names a directory.
+       // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method.
+       IsDir func(path string) bool
+
+       // HasSubdir reports whether dir is lexically a subdirectory of
+       // root, perhaps multiple levels below. It does not try to check
+       // whether dir exists.
+       // If so, HasSubdir sets rel to a slash-separated path that
+       // can be joined to root to produce a path equivalent to dir.
+       // If HasSubdir is nil, Import uses an implementation built on
+       // filepath.EvalSymlinks.
+       HasSubdir func(root, dir string) (rel string, ok bool)
+
+       // ReadDir returns a slice of fs.FileInfo, sorted by Name,
+       // describing the content of the named directory.
+       // If ReadDir is nil, Import uses ioutil.ReadDir.
+       ReadDir func(dir string) ([]fs.FileInfo, error)
+
+       // OpenFile opens a file (not a directory) for reading.
+       // If OpenFile is nil, Import uses os.Open.
+       OpenFile func(path string) (io.ReadCloser, error)
+}
+
+// joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join.
+func (ctxt *Context) joinPath(elem ...string) string {
+       if f := ctxt.JoinPath; f != nil {
+               return f(elem...)
+       }
+       return filepath.Join(elem...)
+}
+
+// splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList.
+func (ctxt *Context) splitPathList(s string) []string {
+       if f := ctxt.SplitPathList; f != nil {
+               return f(s)
+       }
+       return filepath.SplitList(s)
+}
+
+// isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs.
+func (ctxt *Context) isAbsPath(path string) bool {
+       if f := ctxt.IsAbsPath; f != nil {
+               return f(path)
+       }
+       return filepath.IsAbs(path)
+}
+
+// isDir calls ctxt.IsDir (if not nil) or else uses os.Stat.
+func (ctxt *Context) isDir(path string) bool {
+       if f := ctxt.IsDir; f != nil {
+               return f(path)
+       }
+       fi, err := os.Stat(path)
+       return err == nil && fi.IsDir()
+}
+
+// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
+// the local file system to answer the question.
+func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
+       if f := ctxt.HasSubdir; f != nil {
+               return f(root, dir)
+       }
+
+       // Try using paths we received.
+       if rel, ok = hasSubdir(root, dir); ok {
+               return
+       }
+
+       // Try expanding symlinks and comparing
+       // expanded against unexpanded and
+       // expanded against expanded.
+       rootSym, _ := filepath.EvalSymlinks(root)
+       dirSym, _ := filepath.EvalSymlinks(dir)
+
+       if rel, ok = hasSubdir(rootSym, dir); ok {
+               return
+       }
+       if rel, ok = hasSubdir(root, dirSym); ok {
+               return
+       }
+       return hasSubdir(rootSym, dirSym)
+}
+
+// hasSubdir reports if dir is within root by performing lexical analysis only.
+func hasSubdir(root, dir string) (rel string, ok bool) {
+       const sep = string(filepath.Separator)
+       root = filepath.Clean(root)
+       if !strings.HasSuffix(root, sep) {
+               root += sep
+       }
+       dir = filepath.Clean(dir)
+       if !strings.HasPrefix(dir, root) {
+               return "", false
+       }
+       return filepath.ToSlash(dir[len(root):]), true
+}
+
+// readDir calls ctxt.ReadDir (if not nil) or else os.ReadDir.
+func (ctxt *Context) readDir(path string) ([]fs.DirEntry, error) {
+       // TODO: add a fs.DirEntry version of Context.ReadDir
+       if f := ctxt.ReadDir; f != nil {
+               fis, err := f(path)
+               if err != nil {
+                       return nil, err
+               }
+               des := make([]fs.DirEntry, len(fis))
+               for i, fi := range fis {
+                       des[i] = fs.FileInfoToDirEntry(fi)
+               }
+               return des, nil
+       }
+       return os.ReadDir(path)
+}
+
+// openFile calls ctxt.OpenFile (if not nil) or else os.Open.
+func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
+       if fn := ctxt.OpenFile; fn != nil {
+               return fn(path)
+       }
+
+       f, err := os.Open(path)
+       if err != nil {
+               return nil, err // nil interface
+       }
+       return f, nil
+}
+
+// isFile determines whether path is a file by trying to open it.
+// It reuses openFile instead of adding another function to the
+// list in Context.
+func (ctxt *Context) isFile(path string) bool {
+       f, err := ctxt.openFile(path)
+       if err != nil {
+               return false
+       }
+       f.Close()
+       return true
+}
+
+// gopath returns the list of Go path directories.
+func (ctxt *Context) gopath() []string {
+       var all []string
+       for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
+               if p == "" || p == ctxt.GOROOT {
+                       // Empty paths are uninteresting.
+                       // If the path is the GOROOT, ignore it.
+                       // People sometimes set GOPATH=$GOROOT.
+                       // Do not get confused by this common mistake.
+                       continue
+               }
+               if strings.HasPrefix(p, "~") {
+                       // Path segments starting with ~ on Unix are almost always
+                       // users who have incorrectly quoted ~ while setting GOPATH,
+                       // preventing it from expanding to $HOME.
+                       // The situation is made more confusing by the fact that
+                       // bash allows quoted ~ in $PATH (most shells do not).
+                       // Do not get confused by this, and do not try to use the path.
+                       // It does not exist, and printing errors about it confuses
+                       // those users even more, because they think "sure ~ exists!".
+                       // The go command diagnoses this situation and prints a
+                       // useful error.
+                       // On Windows, ~ is used in short names, such as c:\progra~1
+                       // for c:\program files.
+                       continue
+               }
+               all = append(all, p)
+       }
+       return all
+}
+
+// SrcDirs returns a list of package source root directories.
+// It draws from the current Go root and Go path but omits directories
+// that do not exist.
+func (ctxt *Context) SrcDirs() []string {
+       var all []string
+       if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
+               dir := ctxt.joinPath(ctxt.GOROOT, "src")
+               if ctxt.isDir(dir) {
+                       all = append(all, dir)
+               }
+       }
+       for _, p := range ctxt.gopath() {
+               dir := ctxt.joinPath(p, "src")
+               if ctxt.isDir(dir) {
+                       all = append(all, dir)
+               }
+       }
+       return all
+}
+
+// Default is the default Context for builds.
+// It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables
+// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
+var Default Context = defaultContext()
+
+func defaultGOPATH() string {
+       env := "HOME"
+       if runtime.GOOS == "windows" {
+               env = "USERPROFILE"
+       } else if runtime.GOOS == "plan9" {
+               env = "home"
+       }
+       if home := os.Getenv(env); home != "" {
+               def := filepath.Join(home, "go")
+               if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
+                       // Don't set the default GOPATH to GOROOT,
+                       // as that will trigger warnings from the go tool.
+                       return ""
+               }
+               return def
+       }
+       return ""
+}
+
+var defaultToolTags, defaultReleaseTags []string
+
+func defaultContext() Context {
+       var c Context
+
+       c.GOARCH = buildcfg.GOARCH
+       c.GOOS = buildcfg.GOOS
+       if goroot := runtime.GOROOT(); goroot != "" {
+               c.GOROOT = filepath.Clean(goroot)
+       }
+       c.GOPATH = envOr("GOPATH", defaultGOPATH())
+       c.Compiler = runtime.Compiler
+
+       // For each experiment that has been enabled in the toolchain, define a
+       // build tag with the same name but prefixed by "goexperiment." which can be
+       // used for compiling alternative files for the experiment. This allows
+       // changes for the experiment, like extra struct fields in the runtime,
+       // without affecting the base non-experiment code at all.
+       for _, exp := range buildcfg.Experiment.Enabled() {
+               c.ToolTags = append(c.ToolTags, "goexperiment."+exp)
+       }
+       defaultToolTags = append([]string{}, c.ToolTags...) // our own private copy
+
+       // Each major Go release in the Go 1.x series adds a new
+       // "go1.x" release tag. That is, the go1.x tag is present in
+       // all releases >= Go 1.x. Code that requires Go 1.x or later
+       // should say "+build go1.x", and code that should only be
+       // built before Go 1.x (perhaps it is the stub to use in that
+       // case) should say "+build !go1.x".
+       // The last element in ReleaseTags is the current release.
+       for i := 1; i <= goversion.Version; i++ {
+               c.ReleaseTags = append(c.ReleaseTags, "go1."+strconv.Itoa(i))
+       }
+
+       defaultReleaseTags = append([]string{}, c.ReleaseTags...) // our own private copy
+
+       env := os.Getenv("CGO_ENABLED")
+       if env == "" {
+               env = defaultCGO_ENABLED
+       }
+       switch env {
+       case "1":
+               c.CgoEnabled = true
+       case "0":
+               c.CgoEnabled = false
+       default:
+               // cgo must be explicitly enabled for cross compilation builds
+               if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
+                       c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+                       break
+               }
+               c.CgoEnabled = false
+       }
+
+       return c
+}
+
+func envOr(name, def string) string {
+       s := os.Getenv(name)
+       if s == "" {
+               return def
+       }
+       return s
+}
+
+// An ImportMode controls the behavior of the Import method.
+type ImportMode uint
+
+const (
+       // If FindOnly is set, Import stops after locating the directory
+       // that should contain the sources for a package. It does not
+       // read any files in the directory.
+       FindOnly ImportMode = 1 << iota
+
+       // If AllowBinary is set, Import can be satisfied by a compiled
+       // package object without corresponding sources.
+       //
+       // Deprecated:
+       // The supported way to create a compiled-only package is to
+       // write source code containing a //go:binary-only-package comment at
+       // the top of the file. Such a package will be recognized
+       // regardless of this flag setting (because it has source code)
+       // and will have BinaryOnly set to true in the returned Package.
+       AllowBinary
+
+       // If ImportComment is set, parse import comments on package statements.
+       // Import returns an error if it finds a comment it cannot understand
+       // or finds conflicting comments in multiple source files.
+       // See golang.org/s/go14customimport for more information.
+       ImportComment
+
+       // By default, Import searches vendor directories
+       // that apply in the given source directory before searching
+       // the GOROOT and GOPATH roots.
+       // If an Import finds and returns a package using a vendor
+       // directory, the resulting ImportPath is the complete path
+       // to the package, including the path elements leading up
+       // to and including "vendor".
+       // For example, if Import("y", "x/subdir", 0) finds
+       // "x/vendor/y", the returned package's ImportPath is "x/vendor/y",
+       // not plain "y".
+       // See golang.org/s/go15vendor for more information.
+       //
+       // Setting IgnoreVendor ignores vendor directories.
+       //
+       // In contrast to the package's ImportPath,
+       // the returned package's Imports, TestImports, and XTestImports
+       // are always the exact import paths from the source files:
+       // Import makes no attempt to resolve or check those paths.
+       IgnoreVendor
+)
+
+// A Package describes the Go package found in a directory.
+type Package struct {
+       Dir           string   // directory containing package sources
+       Name          string   // package name
+       ImportComment string   // path in import comment on package statement
+       Doc           string   // documentation synopsis
+       ImportPath    string   // import path of package ("" if unknown)
+       Root          string   // root of Go tree where this package lives
+       SrcRoot       string   // package source root directory ("" if unknown)
+       PkgRoot       string   // package install root directory ("" if unknown)
+       PkgTargetRoot string   // architecture dependent install root directory ("" if unknown)
+       BinDir        string   // command install directory ("" if unknown)
+       Goroot        bool     // package found in Go root
+       PkgObj        string   // installed .a file
+       AllTags       []string // tags that can influence file selection in this directory
+       ConflictDir   string   // this directory shadows Dir in $GOPATH
+       BinaryOnly    bool     // cannot be rebuilt from source (has //go:binary-only-package comment)
+
+       // Source files
+       GoFiles           []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+       CgoFiles          []string // .go source files that import "C"
+       IgnoredGoFiles    []string // .go source files ignored for this build (including ignored _test.go files)
+       InvalidGoFiles    []string // .go source files with detected problems (parse error, wrong package name, and so on)
+       IgnoredOtherFiles []string // non-.go source files ignored for this build
+       CFiles            []string // .c source files
+       CXXFiles          []string // .cc, .cpp and .cxx source files
+       MFiles            []string // .m (Objective-C) source files
+       HFiles            []string // .h, .hh, .hpp and .hxx source files
+       FFiles            []string // .f, .F, .for and .f90 Fortran source files
+       SFiles            []string // .s source files
+       SwigFiles         []string // .swig files
+       SwigCXXFiles      []string // .swigcxx files
+       SysoFiles         []string // .syso system object files to add to archive
+
+       // Cgo directives
+       CgoCFLAGS    []string // Cgo CFLAGS directives
+       CgoCPPFLAGS  []string // Cgo CPPFLAGS directives
+       CgoCXXFLAGS  []string // Cgo CXXFLAGS directives
+       CgoFFLAGS    []string // Cgo FFLAGS directives
+       CgoLDFLAGS   []string // Cgo LDFLAGS directives
+       CgoPkgConfig []string // Cgo pkg-config directives
+
+       // Test information
+       TestGoFiles  []string // _test.go files in package
+       XTestGoFiles []string // _test.go files outside package
+
+       // Dependency information
+       Imports        []string                    // import paths from GoFiles, CgoFiles
+       ImportPos      map[string][]token.Position // line information for Imports
+       TestImports    []string                    // import paths from TestGoFiles
+       TestImportPos  map[string][]token.Position // line information for TestImports
+       XTestImports   []string                    // import paths from XTestGoFiles
+       XTestImportPos map[string][]token.Position // line information for XTestImports
+
+       // //go:embed patterns found in Go source files
+       // For example, if a source file says
+       //      //go:embed a* b.c
+       // then the list will contain those two strings as separate entries.
+       // (See package embed for more details about //go:embed.)
+       EmbedPatterns        []string                    // patterns from GoFiles, CgoFiles
+       EmbedPatternPos      map[string][]token.Position // line information for EmbedPatterns
+       TestEmbedPatterns    []string                    // patterns from TestGoFiles
+       TestEmbedPatternPos  map[string][]token.Position // line information for TestEmbedPatterns
+       XTestEmbedPatterns   []string                    // patterns from XTestGoFiles
+       XTestEmbedPatternPos map[string][]token.Position // line information for XTestEmbedPatternPos
+}
+
+// IsCommand reports whether the package is considered a
+// command to be installed (not just a library).
+// Packages named "main" are treated as commands.
+func (p *Package) IsCommand() bool {
+       return p.Name == "main"
+}
+
+// ImportDir is like Import but processes the Go package found in
+// the named directory.
+func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
+       return ctxt.Import(".", dir, mode)
+}
+
+// NoGoError is the error used by Import to describe a directory
+// containing no buildable Go source files. (It may still contain
+// test files, files hidden by build tags, and so on.)
+type NoGoError struct {
+       Dir string
+}
+
+func (e *NoGoError) Error() string {
+       return "no buildable Go source files in " + e.Dir
+}
+
+// MultiplePackageError describes a directory containing
+// multiple buildable Go source files for multiple packages.
+type MultiplePackageError struct {
+       Dir      string   // directory containing files
+       Packages []string // package names found
+       Files    []string // corresponding files: Files[i] declares package Packages[i]
+}
+
+func (e *MultiplePackageError) Error() string {
+       // Error string limited to two entries for compatibility.
+       return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir)
+}
+
+func nameExt(name string) string {
+       i := strings.LastIndex(name, ".")
+       if i < 0 {
+               return ""
+       }
+       return name[i:]
+}
+
+// Import returns details about the Go package named by the import path,
+// interpreting local import paths relative to the srcDir directory.
+// If the path is a local import path naming a package that can be imported
+// using a standard import path, the returned package will set p.ImportPath
+// to that path.
+//
+// In the directory containing the package, .go, .c, .h, and .s files are
+// considered part of the package except for:
+//
+//   - .go files in package documentation
+//   - files starting with _ or . (likely editor temporary files)
+//   - files with build constraints not satisfied by the context
+//
+// If an error occurs, Import returns a non-nil error and a non-nil
+// *Package containing partial information.
+func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
+       p := &Package{
+               ImportPath: path,
+       }
+       if path == "" {
+               return p, fmt.Errorf("import %q: invalid import path", path)
+       }
+
+       var pkgtargetroot string
+       var pkga string
+       var pkgerr error
+       suffix := ""
+       if ctxt.InstallSuffix != "" {
+               suffix = "_" + ctxt.InstallSuffix
+       }
+       switch ctxt.Compiler {
+       case "gccgo":
+               pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
+       case "gc":
+               pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
+       default:
+               // Save error for end of function.
+               pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
+       }
+       setPkga := func() {
+               switch ctxt.Compiler {
+               case "gccgo":
+                       dir, elem := pathpkg.Split(p.ImportPath)
+                       pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
+               case "gc":
+                       pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
+               }
+       }
+       setPkga()
+
+       binaryOnly := false
+       if IsLocalImport(path) {
+               pkga = "" // local imports have no installed path
+               if srcDir == "" {
+                       return p, fmt.Errorf("import %q: import relative to unknown directory", path)
+               }
+               if !ctxt.isAbsPath(path) {
+                       p.Dir = ctxt.joinPath(srcDir, path)
+               }
+               // p.Dir directory may or may not exist. Gather partial information first, check if it exists later.
+               // Determine canonical import path, if any.
+               // Exclude results where the import path would include /testdata/.
+               inTestdata := func(sub string) bool {
+                       return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata"
+               }
+               if ctxt.GOROOT != "" {
+                       root := ctxt.joinPath(ctxt.GOROOT, "src")
+                       if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
+                               p.Goroot = true
+                               p.ImportPath = sub
+                               p.Root = ctxt.GOROOT
+                               setPkga() // p.ImportPath changed
+                               goto Found
+                       }
+               }
+               all := ctxt.gopath()
+               for i, root := range all {
+                       rootsrc := ctxt.joinPath(root, "src")
+                       if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
+                               // We found a potential import path for dir,
+                               // but check that using it wouldn't find something
+                               // else first.
+                               if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
+                                       if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
+                                               p.ConflictDir = dir
+                                               goto Found
+                                       }
+                               }
+                               for _, earlyRoot := range all[:i] {
+                                       if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
+                                               p.ConflictDir = dir
+                                               goto Found
+                                       }
+                               }
+
+                               // sub would not name some other directory instead of this one.
+                               // Record it.
+                               p.ImportPath = sub
+                               p.Root = root
+                               setPkga() // p.ImportPath changed
+                               goto Found
+                       }
+               }
+               // It's okay that we didn't find a root containing dir.
+               // Keep going with the information we have.
+       } else {
+               if strings.HasPrefix(path, "/") {
+                       return p, fmt.Errorf("import %q: cannot import absolute path", path)
+               }
+
+               if err := ctxt.importGo(p, path, srcDir, mode); err == nil {
+                       goto Found
+               } else if err != errNoModules {
+                       return p, err
+               }
+
+               gopath := ctxt.gopath() // needed twice below; avoid computing many times
+
+               // tried records the location of unsuccessful package lookups
+               var tried struct {
+                       vendor []string
+                       goroot string
+                       gopath []string
+               }
+
+               // Vendor directories get first chance to satisfy import.
+               if mode&IgnoreVendor == 0 && srcDir != "" {
+                       searchVendor := func(root string, isGoroot bool) bool {
+                               sub, ok := ctxt.hasSubdir(root, srcDir)
+                               if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") {
+                                       return false
+                               }
+                               for {
+                                       vendor := ctxt.joinPath(root, sub, "vendor")
+                                       if ctxt.isDir(vendor) {
+                                               dir := ctxt.joinPath(vendor, path)
+                                               if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
+                                                       p.Dir = dir
+                                                       p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
+                                                       p.Goroot = isGoroot
+                                                       p.Root = root
+                                                       setPkga() // p.ImportPath changed
+                                                       return true
+                                               }
+                                               tried.vendor = append(tried.vendor, dir)
+                                       }
+                                       i := strings.LastIndex(sub, "/")
+                                       if i < 0 {
+                                               break
+                                       }
+                                       sub = sub[:i]
+                               }
+                               return false
+                       }
+                       if ctxt.Compiler != "gccgo" && ctxt.GOROOT != "" && searchVendor(ctxt.GOROOT, true) {
+                               goto Found
+                       }
+                       for _, root := range gopath {
+                               if searchVendor(root, false) {
+                                       goto Found
+                               }
+                       }
+               }
+
+               // Determine directory from import path.
+               if ctxt.GOROOT != "" {
+                       // If the package path starts with "vendor/", only search GOROOT before
+                       // GOPATH if the importer is also within GOROOT. That way, if the user has
+                       // vendored in a package that is subsequently included in the standard
+                       // distribution, they'll continue to pick up their own vendored copy.
+                       gorootFirst := srcDir == "" || !strings.HasPrefix(path, "vendor/")
+                       if !gorootFirst {
+                               _, gorootFirst = ctxt.hasSubdir(ctxt.GOROOT, srcDir)
+                       }
+                       if gorootFirst {
+                               dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
+                               if ctxt.Compiler != "gccgo" {
+                                       isDir := ctxt.isDir(dir)
+                                       binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
+                                       if isDir || binaryOnly {
+                                               p.Dir = dir
+                                               p.Goroot = true
+                                               p.Root = ctxt.GOROOT
+                                               goto Found
+                                       }
+                               }
+                               tried.goroot = dir
+                       }
+                       if ctxt.Compiler == "gccgo" && goroot.IsStandardPackage(ctxt.GOROOT, ctxt.Compiler, path) {
+                               p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path)
+                               p.Goroot = true
+                               p.Root = ctxt.GOROOT
+                               goto Found
+                       }
+               }
+               for _, root := range gopath {
+                       dir := ctxt.joinPath(root, "src", path)
+                       isDir := ctxt.isDir(dir)
+                       binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
+                       if isDir || binaryOnly {
+                               p.Dir = dir
+                               p.Root = root
+                               goto Found
+                       }
+                       tried.gopath = append(tried.gopath, dir)
+               }
+
+               // If we tried GOPATH first due to a "vendor/" prefix, fall back to GOPATH.
+               // That way, the user can still get useful results from 'go list' for
+               // standard-vendored paths passed on the command line.
+               if ctxt.GOROOT != "" && tried.goroot == "" {
+                       dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
+                       if ctxt.Compiler != "gccgo" {
+                               isDir := ctxt.isDir(dir)
+                               binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
+                               if isDir || binaryOnly {
+                                       p.Dir = dir
+                                       p.Goroot = true
+                                       p.Root = ctxt.GOROOT
+                                       goto Found
+                               }
+                       }
+                       tried.goroot = dir
+               }
+
+               // package was not found
+               var paths []string
+               format := "\t%s (vendor tree)"
+               for _, dir := range tried.vendor {
+                       paths = append(paths, fmt.Sprintf(format, dir))
+                       format = "\t%s"
+               }
+               if tried.goroot != "" {
+                       paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
+               } else {
+                       paths = append(paths, "\t($GOROOT not set)")
+               }
+               format = "\t%s (from $GOPATH)"
+               for _, dir := range tried.gopath {
+                       paths = append(paths, fmt.Sprintf(format, dir))
+                       format = "\t%s"
+               }
+               if len(tried.gopath) == 0 {
+                       paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
+               }
+               return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
+       }
+
+Found:
+       if p.Root != "" {
+               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 it's a local import path, by the time we get here, we still haven't checked
+       // that p.Dir directory exists. This is the right time to do that check.
+       // We can't do it earlier, because we want to gather partial information for the
+       // non-nil *Package returned when an error occurs.
+       // We need to do this before we return early on FindOnly flag.
+       if IsLocalImport(path) && !ctxt.isDir(p.Dir) {
+               if ctxt.Compiler == "gccgo" && p.Goroot {
+                       // gccgo has no sources for GOROOT packages.
+                       return p, nil
+               }
+
+               // package was not found
+               return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir)
+       }
+
+       if mode&FindOnly != 0 {
+               return p, pkgerr
+       }
+       if binaryOnly && (mode&AllowBinary) != 0 {
+               return p, pkgerr
+       }
+
+       if ctxt.Compiler == "gccgo" && p.Goroot {
+               // gccgo has no sources for GOROOT packages.
+               return p, nil
+       }
+
+       dirs, err := ctxt.readDir(p.Dir)
+       if err != nil {
+               return p, err
+       }
+
+       var badGoError error
+       badFiles := make(map[string]bool)
+       badFile := func(name string, err error) {
+               if badGoError == nil {
+                       badGoError = err
+               }
+               if !badFiles[name] {
+                       p.InvalidGoFiles = append(p.InvalidGoFiles, name)
+                       badFiles[name] = true
+               }
+       }
+
+       var Sfiles []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems)
+       var firstFile, firstCommentFile string
+       embedPos := make(map[string][]token.Position)
+       testEmbedPos := make(map[string][]token.Position)
+       xTestEmbedPos := make(map[string][]token.Position)
+       importPos := make(map[string][]token.Position)
+       testImportPos := make(map[string][]token.Position)
+       xTestImportPos := make(map[string][]token.Position)
+       allTags := make(map[string]bool)
+       fset := token.NewFileSet()
+       for _, d := range dirs {
+               if d.IsDir() {
+                       continue
+               }
+               if d.Type() == fs.ModeSymlink {
+                       if ctxt.isDir(ctxt.joinPath(p.Dir, d.Name())) {
+                               // Symlinks to directories are not source files.
+                               continue
+                       }
+               }
+
+               name := d.Name()
+               ext := nameExt(name)
+
+               info, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly, fset)
+               if err != nil {
+                       badFile(name, err)
+                       continue
+               }
+               if info == nil {
+                       if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
+                               // not due to build constraints - don't report
+                       } else if ext == ".go" {
+                               p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+                       } else if fileListForExt(p, ext) != nil {
+                               p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name)
+                       }
+                       continue
+               }
+               data, filename := info.header, info.name
+
+               // Going to save the file. For non-Go files, can stop here.
+               switch ext {
+               case ".go":
+                       // keep going
+               case ".S", ".sx":
+                       // special case for cgo, handled at end
+                       Sfiles = append(Sfiles, name)
+                       continue
+               default:
+                       if list := fileListForExt(p, ext); list != nil {
+                               *list = append(*list, name)
+                       }
+                       continue
+               }
+
+               if info.parseErr != nil {
+                       badFile(name, info.parseErr)
+                       // Fall through: we might still have a partial AST in info.parsed,
+                       // and we want to list files with parse errors anyway.
+               }
+
+               var pkg string
+               if info.parsed != nil {
+                       pkg = info.parsed.Name.Name
+                       if pkg == "documentation" {
+                               p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+                               continue
+                       }
+               }
+
+               isTest := strings.HasSuffix(name, "_test.go")
+               isXTest := false
+               if isTest && strings.HasSuffix(pkg, "_test") && p.Name != pkg {
+                       isXTest = true
+                       pkg = pkg[:len(pkg)-len("_test")]
+               }
+
+               if p.Name == "" {
+                       p.Name = pkg
+                       firstFile = name
+               } else if pkg != p.Name {
+                       // TODO(#45999): The choice of p.Name is arbitrary based on file iteration
+                       // order. Instead of resolving p.Name arbitrarily, we should clear out the
+                       // existing name and mark the existing files as also invalid.
+                       badFile(name, &MultiplePackageError{
+                               Dir:      p.Dir,
+                               Packages: []string{p.Name, pkg},
+                               Files:    []string{firstFile, name},
+                       })
+               }
+               // Grab the first package comment as docs, provided it is not from a test file.
+               if info.parsed != nil && info.parsed.Doc != nil && p.Doc == "" && !isTest && !isXTest {
+                       p.Doc = doc.Synopsis(info.parsed.Doc.Text())
+               }
+
+               if mode&ImportComment != 0 {
+                       qcom, line := findImportComment(data)
+                       if line != 0 {
+                               com, err := strconv.Unquote(qcom)
+                               if err != nil {
+                                       badFile(name, fmt.Errorf("%s:%d: cannot parse import comment", filename, line))
+                               } else if p.ImportComment == "" {
+                                       p.ImportComment = com
+                                       firstCommentFile = name
+                               } else if p.ImportComment != com {
+                                       badFile(name, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir))
+                               }
+                       }
+               }
+
+               // Record imports and information about cgo.
+               isCgo := false
+               for _, imp := range info.imports {
+                       if imp.path == "C" {
+                               if isTest {
+                                       badFile(name, fmt.Errorf("use of cgo in test %s not supported", filename))
+                                       continue
+                               }
+                               isCgo = true
+                               if imp.doc != nil {
+                                       if err := ctxt.saveCgo(filename, p, imp.doc); err != nil {
+                                               badFile(name, err)
+                                       }
+                               }
+                       }
+               }
+
+               var fileList *[]string
+               var importMap, embedMap map[string][]token.Position
+               switch {
+               case isCgo:
+                       allTags["cgo"] = true
+                       if ctxt.CgoEnabled {
+                               fileList = &p.CgoFiles
+                               importMap = importPos
+                               embedMap = embedPos
+                       } else {
+                               // Ignore imports and embeds from cgo files if cgo is disabled.
+                               fileList = &p.IgnoredGoFiles
+                       }
+               case isXTest:
+                       fileList = &p.XTestGoFiles
+                       importMap = xTestImportPos
+                       embedMap = xTestEmbedPos
+               case isTest:
+                       fileList = &p.TestGoFiles
+                       importMap = testImportPos
+                       embedMap = testEmbedPos
+               default:
+                       fileList = &p.GoFiles
+                       importMap = importPos
+                       embedMap = embedPos
+               }
+               *fileList = append(*fileList, name)
+               if importMap != nil {
+                       for _, imp := range info.imports {
+                               importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos))
+                       }
+               }
+               if embedMap != nil {
+                       for _, emb := range info.embeds {
+                               embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
+                       }
+               }
+       }
+
+       for tag := range allTags {
+               p.AllTags = append(p.AllTags, tag)
+       }
+       sort.Strings(p.AllTags)
+
+       p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
+       p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos)
+       p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos)
+
+       p.Imports, p.ImportPos = cleanDecls(importPos)
+       p.TestImports, p.TestImportPos = cleanDecls(testImportPos)
+       p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos)
+
+       // add the .S/.sx files only if we are using cgo
+       // (which means gcc will compile them).
+       // The standard assemblers expect .s files.
+       if len(p.CgoFiles) > 0 {
+               p.SFiles = append(p.SFiles, Sfiles...)
+               sort.Strings(p.SFiles)
+       } else {
+               p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...)
+               sort.Strings(p.IgnoredOtherFiles)
+       }
+
+       if badGoError != nil {
+               return p, badGoError
+       }
+       if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
+               return p, &NoGoError{p.Dir}
+       }
+       return p, pkgerr
+}
+
+func fileListForExt(p *Package, ext string) *[]string {
+       switch ext {
+       case ".c":
+               return &p.CFiles
+       case ".cc", ".cpp", ".cxx":
+               return &p.CXXFiles
+       case ".m":
+               return &p.MFiles
+       case ".h", ".hh", ".hpp", ".hxx":
+               return &p.HFiles
+       case ".f", ".F", ".for", ".f90":
+               return &p.FFiles
+       case ".s", ".S", ".sx":
+               return &p.SFiles
+       case ".swig":
+               return &p.SwigFiles
+       case ".swigcxx":
+               return &p.SwigCXXFiles
+       case ".syso":
+               return &p.SysoFiles
+       }
+       return nil
+}
+
+func uniq(list []string) []string {
+       if list == nil {
+               return nil
+       }
+       out := make([]string, len(list))
+       copy(out, list)
+       sort.Strings(out)
+       uniq := out[:0]
+       for _, x := range out {
+               if len(uniq) == 0 || uniq[len(uniq)-1] != x {
+                       uniq = append(uniq, x)
+               }
+       }
+       return uniq
+}
+
+var errNoModules = errors.New("not using modules")
+
+// importGo checks whether it can use the go command to find the directory for path.
+// If using the go command is not appropriate, importGo returns errNoModules.
+// Otherwise, importGo tries using the go command and reports whether that succeeded.
+// Using the go command lets build.Import and build.Context.Import find code
+// in Go modules. In the long term we want tools to use go/packages (currently golang.org/x/tools/go/packages),
+// which will also use the go command.
+// Invoking the go command here is not very efficient in that it computes information
+// about the requested package and all dependencies and then only reports about the requested package.
+// Then we reinvoke it for every dependency. But this is still better than not working at all.
+// See golang.org/issue/26504.
+func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode) error {
+       // To invoke the go command,
+       // we must not being doing special things like AllowBinary or IgnoreVendor,
+       // and all the file system callbacks must be nil (we're meant to use the local file system).
+       if mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
+               ctxt.JoinPath != nil || ctxt.SplitPathList != nil || ctxt.IsAbsPath != nil || ctxt.IsDir != nil || ctxt.HasSubdir != nil || ctxt.ReadDir != nil || ctxt.OpenFile != nil || !equal(ctxt.ToolTags, defaultToolTags) || !equal(ctxt.ReleaseTags, defaultReleaseTags) {
+               return errNoModules
+       }
+
+       // If ctxt.GOROOT is not set, we don't know which go command to invoke,
+       // and even if we did we might return packages in GOROOT that we wouldn't otherwise find
+       // (because we don't know to search in 'go env GOROOT' otherwise).
+       if ctxt.GOROOT == "" {
+               return errNoModules
+       }
+
+       // Predict whether module aware mode is enabled by checking the value of
+       // GO111MODULE and looking for a go.mod file in the source directory or
+       // one of its parents. Running 'go env GOMOD' in the source directory would
+       // give a canonical answer, but we'd prefer not to execute another command.
+       go111Module := os.Getenv("GO111MODULE")
+       switch go111Module {
+       case "off":
+               return errNoModules
+       default: // "", "on", "auto", anything else
+               // Maybe use modules.
+       }
+
+       if srcDir != "" {
+               var absSrcDir string
+               if filepath.IsAbs(srcDir) {
+                       absSrcDir = srcDir
+               } else if ctxt.Dir != "" {
+                       return fmt.Errorf("go/build: Dir is non-empty, so relative srcDir is not allowed: %v", srcDir)
+               } else {
+                       // Find the absolute source directory. hasSubdir does not handle
+                       // relative paths (and can't because the callbacks don't support this).
+                       var err error
+                       absSrcDir, err = filepath.Abs(srcDir)
+                       if err != nil {
+                               return errNoModules
+                       }
+               }
+
+               // If the source directory is in GOROOT, then the in-process code works fine
+               // and we should keep using it. Moreover, the 'go list' approach below doesn't
+               // take standard-library vendoring into account and will fail.
+               if _, ok := ctxt.hasSubdir(filepath.Join(ctxt.GOROOT, "src"), absSrcDir); ok {
+                       return errNoModules
+               }
+       }
+
+       // For efficiency, if path is a standard library package, let the usual lookup code handle it.
+       if dir := ctxt.joinPath(ctxt.GOROOT, "src", path); ctxt.isDir(dir) {
+               return errNoModules
+       }
+
+       // If GO111MODULE=auto, look to see if there is a go.mod.
+       // Since go1.13, it doesn't matter if we're inside GOPATH.
+       if go111Module == "auto" {
+               var (
+                       parent string
+                       err    error
+               )
+               if ctxt.Dir == "" {
+                       parent, err = os.Getwd()
+                       if err != nil {
+                               // A nonexistent working directory can't be in a module.
+                               return errNoModules
+                       }
+               } else {
+                       parent, err = filepath.Abs(ctxt.Dir)
+                       if err != nil {
+                               // If the caller passed a bogus Dir explicitly, that's materially
+                               // different from not having modules enabled.
+                               return err
+                       }
+               }
+               for {
+                       if f, err := ctxt.openFile(ctxt.joinPath(parent, "go.mod")); err == nil {
+                               buf := make([]byte, 100)
+                               _, err := f.Read(buf)
+                               f.Close()
+                               if err == nil || err == io.EOF {
+                                       // go.mod exists and is readable (is a file, not a directory).
+                                       break
+                               }
+                       }
+                       d := filepath.Dir(parent)
+                       if len(d) >= len(parent) {
+                               return errNoModules // reached top of file system, no go.mod
+                       }
+                       parent = d
+               }
+       }
+
+       goCmd := filepath.Join(ctxt.GOROOT, "bin", "go")
+       cmd := exec.Command(goCmd, "list", "-e", "-compiler="+ctxt.Compiler, "-tags="+strings.Join(ctxt.BuildTags, ","), "-installsuffix="+ctxt.InstallSuffix, "-f={{.Dir}}\n{{.ImportPath}}\n{{.Root}}\n{{.Goroot}}\n{{if .Error}}{{.Error}}{{end}}\n", "--", path)
+
+       if ctxt.Dir != "" {
+               cmd.Dir = ctxt.Dir
+       }
+
+       var stdout, stderr strings.Builder
+       cmd.Stdout = &stdout
+       cmd.Stderr = &stderr
+
+       cgo := "0"
+       if ctxt.CgoEnabled {
+               cgo = "1"
+       }
+       cmd.Env = append(cmd.Environ(),
+               "GOOS="+ctxt.GOOS,
+               "GOARCH="+ctxt.GOARCH,
+               "GOROOT="+ctxt.GOROOT,
+               "GOPATH="+ctxt.GOPATH,
+               "CGO_ENABLED="+cgo,
+       )
+
+       if err := cmd.Run(); err != nil {
+               return fmt.Errorf("go/build: go list %s: %v\n%s\n", path, err, stderr.String())
+       }
+
+       f := strings.SplitN(stdout.String(), "\n", 5)
+       if len(f) != 5 {
+               return fmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", path, stdout.String())
+       }
+       dir := f[0]
+       errStr := strings.TrimSpace(f[4])
+       if errStr != "" && dir == "" {
+               // If 'go list' could not locate the package (dir is empty),
+               // return the same error that 'go list' reported.
+               return errors.New(errStr)
+       }
+
+       // If 'go list' did locate the package, ignore the error.
+       // It was probably related to loading source files, and we'll
+       // encounter it ourselves shortly if the FindOnly flag isn't set.
+       p.Dir = dir
+       p.ImportPath = f[1]
+       p.Root = f[2]
+       p.Goroot = f[3] == "true"
+       return nil
+}
+
+func equal(x, y []string) bool {
+       if len(x) != len(y) {
+               return false
+       }
+       for i, xi := range x {
+               if xi != y[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+// 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(ctxt *Context, dir string) bool {
+       ents, _ := ctxt.readDir(dir)
+       for _, ent := range ents {
+               if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
+                       return true
+               }
+       }
+       return false
+}
+
+func findImportComment(data []byte) (s string, line int) {
+       // expect keyword package
+       word, data := parseWord(data)
+       if string(word) != "package" {
+               return "", 0
+       }
+
+       // expect package name
+       _, data = parseWord(data)
+
+       // now ready for import comment, a // or /* */ comment
+       // beginning and ending on the current line.
+       for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
+               data = data[1:]
+       }
+
+       var comment []byte
+       switch {
+       case bytes.HasPrefix(data, slashSlash):
+               comment, _, _ = bytes.Cut(data[2:], newline)
+       case bytes.HasPrefix(data, slashStar):
+               var ok bool
+               comment, _, ok = bytes.Cut(data[2:], starSlash)
+               if !ok {
+                       // malformed comment
+                       return "", 0
+               }
+               if bytes.Contains(comment, newline) {
+                       return "", 0
+               }
+       }
+       comment = bytes.TrimSpace(comment)
+
+       // split comment into `import`, `"pkg"`
+       word, arg := parseWord(comment)
+       if string(word) != "import" {
+               return "", 0
+       }
+
+       line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
+       return strings.TrimSpace(string(arg)), line
+}
+
+var (
+       slashSlash = []byte("//")
+       slashStar  = []byte("/*")
+       starSlash  = []byte("*/")
+       newline    = []byte("\n")
+)
+
+// skipSpaceOrComment returns data with any leading spaces or comments removed.
+func skipSpaceOrComment(data []byte) []byte {
+       for len(data) > 0 {
+               switch data[0] {
+               case ' ', '\t', '\r', '\n':
+                       data = data[1:]
+                       continue
+               case '/':
+                       if bytes.HasPrefix(data, slashSlash) {
+                               i := bytes.Index(data, newline)
+                               if i < 0 {
+                                       return nil
+                               }
+                               data = data[i+1:]
+                               continue
+                       }
+                       if bytes.HasPrefix(data, slashStar) {
+                               data = data[2:]
+                               i := bytes.Index(data, starSlash)
+                               if i < 0 {
+                                       return nil
+                               }
+                               data = data[i+2:]
+                               continue
+                       }
+               }
+               break
+       }
+       return data
+}
+
+// parseWord skips any leading spaces or comments in data
+// and then parses the beginning of data as an identifier or keyword,
+// returning that word and what remains after the word.
+func parseWord(data []byte) (word, rest []byte) {
+       data = skipSpaceOrComment(data)
+
+       // Parse past leading word characters.
+       rest = data
+       for {
+               r, size := utf8.DecodeRune(rest)
+               if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
+                       rest = rest[size:]
+                       continue
+               }
+               break
+       }
+
+       word = data[:len(data)-len(rest)]
+       if len(word) == 0 {
+               return nil, nil
+       }
+
+       return word, rest
+}
+
+// MatchFile reports whether the file with the given name in the given directory
+// matches the context and would be included in a Package created by ImportDir
+// of that directory.
+//
+// MatchFile considers the name of the file and may use ctxt.OpenFile to
+// read some or all of the file's content.
+func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
+       info, err := ctxt.matchFile(dir, name, nil, nil, nil)
+       return info != nil, err
+}
+
+var dummyPkg Package
+
+// fileInfo records information learned about a file included in a build.
+type fileInfo struct {
+       name     string // full name including dir
+       header   []byte
+       fset     *token.FileSet
+       parsed   *ast.File
+       parseErr error
+       imports  []fileImport
+       embeds   []fileEmbed
+}
+
+type fileImport struct {
+       path string
+       pos  token.Pos
+       doc  *ast.CommentGroup
+}
+
+type fileEmbed struct {
+       pattern string
+       pos     token.Position
+}
+
+// matchFile determines whether the file with the given name in the given directory
+// should be included in the package being constructed.
+// If the file should be included, matchFile returns a non-nil *fileInfo (and a nil error).
+// Non-nil errors are reserved for unexpected problems.
+//
+// If name denotes a Go program, matchFile reads until the end of the
+// imports and returns that section of the file in the fileInfo's header field,
+// even though it only considers text until the first non-comment
+// for +build lines.
+//
+// If allTags is non-nil, matchFile records any encountered build tag
+// by setting allTags[tag] = true.
+func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) {
+       if strings.HasPrefix(name, "_") ||
+               strings.HasPrefix(name, ".") {
+               return nil, nil
+       }
+
+       i := strings.LastIndex(name, ".")
+       if i < 0 {
+               i = len(name)
+       }
+       ext := name[i:]
+
+       if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
+               return nil, nil
+       }
+
+       if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
+               // skip
+               return nil, nil
+       }
+
+       info := &fileInfo{name: ctxt.joinPath(dir, name), fset: fset}
+       if ext == ".syso" {
+               // binary, no reading
+               return info, nil
+       }
+
+       f, err := ctxt.openFile(info.name)
+       if err != nil {
+               return nil, err
+       }
+
+       if strings.HasSuffix(name, ".go") {
+               err = readGoInfo(f, info)
+               if strings.HasSuffix(name, "_test.go") {
+                       binaryOnly = nil // ignore //go:binary-only-package comments in _test.go files
+               }
+       } else {
+               binaryOnly = nil // ignore //go:binary-only-package comments in non-Go sources
+               info.header, err = readComments(f)
+       }
+       f.Close()
+       if err != nil {
+               return nil, fmt.Errorf("read %s: %v", info.name, err)
+       }
+
+       // Look for +build comments to accept or reject the file.
+       ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
+       if err != nil {
+               return nil, fmt.Errorf("%s: %v", name, err)
+       }
+       if !ok && !ctxt.UseAllFiles {
+               return nil, nil
+       }
+
+       if binaryOnly != nil && sawBinaryOnly {
+               *binaryOnly = true
+       }
+
+       return info, nil
+}
+
+func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) {
+       all := make([]string, 0, len(m))
+       for path := range m {
+               all = append(all, path)
+       }
+       sort.Strings(all)
+       return all, m
+}
+
+// Import is shorthand for Default.Import.
+func Import(path, srcDir string, mode ImportMode) (*Package, error) {
+       return Default.Import(path, srcDir, mode)
+}
+
+// ImportDir is shorthand for Default.ImportDir.
+func ImportDir(dir string, mode ImportMode) (*Package, error) {
+       return Default.ImportDir(dir, mode)
+}
+
+var (
+       bSlashSlash = []byte(slashSlash)
+       bStarSlash  = []byte(starSlash)
+       bSlashStar  = []byte(slashStar)
+       bPlusBuild  = []byte("+build")
+
+       goBuildComment = []byte("//go:build")
+
+       errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment")
+       errMultipleGoBuild     = errors.New("multiple //go:build comments")
+)
+
+func isGoBuildComment(line []byte) bool {
+       if !bytes.HasPrefix(line, goBuildComment) {
+               return false
+       }
+       line = bytes.TrimSpace(line)
+       rest := line[len(goBuildComment):]
+       return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
+}
+
+// Special comment denoting a binary-only package.
+// See https://golang.org/design/2775-binary-only-packages
+// for more about the design of binary-only packages.
+var binaryOnlyComment = []byte("//go:binary-only-package")
+
+// shouldBuild reports whether it is okay to use this file,
+// The rule is that in the file's leading run of // comments
+// and blank lines, which must be followed by a blank line
+// (to avoid including a Go package clause doc comment),
+// lines beginning with '// +build' are taken as build directives.
+//
+// The file is accepted only if each such line lists something
+// matching the file. For example:
+//
+//     // +build windows linux
+//
+// marks the file as applicable only on Windows and Linux.
+//
+// For each build tag it consults, shouldBuild sets allTags[tag] = true.
+//
+// shouldBuild reports whether the file should be built
+// and whether a //go:binary-only-package comment was found.
+func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) (shouldBuild, binaryOnly bool, err error) {
+       // Identify leading run of // comments and blank lines,
+       // which must be followed by a blank line.
+       // Also identify any //go:build comments.
+       content, goBuild, sawBinaryOnly, err := parseFileHeader(content)
+       if err != nil {
+               return false, false, err
+       }
+
+       // If //go:build line is present, it controls.
+       // Otherwise fall back to +build processing.
+       switch {
+       case goBuild != nil:
+               x, err := constraint.Parse(string(goBuild))
+               if err != nil {
+                       return false, false, fmt.Errorf("parsing //go:build line: %v", err)
+               }
+               shouldBuild = ctxt.eval(x, allTags)
+
+       default:
+               shouldBuild = true
+               p := content
+               for len(p) > 0 {
+                       line := p
+                       if i := bytes.IndexByte(line, '\n'); i >= 0 {
+                               line, p = line[:i], p[i+1:]
+                       } else {
+                               p = p[len(p):]
+                       }
+                       line = bytes.TrimSpace(line)
+                       if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
+                               continue
+                       }
+                       text := string(line)
+                       if !constraint.IsPlusBuild(text) {
+                               continue
+                       }
+                       if x, err := constraint.Parse(text); err == nil {
+                               if !ctxt.eval(x, allTags) {
+                                       shouldBuild = false
+                               }
+                       }
+               }
+       }
+
+       return shouldBuild, sawBinaryOnly, nil
+}
+
+func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
+       end := 0
+       p := content
+       ended := false       // found non-blank, non-// line, so stopped accepting // +build lines
+       inSlashStar := false // in /* */ comment
+
+Lines:
+       for len(p) > 0 {
+               line := p
+               if i := bytes.IndexByte(line, '\n'); i >= 0 {
+                       line, p = line[:i], p[i+1:]
+               } else {
+                       p = p[len(p):]
+               }
+               line = bytes.TrimSpace(line)
+               if len(line) == 0 && !ended { // Blank line
+                       // Remember position of most recent blank line.
+                       // When we find the first non-blank, non-// line,
+                       // this "end" position marks the latest file position
+                       // where a // +build line can appear.
+                       // (It must appear _before_ a blank line before the non-blank, non-// line.
+                       // Yes, that's confusing, which is part of why we moved to //go:build lines.)
+                       // Note that ended==false here means that inSlashStar==false,
+                       // since seeing a /* would have set ended==true.
+                       end = len(content) - len(p)
+                       continue Lines
+               }
+               if !bytes.HasPrefix(line, slashSlash) { // Not comment line
+                       ended = true
+               }
+
+               if !inSlashStar && isGoBuildComment(line) {
+                       if goBuild != nil {
+                               return nil, nil, false, errMultipleGoBuild
+                       }
+                       goBuild = line
+               }
+               if !inSlashStar && bytes.Equal(line, binaryOnlyComment) {
+                       sawBinaryOnly = true
+               }
+
+       Comments:
+               for len(line) > 0 {
+                       if inSlashStar {
+                               if i := bytes.Index(line, starSlash); i >= 0 {
+                                       inSlashStar = false
+                                       line = bytes.TrimSpace(line[i+len(starSlash):])
+                                       continue Comments
+                               }
+                               continue Lines
+                       }
+                       if bytes.HasPrefix(line, bSlashSlash) {
+                               continue Lines
+                       }
+                       if bytes.HasPrefix(line, bSlashStar) {
+                               inSlashStar = true
+                               line = bytes.TrimSpace(line[len(bSlashStar):])
+                               continue Comments
+                       }
+                       // Found non-comment text.
+                       break Lines
+               }
+       }
+
+       return content[:end], goBuild, sawBinaryOnly, nil
+}
+
+// saveCgo saves the information from the #cgo lines in the import "C" comment.
+// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
+// that affect the way cgo's C code is built.
+func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
+       text := cg.Text()
+       for _, line := range strings.Split(text, "\n") {
+               orig := line
+
+               // Line is
+               //      #cgo [GOOS/GOARCH...] LDFLAGS: stuff
+               //
+               line = strings.TrimSpace(line)
+               if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
+                       continue
+               }
+
+               // Split at colon.
+               line, argstr, ok := strings.Cut(strings.TrimSpace(line[4:]), ":")
+               if !ok {
+                       return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
+               }
+
+               // Parse GOOS/GOARCH stuff.
+               f := strings.Fields(line)
+               if len(f) < 1 {
+                       return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
+               }
+
+               cond, verb := f[:len(f)-1], f[len(f)-1]
+               if len(cond) > 0 {
+                       ok := false
+                       for _, c := range cond {
+                               if ctxt.matchAuto(c, nil) {
+                                       ok = true
+                                       break
+                               }
+                       }
+                       if !ok {
+                               continue
+                       }
+               }
+
+               args, err := splitQuoted(argstr)
+               if err != nil {
+                       return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
+               }
+               for i, arg := range args {
+                       if arg, ok = expandSrcDir(arg, di.Dir); !ok {
+                               return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
+                       }
+                       args[i] = arg
+               }
+
+               switch verb {
+               case "CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS":
+                       // Change relative paths to absolute.
+                       ctxt.makePathsAbsolute(args, di.Dir)
+               }
+
+               switch verb {
+               case "CFLAGS":
+                       di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
+               case "CPPFLAGS":
+                       di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
+               case "CXXFLAGS":
+                       di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
+               case "FFLAGS":
+                       di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
+               case "LDFLAGS":
+                       di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
+               case "pkg-config":
+                       di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
+               default:
+                       return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
+               }
+       }
+       return nil
+}
+
+// expandSrcDir expands any occurrence of ${SRCDIR}, making sure
+// the result is safe for the shell.
+func expandSrcDir(str string, srcdir string) (string, bool) {
+       // "\" delimited paths cause safeCgoName to fail
+       // so convert native paths with a different delimiter
+       // to "/" before starting (eg: on windows).
+       srcdir = filepath.ToSlash(srcdir)
+
+       chunks := strings.Split(str, "${SRCDIR}")
+       if len(chunks) < 2 {
+               return str, safeCgoName(str)
+       }
+       ok := true
+       for _, chunk := range chunks {
+               ok = ok && (chunk == "" || safeCgoName(chunk))
+       }
+       ok = ok && (srcdir == "" || safeCgoName(srcdir))
+       res := strings.Join(chunks, srcdir)
+       return res, ok && res != ""
+}
+
+// makePathsAbsolute looks for compiler options that take paths and
+// makes them absolute. We do this because through the 1.8 release we
+// ran the compiler in the package directory, so any relative -I or -L
+// options would be relative to that directory. In 1.9 we changed to
+// running the compiler in the build directory, to get consistent
+// build results (issue #19964). To keep builds working, we change any
+// relative -I or -L options to be absolute.
+//
+// Using filepath.IsAbs and filepath.Join here means the results will be
+// different on different systems, but that's OK: -I and -L options are
+// inherently system-dependent.
+func (ctxt *Context) makePathsAbsolute(args []string, srcDir string) {
+       nextPath := false
+       for i, arg := range args {
+               if nextPath {
+                       if !filepath.IsAbs(arg) {
+                               args[i] = filepath.Join(srcDir, arg)
+                       }
+                       nextPath = false
+               } else if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
+                       if len(arg) == 2 {
+                               nextPath = true
+                       } else {
+                               if !filepath.IsAbs(arg[2:]) {
+                                       args[i] = arg[:2] + filepath.Join(srcDir, arg[2:])
+                               }
+                       }
+               }
+       }
+}
+
+// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
+// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
+// See golang.org/issue/6038.
+// The @ is for OS X. See golang.org/issue/13720.
+// The % is for Jenkins. See golang.org/issue/16959.
+// The ! is because module paths may use them. See golang.org/issue/26716.
+// The ~ and ^ are for sr.ht. See golang.org/issue/32260.
+const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^"
+
+func safeCgoName(s string) bool {
+       if s == "" {
+               return false
+       }
+       for i := 0; i < len(s); i++ {
+               if c := s[i]; c < utf8.RuneSelf && strings.IndexByte(safeString, c) < 0 {
+                       return false
+               }
+       }
+       return true
+}
+
+// splitQuoted splits the string s around each instance of one or more consecutive
+// white space characters while taking into account quotes and escaping, and
+// returns an array of substrings of s or an empty list if s contains only white space.
+// Single quotes and double quotes are recognized to prevent splitting within the
+// quoted region, and are removed from the resulting substrings. If a quote in s
+// isn't closed err will be set and r will have the unclosed argument as the
+// last element. The backslash is used for escaping.
+//
+// For example, the following string:
+//
+//     a b:"c d" 'e''f'  "g\""
+//
+// Would be parsed as:
+//
+//     []string{"a", "b:c d", "ef", `g"`}
+func splitQuoted(s string) (r []string, err error) {
+       var args []string
+       arg := make([]rune, len(s))
+       escaped := false
+       quoted := false
+       quote := '\x00'
+       i := 0
+       for _, rune := range s {
+               switch {
+               case escaped:
+                       escaped = false
+               case rune == '\\':
+                       escaped = true
+                       continue
+               case quote != '\x00':
+                       if rune == quote {
+                               quote = '\x00'
+                               continue
+                       }
+               case rune == '"' || rune == '\'':
+                       quoted = true
+                       quote = rune
+                       continue
+               case unicode.IsSpace(rune):
+                       if quoted || i > 0 {
+                               quoted = false
+                               args = append(args, string(arg[:i]))
+                               i = 0
+                       }
+                       continue
+               }
+               arg[i] = rune
+               i++
+       }
+       if quoted || i > 0 {
+               args = append(args, string(arg[:i]))
+       }
+       if quote != 0 {
+               err = errors.New("unclosed quote")
+       } else if escaped {
+               err = errors.New("unfinished escaping")
+       }
+       return args, err
+}
+
+// matchAuto interprets text as either a +build or //go:build expression (whichever works),
+// reporting whether the expression matches the build context.
+//
+// matchAuto is only used for testing of tag evaluation
+// and in #cgo lines, which accept either syntax.
+func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool {
+       if strings.ContainsAny(text, "&|()") {
+               text = "//go:build " + text
+       } else {
+               text = "// +build " + text
+       }
+       x, err := constraint.Parse(text)
+       if err != nil {
+               return false
+       }
+       return ctxt.eval(x, allTags)
+}
+
+func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool {
+       return x.Eval(func(tag string) bool { return ctxt.matchTag(tag, allTags) })
+}
+
+// matchTag reports whether the name is one of:
+//
+//     cgo (if cgo is enabled)
+//     $GOOS
+//     $GOARCH
+//     boringcrypto
+//     ctxt.Compiler
+//     linux (if GOOS = android)
+//     solaris (if GOOS = illumos)
+//     tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags)
+//
+// It records all consulted tags in allTags.
+func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
+       if allTags != nil {
+               allTags[name] = true
+       }
+
+       // special tags
+       if ctxt.CgoEnabled && name == "cgo" {
+               return true
+       }
+       if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
+               return true
+       }
+       if ctxt.GOOS == "android" && name == "linux" {
+               return true
+       }
+       if ctxt.GOOS == "illumos" && name == "solaris" {
+               return true
+       }
+       if ctxt.GOOS == "ios" && name == "darwin" {
+               return true
+       }
+       if name == "unix" && unixOS[ctxt.GOOS] {
+               return true
+       }
+       if name == "boringcrypto" {
+               name = "goexperiment.boringcrypto" // boringcrypto is an old name for goexperiment.boringcrypto
+       }
+
+       // other tags
+       for _, tag := range ctxt.BuildTags {
+               if tag == name {
+                       return true
+               }
+       }
+       for _, tag := range ctxt.ToolTags {
+               if tag == name {
+                       return true
+               }
+       }
+       for _, tag := range ctxt.ReleaseTags {
+               if tag == name {
+                       return true
+               }
+       }
+
+       return false
+}
+
+// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
+// suffix which does not match the current system.
+// The recognized name formats are:
+//
+//     name_$(GOOS).*
+//     name_$(GOARCH).*
+//     name_$(GOOS)_$(GOARCH).*
+//     name_$(GOOS)_test.*
+//     name_$(GOARCH)_test.*
+//     name_$(GOOS)_$(GOARCH)_test.*
+//
+// Exceptions:
+// if GOOS=android, then files with GOOS=linux are also matched.
+// if GOOS=illumos, then files with GOOS=solaris are also matched.
+// if GOOS=ios, then files with GOOS=darwin are also matched.
+func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
+       name, _, _ = strings.Cut(name, ".")
+
+       // Before Go 1.4, a file called "linux.go" would be equivalent to having a
+       // build tag "linux" in that file. For Go 1.4 and beyond, we require this
+       // auto-tagging to apply only to files with a non-empty prefix, so
+       // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
+       // systems, such as android, to arrive without breaking existing code with
+       // innocuous source code in "android.go". The easiest fix: cut everything
+       // in the name before the initial _.
+       i := strings.Index(name, "_")
+       if i < 0 {
+               return true
+       }
+       name = name[i:] // ignore everything before first _
+
+       l := strings.Split(name, "_")
+       if n := len(l); n > 0 && l[n-1] == "test" {
+               l = l[:n-1]
+       }
+       n := len(l)
+       if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+               if allTags != nil {
+                       // In case we short-circuit on l[n-1].
+                       allTags[l[n-2]] = true
+               }
+               return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags)
+       }
+       if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) {
+               return ctxt.matchTag(l[n-1], allTags)
+       }
+       return true
+}
+
+// ToolDir is the directory containing build tools.
+var ToolDir = getToolDir()
+
+// IsLocalImport reports whether the import path is
+// a local import path, like ".", "..", "./foo", or "../foo".
+func IsLocalImport(path string) bool {
+       return path == "." || path == ".." ||
+               strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
+}
+
+// ArchChar returns "?" and an error.
+// In earlier versions of Go, the returned string was used to derive
+// the compiler and linker tool names, the default object file suffix,
+// and the default linker output name. As of Go 1.5, those strings
+// no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
+func ArchChar(goarch string) (string, error) {
+       return "?", errors.New("architecture letter no longer used")
+}
diff --git a/src/cmd/go/internal/modindex/build_read.go b/src/cmd/go/internal/modindex/build_read.go
new file mode 100644 (file)
index 0000000..52adfea
--- /dev/null
@@ -0,0 +1,578 @@
+// Copyright 2012 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 build
+
+import (
+       "bufio"
+       "bytes"
+       "errors"
+       "fmt"
+       "go/ast"
+       "go/parser"
+       "go/token"
+       "io"
+       "strconv"
+       "strings"
+       "unicode"
+       "unicode/utf8"
+)
+
+type importReader struct {
+       b    *bufio.Reader
+       buf  []byte
+       peek byte
+       err  error
+       eof  bool
+       nerr int
+       pos  token.Position
+}
+
+var bom = []byte{0xef, 0xbb, 0xbf}
+
+func newImportReader(name string, r io.Reader) *importReader {
+       b := bufio.NewReader(r)
+       // Remove leading UTF-8 BOM.
+       // Per https://golang.org/ref/spec#Source_code_representation:
+       // a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF)
+       // if it is the first Unicode code point in the source text.
+       if leadingBytes, err := b.Peek(3); err == nil && bytes.Equal(leadingBytes, bom) {
+               b.Discard(3)
+       }
+       return &importReader{
+               b: b,
+               pos: token.Position{
+                       Filename: name,
+                       Line:     1,
+                       Column:   1,
+               },
+       }
+}
+
+func isIdent(c byte) bool {
+       return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
+}
+
+var (
+       errSyntax = errors.New("syntax error")
+       errNUL    = errors.New("unexpected NUL in input")
+)
+
+// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
+func (r *importReader) syntaxError() {
+       if r.err == nil {
+               r.err = errSyntax
+       }
+}
+
+// readByte reads the next byte from the input, saves it in buf, and returns it.
+// If an error occurs, readByte records the error in r.err and returns 0.
+func (r *importReader) readByte() byte {
+       c, err := r.b.ReadByte()
+       if err == nil {
+               r.buf = append(r.buf, c)
+               if c == 0 {
+                       err = errNUL
+               }
+       }
+       if err != nil {
+               if err == io.EOF {
+                       r.eof = true
+               } else if r.err == nil {
+                       r.err = err
+               }
+               c = 0
+       }
+       return c
+}
+
+// readByteNoBuf is like readByte but doesn't buffer the byte.
+// It exhausts r.buf before reading from r.b.
+func (r *importReader) readByteNoBuf() byte {
+       var c byte
+       var err error
+       if len(r.buf) > 0 {
+               c = r.buf[0]
+               r.buf = r.buf[1:]
+       } else {
+               c, err = r.b.ReadByte()
+               if err == nil && c == 0 {
+                       err = errNUL
+               }
+       }
+
+       if err != nil {
+               if err == io.EOF {
+                       r.eof = true
+               } else if r.err == nil {
+                       r.err = err
+               }
+               return 0
+       }
+       r.pos.Offset++
+       if c == '\n' {
+               r.pos.Line++
+               r.pos.Column = 1
+       } else {
+               r.pos.Column++
+       }
+       return c
+}
+
+// peekByte returns the next byte from the input reader but does not advance beyond it.
+// If skipSpace is set, peekByte skips leading spaces and comments.
+func (r *importReader) peekByte(skipSpace bool) byte {
+       if r.err != nil {
+               if r.nerr++; r.nerr > 10000 {
+                       panic("go/build: import reader looping")
+               }
+               return 0
+       }
+
+       // Use r.peek as first input byte.
+       // Don't just return r.peek here: it might have been left by peekByte(false)
+       // and this might be peekByte(true).
+       c := r.peek
+       if c == 0 {
+               c = r.readByte()
+       }
+       for r.err == nil && !r.eof {
+               if skipSpace {
+                       // For the purposes of this reader, semicolons are never necessary to
+                       // understand the input and are treated as spaces.
+                       switch c {
+                       case ' ', '\f', '\t', '\r', '\n', ';':
+                               c = r.readByte()
+                               continue
+
+                       case '/':
+                               c = r.readByte()
+                               if c == '/' {
+                                       for c != '\n' && r.err == nil && !r.eof {
+                                               c = r.readByte()
+                                       }
+                               } else if c == '*' {
+                                       var c1 byte
+                                       for (c != '*' || c1 != '/') && r.err == nil {
+                                               if r.eof {
+                                                       r.syntaxError()
+                                               }
+                                               c, c1 = c1, r.readByte()
+                                       }
+                               } else {
+                                       r.syntaxError()
+                               }
+                               c = r.readByte()
+                               continue
+                       }
+               }
+               break
+       }
+       r.peek = c
+       return r.peek
+}
+
+// nextByte is like peekByte but advances beyond the returned byte.
+func (r *importReader) nextByte(skipSpace bool) byte {
+       c := r.peekByte(skipSpace)
+       r.peek = 0
+       return c
+}
+
+var goEmbed = []byte("go:embed")
+
+// findEmbed advances the input reader to the next //go:embed comment.
+// It reports whether it found a comment.
+// (Otherwise it found an error or EOF.)
+func (r *importReader) findEmbed(first bool) bool {
+       // The import block scan stopped after a non-space character,
+       // so the reader is not at the start of a line on the first call.
+       // After that, each //go:embed extraction leaves the reader
+       // at the end of a line.
+       startLine := !first
+       var c byte
+       for r.err == nil && !r.eof {
+               c = r.readByteNoBuf()
+       Reswitch:
+               switch c {
+               default:
+                       startLine = false
+
+               case '\n':
+                       startLine = true
+
+               case ' ', '\t':
+                       // leave startLine alone
+
+               case '"':
+                       startLine = false
+                       for r.err == nil {
+                               if r.eof {
+                                       r.syntaxError()
+                               }
+                               c = r.readByteNoBuf()
+                               if c == '\\' {
+                                       r.readByteNoBuf()
+                                       if r.err != nil {
+                                               r.syntaxError()
+                                               return false
+                                       }
+                                       continue
+                               }
+                               if c == '"' {
+                                       c = r.readByteNoBuf()
+                                       goto Reswitch
+                               }
+                       }
+                       goto Reswitch
+
+               case '`':
+                       startLine = false
+                       for r.err == nil {
+                               if r.eof {
+                                       r.syntaxError()
+                               }
+                               c = r.readByteNoBuf()
+                               if c == '`' {
+                                       c = r.readByteNoBuf()
+                                       goto Reswitch
+                               }
+                       }
+
+               case '\'':
+                       startLine = false
+                       for r.err == nil {
+                               if r.eof {
+                                       r.syntaxError()
+                               }
+                               c = r.readByteNoBuf()
+                               if c == '\\' {
+                                       r.readByteNoBuf()
+                                       if r.err != nil {
+                                               r.syntaxError()
+                                               return false
+                                       }
+                                       continue
+                               }
+                               if c == '\'' {
+                                       c = r.readByteNoBuf()
+                                       goto Reswitch
+                               }
+                       }
+
+               case '/':
+                       c = r.readByteNoBuf()
+                       switch c {
+                       default:
+                               startLine = false
+                               goto Reswitch
+
+                       case '*':
+                               var c1 byte
+                               for (c != '*' || c1 != '/') && r.err == nil {
+                                       if r.eof {
+                                               r.syntaxError()
+                                       }
+                                       c, c1 = c1, r.readByteNoBuf()
+                               }
+                               startLine = false
+
+                       case '/':
+                               if startLine {
+                                       // Try to read this as a //go:embed comment.
+                                       for i := range goEmbed {
+                                               c = r.readByteNoBuf()
+                                               if c != goEmbed[i] {
+                                                       goto SkipSlashSlash
+                                               }
+                                       }
+                                       c = r.readByteNoBuf()
+                                       if c == ' ' || c == '\t' {
+                                               // Found one!
+                                               return true
+                                       }
+                               }
+                       SkipSlashSlash:
+                               for c != '\n' && r.err == nil && !r.eof {
+                                       c = r.readByteNoBuf()
+                               }
+                               startLine = true
+                       }
+               }
+       }
+       return false
+}
+
+// readKeyword reads the given keyword from the input.
+// If the keyword is not present, readKeyword records a syntax error.
+func (r *importReader) readKeyword(kw string) {
+       r.peekByte(true)
+       for i := 0; i < len(kw); i++ {
+               if r.nextByte(false) != kw[i] {
+                       r.syntaxError()
+                       return
+               }
+       }
+       if isIdent(r.peekByte(false)) {
+               r.syntaxError()
+       }
+}
+
+// readIdent reads an identifier from the input.
+// If an identifier is not present, readIdent records a syntax error.
+func (r *importReader) readIdent() {
+       c := r.peekByte(true)
+       if !isIdent(c) {
+               r.syntaxError()
+               return
+       }
+       for isIdent(r.peekByte(false)) {
+               r.peek = 0
+       }
+}
+
+// readString reads a quoted string literal from the input.
+// If an identifier is not present, readString records a syntax error.
+func (r *importReader) readString() {
+       switch r.nextByte(true) {
+       case '`':
+               for r.err == nil {
+                       if r.nextByte(false) == '`' {
+                               break
+                       }
+                       if r.eof {
+                               r.syntaxError()
+                       }
+               }
+       case '"':
+               for r.err == nil {
+                       c := r.nextByte(false)
+                       if c == '"' {
+                               break
+                       }
+                       if r.eof || c == '\n' {
+                               r.syntaxError()
+                       }
+                       if c == '\\' {
+                               r.nextByte(false)
+                       }
+               }
+       default:
+               r.syntaxError()
+       }
+}
+
+// readImport reads an import clause - optional identifier followed by quoted string -
+// from the input.
+func (r *importReader) readImport() {
+       c := r.peekByte(true)
+       if c == '.' {
+               r.peek = 0
+       } else if isIdent(c) {
+               r.readIdent()
+       }
+       r.readString()
+}
+
+// readComments is like io.ReadAll, except that it only reads the leading
+// block of comments in the file.
+func readComments(f io.Reader) ([]byte, error) {
+       r := newImportReader("", f)
+       r.peekByte(true)
+       if r.err == nil && !r.eof {
+               // Didn't reach EOF, so must have found a non-space byte. Remove it.
+               r.buf = r.buf[:len(r.buf)-1]
+       }
+       return r.buf, r.err
+}
+
+// readGoInfo expects a Go file as input and reads the file up to and including the import section.
+// It records what it learned in *info.
+// If info.fset is non-nil, readGoInfo parses the file and sets info.parsed, info.parseErr,
+// info.imports and info.embeds.
+//
+// It only returns an error if there are problems reading the file,
+// not for syntax errors in the file itself.
+func readGoInfo(f io.Reader, info *fileInfo) error {
+       r := newImportReader(info.name, f)
+
+       r.readKeyword("package")
+       r.readIdent()
+       for r.peekByte(true) == 'i' {
+               r.readKeyword("import")
+               if r.peekByte(true) == '(' {
+                       r.nextByte(false)
+                       for r.peekByte(true) != ')' && r.err == nil {
+                               r.readImport()
+                       }
+                       r.nextByte(false)
+               } else {
+                       r.readImport()
+               }
+       }
+
+       info.header = r.buf
+
+       // If we stopped successfully before EOF, we read a byte that told us we were done.
+       // Return all but that last byte, which would cause a syntax error if we let it through.
+       if r.err == nil && !r.eof {
+               info.header = r.buf[:len(r.buf)-1]
+       }
+
+       // If we stopped for a syntax error, consume the whole file so that
+       // we are sure we don't change the errors that go/parser returns.
+       if r.err == errSyntax {
+               r.err = nil
+               for r.err == nil && !r.eof {
+                       r.readByte()
+               }
+               info.header = r.buf
+       }
+       if r.err != nil {
+               return r.err
+       }
+
+       if info.fset == nil {
+               return nil
+       }
+
+       // Parse file header & record imports.
+       info.parsed, info.parseErr = parser.ParseFile(info.fset, info.name, info.header, parser.ImportsOnly|parser.ParseComments)
+       if info.parseErr != nil {
+               return nil
+       }
+
+       hasEmbed := false
+       for _, decl := range info.parsed.Decls {
+               d, ok := decl.(*ast.GenDecl)
+               if !ok {
+                       continue
+               }
+               for _, dspec := range d.Specs {
+                       spec, ok := dspec.(*ast.ImportSpec)
+                       if !ok {
+                               continue
+                       }
+                       quoted := spec.Path.Value
+                       path, err := strconv.Unquote(quoted)
+                       if err != nil {
+                               return fmt.Errorf("parser returned invalid quoted string: <%s>", quoted)
+                       }
+                       if path == "embed" {
+                               hasEmbed = true
+                       }
+
+                       doc := spec.Doc
+                       if doc == nil && len(d.Specs) == 1 {
+                               doc = d.Doc
+                       }
+                       info.imports = append(info.imports, fileImport{path, spec.Pos(), doc})
+               }
+       }
+
+       // If the file imports "embed",
+       // we have to look for //go:embed comments
+       // in the remainder of the file.
+       // The compiler will enforce the mapping of comments to
+       // declared variables. We just need to know the patterns.
+       // If there were //go:embed comments earlier in the file
+       // (near the package statement or imports), the compiler
+       // will reject them. They can be (and have already been) ignored.
+       if hasEmbed {
+               var line []byte
+               for first := true; r.findEmbed(first); first = false {
+                       line = line[:0]
+                       pos := r.pos
+                       for {
+                               c := r.readByteNoBuf()
+                               if c == '\n' || r.err != nil || r.eof {
+                                       break
+                               }
+                               line = append(line, c)
+                       }
+                       // Add args if line is well-formed.
+                       // Ignore badly-formed lines - the compiler will report them when it finds them,
+                       // and we can pretend they are not there to help go list succeed with what it knows.
+                       embs, err := parseGoEmbed(string(line), pos)
+                       if err == nil {
+                               info.embeds = append(info.embeds, embs...)
+                       }
+               }
+       }
+
+       return nil
+}
+
+// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
+// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
+// This is based on a similar function in cmd/compile/internal/gc/noder.go;
+// this version calculates position information as well.
+func parseGoEmbed(args string, pos token.Position) ([]fileEmbed, error) {
+       trimBytes := func(n int) {
+               pos.Offset += n
+               pos.Column += utf8.RuneCountInString(args[:n])
+               args = args[n:]
+       }
+       trimSpace := func() {
+               trim := strings.TrimLeftFunc(args, unicode.IsSpace)
+               trimBytes(len(args) - len(trim))
+       }
+
+       var list []fileEmbed
+       for trimSpace(); args != ""; trimSpace() {
+               var path string
+               pathPos := pos
+       Switch:
+               switch args[0] {
+               default:
+                       i := len(args)
+                       for j, c := range args {
+                               if unicode.IsSpace(c) {
+                                       i = j
+                                       break
+                               }
+                       }
+                       path = args[:i]
+                       trimBytes(i)
+
+               case '`':
+                       var ok bool
+                       path, _, ok = strings.Cut(args[1:], "`")
+                       if !ok {
+                               return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+                       }
+                       trimBytes(1 + len(path) + 1)
+
+               case '"':
+                       i := 1
+                       for ; i < len(args); i++ {
+                               if args[i] == '\\' {
+                                       i++
+                                       continue
+                               }
+                               if args[i] == '"' {
+                                       q, err := strconv.Unquote(args[:i+1])
+                                       if err != nil {
+                                               return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
+                                       }
+                                       path = q
+                                       trimBytes(i + 1)
+                                       break Switch
+                               }
+                       }
+                       if i >= len(args) {
+                               return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+                       }
+               }
+
+               if args != "" {
+                       r, _ := utf8.DecodeRuneInString(args)
+                       if !unicode.IsSpace(r) {
+                               return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+                       }
+               }
+               list = append(list, fileEmbed{path, pathPos})
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modindex/stubs.go b/src/cmd/go/internal/modindex/stubs.go
new file mode 100644 (file)
index 0000000..8ccf254
--- /dev/null
@@ -0,0 +1,10 @@
+package build
+
+// This file is being added for this cl only just to get builds to pass and
+// let the other files be submitted unchanged.
+
+const defaultCGO_ENABLED = ""
+
+var cgoEnabled = map[string]bool{}
+
+func getToolDir() string { return "" }
diff --git a/src/cmd/go/internal/modindex/syslist.go b/src/cmd/go/internal/modindex/syslist.go
new file mode 100644 (file)
index 0000000..ea67662
--- /dev/null
@@ -0,0 +1,75 @@
+// 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 build
+
+// knownOS is the list of past, present, and future known GOOS values.
+// Do not remove from this list, as it is used for filename matching.
+// If you add an entry to this list, look at unixOS, below.
+var knownOS = map[string]bool{
+       "aix":       true,
+       "android":   true,
+       "darwin":    true,
+       "dragonfly": true,
+       "freebsd":   true,
+       "hurd":      true,
+       "illumos":   true,
+       "ios":       true,
+       "js":        true,
+       "linux":     true,
+       "nacl":      true,
+       "netbsd":    true,
+       "openbsd":   true,
+       "plan9":     true,
+       "solaris":   true,
+       "windows":   true,
+       "zos":       true,
+}
+
+// unixOS is the set of GOOS values matched by the "unix" build tag.
+// This is not used for filename matching.
+// This list also appears in cmd/dist/build.go.
+var unixOS = map[string]bool{
+       "aix":       true,
+       "android":   true,
+       "darwin":    true,
+       "dragonfly": true,
+       "freebsd":   true,
+       "hurd":      true,
+       "illumos":   true,
+       "ios":       true,
+       "linux":     true,
+       "netbsd":    true,
+       "openbsd":   true,
+       "solaris":   true,
+}
+
+// knownArch is the list of past, present, and future known GOARCH values.
+// Do not remove from this list, as it is used for filename matching.
+var knownArch = map[string]bool{
+       "386":         true,
+       "amd64":       true,
+       "amd64p32":    true,
+       "arm":         true,
+       "armbe":       true,
+       "arm64":       true,
+       "arm64be":     true,
+       "loong64":     true,
+       "mips":        true,
+       "mipsle":      true,
+       "mips64":      true,
+       "mips64le":    true,
+       "mips64p32":   true,
+       "mips64p32le": true,
+       "ppc":         true,
+       "ppc64":       true,
+       "ppc64le":     true,
+       "riscv":       true,
+       "riscv64":     true,
+       "s390":        true,
+       "s390x":       true,
+       "sparc":       true,
+       "sparc64":     true,
+       "wasm":        true,
+}
diff --git a/src/cmd/go/internal/modindex/syslist_test.go b/src/cmd/go/internal/modindex/syslist_test.go
new file mode 100644 (file)
index 0000000..2b7b4c7
--- /dev/null
@@ -0,0 +1,62 @@
+// 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 build
+
+import (
+       "runtime"
+       "testing"
+)
+
+var (
+       thisOS    = runtime.GOOS
+       thisArch  = runtime.GOARCH
+       otherOS   = anotherOS()
+       otherArch = anotherArch()
+)
+
+func anotherOS() string {
+       if thisOS != "darwin" && thisOS != "ios" {
+               return "darwin"
+       }
+       return "linux"
+}
+
+func anotherArch() string {
+       if thisArch != "amd64" {
+               return "amd64"
+       }
+       return "386"
+}
+
+type GoodFileTest struct {
+       name   string
+       result bool
+}
+
+var tests = []GoodFileTest{
+       {"file.go", true},
+       {"file.c", true},
+       {"file_foo.go", true},
+       {"file_" + thisArch + ".go", true},
+       {"file_" + otherArch + ".go", false},
+       {"file_" + thisOS + ".go", true},
+       {"file_" + otherOS + ".go", false},
+       {"file_" + thisOS + "_" + thisArch + ".go", true},
+       {"file_" + otherOS + "_" + thisArch + ".go", false},
+       {"file_" + thisOS + "_" + otherArch + ".go", false},
+       {"file_" + otherOS + "_" + otherArch + ".go", false},
+       {"file_foo_" + thisArch + ".go", true},
+       {"file_foo_" + otherArch + ".go", false},
+       {"file_" + thisOS + ".c", true},
+       {"file_" + otherOS + ".c", false},
+}
+
+func TestGoodOSArch(t *testing.T) {
+       for _, test := range tests {
+               if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result {
+                       t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
+               }
+       }
+}