]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/go/internal/load/pkg.go
[dev.boringcrypto] all: merge master into dev.boringcrypto
[gostls13.git] / src / cmd / go / internal / load / pkg.go
index e89eadc96229bbc1b65b17122f05643423a4eb23..b5a27c63065f303871fe173c416a4a179a8f1dc2 100644 (file)
@@ -6,6 +6,7 @@
 package load
 
 import (
+       "bytes"
        "fmt"
        "go/build"
        "go/token"
@@ -14,6 +15,7 @@ import (
        pathpkg "path"
        "path/filepath"
        "sort"
+       "strconv"
        "strings"
        "unicode"
        "unicode/utf8"
@@ -49,6 +51,7 @@ type PackagePublic struct {
        BinaryOnly    bool   `json:",omitempty"` // package cannot be recompiled
        ForTest       string `json:",omitempty"` // package is only for use in named test
        DepOnly       bool   `json:",omitempty"` // package is only as a dependency, not explicitly listed
+       Export        string `json:",omitempty"` // file containing export data (set by go list -export)
 
        // Stale and StaleReason remain here *only* for the list command.
        // They are only initialized in preparation for list execution.
@@ -179,7 +182,7 @@ func (e *NoGoError) Error() string {
        return "no Go files in " + e.Package.Dir
 }
 
-// Vendored returns the vendor-resolved version of imports,
+// Resolve returns the resolved version of imports,
 // which should be p.TestImports or p.XTestImports, NOT p.Imports.
 // The imports in p.TestImports and p.XTestImports are not recursively
 // loaded during the initial load of p, so they list the imports found in
@@ -189,14 +192,14 @@ func (e *NoGoError) Error() string {
 // can produce better error messages if it starts with the original paths.
 // The initial load of p loads all the non-test imports and rewrites
 // the vendored paths, so nothing should ever call p.vendored(p.Imports).
-func (p *Package) Vendored(imports []string) []string {
+func (p *Package) Resolve(imports []string) []string {
        if len(imports) > 0 && len(p.Imports) > 0 && &imports[0] == &p.Imports[0] {
-               panic("internal error: p.vendored(p.Imports) called")
+               panic("internal error: p.Resolve(p.Imports) called")
        }
        seen := make(map[string]bool)
        var all []string
        for _, path := range imports {
-               path = VendoredImportPath(p, path)
+               path = ResolveImportPath(p, path)
                if !seen[path] {
                        seen[path] = true
                        all = append(all, path)
@@ -397,16 +400,20 @@ func makeImportValid(r rune) rune {
 
 // Mode flags for loadImport and download (in get.go).
 const (
-       // UseVendor means that loadImport should do vendor expansion
-       // (provided the vendoring experiment is enabled).
-       // That is, useVendor means that the import path came from
-       // a source file and has not been vendor-expanded yet.
-       // Every import path should be loaded initially with useVendor,
-       // and then the expanded version (with the /vendor/ in it) gets
-       // recorded as the canonical import path. At that point, future loads
-       // of that package must not pass useVendor, because
+       // ResolveImport means that loadImport should do import path expansion.
+       // That is, ResolveImport means that the import path came from
+       // a source file and has not been expanded yet to account for
+       // vendoring or possible module adjustment.
+       // Every import path should be loaded initially with ResolveImport,
+       // and then the expanded version (for example with the /vendor/ in it)
+       // gets recorded as the canonical import path. At that point, future loads
+       // of that package must not pass ResolveImport, because
        // disallowVendor will reject direct use of paths containing /vendor/.
-       UseVendor = 1 << iota
+       ResolveImport = 1 << iota
+
+       // ResolveModule is for download (part of "go get") and indicates
+       // that the module adjustment should be done, but not vendor adjustment.
+       ResolveModule
 
        // GetTestDeps is for download (part of "go get") and indicates
        // that test dependencies should be fetched too.
@@ -431,12 +438,15 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
        isLocal := build.IsLocalImport(path)
        if isLocal {
                importPath = dirToImportPath(filepath.Join(srcDir, path))
-       } else if mode&UseVendor != 0 {
-               // We do our own vendor resolution, because we want to
+       } else if mode&ResolveImport != 0 {
+               // We do our own path resolution, because we want to
                // find out the key to use in packageCache without the
                // overhead of repeated calls to buildContext.Import.
                // The code is also needed in a few other places anyway.
-               path = VendoredImportPath(parent, path)
+               path = ResolveImportPath(parent, path)
+               importPath = path
+       } else if mode&ResolveModule != 0 {
+               path = ModuleImportPath(parent, path)
                importPath = path
        }
 
@@ -453,7 +463,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
                // Import always returns bp != nil, even if an error occurs,
                // in order to return partial information.
                buildMode := build.ImportComment
-               if mode&UseVendor == 0 || path != origPath {
+               if mode&ResolveImport == 0 || path != origPath {
                        // Not vendoring, or we already found the vendored path.
                        buildMode |= build.IgnoreVendor
                }
@@ -484,7 +494,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
        if perr := disallowInternal(srcDir, p, stk); perr != p {
                return setErrorPos(perr, importPos)
        }
-       if mode&UseVendor != 0 {
+       if mode&ResolveImport != 0 {
                if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
                        return setErrorPos(perr, importPos)
                }
@@ -543,24 +553,31 @@ func isDir(path string) bool {
        return result
 }
 
-// VendoredImportPath returns the expansion of path when it appears in parent.
-// If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path,
-// x/vendor/path, vendor/path, or else stay path if none of those exist.
-// VendoredImportPath returns the expanded path or, if no expansion is found, the original.
-func VendoredImportPath(parent *Package, path string) (found string) {
-       if parent == nil || parent.Root == "" {
-               return path
-       }
+// ResolveImportPath returns the true meaning of path when it appears in parent.
+// There are two different resolutions applied.
+// First, there is Go 1.5 vendoring (golang.org/s/go15vendor).
+// If vendor expansion doesn't trigger, then the path is also subject to
+// Go 1.11 vgo legacy conversion (golang.org/issue/25069).
+func ResolveImportPath(parent *Package, path string) (found string) {
+       found = VendoredImportPath(parent, path)
+       if found != path {
+               return found
+       }
+       return ModuleImportPath(parent, path)
+}
 
-       dir := filepath.Clean(parent.Dir)
-       root := filepath.Join(parent.Root, "src")
-       if !str.HasFilePathPrefix(dir, root) || parent.ImportPath != "command-line-arguments" && filepath.Join(root, parent.ImportPath) != dir {
+// dirAndRoot returns the source directory and workspace root
+// for the package p, guaranteeing that root is a path prefix of dir.
+func dirAndRoot(p *Package) (dir, root string) {
+       dir = filepath.Clean(p.Dir)
+       root = filepath.Join(p.Root, "src")
+       if !str.HasFilePathPrefix(dir, root) || p.ImportPath != "command-line-arguments" && filepath.Join(root, p.ImportPath) != dir {
                // Look for symlinks before reporting error.
                dir = expandPath(dir)
                root = expandPath(root)
        }
 
-       if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || parent.ImportPath != "command-line-arguments" && !parent.Internal.Local && filepath.Join(root, parent.ImportPath) != dir {
+       if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || p.ImportPath != "command-line-arguments" && !p.Internal.Local && filepath.Join(root, p.ImportPath) != dir {
                base.Fatalf("unexpected directory layout:\n"+
                        "       import path: %s\n"+
                        "       root: %s\n"+
@@ -568,14 +585,28 @@ func VendoredImportPath(parent *Package, path string) (found string) {
                        "       expand root: %s\n"+
                        "       expand dir: %s\n"+
                        "       separator: %s",
-                       parent.ImportPath,
-                       filepath.Join(parent.Root, "src"),
-                       filepath.Clean(parent.Dir),
+                       p.ImportPath,
+                       filepath.Join(p.Root, "src"),
+                       filepath.Clean(p.Dir),
                        root,
                        dir,
                        string(filepath.Separator))
        }
 
+       return dir, root
+}
+
+// VendoredImportPath returns the vendor-expansion of path when it appears in parent.
+// If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path,
+// x/vendor/path, vendor/path, or else stay path if none of those exist.
+// VendoredImportPath returns the expanded path or, if no expansion is found, the original.
+func VendoredImportPath(parent *Package, path string) (found string) {
+       if parent == nil || parent.Root == "" {
+               return path
+       }
+
+       dir, root := dirAndRoot(parent)
+
        vpath := "vendor/" + path
        for i := len(dir); i >= len(root); i-- {
                if i < len(dir) && dir[i] != filepath.Separator {
@@ -618,6 +649,164 @@ func VendoredImportPath(parent *Package, path string) (found string) {
        return path
 }
 
+var (
+       modulePrefix   = []byte("\nmodule ")
+       goModPathCache = make(map[string]string)
+)
+
+// goModPath returns the module path in the go.mod in dir, if any.
+func goModPath(dir string) (path string) {
+       path, ok := goModPathCache[dir]
+       if ok {
+               return path
+       }
+       defer func() {
+               goModPathCache[dir] = path
+       }()
+
+       data, err := ioutil.ReadFile(filepath.Join(dir, "go.mod"))
+       if err != nil {
+               return ""
+       }
+       var i int
+       if bytes.HasPrefix(data, modulePrefix[1:]) {
+               i = 0
+       } else {
+               i = bytes.Index(data, modulePrefix)
+               if i < 0 {
+                       return ""
+               }
+               i++
+       }
+       line := data[i:]
+
+       // Cut line at \n, drop trailing \r if present.
+       if j := bytes.IndexByte(line, '\n'); j >= 0 {
+               line = line[:j]
+       }
+       if line[len(line)-1] == '\r' {
+               line = line[:len(line)-1]
+       }
+       line = line[len("module "):]
+
+       // If quoted, unquote.
+       path = strings.TrimSpace(string(line))
+       if path != "" && path[0] == '"' {
+               s, err := strconv.Unquote(path)
+               if err != nil {
+                       return ""
+               }
+               path = s
+       }
+       return path
+}
+
+// findVersionElement returns the slice indices of the final version element /vN in path.
+// If there is no such element, it returns -1, -1.
+func findVersionElement(path string) (i, j int) {
+       j = len(path)
+       for i = len(path) - 1; i >= 0; i-- {
+               if path[i] == '/' {
+                       if isVersionElement(path[i:j]) {
+                               return i, j
+                       }
+                       j = i
+               }
+       }
+       return -1, -1
+}
+
+// isVersionElement reports whether s is a well-formed path version element:
+// v2, v3, v10, etc, but not v0, v05, v1.
+func isVersionElement(s string) bool {
+       if len(s) < 3 || s[0] != '/' || s[1] != 'v' || s[2] == '0' || s[2] == '1' && len(s) == 3 {
+               return false
+       }
+       for i := 2; i < len(s); i++ {
+               if s[i] < '0' || '9' < s[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+// ModuleImportPath translates import paths found in go modules
+// back down to paths that can be resolved in ordinary builds.
+//
+// Define “new” code as code with a go.mod file in the same directory
+// or a parent directory. If an import in new code says x/y/v2/z but
+// x/y/v2/z does not exist and x/y/go.mod says “module x/y/v2”,
+// then go build will read the import as x/y/z instead.
+// See golang.org/issue/25069.
+func ModuleImportPath(parent *Package, path string) (found string) {
+       if parent == nil || parent.Root == "" {
+               return path
+       }
+
+       // If there are no vN elements in path, leave it alone.
+       // (The code below would do the same, but only after
+       // some other file system accesses that we can avoid
+       // here by returning early.)
+       if i, _ := findVersionElement(path); i < 0 {
+               return path
+       }
+
+       dir, root := dirAndRoot(parent)
+
+       // Consider dir and parents, up to and including root.
+       for i := len(dir); i >= len(root); i-- {
+               if i < len(dir) && dir[i] != filepath.Separator {
+                       continue
+               }
+               if goModPath(dir[:i]) != "" {
+                       goto HaveGoMod
+               }
+       }
+       // This code is not in a tree with a go.mod,
+       // so apply no changes to the path.
+       return path
+
+HaveGoMod:
+       // This import is in a tree with a go.mod.
+       // Allow it to refer to code in GOPATH/src/x/y/z as x/y/v2/z
+       // if GOPATH/src/x/y/go.mod says module "x/y/v2",
+
+       // If x/y/v2/z exists, use it unmodified.
+       if bp, _ := cfg.BuildContext.Import(path, "", build.IgnoreVendor); bp.Dir != "" {
+               return path
+       }
+
+       // Otherwise look for a go.mod supplying a version element.
+       // Some version-like elements may appear in paths but not
+       // be module versions; we skip over those to look for module
+       // versions. For example the module m/v2 might have a
+       // package m/v2/api/v1/foo.
+       limit := len(path)
+       for limit > 0 {
+               i, j := findVersionElement(path[:limit])
+               if i < 0 {
+                       return path
+               }
+               if bp, _ := cfg.BuildContext.Import(path[:i], "", build.IgnoreVendor); bp.Dir != "" {
+                       if mpath := goModPath(bp.Dir); mpath != "" {
+                               // Found a valid go.mod file, so we're stopping the search.
+                               // If the path is m/v2/p and we found m/go.mod that says
+                               // "module m/v2", then we return "m/p".
+                               if mpath == path[:j] {
+                                       return path[:i] + path[j:]
+                               }
+                               // Otherwise just return the original path.
+                               // We didn't find anything worth rewriting,
+                               // and the go.mod indicates that we should
+                               // not consider parent directories.
+                               return path
+                       }
+               }
+               limit = i
+       }
+       return path
+}
+
 // hasGoFiles reports whether dir contains any files with names ending in .go.
 // For a vendor check we must exclude directories that contain no .go files.
 // Otherwise it is not possible to vendor just a/b/c and still import the
@@ -1082,7 +1271,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
                if path == "C" {
                        continue
                }
-               p1 := LoadImport(path, p.Dir, p, stk, p.Internal.Build.ImportPos[path], UseVendor)
+               p1 := LoadImport(path, p.Dir, p, stk, p.Internal.Build.ImportPos[path], ResolveImport)
                if p.Standard && p.Error == nil && !p1.Standard && p1.Error == nil {
                        p.Error = &PackageError{
                                ImportStack: stk.Copy(),