x/vgo: Fix parsing of go.mod with packages from gopkg.in This addresses an issue where Parsing of `go.mod` with modules from `gopkg.in` fails. The `go.mod` file is produced automatically, so the first time something like `vgo build` is run (before `go.mod` is created), it succeeds, but the second time it is run, it fails. This fixes the latest issues described in https://github.com/golang/go/issues/24099 The funcs `parseGopkgIn` and `dotV` were pulled in (and slightly modified/simplified) from `vendor/cmd/go/internal/modfetch/gopkgin.go`. I'm sure that this is not the best way to handle the special case of `gopkg.in` (especially compared to the effort put into creating the `VersionFixer`), but it's a starting point that has test cases that prove the autogenerated `go.mod` file can't be parsed. Fixes #24099 Change-Id: Ie0bee2a50b330a9f85c1459ff6eb0bfa43058bf1 GitHub-Last-Rev: f60854418215026cdc3c9292a99e6dfd90557cd2 GitHub-Pull-Request: golang/vgo#4 Reviewed-on: https://go-review.googlesource.com/112277 Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/vendor/cmd/go/internal/modfetch/gopkgin.go b/vendor/cmd/go/internal/modfetch/gopkgin.go index ec72dcd..d49b60b 100644 --- a/vendor/cmd/go/internal/modfetch/gopkgin.go +++ b/vendor/cmd/go/internal/modfetch/gopkgin.go
@@ -9,49 +9,12 @@ import ( "cmd/go/internal/modfetch/codehost" "cmd/go/internal/modfetch/gitrepo" + "cmd/go/internal/modfile" "fmt" - "strings" ) -func parseGopkgIn(path string) (root, repo, major, subdir string, ok bool) { - if !strings.HasPrefix(path, "gopkg.in/") { - return - } - f := strings.Split(path, "/") - if len(f) >= 2 { - if elem, v, ok := dotV(f[1]); ok { - root = strings.Join(f[:2], "/") - repo = "github.com/go-" + elem + "/" + elem - major = v - subdir = strings.Join(f[2:], "/") - return root, repo, major, subdir, true - } - } - if len(f) >= 3 { - if elem, v, ok := dotV(f[2]); ok { - root = strings.Join(f[:3], "/") - repo = "github.com/" + f[1] + "/" + elem - major = v - subdir = strings.Join(f[3:], "/") - return root, repo, major, subdir, true - } - } - return -} - -func dotV(name string) (elem, v string, ok bool) { - i := len(name) - 1 - for i >= 0 && '0' <= name[i] && name[i] <= '9' { - i-- - } - if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 { - return "", "", false - } - return name[:i-1], name[i:], true -} - func gopkginLookup(path string) (codehost.Repo, error) { - root, _, _, _, ok := parseGopkgIn(path) + root, _, _, _, ok := modfile.ParseGopkgIn(path) if !ok { return nil, fmt.Errorf("invalid gopkg.in/ path: %q", path) }
diff --git a/vendor/cmd/go/internal/modfile/gopkgin.go b/vendor/cmd/go/internal/modfile/gopkgin.go new file mode 100644 index 0000000..c94b384 --- /dev/null +++ b/vendor/cmd/go/internal/modfile/gopkgin.go
@@ -0,0 +1,47 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO: Figure out what gopkg.in should do. + +package modfile + +import "strings" + +// ParseGopkgIn splits gopkg.in import paths into their constituent parts +func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return + } + f := strings.Split(path, "/") + if len(f) >= 2 { + if elem, v, ok := dotV(f[1]); ok { + root = strings.Join(f[:2], "/") + repo = "github.com/go-" + elem + "/" + elem + major = v + subdir = strings.Join(f[2:], "/") + return root, repo, major, subdir, true + } + } + if len(f) >= 3 { + if elem, v, ok := dotV(f[2]); ok { + root = strings.Join(f[:3], "/") + repo = "github.com/" + f[1] + "/" + elem + major = v + subdir = strings.Join(f[3:], "/") + return root, repo, major, subdir, true + } + } + return +} + +func dotV(name string) (elem, v string, ok bool) { + i := len(name) - 1 + for i >= 0 && '0' <= name[i] && name[i] <= '9' { + i-- + } + if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 { + return "", "", false + } + return name[:i-1], name[i:], true +}
diff --git a/vendor/cmd/go/internal/modfile/read_test.go b/vendor/cmd/go/internal/modfile/read_test.go index 3c73528..2c617b8 100644 --- a/vendor/cmd/go/internal/modfile/read_test.go +++ b/vendor/cmd/go/internal/modfile/read_test.go
@@ -101,8 +101,11 @@ } pf1, err := Parse(base, data, nil) - if err != nil && base == "testdata/replace2.in" { - t.Errorf("should parse %v: %v", base, err) + if err != nil { + switch base { + case "testdata/replace2.in", "testdata/gopkg.in.golden": + t.Errorf("should parse %v: %v", base, err) + } } if err == nil { pf2, err := Parse(base, ndata, nil)
diff --git a/vendor/cmd/go/internal/modfile/rule.go b/vendor/cmd/go/internal/modfile/rule.go index 11fa36c..bf62d37 100644 --- a/vendor/cmd/go/internal/modfile/rule.go +++ b/vendor/cmd/go/internal/modfile/rule.go
@@ -293,6 +293,10 @@ } func moduleMajorVersion(p string) (string, error) { + if _, _, major, _, ok := ParseGopkgIn(p); ok { + return major, nil + } + start := 0 for i := 0; i < len(p); i++ { if p[i] == '/' {
diff --git a/vendor/cmd/go/internal/modfile/testdata/gopkg.in.golden b/vendor/cmd/go/internal/modfile/testdata/gopkg.in.golden new file mode 100644 index 0000000..41669b3 --- /dev/null +++ b/vendor/cmd/go/internal/modfile/testdata/gopkg.in.golden
@@ -0,0 +1,6 @@ +module x + +require ( + gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 + gopkg.in/yaml.v2 v2.2.1 +)