]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: add dark copy of golang.org/x/vgo
authorRuss Cox <rsc@golang.org>
Fri, 8 Jun 2018 17:59:17 +0000 (13:59 -0400)
committerRuss Cox <rsc@golang.org>
Fri, 15 Jun 2018 18:57:58 +0000 (18:57 +0000)
This CL corresponds to golang.org/cl/118096 (7fbc8df48a7)
in the vgo repo.

It copies the bulk of the code from vgo back into the main repo,
but completely disabled - vgo.Init is a no-op and vgo.Enabled
returns false unconditionally.

The point of this CL is to make the two trees easier to diff and
to make future syncs smaller.

Change-Id: Ic34fd5ddd8272a70c5a3b3437b5169e967d0ed03
Reviewed-on: https://go-review.googlesource.com/118095
Reviewed-by: Bryan C. Mills <bcmills@google.com>
125 files changed:
src/cmd/go/go_test.go
src/cmd/go/internal/dirhash/hash.go [new file with mode: 0644]
src/cmd/go/internal/dirhash/hash_test.go [new file with mode: 0644]
src/cmd/go/internal/envcmd/env.go
src/cmd/go/internal/fix/fix.go
src/cmd/go/internal/fmtcmd/fmt.go
src/cmd/go/internal/generate/generate.go
src/cmd/go/internal/get/get.go
src/cmd/go/internal/help/help.go
src/cmd/go/internal/imports/build.go [new file with mode: 0644]
src/cmd/go/internal/imports/read.go [new file with mode: 0644]
src/cmd/go/internal/imports/read_test.go [new file with mode: 0644]
src/cmd/go/internal/imports/scan.go [new file with mode: 0644]
src/cmd/go/internal/imports/scan_test.go [new file with mode: 0644]
src/cmd/go/internal/imports/tags.go [new file with mode: 0644]
src/cmd/go/internal/imports/testdata/import1/x.go [new file with mode: 0644]
src/cmd/go/internal/imports/testdata/import1/x1.go [new file with mode: 0644]
src/cmd/go/internal/imports/testdata/import1/x_darwin.go [new file with mode: 0644]
src/cmd/go/internal/imports/testdata/import1/x_windows.go [new file with mode: 0644]
src/cmd/go/internal/load/path.go
src/cmd/go/internal/load/pkg.go
src/cmd/go/internal/load/search.go
src/cmd/go/internal/modconv/dep.go [new file with mode: 0644]
src/cmd/go/internal/modconv/glide.go [new file with mode: 0644]
src/cmd/go/internal/modconv/glock.go [new file with mode: 0644]
src/cmd/go/internal/modconv/godeps.go [new file with mode: 0644]
src/cmd/go/internal/modconv/modconv.go [new file with mode: 0644]
src/cmd/go/internal/modconv/modconv_test.go [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/cockroach.glock [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/cockroach.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/dockermachine.godeps [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/dockermachine.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/dockerman.glide [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/dockerman.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/govmomi.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/govmomi.vmanifest [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/juju.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/juju.tsv [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/moby.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/moby.vconf [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/panicparse.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/panicparse.vyml [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/prometheus.out [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/prometheus.vjson [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/upspin.dep [new file with mode: 0644]
src/cmd/go/internal/modconv/testdata/upspin.out [new file with mode: 0644]
src/cmd/go/internal/modconv/tsv.go [new file with mode: 0644]
src/cmd/go/internal/modconv/vconf.go [new file with mode: 0644]
src/cmd/go/internal/modconv/vjson.go [new file with mode: 0644]
src/cmd/go/internal/modconv/vmanifest.go [new file with mode: 0644]
src/cmd/go/internal/modconv/vyml.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/bitbucket/fetch.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/codehost/codehost.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/coderepo.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/coderepo_test.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/convert.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/convert_test.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/domain.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/github/fetch.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/gitrepo/fetch.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/gitrepo/fetch_test.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/googlesource/fetch.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/gopkgin.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/noweb.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/proxy.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/query.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/repo.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/unzip.go [new file with mode: 0644]
src/cmd/go/internal/modfetch/web.go [new file with mode: 0644]
src/cmd/go/internal/modfile/gopkgin.go [new file with mode: 0644]
src/cmd/go/internal/modfile/print.go [new file with mode: 0644]
src/cmd/go/internal/modfile/read.go [new file with mode: 0644]
src/cmd/go/internal/modfile/read_test.go [new file with mode: 0644]
src/cmd/go/internal/modfile/rule.go [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/block.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/block.in [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/comment.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/comment.in [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/empty.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/empty.in [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/gopkg.in.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/module.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/module.in [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/replace.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/replace.in [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/replace2.golden [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/replace2.in [new file with mode: 0644]
src/cmd/go/internal/modfile/testdata/rule1.golden [new file with mode: 0644]
src/cmd/go/internal/modinfo/info.go [new file with mode: 0644]
src/cmd/go/internal/module/module.go [new file with mode: 0644]
src/cmd/go/internal/module/module_test.go [new file with mode: 0644]
src/cmd/go/internal/mvs/mvs.go [new file with mode: 0644]
src/cmd/go/internal/mvs/mvs_test.go [new file with mode: 0644]
src/cmd/go/internal/search/search.go [new file with mode: 0644]
src/cmd/go/internal/search/search_test.go [moved from src/cmd/go/internal/load/match_test.go with 94% similarity]
src/cmd/go/internal/semver/semver.go [new file with mode: 0644]
src/cmd/go/internal/semver/semver_test.go [new file with mode: 0644]
src/cmd/go/internal/vgo/build.go [new file with mode: 0644]
src/cmd/go/internal/vgo/fetch.go [new file with mode: 0644]
src/cmd/go/internal/vgo/get.go [new file with mode: 0644]
src/cmd/go/internal/vgo/init.go [new file with mode: 0644]
src/cmd/go/internal/vgo/list.go [new file with mode: 0644]
src/cmd/go/internal/vgo/load.go [new file with mode: 0644]
src/cmd/go/internal/vgo/search.go [new file with mode: 0644]
src/cmd/go/internal/vgo/vendor.go [new file with mode: 0644]
src/cmd/go/internal/vgo/verify.go [new file with mode: 0644]
src/cmd/go/internal/web2/web.go [new file with mode: 0644]
src/cmd/go/internal/web2/web_test.go [new file with mode: 0644]
src/cmd/go/internal/webtest/test.go [new file with mode: 0644]
src/cmd/go/internal/work/build.go
src/cmd/go/internal/work/exec.go
src/cmd/go/testdata/badmod/go.mod [new file with mode: 0644]
src/cmd/go/testdata/badmod/x.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/go.mod [new file with mode: 0644]
src/cmd/go/testdata/vendormod/v1.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/v2.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/v3.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/w/go.mod [new file with mode: 0644]
src/cmd/go/testdata/vendormod/w/w.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/x/go.mod [new file with mode: 0644]
src/cmd/go/testdata/vendormod/x/x.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/y/go.mod [new file with mode: 0644]
src/cmd/go/testdata/vendormod/y/y.go [new file with mode: 0644]
src/cmd/go/testdata/vendormod/z/go.mod [new file with mode: 0644]
src/cmd/go/testdata/vendormod/z/z.go [new file with mode: 0644]

index 21dc9607d55d6b4ab3c37d3fb27c5aae4693f593..8d486b7a77108f7df3bea790ddd9ba953a8d406d 100644 (file)
@@ -1261,14 +1261,14 @@ func TestInternalPackagesInGOROOTAreRespected(t *testing.T) {
        tg := testgo(t)
        defer tg.cleanup()
        tg.runFail("build", "-v", "./testdata/testinternal")
-       tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrong error message for testdata/testinternal")
+       tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package net/http/internal not allowed`, "wrong error message for testdata/testinternal")
 }
 
 func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) {
        tg := testgo(t)
        defer tg.cleanup()
        tg.runFail("build", "-v", "./testdata/testinternal2")
-       tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrote error message for testdata/testinternal2")
+       tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package .*internal/w not allowed`, "wrote error message for testdata/testinternal2")
 }
 
 func TestRunInternal(t *testing.T) {
@@ -1278,7 +1278,7 @@ func TestRunInternal(t *testing.T) {
        tg.setenv("GOPATH", dir)
        tg.run("run", filepath.Join(dir, "src/run/good.go"))
        tg.runFail("run", filepath.Join(dir, "src/run/bad.go"))
-       tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package not allowed`, "unexpected error for run/bad.go")
+       tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package run/subdir/internal/private not allowed`, "unexpected error for run/bad.go")
 }
 
 func TestRunPkg(t *testing.T) {
diff --git a/src/cmd/go/internal/dirhash/hash.go b/src/cmd/go/internal/dirhash/hash.go
new file mode 100644 (file)
index 0000000..61d8fac
--- /dev/null
@@ -0,0 +1,103 @@
+// 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.
+
+// Package dirhash defines hashes over directory trees.
+package dirhash
+
+import (
+       "archive/zip"
+       "crypto/sha256"
+       "encoding/base64"
+       "errors"
+       "fmt"
+       "io"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
+)
+
+var DefaultHash = Hash1
+
+type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error)
+
+func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) {
+       h := sha256.New()
+       files = append([]string(nil), files...)
+       sort.Strings(files)
+       for _, file := range files {
+               if strings.Contains(file, "\n") {
+                       return "", errors.New("filenames with newlines are not supported")
+               }
+               r, err := open(file)
+               if err != nil {
+                       return "", err
+               }
+               hf := sha256.New()
+               _, err = io.Copy(hf, r)
+               r.Close()
+               if err != nil {
+                       return "", err
+               }
+               fmt.Fprintf(h, "%x  %s\n", hf.Sum(nil), file)
+       }
+       return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
+}
+
+func HashDir(dir, prefix string, hash Hash) (string, error) {
+       files, err := DirFiles(dir, prefix)
+       if err != nil {
+               return "", err
+       }
+       osOpen := func(name string) (io.ReadCloser, error) {
+               return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix)))
+       }
+       return hash(files, osOpen)
+}
+
+func DirFiles(dir, prefix string) ([]string, error) {
+       var files []string
+       dir = filepath.Clean(dir)
+       err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
+               if err != nil {
+                       return err
+               }
+               if info.IsDir() {
+                       return nil
+               }
+               rel := file
+               if dir != "." {
+                       rel = file[len(dir)+1:]
+               }
+               f := filepath.Join(prefix, rel)
+               files = append(files, filepath.ToSlash(f))
+               return nil
+       })
+       if err != nil {
+               return nil, err
+       }
+       return files, nil
+}
+
+func HashZip(zipfile string, hash Hash) (string, error) {
+       z, err := zip.OpenReader(zipfile)
+       if err != nil {
+               return "", err
+       }
+       defer z.Close()
+       var files []string
+       zfiles := make(map[string]*zip.File)
+       for _, file := range z.File {
+               files = append(files, file.Name)
+               zfiles[file.Name] = file
+       }
+       zipOpen := func(name string) (io.ReadCloser, error) {
+               f := zfiles[name]
+               if f == nil {
+                       return nil, fmt.Errorf("file %q not found in zip", name) // should never happen
+               }
+               return f.Open()
+       }
+       return hash(files, zipOpen)
+}
diff --git a/src/cmd/go/internal/dirhash/hash_test.go b/src/cmd/go/internal/dirhash/hash_test.go
new file mode 100644 (file)
index 0000000..ed463c1
--- /dev/null
@@ -0,0 +1,135 @@
+// 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.
+
+package dirhash
+
+import (
+       "archive/zip"
+       "crypto/sha256"
+       "encoding/base64"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "strings"
+       "testing"
+)
+
+func h(s string) string {
+       return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
+}
+
+func htop(k string, s string) string {
+       sum := sha256.Sum256([]byte(s))
+       return k + ":" + base64.StdEncoding.EncodeToString(sum[:])
+}
+
+func TestHash1(t *testing.T) {
+       files := []string{"xyz", "abc"}
+       open := func(name string) (io.ReadCloser, error) {
+               return ioutil.NopCloser(strings.NewReader("data for " + name)), nil
+       }
+       want := htop("h1", fmt.Sprintf("%s  %s\n%s  %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz"))
+       out, err := Hash1(files, open)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if out != want {
+               t.Errorf("Hash1(...) = %s, want %s", out, want)
+       }
+
+       _, err = Hash1([]string{"xyz", "a\nbc"}, open)
+       if err == nil {
+               t.Error("Hash1: expected error on newline in filenames")
+       }
+}
+
+func TestHashDir(t *testing.T) {
+       dir, err := ioutil.TempDir("", "dirhash-test-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+       if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
+               t.Fatal(err)
+       }
+       want := htop("h1", fmt.Sprintf("%s  %s\n%s  %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
+       out, err := HashDir(dir, "prefix", Hash1)
+       if err != nil {
+               t.Fatalf("HashDir: %v", err)
+       }
+       if out != want {
+               t.Errorf("HashDir(...) = %s, want %s", out, want)
+       }
+}
+
+func TestHashZip(t *testing.T) {
+       f, err := ioutil.TempFile("", "dirhash-test-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.Remove(f.Name())
+       defer f.Close()
+
+       z := zip.NewWriter(f)
+       w, err := z.Create("prefix/xyz")
+       if err != nil {
+               t.Fatal(err)
+       }
+       w.Write([]byte("data for xyz"))
+       w, err = z.Create("prefix/abc")
+       if err != nil {
+               t.Fatal(err)
+       }
+       w.Write([]byte("data for abc"))
+       if err := z.Close(); err != nil {
+               t.Fatal(err)
+       }
+       if err := f.Close(); err != nil {
+               t.Fatal(err)
+       }
+
+       want := htop("h1", fmt.Sprintf("%s  %s\n%s  %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
+       out, err := HashZip(f.Name(), Hash1)
+       if err != nil {
+               t.Fatalf("HashDir: %v", err)
+       }
+       if out != want {
+               t.Errorf("HashDir(...) = %s, want %s", out, want)
+       }
+}
+
+func TestDirFiles(t *testing.T) {
+       dir, err := ioutil.TempDir("", "dirfiles-test-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+       if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
+               t.Fatal(err)
+       }
+       prefix := "foo/bar@v2.3.4"
+       out, err := DirFiles(dir, prefix)
+       if err != nil {
+               t.Fatalf("DirFiles: %v", err)
+       }
+       for _, file := range out {
+               if !strings.HasPrefix(file, prefix) {
+                       t.Errorf("Dir file = %s, want prefix %s", file, prefix)
+               }
+       }
+}
index f682c3a7891273b8f111dfc4c436058e75f0b02b..bd66a98f21fc949ade7d67e896082c7a3199c63d 100644 (file)
@@ -16,6 +16,7 @@ import (
        "cmd/go/internal/cache"
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
+       "cmd/go/internal/vgo"
        "cmd/go/internal/work"
 )
 
@@ -56,6 +57,7 @@ func MkEnv() []cfg.EnvVar {
                {Name: "GOHOSTOS", Value: runtime.GOOS},
                {Name: "GOOS", Value: cfg.Goos},
                {Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
+               {Name: "GOPROXY", Value: os.Getenv("GOPROXY")},
                {Name: "GORACE", Value: os.Getenv("GORACE")},
                {Name: "GOROOT", Value: cfg.GOROOT},
                {Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")},
@@ -131,6 +133,7 @@ func ExtraEnvVars() []cfg.EnvVar {
                {Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")},
                {Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
                {Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")},
+               {Name: "VGOMODROOT", Value: vgo.ModRoot},
        }
 }
 
index 99c7ca51acf38084ba06852bc9c1f9383c4e3b91..56f88329ab08877d03a0f670d78700711c0f463e 100644 (file)
@@ -10,6 +10,9 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
        "cmd/go/internal/str"
+       "cmd/go/internal/vgo"
+       "fmt"
+       "os"
 )
 
 var CmdFix = &base.Command{
@@ -29,7 +32,15 @@ See also: go fmt, go vet.
 }
 
 func runFix(cmd *base.Command, args []string) {
+       printed := false
        for _, pkg := range load.Packages(args) {
+               if vgo.Enabled() && !pkg.Module.Top {
+                       if !printed {
+                               fmt.Fprintf(os.Stderr, "vgo: not fixing packages in dependency modules\n")
+                               printed = true
+                       }
+                       continue
+               }
                // Use pkg.gofiles instead of pkg.Dir so that
                // the command only applies to this package,
                // not to packages in subdirectories.
index eb96823fa6aa12f1f005ea6da3dbe4f7100aaec1..c3a90d069cbae7cfb5eac43d22d32187aec12e77 100644 (file)
@@ -6,6 +6,7 @@
 package fmtcmd
 
 import (
+       "fmt"
        "os"
        "path/filepath"
        "runtime"
@@ -16,6 +17,7 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
        "cmd/go/internal/str"
+       "cmd/go/internal/vgo"
 )
 
 func init() {
@@ -43,6 +45,7 @@ See also: go fix, go vet.
 }
 
 func runFmt(cmd *base.Command, args []string) {
+       printed := false
        gofmt := gofmtPath()
        procs := runtime.GOMAXPROCS(0)
        var wg sync.WaitGroup
@@ -57,6 +60,13 @@ func runFmt(cmd *base.Command, args []string) {
                }()
        }
        for _, pkg := range load.PackagesAndErrors(args) {
+               if vgo.Enabled() && !pkg.Module.Top {
+                       if !printed {
+                               fmt.Fprintf(os.Stderr, "vgo: not formatting packages in dependency modules\n")
+                               printed = true
+                       }
+                       continue
+               }
                if pkg.Error != nil {
                        if strings.HasPrefix(pkg.Error.Err, "build constraints exclude all Go files") {
                                // Skip this error, as we will format
index 971844d2ea15a231ea68e56d206a942b470fadfe..365427d44277c0ac67c00580f91479eeecbeb77f 100644 (file)
@@ -21,6 +21,7 @@ import (
        "cmd/go/internal/base"
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
+       "cmd/go/internal/vgo"
        "cmd/go/internal/work"
 )
 
@@ -158,7 +159,16 @@ func runGenerate(cmd *base.Command, args []string) {
                }
        }
        // Even if the arguments are .go files, this loop suffices.
+       printed := false
        for _, pkg := range load.Packages(args) {
+               if vgo.Enabled() && !pkg.Module.Top {
+                       if !printed {
+                               fmt.Fprintf(os.Stderr, "vgo: not generating in packages in dependency modules\n")
+                               printed = true
+                       }
+                       continue
+               }
+
                pkgName := pkg.Name
 
                for _, file := range pkg.InternalGoFiles() {
index ffa15c5ba48e4c8f5a0e150c54dc24d8a9e5d616..6eabc4eabb7ca75944bff16c3c58fb8eb1f85d99 100644 (file)
@@ -16,7 +16,9 @@ import (
        "cmd/go/internal/base"
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
+       "cmd/go/internal/search"
        "cmd/go/internal/str"
+       "cmd/go/internal/vgo"
        "cmd/go/internal/web"
        "cmd/go/internal/work"
 )
@@ -90,6 +92,10 @@ func init() {
 }
 
 func runGet(cmd *base.Command, args []string) {
+       if vgo.Enabled() {
+               base.Fatalf("go get: vgo not implemented")
+       }
+
        work.BuildInit()
 
        if *getF && !*getU {
@@ -170,7 +176,7 @@ func runGet(cmd *base.Command, args []string) {
 // in the hope that we can figure out the repository from the
 // initial ...-free prefix.
 func downloadPaths(args []string) []string {
-       args = load.ImportPathsNoDotExpansion(args)
+       args = load.ImportPathsForGoGet(args)
        var out []string
        for _, a := range args {
                if strings.Contains(a, "...") {
@@ -179,9 +185,9 @@ func downloadPaths(args []string) []string {
                        // warnings. They will be printed by the
                        // eventual call to importPaths instead.
                        if build.IsLocalImport(a) {
-                               expand = load.MatchPackagesInFS(a)
+                               expand = search.MatchPackagesInFS(a)
                        } else {
-                               expand = load.MatchPackages(a)
+                               expand = search.MatchPackages(a)
                        }
                        if len(expand) > 0 {
                                out = append(out, expand...)
@@ -271,9 +277,9 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
                // for p has been replaced in the package cache.
                if wildcardOkay && strings.Contains(arg, "...") {
                        if build.IsLocalImport(arg) {
-                               args = load.MatchPackagesInFS(arg)
+                               args = search.MatchPackagesInFS(arg)
                        } else {
-                               args = load.MatchPackages(arg)
+                               args = search.MatchPackages(arg)
                        }
                        isWildcard = true
                }
index c79bf8bebba262fab7c771296456774d7c8f60f0..68b2c940d103d2b42ecb68c411f98d1f141a720e 100644 (file)
@@ -64,6 +64,10 @@ func Help(args []string) {
 
 var usageTemplate = `Go is a tool for managing Go source code.
 
+This is vgo, an experimental go command with support for package versioning.
+Even though you are invoking it as vgo, most of the messages printed will
+still say "go", not "vgo". Sorry.
+
 Usage:
 
        go command [arguments]
diff --git a/src/cmd/go/internal/imports/build.go b/src/cmd/go/internal/imports/build.go
new file mode 100644 (file)
index 0000000..5597870
--- /dev/null
@@ -0,0 +1,211 @@
+// 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.
+
+// Copied from Go distribution src/go/build/build.go, syslist.go
+
+package imports
+
+import (
+       "bytes"
+       "strings"
+       "unicode"
+)
+
+var slashslash = []byte("//")
+
+// 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.
+//
+// If tags["*"] is true, then ShouldBuild will consider every
+// build tag except "ignore" to be both true and false for
+// the purpose of satisfying build tags, in order to estimate
+// (conservatively) whether a file could ever possibly be used
+// in any build.
+//
+func ShouldBuild(content []byte, tags map[string]bool) bool {
+       // Pass 1. Identify leading run of // comments and blank lines,
+       // which must be followed by a blank line.
+       end := 0
+       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 len(line) == 0 { // Blank line
+                       end = len(content) - len(p)
+                       continue
+               }
+               if !bytes.HasPrefix(line, slashslash) { // Not comment line
+                       break
+               }
+       }
+       content = content[:end]
+
+       // Pass 2.  Process each line in the run.
+       p = content
+       allok := true
+       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, slashslash) {
+                       continue
+               }
+               line = bytes.TrimSpace(line[len(slashslash):])
+               if len(line) > 0 && line[0] == '+' {
+                       // Looks like a comment +line.
+                       f := strings.Fields(string(line))
+                       if f[0] == "+build" {
+                               ok := false
+                               for _, tok := range f[1:] {
+                                       if matchTags(tok, tags) {
+                                               ok = true
+                                       }
+                               }
+                               if !ok {
+                                       allok = false
+                               }
+                       }
+               }
+       }
+
+       return allok
+}
+
+// matchTags reports whether the name is one of:
+//
+//     tag (if tags[tag] is true)
+//     !tag (if tags[tag] is false)
+//     a comma-separated list of any of these
+//
+func matchTags(name string, tags map[string]bool) bool {
+       if name == "" {
+               return false
+       }
+       if i := strings.Index(name, ","); i >= 0 {
+               // comma-separated list
+               ok1 := matchTags(name[:i], tags)
+               ok2 := matchTags(name[i+1:], tags)
+               return ok1 && ok2
+       }
+       if strings.HasPrefix(name, "!!") { // bad syntax, reject always
+               return false
+       }
+       if strings.HasPrefix(name, "!") { // negation
+               return len(name) > 1 && matchTag(name[1:], tags, false)
+       }
+       return matchTag(name, tags, true)
+}
+
+// matchTag reports whether the tag name is valid and satisfied by tags[name]==want.
+func matchTag(name string, tags map[string]bool, want bool) bool {
+       // Tags must be letters, digits, underscores or dots.
+       // Unlike in Go identifiers, all digits are fine (e.g., "386").
+       for _, c := range name {
+               if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+                       return false
+               }
+       }
+
+       if tags["*"] && name != "" && name != "ignore" {
+               // Special case for gathering all possible imports:
+               // if we put * in the tags map then all tags
+               // except "ignore" are considered both present and not
+               // (so we return true no matter how 'want' is set).
+               return true
+       }
+
+       have := tags[name]
+       if name == "linux" {
+               have = have || tags["android"]
+       }
+       return have == want
+}
+
+// MatchFile 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.*
+//
+// An exception: if GOOS=android, then files with GOOS=linux are also matched.
+//
+// If tags["*"] is true, then MatchFile will consider all possible
+// GOOS and GOARCH to be available and will consequently
+// always return true.
+func MatchFile(name string, tags map[string]bool) bool {
+       if tags["*"] {
+               return true
+       }
+       if dot := strings.Index(name, "."); dot != -1 {
+               name = name[:dot]
+       }
+
+       // 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]] {
+               return tags[l[n-2]] && tags[l[n-1]]
+       }
+       if n >= 1 && knownOS[l[n-1]] {
+               return tags[l[n-1]]
+       }
+       if n >= 1 && knownArch[l[n-1]] {
+               return tags[l[n-1]]
+       }
+       return true
+}
+
+var knownOS = make(map[string]bool)
+var knownArch = make(map[string]bool)
+
+func init() {
+       for _, v := range strings.Fields(goosList) {
+               knownOS[v] = true
+       }
+       for _, v := range strings.Fields(goarchList) {
+               knownArch[v] = true
+       }
+}
+
+const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos "
+const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "
diff --git a/src/cmd/go/internal/imports/read.go b/src/cmd/go/internal/imports/read.go
new file mode 100644 (file)
index 0000000..58c2abd
--- /dev/null
@@ -0,0 +1,249 @@
+// 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.
+
+// Copied from Go distribution src/go/build/read.go.
+
+package imports
+
+import (
+       "bufio"
+       "errors"
+       "io"
+       "unicode/utf8"
+)
+
+type importReader struct {
+       b    *bufio.Reader
+       buf  []byte
+       peek byte
+       err  error
+       eof  bool
+       nerr int
+}
+
+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
+}
+
+// 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
+}
+
+// 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(save *[]string) {
+       switch r.nextByte(true) {
+       case '`':
+               start := len(r.buf) - 1
+               for r.err == nil {
+                       if r.nextByte(false) == '`' {
+                               if save != nil {
+                                       *save = append(*save, string(r.buf[start:]))
+                               }
+                               break
+                       }
+                       if r.eof {
+                               r.syntaxError()
+                       }
+               }
+       case '"':
+               start := len(r.buf) - 1
+               for r.err == nil {
+                       c := r.nextByte(false)
+                       if c == '"' {
+                               if save != nil {
+                                       *save = append(*save, string(r.buf[start:]))
+                               }
+                               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(imports *[]string) {
+       c := r.peekByte(true)
+       if c == '.' {
+               r.peek = 0
+       } else if isIdent(c) {
+               r.readIdent()
+       }
+       r.readString(imports)
+}
+
+// ReadComments is like ioutil.ReadAll, except that it only reads the leading
+// block of comments in the file.
+func ReadComments(f io.Reader) ([]byte, error) {
+       r := &importReader{b: bufio.NewReader(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
+}
+
+// ReadImports is like ioutil.ReadAll, except that it expects a Go file as input
+// and stops reading the input once the imports have completed.
+func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
+       r := &importReader{b: bufio.NewReader(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(imports)
+                       }
+                       r.nextByte(false)
+               } else {
+                       r.readImport(imports)
+               }
+       }
+
+       // 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 {
+               return r.buf[:len(r.buf)-1], nil
+       }
+
+       // 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 && !reportSyntaxError {
+               r.err = nil
+               for r.err == nil && !r.eof {
+                       r.readByte()
+               }
+       }
+
+       return r.buf, r.err
+}
diff --git a/src/cmd/go/internal/imports/read_test.go b/src/cmd/go/internal/imports/read_test.go
new file mode 100644 (file)
index 0000000..6ea356f
--- /dev/null
@@ -0,0 +1,228 @@
+// 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.
+
+// Copied from Go distribution src/go/build/read.go.
+
+package imports
+
+import (
+       "io"
+       "strings"
+       "testing"
+)
+
+const quote = "`"
+
+type readTest struct {
+       // Test input contains ℙ where readImports should stop.
+       in  string
+       err string
+}
+
+var readImportsTests = []readTest{
+       {
+               `package p`,
+               "",
+       },
+       {
+               `package p; import "x"`,
+               "",
+       },
+       {
+               `package p; import . "x"`,
+               "",
+       },
+       {
+               `package p; import "x";ℙvar x = 1`,
+               "",
+       },
+       {
+               `package p
+               
+               // comment
+               
+               import "x"
+               import _ "x"
+               import a "x"
+               
+               /* comment */
+               
+               import (
+                       "x" /* comment */
+                       _ "x"
+                       a "x" // comment
+                       ` + quote + `x` + quote + `
+                       _ /*comment*/ ` + quote + `x` + quote + `
+                       a ` + quote + `x` + quote + `
+               )
+               import (
+               )
+               import ()
+               import()import()import()
+               import();import();import()
+               
+               ℙvar x = 1
+               `,
+               "",
+       },
+}
+
+var readCommentsTests = []readTest{
+       {
+               `ℙpackage p`,
+               "",
+       },
+       {
+               `ℙpackage p; import "x"`,
+               "",
+       },
+       {
+               `ℙpackage p; import . "x"`,
+               "",
+       },
+       {
+               `// foo
+
+               /* bar */
+
+               /* quux */ // baz
+               
+               /*/ zot */
+
+               // asdf
+               ℙHello, world`,
+               "",
+       },
+}
+
+func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
+       for i, tt := range tests {
+               var in, testOut string
+               j := strings.Index(tt.in, "ℙ")
+               if j < 0 {
+                       in = tt.in
+                       testOut = tt.in
+               } else {
+                       in = tt.in[:j] + tt.in[j+len("ℙ"):]
+                       testOut = tt.in[:j]
+               }
+               r := strings.NewReader(in)
+               buf, err := read(r)
+               if err != nil {
+                       if tt.err == "" {
+                               t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
+                               continue
+                       }
+                       if !strings.Contains(err.Error(), tt.err) {
+                               t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
+                               continue
+                       }
+                       continue
+               }
+               if err == nil && tt.err != "" {
+                       t.Errorf("#%d: success, expected %q", i, tt.err)
+                       continue
+               }
+
+               out := string(buf)
+               if out != testOut {
+                       t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
+               }
+       }
+}
+
+func TestReadImports(t *testing.T) {
+       testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
+}
+
+func TestReadComments(t *testing.T) {
+       testRead(t, readCommentsTests, ReadComments)
+}
+
+var readFailuresTests = []readTest{
+       {
+               `package`,
+               "syntax error",
+       },
+       {
+               "package p\n\x00\nimport `math`\n",
+               "unexpected NUL in input",
+       },
+       {
+               `package p; import`,
+               "syntax error",
+       },
+       {
+               `package p; import "`,
+               "syntax error",
+       },
+       {
+               "package p; import ` \n\n",
+               "syntax error",
+       },
+       {
+               `package p; import "x`,
+               "syntax error",
+       },
+       {
+               `package p; import _`,
+               "syntax error",
+       },
+       {
+               `package p; import _ "`,
+               "syntax error",
+       },
+       {
+               `package p; import _ "x`,
+               "syntax error",
+       },
+       {
+               `package p; import .`,
+               "syntax error",
+       },
+       {
+               `package p; import . "`,
+               "syntax error",
+       },
+       {
+               `package p; import . "x`,
+               "syntax error",
+       },
+       {
+               `package p; import (`,
+               "syntax error",
+       },
+       {
+               `package p; import ("`,
+               "syntax error",
+       },
+       {
+               `package p; import ("x`,
+               "syntax error",
+       },
+       {
+               `package p; import ("x"`,
+               "syntax error",
+       },
+}
+
+func TestReadFailures(t *testing.T) {
+       // Errors should be reported (true arg to readImports).
+       testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
+}
+
+func TestReadFailuresIgnored(t *testing.T) {
+       // Syntax errors should not be reported (false arg to readImports).
+       // Instead, entire file should be the output and no error.
+       // Convert tests not to return syntax errors.
+       tests := make([]readTest, len(readFailuresTests))
+       copy(tests, readFailuresTests)
+       for i := range tests {
+               tt := &tests[i]
+               if !strings.Contains(tt.err, "NUL") {
+                       tt.err = ""
+               }
+       }
+       testRead(t, tests, func(r io.Reader) ([]byte, error) { return ReadImports(r, false, nil) })
+}
diff --git a/src/cmd/go/internal/imports/scan.go b/src/cmd/go/internal/imports/scan.go
new file mode 100644 (file)
index 0000000..095bb64
--- /dev/null
@@ -0,0 +1,82 @@
+// 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.
+
+package imports
+
+import (
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "sort"
+       "strconv"
+       "strings"
+)
+
+func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
+       infos, err := ioutil.ReadDir(dir)
+       if err != nil {
+               return nil, nil, err
+       }
+       var files []string
+       for _, info := range infos {
+               name := info.Name()
+               if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) {
+                       files = append(files, filepath.Join(dir, name))
+               }
+       }
+       return scanFiles(files, tags, false)
+}
+
+func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) {
+       return scanFiles(files, tags, true)
+}
+
+func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) {
+       imports := make(map[string]bool)
+       testImports := make(map[string]bool)
+       numFiles := 0
+       for _, name := range files {
+               r, err := os.Open(name)
+               if err != nil {
+                       return nil, nil, err
+               }
+               var list []string
+               data, err := ReadImports(r, false, &list)
+               r.Close()
+               if err != nil {
+                       return nil, nil, fmt.Errorf("reading %s: %v", name, err)
+               }
+               if !explicitFiles && !ShouldBuild(data, tags) {
+                       continue
+               }
+               numFiles++
+               m := imports
+               if strings.HasSuffix(name, "_test.go") {
+                       m = testImports
+               }
+               for _, p := range list {
+                       q, err := strconv.Unquote(p)
+                       if err != nil {
+                               continue
+                       }
+                       m[q] = true
+               }
+       }
+       if numFiles == 0 {
+               return nil, nil, ErrNoGo
+       }
+       return keys(imports), keys(testImports), nil
+}
+
+var ErrNoGo = fmt.Errorf("no Go source files")
+
+func keys(m map[string]bool) []string {
+       var list []string
+       for k := range m {
+               list = append(list, k)
+       }
+       sort.Strings(list)
+       return list
+}
diff --git a/src/cmd/go/internal/imports/scan_test.go b/src/cmd/go/internal/imports/scan_test.go
new file mode 100644 (file)
index 0000000..6a2ff62
--- /dev/null
@@ -0,0 +1,67 @@
+// 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.
+
+package imports
+
+import (
+       "internal/testenv"
+       "path/filepath"
+       "reflect"
+       "runtime"
+       "testing"
+)
+
+func TestScan(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags())
+       if err != nil {
+               t.Fatal(err)
+       }
+       foundBase64 := false
+       for _, p := range imports {
+               if p == "encoding/base64" {
+                       foundBase64 = true
+               }
+               if p == "encoding/binary" {
+                       // A dependency but not an import
+                       t.Errorf("json reported as importing encoding/binary but does not")
+               }
+               if p == "net/http" {
+                       // A test import but not an import
+                       t.Errorf("json reported as importing encoding/binary but does not")
+               }
+       }
+       if !foundBase64 {
+               t.Errorf("json missing import encoding/base64 (%q)", imports)
+       }
+
+       foundHTTP := false
+       for _, p := range testImports {
+               if p == "net/http" {
+                       foundHTTP = true
+               }
+               if p == "unicode/utf16" {
+                       // A package import but not a test import
+                       t.Errorf("json reported as test-importing unicode/utf16  but does not")
+               }
+       }
+       if !foundHTTP {
+               t.Errorf("json missing test import net/http (%q)", testImports)
+       }
+}
+
+func TestScanStar(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true})
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       want := []string{"import1", "import2", "import3", "import4"}
+       if !reflect.DeepEqual(imports, want) {
+               t.Errorf("ScanDir testdata/import1:\nhave %v\nwant %v", imports, want)
+       }
+}
diff --git a/src/cmd/go/internal/imports/tags.go b/src/cmd/go/internal/imports/tags.go
new file mode 100644 (file)
index 0000000..ba0ca94
--- /dev/null
@@ -0,0 +1,35 @@
+// 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.
+
+package imports
+
+import "cmd/go/internal/cfg"
+
+var tags map[string]bool
+
+func Tags() map[string]bool {
+       if tags == nil {
+               tags = loadTags()
+       }
+       return tags
+}
+
+func loadTags() map[string]bool {
+       tags := map[string]bool{
+               cfg.BuildContext.GOOS:     true,
+               cfg.BuildContext.GOARCH:   true,
+               cfg.BuildContext.Compiler: true,
+       }
+       if cfg.BuildContext.CgoEnabled {
+               tags["cgo"] = true
+       }
+       // TODO: Should read these out of GOROOT source code?
+       for _, tag := range cfg.BuildContext.BuildTags {
+               tags[tag] = true
+       }
+       for _, tag := range cfg.BuildContext.ReleaseTags {
+               tags[tag] = true
+       }
+       return tags
+}
diff --git a/src/cmd/go/internal/imports/testdata/import1/x.go b/src/cmd/go/internal/imports/testdata/import1/x.go
new file mode 100644 (file)
index 0000000..98f9191
--- /dev/null
@@ -0,0 +1,3 @@
+package x
+
+import "import1"
diff --git a/src/cmd/go/internal/imports/testdata/import1/x1.go b/src/cmd/go/internal/imports/testdata/import1/x1.go
new file mode 100644 (file)
index 0000000..6a9594a
--- /dev/null
@@ -0,0 +1,9 @@
+// +build blahblh
+// +build linux
+// +build !linux
+// +build windows
+// +build darwin
+
+package x
+
+import "import4"
diff --git a/src/cmd/go/internal/imports/testdata/import1/x_darwin.go b/src/cmd/go/internal/imports/testdata/import1/x_darwin.go
new file mode 100644 (file)
index 0000000..a0c3fdd
--- /dev/null
@@ -0,0 +1,3 @@
+package xxxx
+
+import "import3"
diff --git a/src/cmd/go/internal/imports/testdata/import1/x_windows.go b/src/cmd/go/internal/imports/testdata/import1/x_windows.go
new file mode 100644 (file)
index 0000000..63c5082
--- /dev/null
@@ -0,0 +1,3 @@
+package x
+
+import "import2"
index 45a9e7b242be1fc73cad3e76063797a678b188f0..0211b284a407a2b612299570f1db7380aa05cb31 100644 (file)
@@ -32,22 +32,6 @@ func hasSubdir(root, dir string) (rel string, ok bool) {
        return filepath.ToSlash(dir[len(root):]), true
 }
 
-// hasPathPrefix reports whether the path s begins with the
-// elements in prefix.
-func hasPathPrefix(s, prefix string) bool {
-       switch {
-       default:
-               return false
-       case len(s) == len(prefix):
-               return s == prefix
-       case len(s) > len(prefix):
-               if prefix != "" && prefix[len(prefix)-1] == '/' {
-                       return strings.HasPrefix(s, prefix)
-               }
-               return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
-       }
-}
-
 // expandPath returns the symlink-expanded form of path.
 func expandPath(p string) string {
        x, err := filepath.EvalSymlinks(p)
index 693d862214345301e1901dd66053819e129b9a0b..400b338a206d5da470a45cf1ed86143148e1a85d 100644 (file)
@@ -22,7 +22,10 @@ import (
 
        "cmd/go/internal/base"
        "cmd/go/internal/cfg"
+       "cmd/go/internal/modinfo"
+       "cmd/go/internal/search"
        "cmd/go/internal/str"
+       "cmd/go/internal/vgo"
 )
 
 var IgnoreImports bool // control whether we ignore imports in packages
@@ -99,6 +102,8 @@ type PackagePublic struct {
        TestImports  []string `json:",omitempty"` // imports from TestGoFiles
        XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
        XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
+
+       Module *modinfo.ModulePublic `json:",omitempty"` // info about package module
 }
 
 // AllFiles returns the names of all the files considered for the package.
@@ -148,6 +153,7 @@ type PackageInternal struct {
        CoverVars    map[string]*CoverVar // variables created by coverage analysis
        OmitDebug    bool                 // tell linker not to write debug information
        GobinSubdir  bool                 // install target would be subdir of GOBIN
+       BuildInfo    string               // add this info to package main
        TestmainGo   *[]byte              // content for _testmain.go
 
        Asmflags   []string // -asmflags for this package
@@ -236,7 +242,7 @@ func (p *Package) copyBuild(pp *build.Package) {
 
        // TODO? Target
        p.Goroot = pp.Goroot
-       p.Standard = p.Goroot && p.ImportPath != "" && isStandardImportPath(p.ImportPath)
+       p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath)
        p.GoFiles = pp.GoFiles
        p.CgoFiles = pp.CgoFiles
        p.IgnoredGoFiles = pp.IgnoredGoFiles
@@ -270,19 +276,6 @@ func (p *Package) copyBuild(pp *build.Package) {
        }
 }
 
-// isStandardImportPath reports whether $GOROOT/src/path should be considered
-// part of the standard distribution. For historical reasons we allow people to add
-// their own code to $GOROOT instead of using $GOPATH, but we assume that
-// code will start with a domain name (dot in the first element).
-func isStandardImportPath(path string) bool {
-       i := strings.Index(path, "/")
-       if i < 0 {
-               i = len(path)
-       }
-       elem := path[:i]
-       return !strings.Contains(elem, ".")
-}
-
 // A PackageError describes an error loading information about a package.
 type PackageError struct {
        ImportStack   []string // shortest path from package named on command line to this one
@@ -430,8 +423,20 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
        importPath := path
        origPath := path
        isLocal := build.IsLocalImport(path)
+       var vgoDir string
+       var vgoErr error
        if isLocal {
                importPath = dirToImportPath(filepath.Join(srcDir, path))
+       } else if vgo.Enabled() {
+               parentPath := ""
+               if parent != nil {
+                       parentPath = parent.ImportPath
+               }
+               var p string
+               vgoDir, p, vgoErr = vgo.Lookup(parentPath, path)
+               if vgoErr == nil {
+                       importPath = p
+               }
        } else if mode&ResolveImport != 0 {
                // We do our own path resolution, because we want to
                // find out the key to use in packageCache without the
@@ -456,17 +461,28 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
                // Load package.
                // Import always returns bp != nil, even if an error occurs,
                // in order to return partial information.
-               buildMode := build.ImportComment
-               if mode&ResolveImport == 0 || path != origPath {
-                       // Not vendoring, or we already found the vendored path.
-                       buildMode |= build.IgnoreVendor
+               var bp *build.Package
+               var err error
+               if vgoDir != "" {
+                       bp, err = cfg.BuildContext.ImportDir(vgoDir, 0)
+               } else if vgoErr != nil {
+                       bp = new(build.Package)
+                       err = fmt.Errorf("unknown import path %q: %v", importPath, vgoErr)
+               } else {
+                       buildMode := build.ImportComment
+                       if mode&ResolveImport == 0 || path != origPath {
+                               // Not vendoring, or we already found the vendored path.
+                               buildMode |= build.IgnoreVendor
+                       }
+                       bp, err = cfg.BuildContext.Import(path, srcDir, buildMode)
                }
-               bp, err := cfg.BuildContext.Import(path, srcDir, buildMode)
                bp.ImportPath = importPath
                if cfg.GOBIN != "" {
                        bp.BinDir = cfg.GOBIN
+               } else if vgo.Enabled() {
+                       bp.BinDir = vgo.BinDir()
                }
-               if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
+               if vgoDir == "" && err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
                        !strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") {
                        err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
                }
@@ -475,7 +491,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
                        p = setErrorPos(p, importPos)
                }
 
-               if origPath != cleanImport(origPath) {
+               if vgoDir == "" && origPath != cleanImport(origPath) {
                        p.Error = &PackageError{
                                ImportStack: stk.Copy(),
                                Err:         fmt.Sprintf("non-canonical import path: %q should be %q", origPath, pathpkg.Clean(origPath)),
@@ -553,6 +569,16 @@ func isDir(path string) bool {
 // 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) {
+       if vgo.Enabled() {
+               parentPath := ""
+               if parent != nil {
+                       parentPath = parent.ImportPath
+               }
+               if _, p, e := vgo.Lookup(parentPath, path); e == nil {
+                       return p
+               }
+               return path
+       }
        found = VendoredImportPath(parent, path)
        if found != path {
                return found
@@ -902,7 +928,7 @@ func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package {
        perr := *p
        perr.Error = &PackageError{
                ImportStack: stk.Copy(),
-               Err:         "use of internal package not allowed",
+               Err:         "use of internal package " + p.ImportPath + " not allowed",
        }
        perr.Incomplete = true
        return &perr
@@ -1130,6 +1156,9 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
                        // Install cross-compiled binaries to subdirectories of bin.
                        elem = full
                }
+               if p.Internal.Build.BinDir == "" && vgo.Enabled() {
+                       p.Internal.Build.BinDir = vgo.BinDir()
+               }
                if p.Internal.Build.BinDir != "" {
                        // Install to GOBIN or bin of GOPATH entry.
                        p.Target = filepath.Join(p.Internal.Build.BinDir, elem)
@@ -1380,6 +1409,13 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
                setError(fmt.Sprintf("case-insensitive import collision: %q and %q", p.ImportPath, other))
                return
        }
+
+       if vgo.Enabled() {
+               p.Module = vgo.PackageModuleInfo(p.ImportPath)
+               if p.Name == "main" {
+                       p.Internal.BuildInfo = vgo.PackageBuildInfo(p.ImportPath, p.Deps)
+               }
+       }
 }
 
 // SafeArg reports whether arg is a "safe" command-line argument,
@@ -1654,6 +1690,20 @@ func PackagesAndErrors(args []string) []*Package {
        return pkgs
 }
 
+func ImportPaths(args []string) []string {
+       if cmdlineMatchers == nil {
+               SetCmdlinePatterns(search.CleanImportPaths(args))
+       }
+       return vgo.ImportPaths(args)
+}
+
+func ImportPathsForGoGet(args []string) []string {
+       if cmdlineMatchers == nil {
+               SetCmdlinePatterns(search.CleanImportPaths(args))
+       }
+       return search.ImportPathsNoDotExpansion(args)
+}
+
 // packagesForBuild is like 'packages' but fails if any of
 // the packages or their dependencies have errors
 // (cannot be built).
@@ -1739,6 +1789,8 @@ func GoFilesPackage(gofiles []string) *Package {
        }
        ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
 
+       vgo.AddImports(gofiles)
+
        var err error
        if dir == "" {
                dir = base.Cwd
@@ -1767,6 +1819,8 @@ func GoFilesPackage(gofiles []string) *Package {
                }
                if cfg.GOBIN != "" {
                        pkg.Target = filepath.Join(cfg.GOBIN, exe)
+               } else if vgo.Enabled() {
+                       pkg.Target = filepath.Join(vgo.BinDir(), exe)
                }
        }
 
index 6494f8e569f3c2266947c0e8e088f6530f432bfc..49433984a8446d9ef7109c0be98958101c643b3c 100644 (file)
 package load
 
 import (
-       "cmd/go/internal/cfg"
-       "fmt"
-       "go/build"
-       "log"
-       "os"
-       "path"
        "path/filepath"
-       "regexp"
        "runtime"
        "strings"
-)
-
-// allPackages returns all the packages that can be found
-// under the $GOPATH directories and $GOROOT matching pattern.
-// The pattern is either "all" (all packages), "std" (standard packages),
-// "cmd" (standard commands), or a path including "...".
-func allPackages(pattern string) []string {
-       pkgs := MatchPackages(pattern)
-       if len(pkgs) == 0 {
-               fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
-       }
-       return pkgs
-}
-
-// allPackagesInFS is like allPackages but is passed a pattern
-// beginning ./ or ../, meaning it should scan the tree rooted
-// at the given directory. There are ... in the pattern too.
-func allPackagesInFS(pattern string) []string {
-       pkgs := MatchPackagesInFS(pattern)
-       if len(pkgs) == 0 {
-               fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
-       }
-       return pkgs
-}
-
-// MatchPackages returns a list of package paths matching pattern
-// (see go help packages for pattern syntax).
-func MatchPackages(pattern string) []string {
-       match := func(string) bool { return true }
-       treeCanMatch := func(string) bool { return true }
-       if !IsMetaPackage(pattern) {
-               match = matchPattern(pattern)
-               treeCanMatch = treeCanMatchPattern(pattern)
-       }
-
-       have := map[string]bool{
-               "builtin": true, // ignore pseudo-package that exists only for documentation
-       }
-       if !cfg.BuildContext.CgoEnabled {
-               have["runtime/cgo"] = true // ignore during walk
-       }
-       var pkgs []string
 
-       for _, src := range cfg.BuildContext.SrcDirs() {
-               if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
-                       continue
-               }
-               src = filepath.Clean(src) + string(filepath.Separator)
-               root := src
-               if pattern == "cmd" {
-                       root += "cmd" + string(filepath.Separator)
-               }
-               filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
-                       if err != nil || path == src {
-                               return nil
-                       }
-
-                       want := true
-                       // Avoid .foo, _foo, and testdata directory trees.
-                       _, elem := filepath.Split(path)
-                       if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
-                               want = false
-                       }
-
-                       name := filepath.ToSlash(path[len(src):])
-                       if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
-                               // The name "std" is only the standard library.
-                               // If the name is cmd, it's the root of the command tree.
-                               want = false
-                       }
-                       if !treeCanMatch(name) {
-                               want = false
-                       }
-
-                       if !fi.IsDir() {
-                               if fi.Mode()&os.ModeSymlink != 0 && want {
-                                       if target, err := os.Stat(path); err == nil && target.IsDir() {
-                                               fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
-                                       }
-                               }
-                               return nil
-                       }
-                       if !want {
-                               return filepath.SkipDir
-                       }
-
-                       if have[name] {
-                               return nil
-                       }
-                       have[name] = true
-                       if !match(name) {
-                               return nil
-                       }
-                       pkg, err := cfg.BuildContext.ImportDir(path, 0)
-                       if err != nil {
-                               if _, noGo := err.(*build.NoGoError); noGo {
-                                       return nil
-                               }
-                       }
-
-                       // If we are expanding "cmd", skip main
-                       // packages under cmd/vendor. At least as of
-                       // March, 2017, there is one there for the
-                       // vendored pprof tool.
-                       if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
-                               return nil
-                       }
-
-                       pkgs = append(pkgs, name)
-                       return nil
-               })
-       }
-       return pkgs
-}
-
-// MatchPackagesInFS returns a list of package paths matching pattern,
-// which must begin with ./ or ../
-// (see go help packages for pattern syntax).
-func MatchPackagesInFS(pattern string) []string {
-       // Find directory to begin the scan.
-       // Could be smarter but this one optimization
-       // is enough for now, since ... is usually at the
-       // end of a path.
-       i := strings.Index(pattern, "...")
-       dir, _ := path.Split(pattern[:i])
-
-       // pattern begins with ./ or ../.
-       // path.Clean will discard the ./ but not the ../.
-       // We need to preserve the ./ for pattern matching
-       // and in the returned import paths.
-       prefix := ""
-       if strings.HasPrefix(pattern, "./") {
-               prefix = "./"
-       }
-       match := matchPattern(pattern)
-
-       var pkgs []string
-       filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
-               if err != nil || !fi.IsDir() {
-                       return nil
-               }
-               if path == dir {
-                       // filepath.Walk starts at dir and recurses. For the recursive case,
-                       // the path is the result of filepath.Join, which calls filepath.Clean.
-                       // The initial case is not Cleaned, though, so we do this explicitly.
-                       //
-                       // This converts a path like "./io/" to "io". Without this step, running
-                       // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
-                       // package, because prepending the prefix "./" to the unclean path would
-                       // result in "././io", and match("././io") returns false.
-                       path = filepath.Clean(path)
-               }
-
-               // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
-               _, elem := filepath.Split(path)
-               dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
-               if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
-                       return filepath.SkipDir
-               }
-
-               name := prefix + filepath.ToSlash(path)
-               if !match(name) {
-                       return nil
-               }
-
-               // We keep the directory if we can import it, or if we can't import it
-               // due to invalid Go source files. This means that directories containing
-               // parse errors will be built (and fail) instead of being silently skipped
-               // as not matching the pattern. Go 1.5 and earlier skipped, but that
-               // behavior means people miss serious mistakes.
-               // See golang.org/issue/11407.
-               if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
-                       if _, noGo := err.(*build.NoGoError); !noGo {
-                               log.Print(err)
-                       }
-                       return nil
-               }
-               pkgs = append(pkgs, name)
-               return nil
-       })
-       return pkgs
-}
-
-// treeCanMatchPattern(pattern)(name) reports whether
-// name or children of name can possibly match pattern.
-// Pattern is the same limited glob accepted by matchPattern.
-func treeCanMatchPattern(pattern string) func(name string) bool {
-       wildCard := false
-       if i := strings.Index(pattern, "..."); i >= 0 {
-               wildCard = true
-               pattern = pattern[:i]
-       }
-       return func(name string) bool {
-               return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
-                       wildCard && strings.HasPrefix(name, pattern)
-       }
-}
-
-// matchPattern(pattern)(name) reports whether
-// name matches pattern. Pattern is a limited glob
-// pattern in which '...' means 'any string' and there
-// is no other special syntax.
-// Unfortunately, there are two special cases. Quoting "go help packages":
-//
-// First, /... at the end of the pattern can match an empty string,
-// so that net/... matches both net and packages in its subdirectories, like net/http.
-// Second, any slash-separted pattern element containing a wildcard never
-// participates in a match of the "vendor" element in the path of a vendored
-// package, so that ./... does not match packages in subdirectories of
-// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
-// Note, however, that a directory named vendor that itself contains code
-// is not a vendored package: cmd/vendor would be a command named vendor,
-// and the pattern cmd/... matches it.
-func matchPattern(pattern string) func(name string) bool {
-       // Convert pattern to regular expression.
-       // The strategy for the trailing /... is to nest it in an explicit ? expression.
-       // The strategy for the vendor exclusion is to change the unmatchable
-       // vendor strings to a disallowed code point (vendorChar) and to use
-       // "(anything but that codepoint)*" as the implementation of the ... wildcard.
-       // This is a bit complicated but the obvious alternative,
-       // namely a hand-written search like in most shell glob matchers,
-       // is too easy to make accidentally exponential.
-       // Using package regexp guarantees linear-time matching.
-
-       const vendorChar = "\x00"
-
-       if strings.Contains(pattern, vendorChar) {
-               return func(name string) bool { return false }
-       }
-
-       re := regexp.QuoteMeta(pattern)
-       re = replaceVendor(re, vendorChar)
-       switch {
-       case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
-               re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
-       case re == vendorChar+`/\.\.\.`:
-               re = `(/vendor|/` + vendorChar + `/\.\.\.)`
-       case strings.HasSuffix(re, `/\.\.\.`):
-               re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
-       }
-       re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
-
-       reg := regexp.MustCompile(`^` + re + `$`)
-
-       return func(name string) bool {
-               if strings.Contains(name, vendorChar) {
-                       return false
-               }
-               return reg.MatchString(replaceVendor(name, vendorChar))
-       }
-}
+       "cmd/go/internal/search"
+)
 
 // MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
 func MatchPackage(pattern, cwd string) func(*Package) bool {
@@ -284,13 +29,14 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
                dir = filepath.Join(cwd, dir)
                if pattern == "" {
                        return func(p *Package) bool {
+                               // TODO(rsc): This is wrong. See golang.org/issue/25878.
                                if runtime.GOOS != "windows" {
                                        return p.Dir == dir
                                }
                                return strings.EqualFold(p.Dir, dir)
                        }
                }
-               matchPath := matchPattern(pattern)
+               matchPath := search.MatchPattern(pattern)
                return func(p *Package) bool {
                        // Compute relative path to dir and see if it matches the pattern.
                        rel, err := filepath.Rel(dir, p.Dir)
@@ -311,81 +57,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
        case pattern == "cmd":
                return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") }
        default:
-               matchPath := matchPattern(pattern)
+               matchPath := search.MatchPattern(pattern)
                return func(p *Package) bool { return matchPath(p.ImportPath) }
        }
 }
-
-// replaceVendor returns the result of replacing
-// non-trailing vendor path elements in x with repl.
-func replaceVendor(x, repl string) string {
-       if !strings.Contains(x, "vendor") {
-               return x
-       }
-       elem := strings.Split(x, "/")
-       for i := 0; i < len(elem)-1; i++ {
-               if elem[i] == "vendor" {
-                       elem[i] = repl
-               }
-       }
-       return strings.Join(elem, "/")
-}
-
-// ImportPaths returns the import paths to use for the given command line.
-func ImportPaths(args []string) []string {
-       args = ImportPathsNoDotExpansion(args)
-       var out []string
-       for _, a := range args {
-               if strings.Contains(a, "...") {
-                       if build.IsLocalImport(a) {
-                               out = append(out, allPackagesInFS(a)...)
-                       } else {
-                               out = append(out, allPackages(a)...)
-                       }
-                       continue
-               }
-               out = append(out, a)
-       }
-       return out
-}
-
-// ImportPathsNoDotExpansion returns the import paths to use for the given
-// command line, but it does no ... expansion.
-func ImportPathsNoDotExpansion(args []string) []string {
-       if cmdlineMatchers == nil {
-               SetCmdlinePatterns(args)
-       }
-       if len(args) == 0 {
-               return []string{"."}
-       }
-       var out []string
-       for _, a := range args {
-               // Arguments are supposed to be import paths, but
-               // as a courtesy to Windows developers, rewrite \ to /
-               // in command-line arguments. Handles .\... and so on.
-               if filepath.Separator == '\\' {
-                       a = strings.Replace(a, `\`, `/`, -1)
-               }
-
-               // Put argument in canonical form, but preserve leading ./.
-               if strings.HasPrefix(a, "./") {
-                       a = "./" + path.Clean(a)
-                       if a == "./." {
-                               a = "."
-                       }
-               } else {
-                       a = path.Clean(a)
-               }
-               if IsMetaPackage(a) {
-                       out = append(out, allPackages(a)...)
-                       continue
-               }
-               out = append(out, a)
-       }
-       return out
-}
-
-// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
-func IsMetaPackage(name string) bool {
-       return name == "std" || name == "cmd" || name == "all"
-}
diff --git a/src/cmd/go/internal/modconv/dep.go b/src/cmd/go/internal/modconv/dep.go
new file mode 100644 (file)
index 0000000..28dd28a
--- /dev/null
@@ -0,0 +1,71 @@
+// 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.
+
+package modconv
+
+import (
+       "fmt"
+       "strconv"
+       "strings"
+
+       "cmd/go/internal/module"
+       "cmd/go/internal/semver"
+)
+
+func ParseGopkgLock(file string, data []byte) ([]module.Version, error) {
+       var list []module.Version
+       var r *module.Version
+       for lineno, line := range strings.Split(string(data), "\n") {
+               lineno++
+               if i := strings.Index(line, "#"); i >= 0 {
+                       line = line[:i]
+               }
+               line = strings.TrimSpace(line)
+               if line == "[[projects]]" {
+                       list = append(list, module.Version{})
+                       r = &list[len(list)-1]
+                       continue
+               }
+               if strings.HasPrefix(line, "[") {
+                       r = nil
+                       continue
+               }
+               if r == nil {
+                       continue
+               }
+               i := strings.Index(line, "=")
+               if i < 0 {
+                       continue
+               }
+               key := strings.TrimSpace(line[:i])
+               val := strings.TrimSpace(line[i+1:])
+               if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
+                       q, err := strconv.Unquote(val) // Go unquoting, but close enough for now
+                       if err != nil {
+                               return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err)
+                       }
+                       val = q
+               }
+               switch key {
+               case "name":
+                       r.Path = val
+               case "revision", "version":
+                       // Note: key "version" should take priority over "revision",
+                       // and it does, because dep writes toml keys in alphabetical order,
+                       // so we see version (if present) second.
+                       if key == "version" {
+                               if !semver.IsValid(val) || semver.Canonical(val) != val {
+                                       break
+                               }
+                       }
+                       r.Version = val
+               }
+       }
+       for _, r := range list {
+               if r.Path == "" || r.Version == "" {
+                       return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path)
+               }
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/glide.go b/src/cmd/go/internal/modconv/glide.go
new file mode 100644 (file)
index 0000000..abe88c4
--- /dev/null
@@ -0,0 +1,40 @@
+// 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.
+
+package modconv
+
+import (
+       "cmd/go/internal/module"
+       "strings"
+)
+
+func ParseGlideLock(file string, data []byte) ([]module.Version, error) {
+       var list []module.Version
+       imports := false
+       name := ""
+       for lineno, line := range strings.Split(string(data), "\n") {
+               lineno++
+               if line == "" {
+                       continue
+               }
+               if strings.HasPrefix(line, "imports:") {
+                       imports = true
+               } else if line[0] != '-' && line[0] != ' ' && line[0] != '\t' {
+                       imports = false
+               }
+               if !imports {
+                       continue
+               }
+               if strings.HasPrefix(line, "- name:") {
+                       name = strings.TrimSpace(line[len("- name:"):])
+               }
+               if strings.HasPrefix(line, "  version:") {
+                       version := strings.TrimSpace(line[len("  version:"):])
+                       if name != "" && version != "" {
+                               list = append(list, module.Version{Path: name, Version: version})
+                       }
+               }
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/glock.go b/src/cmd/go/internal/modconv/glock.go
new file mode 100644 (file)
index 0000000..57eb66e
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+package modconv
+
+import (
+       "strings"
+
+       "cmd/go/internal/module"
+)
+
+func ParseGLOCKFILE(file string, data []byte) ([]module.Version, error) {
+       var list []module.Version
+       for lineno, line := range strings.Split(string(data), "\n") {
+               lineno++
+               f := strings.Fields(line)
+               if len(f) >= 2 && f[0] != "cmd" {
+                       list = append(list, module.Version{Path: f[0], Version: f[1]})
+               }
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/godeps.go b/src/cmd/go/internal/modconv/godeps.go
new file mode 100644 (file)
index 0000000..904fd70
--- /dev/null
@@ -0,0 +1,29 @@
+// 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.
+
+package modconv
+
+import (
+       "encoding/json"
+
+       "cmd/go/internal/module"
+)
+
+func ParseGodepsJSON(file string, data []byte) ([]module.Version, error) {
+       var cfg struct {
+               ImportPath string
+               Deps       []struct {
+                       ImportPath string
+                       Rev        string
+               }
+       }
+       if err := json.Unmarshal(data, &cfg); err != nil {
+               return nil, err
+       }
+       var list []module.Version
+       for _, d := range cfg.Deps {
+               list = append(list, module.Version{Path: d.ImportPath, Version: d.Rev})
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/modconv.go b/src/cmd/go/internal/modconv/modconv.go
new file mode 100644 (file)
index 0000000..b689b52
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.
+
+package modconv
+
+import "cmd/go/internal/module"
+
+var Converters = map[string]func(string, []byte) ([]module.Version, error){
+       "GLOCKFILE":          ParseGLOCKFILE,
+       "Godeps/Godeps.json": ParseGodepsJSON,
+       "Gopkg.lock":         ParseGopkgLock,
+       "dependencies.tsv":   ParseDependenciesTSV,
+       "glide.lock":         ParseGlideLock,
+       "vendor.conf":        ParseVendorConf,
+       "vendor.yml":         ParseVendorYML,
+       "vendor/manifest":    ParseVendorManifest,
+       "vendor/vendor.json": ParseVendorJSON,
+}
+
+// Prefix is a line we write at the top of auto-converted go.mod files
+// for dependencies before caching them.
+// In case of bugs in the converter, if we bump this version number,
+// then all the cached copies will be ignored.
+const Prefix = "//vgo 0.0.4\n"
diff --git a/src/cmd/go/internal/modconv/modconv_test.go b/src/cmd/go/internal/modconv/modconv_test.go
new file mode 100644 (file)
index 0000000..04a1db3
--- /dev/null
@@ -0,0 +1,69 @@
+// 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.
+
+package modconv
+
+import (
+       "bytes"
+       "fmt"
+       "internal/testenv"
+       "io/ioutil"
+       "path/filepath"
+       "testing"
+)
+
+var extMap = map[string]string{
+       ".dep":       "Gopkg.lock",
+       ".glide":     "glide.lock",
+       ".glock":     "GLOCKFILE",
+       ".godeps":    "Godeps/Godeps.json",
+       ".tsv":       "dependencies.tsv",
+       ".vconf":     "vendor.conf",
+       ".vjson":     "vendor/vendor.json",
+       ".vyml":      "vendor.yml",
+       ".vmanifest": "vendor/manifest",
+}
+
+func Test(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+
+       tests, _ := filepath.Glob("testdata/*")
+       if len(tests) == 0 {
+               t.Fatalf("no tests found")
+       }
+       for _, test := range tests {
+               file := filepath.Base(test)
+               ext := filepath.Ext(file)
+               if ext == ".out" {
+                       continue
+               }
+               t.Run(file, func(t *testing.T) {
+                       if extMap[ext] == "" {
+                               t.Fatal("unknown extension")
+                       }
+                       if Converters[extMap[ext]] == nil {
+                               t.Fatalf("Converters[%q] == nil", extMap[ext])
+                       }
+                       data, err := ioutil.ReadFile(test)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       out, err := Converters[extMap[ext]](test, data)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       want, err := ioutil.ReadFile(test[:len(test)-len(ext)] + ".out")
+                       if err != nil {
+                               t.Error(err)
+                       }
+                       var buf bytes.Buffer
+                       for _, r := range out {
+                               fmt.Fprintf(&buf, "%s %s\n", r.Path, r.Version)
+                       }
+                       if !bytes.Equal(buf.Bytes(), want) {
+                               t.Errorf("have:\n%s\nwant:\n%s", buf.Bytes(), want)
+                       }
+               })
+       }
+}
diff --git a/src/cmd/go/internal/modconv/testdata/cockroach.glock b/src/cmd/go/internal/modconv/testdata/cockroach.glock
new file mode 100644 (file)
index 0000000..221c8ac
--- /dev/null
@@ -0,0 +1,41 @@
+cmd github.com/cockroachdb/c-protobuf/cmd/protoc
+cmd github.com/cockroachdb/yacc
+cmd github.com/gogo/protobuf/protoc-gen-gogo
+cmd github.com/golang/lint/golint
+cmd github.com/jteeuwen/go-bindata/go-bindata
+cmd github.com/kisielk/errcheck
+cmd github.com/robfig/glock
+cmd github.com/tebeka/go2xunit
+cmd golang.org/x/tools/cmd/goimports
+cmd golang.org/x/tools/cmd/stringer
+github.com/agtorre/gocolorize f42b554bf7f006936130c9bb4f971afd2d87f671
+github.com/biogo/store e1f74b3c58befe661feed7fa4cf52436de753128
+github.com/cockroachdb/c-lz4 6e71f140a365017bbe0904710007f8725fd3f809
+github.com/cockroachdb/c-protobuf 0f9ab7b988ca7474cf76b9a961ab03c0552abcb3
+github.com/cockroachdb/c-rocksdb 7fc876fe79b96de0e25069c9ae27e6444637bd54
+github.com/cockroachdb/c-snappy 618733f9e5bab8463b9049117a335a7a1bfc9fd5
+github.com/cockroachdb/yacc 572e006f8e6b0061ebda949d13744f5108389514
+github.com/coreos/etcd 18ecc297bc913bed6fc093d66b1fa22020dba7dc
+github.com/docker/docker 7374852be9def787921aea2ca831771982badecf
+github.com/elazarl/go-bindata-assetfs 3dcc96556217539f50599357fb481ac0dc7439b9
+github.com/gogo/protobuf 98e73e511a62a9c232152f94999112c80142a813
+github.com/golang/lint 7b7f4364ff76043e6c3610281525fabc0d90f0e4
+github.com/google/btree cc6329d4279e3f025a53a83c397d2339b5705c45
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/jteeuwen/go-bindata dce55d09e24ac40a6e725c8420902b86554f8046
+github.com/julienschmidt/httprouter 6aacfd5ab513e34f7e64ea9627ab9670371b34e7
+github.com/kisielk/errcheck 50b84cf7fa18ee2985b8c63ba3de5edd604b9259
+github.com/kisielk/gotool d678387370a2eb9b5b0a33218bc8c9d8de15b6be
+github.com/lib/pq a8d8d01c4f91602f876bf5aa210274e8203a6b45
+github.com/montanaflynn/stats 44fb56da2a2a67d394dec0e18a82dd316f192529
+github.com/peterh/liner 1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced
+github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9
+github.com/samalba/dockerclient 12570e600d71374233e5056ba315f657ced496c7
+github.com/spf13/cobra 66816bcd0378e248c613e3c443c020f544c28804
+github.com/spf13/pflag 67cbc198fd11dab704b214c1e629a97af392c085
+github.com/tebeka/go2xunit d45000af2242dd0e7b8c7b07d82a1068adc5fd40
+golang.org/x/crypto cc04154d65fb9296747569b107cfd05380b1ea3e
+golang.org/x/net 8bfde94a845cb31000de3266ac83edbda58dab09
+golang.org/x/text d4cc1b1e16b49d6dafc4982403b40fe89c512cd5
+golang.org/x/tools d02228d1857b9f49cd0252788516ff5584266eb6
+gopkg.in/yaml.v1 9f9df34309c04878acc86042b16630b0f696e1de
diff --git a/src/cmd/go/internal/modconv/testdata/cockroach.out b/src/cmd/go/internal/modconv/testdata/cockroach.out
new file mode 100644 (file)
index 0000000..30cdbb7
--- /dev/null
@@ -0,0 +1,31 @@
+github.com/agtorre/gocolorize f42b554bf7f006936130c9bb4f971afd2d87f671
+github.com/biogo/store e1f74b3c58befe661feed7fa4cf52436de753128
+github.com/cockroachdb/c-lz4 6e71f140a365017bbe0904710007f8725fd3f809
+github.com/cockroachdb/c-protobuf 0f9ab7b988ca7474cf76b9a961ab03c0552abcb3
+github.com/cockroachdb/c-rocksdb 7fc876fe79b96de0e25069c9ae27e6444637bd54
+github.com/cockroachdb/c-snappy 618733f9e5bab8463b9049117a335a7a1bfc9fd5
+github.com/cockroachdb/yacc 572e006f8e6b0061ebda949d13744f5108389514
+github.com/coreos/etcd 18ecc297bc913bed6fc093d66b1fa22020dba7dc
+github.com/docker/docker 7374852be9def787921aea2ca831771982badecf
+github.com/elazarl/go-bindata-assetfs 3dcc96556217539f50599357fb481ac0dc7439b9
+github.com/gogo/protobuf 98e73e511a62a9c232152f94999112c80142a813
+github.com/golang/lint 7b7f4364ff76043e6c3610281525fabc0d90f0e4
+github.com/google/btree cc6329d4279e3f025a53a83c397d2339b5705c45
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/jteeuwen/go-bindata dce55d09e24ac40a6e725c8420902b86554f8046
+github.com/julienschmidt/httprouter 6aacfd5ab513e34f7e64ea9627ab9670371b34e7
+github.com/kisielk/errcheck 50b84cf7fa18ee2985b8c63ba3de5edd604b9259
+github.com/kisielk/gotool d678387370a2eb9b5b0a33218bc8c9d8de15b6be
+github.com/lib/pq a8d8d01c4f91602f876bf5aa210274e8203a6b45
+github.com/montanaflynn/stats 44fb56da2a2a67d394dec0e18a82dd316f192529
+github.com/peterh/liner 1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced
+github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9
+github.com/samalba/dockerclient 12570e600d71374233e5056ba315f657ced496c7
+github.com/spf13/cobra 66816bcd0378e248c613e3c443c020f544c28804
+github.com/spf13/pflag 67cbc198fd11dab704b214c1e629a97af392c085
+github.com/tebeka/go2xunit d45000af2242dd0e7b8c7b07d82a1068adc5fd40
+golang.org/x/crypto cc04154d65fb9296747569b107cfd05380b1ea3e
+golang.org/x/net 8bfde94a845cb31000de3266ac83edbda58dab09
+golang.org/x/text d4cc1b1e16b49d6dafc4982403b40fe89c512cd5
+golang.org/x/tools d02228d1857b9f49cd0252788516ff5584266eb6
+gopkg.in/yaml.v1 9f9df34309c04878acc86042b16630b0f696e1de
diff --git a/src/cmd/go/internal/modconv/testdata/dockermachine.godeps b/src/cmd/go/internal/modconv/testdata/dockermachine.godeps
new file mode 100644 (file)
index 0000000..a551002
--- /dev/null
@@ -0,0 +1,159 @@
+{
+       "ImportPath": "github.com/docker/machine",
+       "GoVersion": "go1.4.2",
+       "Deps": [
+               {
+                       "ImportPath": "code.google.com/p/goauth2/oauth",
+                       "Comment": "weekly-56",
+                       "Rev": "afe77d958c701557ec5dc56f6936fcc194d15520"
+               },
+               {
+                       "ImportPath": "github.com/MSOpenTech/azure-sdk-for-go",
+                       "Comment": "v1.1-17-g515f3ec",
+                       "Rev": "515f3ec74ce6a5b31e934cefae997c97bd0a1b1e"
+               },
+               {
+                       "ImportPath": "github.com/cenkalti/backoff",
+                       "Rev": "9831e1e25c874e0a0601b6dc43641071414eec7a"
+               },
+               {
+                       "ImportPath": "github.com/codegangsta/cli",
+                       "Comment": "1.2.0-64-ge1712f3",
+                       "Rev": "e1712f381785e32046927f64a7c86fe569203196"
+               },
+               {
+                       "ImportPath": "github.com/digitalocean/godo",
+                       "Comment": "v0.5.0",
+                       "Rev": "5478aae80694de1d2d0e02c386bbedd201266234"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/dockerversion",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/engine",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/archive",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/fileutils",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/ioutils",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/mflag",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/parsers",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/pools",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/promise",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/system",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/term",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/timeutils",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/units",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/pkg/version",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar",
+                       "Comment": "v1.5.0",
+                       "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+               },
+               {
+                       "ImportPath": "github.com/docker/libtrust",
+                       "Rev": "c54fbb67c1f1e68d7d6f8d2ad7c9360404616a41"
+               },
+               {
+                       "ImportPath": "github.com/google/go-querystring/query",
+                       "Rev": "30f7a39f4a218feb5325f3aebc60c32a572a8274"
+               },
+               {
+                       "ImportPath": "github.com/mitchellh/mapstructure",
+                       "Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf"
+               },
+               {
+                       "ImportPath": "github.com/rackspace/gophercloud",
+                       "Comment": "v1.0.0-558-ce0f487",
+                       "Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
+               },
+               {
+                       "ImportPath": "github.com/skarademir/naturalsort",
+                       "Rev": "983d4d86054d80f91fd04dd62ec52c1d078ce403"
+               },
+               {
+                       "ImportPath": "github.com/smartystreets/go-aws-auth",
+                       "Rev": "1f0db8c0ee6362470abe06a94e3385927ed72a4b"
+               },
+               {
+                       "ImportPath": "github.com/stretchr/testify/assert",
+                       "Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
+               },
+               {
+                       "ImportPath": "github.com/pyr/egoscale/src/egoscale",
+                       "Rev": "bbaa67324aeeacc90430c1fe0a9c620d3929512e"
+               },
+               {
+                       "ImportPath": "github.com/tent/http-link-go",
+                       "Rev": "ac974c61c2f990f4115b119354b5e0b47550e888"
+               },
+               {
+                       "ImportPath": "github.com/vmware/govcloudair",
+                       "Comment": "v0.0.2",
+                       "Rev": "66a23eaabc61518f91769939ff541886fe1dceef"
+               },
+               {
+                       "ImportPath": "golang.org/x/crypto/ssh",
+                       "Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e"
+               },
+               {
+                       "ImportPath": "google.golang.org/api/compute/v1",
+                       "Rev": "aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5"
+               },
+               {
+                       "ImportPath": "google.golang.org/api/googleapi",
+                       "Rev": "aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5"
+               }
+       ]
+}
diff --git a/src/cmd/go/internal/modconv/testdata/dockermachine.out b/src/cmd/go/internal/modconv/testdata/dockermachine.out
new file mode 100644 (file)
index 0000000..0b39cea
--- /dev/null
@@ -0,0 +1,33 @@
+code.google.com/p/goauth2/oauth afe77d958c701557ec5dc56f6936fcc194d15520
+github.com/MSOpenTech/azure-sdk-for-go 515f3ec74ce6a5b31e934cefae997c97bd0a1b1e
+github.com/cenkalti/backoff 9831e1e25c874e0a0601b6dc43641071414eec7a
+github.com/codegangsta/cli e1712f381785e32046927f64a7c86fe569203196
+github.com/digitalocean/godo 5478aae80694de1d2d0e02c386bbedd201266234
+github.com/docker/docker/dockerversion a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/engine a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/archive a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/fileutils a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/ioutils a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/mflag a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/parsers a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/pools a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/promise a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/system a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/term a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/timeutils a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/units a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/version a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/libtrust c54fbb67c1f1e68d7d6f8d2ad7c9360404616a41
+github.com/google/go-querystring/query 30f7a39f4a218feb5325f3aebc60c32a572a8274
+github.com/mitchellh/mapstructure 740c764bc6149d3f1806231418adb9f52c11bcbf
+github.com/rackspace/gophercloud ce0f487f6747ab43c4e4404722df25349385bebd
+github.com/skarademir/naturalsort 983d4d86054d80f91fd04dd62ec52c1d078ce403
+github.com/smartystreets/go-aws-auth 1f0db8c0ee6362470abe06a94e3385927ed72a4b
+github.com/stretchr/testify/assert e4ec8152c15fc46bd5056ce65997a07c7d415325
+github.com/pyr/egoscale/src/egoscale bbaa67324aeeacc90430c1fe0a9c620d3929512e
+github.com/tent/http-link-go ac974c61c2f990f4115b119354b5e0b47550e888
+github.com/vmware/govcloudair 66a23eaabc61518f91769939ff541886fe1dceef
+golang.org/x/crypto/ssh 1fbbd62cfec66bd39d91e97749579579d4d3037e
+google.golang.org/api/compute/v1 aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5
+google.golang.org/api/googleapi aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5
diff --git a/src/cmd/go/internal/modconv/testdata/dockerman.glide b/src/cmd/go/internal/modconv/testdata/dockerman.glide
new file mode 100644 (file)
index 0000000..5ec765a
--- /dev/null
@@ -0,0 +1,52 @@
+hash: ead3ea293a6143fe41069ebec814bf197d8c43a92cc7666b1f7e21a419b46feb
+updated: 2016-06-20T21:53:35.420817456Z
+imports:
+- name: github.com/BurntSushi/toml
+  version: f0aeabca5a127c4078abb8c8d64298b147264b55
+- name: github.com/cpuguy83/go-md2man
+  version: a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
+  subpackages:
+  - md2man
+- name: github.com/fsnotify/fsnotify
+  version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
+- name: github.com/hashicorp/hcl
+  version: da486364306ed66c218be9b7953e19173447c18b
+  subpackages:
+  - hcl/ast
+  - hcl/parser
+  - hcl/token
+  - json/parser
+  - hcl/scanner
+  - hcl/strconv
+  - json/scanner
+  - json/token
+- name: github.com/inconshreveable/mousetrap
+  version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+- name: github.com/magiconair/properties
+  version: c265cfa48dda6474e208715ca93e987829f572f8
+- name: github.com/mitchellh/mapstructure
+  version: d2dd0262208475919e1a362f675cfc0e7c10e905
+- name: github.com/russross/blackfriday
+  version: 1d6b8e9301e720b08a8938b8c25c018285885438
+- name: github.com/shurcooL/sanitized_anchor_name
+  version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
+- name: github.com/spf13/cast
+  version: 27b586b42e29bec072fe7379259cc719e1289da6
+- name: github.com/spf13/jwalterweatherman
+  version: 33c24e77fb80341fe7130ee7c594256ff08ccc46
+- name: github.com/spf13/pflag
+  version: dabebe21bf790f782ea4c7bbd2efc430de182afd
+- name: github.com/spf13/viper
+  version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
+- name: golang.org/x/sys
+  version: 62bee037599929a6e9146f29d10dd5208c43507d
+  subpackages:
+  - unix
+- name: gopkg.in/yaml.v2
+  version: a83829b6f1293c91addabc89d0571c246397bbf4
+- name: github.com/spf13/cobra
+  repo: https://github.com/dnephin/cobra
+  subpackages:
+  - doc
+  version: v1.3
+devImports: []
diff --git a/src/cmd/go/internal/modconv/testdata/dockerman.out b/src/cmd/go/internal/modconv/testdata/dockerman.out
new file mode 100644 (file)
index 0000000..5e6370b
--- /dev/null
@@ -0,0 +1,16 @@
+github.com/BurntSushi/toml f0aeabca5a127c4078abb8c8d64298b147264b55
+github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
+github.com/fsnotify/fsnotify 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
+github.com/hashicorp/hcl da486364306ed66c218be9b7953e19173447c18b
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/magiconair/properties c265cfa48dda6474e208715ca93e987829f572f8
+github.com/mitchellh/mapstructure d2dd0262208475919e1a362f675cfc0e7c10e905
+github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
+github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
+github.com/spf13/cast 27b586b42e29bec072fe7379259cc719e1289da6
+github.com/spf13/jwalterweatherman 33c24e77fb80341fe7130ee7c594256ff08ccc46
+github.com/spf13/pflag dabebe21bf790f782ea4c7bbd2efc430de182afd
+github.com/spf13/viper c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
+golang.org/x/sys 62bee037599929a6e9146f29d10dd5208c43507d
+gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4
+github.com/spf13/cobra v1.3
diff --git a/src/cmd/go/internal/modconv/testdata/govmomi.out b/src/cmd/go/internal/modconv/testdata/govmomi.out
new file mode 100644 (file)
index 0000000..188c458
--- /dev/null
@@ -0,0 +1,5 @@
+github.com/davecgh/go-xdr/xdr2 4930550ba2e22f87187498acfd78348b15f4e7a8
+github.com/google/uuid 6a5e28554805e78ea6141142aba763936c4761c0
+github.com/kr/pretty 2ee9d7453c02ef7fa518a83ae23644eb8872186a
+github.com/kr/pty 95d05c1eef33a45bd58676b6ce28d105839b8d0b
+github.com/vmware/vmw-guestinfo 25eff159a728be87e103a0b8045e08273f4dbec4
diff --git a/src/cmd/go/internal/modconv/testdata/govmomi.vmanifest b/src/cmd/go/internal/modconv/testdata/govmomi.vmanifest
new file mode 100644 (file)
index 0000000..b89e4ab
--- /dev/null
@@ -0,0 +1,46 @@
+{
+       "version": 0,
+       "dependencies": [
+               {
+                       "importpath": "github.com/davecgh/go-xdr/xdr2",
+                       "repository": "https://github.com/rasky/go-xdr",
+                       "vcs": "git",
+                       "revision": "4930550ba2e22f87187498acfd78348b15f4e7a8",
+                       "branch": "improvements",
+                       "path": "/xdr2",
+                       "notests": true
+               },
+               {
+                       "importpath": "github.com/google/uuid",
+                       "repository": "https://github.com/google/uuid",
+                       "vcs": "git",
+                       "revision": "6a5e28554805e78ea6141142aba763936c4761c0",
+                       "branch": "master",
+                       "notests": true
+               },
+               {
+                       "importpath": "github.com/kr/pretty",
+                       "repository": "https://github.com/dougm/pretty",
+                       "vcs": "git",
+                       "revision": "2ee9d7453c02ef7fa518a83ae23644eb8872186a",
+                       "branch": "govmomi",
+                       "notests": true
+               },
+               {
+                       "importpath": "github.com/kr/pty",
+                       "repository": "https://github.com/kr/pty",
+                       "vcs": "git",
+                       "revision": "95d05c1eef33a45bd58676b6ce28d105839b8d0b",
+                       "branch": "master",
+                       "notests": true
+               },
+               {
+                       "importpath": "github.com/vmware/vmw-guestinfo",
+                       "repository": "https://github.com/vmware/vmw-guestinfo",
+                       "vcs": "git",
+                       "revision": "25eff159a728be87e103a0b8045e08273f4dbec4",
+                       "branch": "master",
+                       "notests": true
+               }
+       ]
+}
diff --git a/src/cmd/go/internal/modconv/testdata/juju.out b/src/cmd/go/internal/modconv/testdata/juju.out
new file mode 100644 (file)
index 0000000..c2430b1
--- /dev/null
@@ -0,0 +1,106 @@
+github.com/Azure/azure-sdk-for-go 902d95d9f311ae585ee98cfd18f418b467d60d5a
+github.com/Azure/go-autorest 6f40a8acfe03270d792cb8155e2942c09d7cff95
+github.com/ajstarks/svgo 89e3ac64b5b3e403a5e7c35ea4f98d45db7b4518
+github.com/altoros/gosigma 31228935eec685587914528585da4eb9b073c76d
+github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
+github.com/bmizerany/pat c068ca2f0aacee5ac3681d68e4d0a003b7d1fd2c
+github.com/coreos/go-systemd 7b2428fec40033549c68f54e26e89e7ca9a9ce31
+github.com/dgrijalva/jwt-go 01aeca54ebda6e0fbfafd0a524d234159c05ec20
+github.com/dustin/go-humanize 145fabdb1ab757076a70a886d092a3af27f66f4c
+github.com/godbus/dbus 32c6cc29c14570de4cf6d7e7737d68fb2d01ad15
+github.com/golang/protobuf 4bd1920723d7b7c925de087aa32e2187708897f7
+github.com/google/go-querystring 9235644dd9e52eeae6fa48efd539fdc351a0af53
+github.com/gorilla/schema 08023a0215e7fc27a9aecd8b8c50913c40019478
+github.com/gorilla/websocket 804cb600d06b10672f2fbc0a336a7bee507a428e
+github.com/gosuri/uitable 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
+github.com/joyent/gocommon ade826b8b54e81a779ccb29d358a45ba24b7809c
+github.com/joyent/gosdc 2f11feadd2d9891e92296a1077c3e2e56939547d
+github.com/joyent/gosign 0da0d5f1342065321c97812b1f4ac0c2b0bab56c
+github.com/juju/ansiterm b99631de12cf04a906c1d4e4ec54fb86eae5863d
+github.com/juju/blobstore 06056004b3d7b54bbb7984d830c537bad00fec21
+github.com/juju/bundlechanges 7725027b95e0d54635e0fb11efc2debdcdf19f75
+github.com/juju/cmd 9425a576247f348b9b40afe3b60085de63470de5
+github.com/juju/description d3742c23561884cd7d759ef7142340af1d22cab0
+github.com/juju/errors 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3
+github.com/juju/gnuflag 4e76c56581859c14d9d87e1ddbe29e1c0f10195f
+github.com/juju/go4 40d72ab9641a2a8c36a9c46a51e28367115c8e59
+github.com/juju/gojsonpointer afe8b77aa08f272b49e01b82de78510c11f61500
+github.com/juju/gojsonreference f0d24ac5ee330baa21721cdff56d45e4ee42628e
+github.com/juju/gojsonschema e1ad140384f254c82f89450d9a7c8dd38a632838
+github.com/juju/gomaasapi cfbc096bd45f276c17a391efc4db710b60ae3ad7
+github.com/juju/httpprof 14bf14c307672fd2456bdbf35d19cf0ccd3cf565
+github.com/juju/httprequest 266fd1e9debf09c037a63f074d099a2da4559ece
+github.com/juju/idmclient 4dc25171f675da4206b71695d3fd80e519ad05c1
+github.com/juju/jsonschema a0ef8b74ebcffeeff9fc374854deb4af388f037e
+github.com/juju/loggo 21bc4c63e8b435779a080e39e592969b7b90b889
+github.com/juju/mempool 24974d6c264fe5a29716e7d56ea24c4bd904b7cc
+github.com/juju/mutex 59c26ee163447c5c57f63ff71610d433862013de
+github.com/juju/persistent-cookiejar 5243747bf8f2d0897f6c7a52799327dc97d585e8
+github.com/juju/pubsub 9dcaca7eb4340dbf685aa7b3ad4cc4f8691a33d4
+github.com/juju/replicaset 6b5becf2232ce76656ea765d8d915d41755a1513
+github.com/juju/retry 62c62032529169c7ec02fa48f93349604c345e1f
+github.com/juju/rfc ebdbbdb950cd039a531d15cdc2ac2cbd94f068ee
+github.com/juju/romulus 98d6700423d63971f10ca14afea9ecf2b9b99f0f
+github.com/juju/schema 075de04f9b7d7580d60a1e12a0b3f50bb18e6998
+github.com/juju/terms-client 9b925afd677234e4146dde3cb1a11e187cbed64e
+github.com/juju/testing fce9bc4ebf7a77310c262ac4884e03b778eae06a
+github.com/juju/txn 28898197906200d603394d8e4ce537436529f1c5
+github.com/juju/usso 68a59c96c178fbbad65926e7f93db50a2cd14f33
+github.com/juju/utils 9f8aeb9b09e2d8c769be8317ccfa23f7eec62c26
+github.com/juju/version 1f41e27e54f21acccf9b2dddae063a782a8a7ceb
+github.com/juju/webbrowser 54b8c57083b4afb7dc75da7f13e2967b2606a507
+github.com/juju/xml eb759a627588d35166bc505fceb51b88500e291e
+github.com/juju/zip f6b1e93fa2e29a1d7d49b566b2b51efb060c982a
+github.com/julienschmidt/httprouter 77a895ad01ebc98a4dc95d8355bc825ce80a56f6
+github.com/lestrrat/go-jspointer f4881e611bdbe9fb413a7780721ef8400a1f2341
+github.com/lestrrat/go-jsref e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1
+github.com/lestrrat/go-jsschema b09d7650b822d2ea3dc83d5091a5e2acd8330051
+github.com/lestrrat/go-jsval b1258a10419fe0693f7b35ad65cd5074bc0ba1e5
+github.com/lestrrat/go-pdebug 2e6eaaa5717f81bda41d27070d3c966f40a1e75f
+github.com/lestrrat/go-structinfo f74c056fe41f860aa6264478c664a6fff8a64298
+github.com/lunixbochs/vtclean 4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36
+github.com/lxc/lxd 23da0234979fa6299565b91b529a6dbeb42ee36d
+github.com/masterzen/azure-sdk-for-go ee4f0065d00cd12b542f18f5bc45799e88163b12
+github.com/masterzen/simplexml 4572e39b1ab9fe03ee513ce6fc7e289e98482190
+github.com/masterzen/winrm 7a535cd943fccaeed196718896beec3fb51aff41
+github.com/masterzen/xmlpath 13f4951698adc0fa9c1dda3e275d489a24201161
+github.com/mattn/go-colorable ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8
+github.com/mattn/go-isatty 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
+github.com/mattn/go-runewidth d96d1bd051f2bd9e7e43d602782b37b93b1b5666
+github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
+github.com/nu7hatch/gouuid 179d4d0c4d8d407a32af483c2354df1d2c91e6c3
+github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
+github.com/prometheus/client_golang 575f371f7862609249a1be4c9145f429fe065e32
+github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common dd586c1c5abb0be59e60f942c22af711a2008cb4
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/rogpeppe/fastuuid 6724a57986aff9bff1a1770e9347036def7c89f6
+github.com/vmware/govmomi c0c7ce63df7edd78e713257b924c89d9a2dac119
+golang.org/x/crypto 8e06e8ddd9629eb88639aba897641bff8031f1d3
+golang.org/x/net ea47fc708ee3e20177f3ca3716217c4ab75942cb
+golang.org/x/oauth2 11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a
+golang.org/x/sys 7a6e5648d140666db5d920909e082ca00a87ba2c
+golang.org/x/text 2910a502d2bf9e43193af9d68ca516529614eed3
+google.golang.org/api 0d3983fb069cb6651353fc44c5cb604e263f2a93
+google.golang.org/cloud f20d6dcccb44ed49de45ae3703312cb46e627db1
+gopkg.in/amz.v3 8c3190dff075bf5442c9eedbf8f8ed6144a099e7
+gopkg.in/check.v1 4f90aeace3a26ad7021961c297b22c42160c7b25
+gopkg.in/errgo.v1 442357a80af5c6bf9b6d51ae791a39c3421004f3
+gopkg.in/goose.v1 ac43167b647feacdd9a1e34ee81e574551bc748d
+gopkg.in/ini.v1 776aa739ce9373377cd16f526cdf06cb4c89b40f
+gopkg.in/juju/blobstore.v2 51fa6e26128d74e445c72d3a91af555151cc3654
+gopkg.in/juju/charm.v6-unstable 83771c4919d6810bce5b7e63f46bea5fbfed0b93
+gopkg.in/juju/charmrepo.v2-unstable e79aa298df89ea887c9bffec46063c24bfb730f7
+gopkg.in/juju/charmstore.v5-unstable fd1eef3002fc6b6daff5e97efab6f5056d22dcc7
+gopkg.in/juju/environschema.v1 7359fc7857abe2b11b5b3e23811a9c64cb6b01e0
+gopkg.in/juju/jujusvg.v2 d82160011935ef79fc7aca84aba2c6f74700fe75
+gopkg.in/juju/names.v2 0847c26d322a121e52614f969fb82eae2820c715
+gopkg.in/juju/worker.v1 6965b9d826717287bb002e02d1fd4d079978083e
+gopkg.in/macaroon-bakery.v1 469b44e6f1f9479e115c8ae879ef80695be624d5
+gopkg.in/macaroon.v1 ab3940c6c16510a850e1c2dd628b919f0f3f1464
+gopkg.in/mgo.v2 f2b6f6c918c452ad107eec89615f074e3bd80e33
+gopkg.in/natefinch/lumberjack.v2 514cbda263a734ae8caac038dadf05f8f3f9f738
+gopkg.in/natefinch/npipe.v2 c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6
+gopkg.in/retry.v1 c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db
+gopkg.in/tomb.v1 dd632973f1e7218eb1089048e0798ec9ae7dceb8
+gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a
diff --git a/src/cmd/go/internal/modconv/testdata/juju.tsv b/src/cmd/go/internal/modconv/testdata/juju.tsv
new file mode 100644 (file)
index 0000000..0bddcef
--- /dev/null
@@ -0,0 +1,106 @@
+github.com/Azure/azure-sdk-for-go      git     902d95d9f311ae585ee98cfd18f418b467d60d5a        2016-07-20T05:16:58Z
+github.com/Azure/go-autorest   git     6f40a8acfe03270d792cb8155e2942c09d7cff95        2016-07-19T23:14:56Z
+github.com/ajstarks/svgo       git     89e3ac64b5b3e403a5e7c35ea4f98d45db7b4518        2014-10-04T21:11:59Z
+github.com/altoros/gosigma     git     31228935eec685587914528585da4eb9b073c76d        2015-04-08T14:52:32Z
+github.com/beorn7/perks        git     3ac7bf7a47d159a033b107610db8a1b6575507a4        2016-02-29T21:34:45Z
+github.com/bmizerany/pat       git     c068ca2f0aacee5ac3681d68e4d0a003b7d1fd2c        2016-02-17T10:32:42Z
+github.com/coreos/go-systemd   git     7b2428fec40033549c68f54e26e89e7ca9a9ce31        2016-02-02T21:14:25Z
+github.com/dgrijalva/jwt-go    git     01aeca54ebda6e0fbfafd0a524d234159c05ec20        2016-07-05T20:30:06Z
+github.com/dustin/go-humanize  git     145fabdb1ab757076a70a886d092a3af27f66f4c        2014-12-28T07:11:48Z
+github.com/godbus/dbus git     32c6cc29c14570de4cf6d7e7737d68fb2d01ad15        2016-05-06T22:25:50Z
+github.com/golang/protobuf     git     4bd1920723d7b7c925de087aa32e2187708897f7        2016-11-09T07:27:36Z
+github.com/google/go-querystring       git     9235644dd9e52eeae6fa48efd539fdc351a0af53        2016-04-01T23:30:42Z
+github.com/gorilla/schema      git     08023a0215e7fc27a9aecd8b8c50913c40019478        2016-04-26T23:15:12Z
+github.com/gorilla/websocket   git     804cb600d06b10672f2fbc0a336a7bee507a428e        2017-02-14T17:41:18Z
+github.com/gosuri/uitable      git     36ee7e946282a3fb1cfecd476ddc9b35d8847e42        2016-04-04T20:39:58Z
+github.com/joyent/gocommon     git     ade826b8b54e81a779ccb29d358a45ba24b7809c        2016-03-20T19:31:33Z
+github.com/joyent/gosdc        git     2f11feadd2d9891e92296a1077c3e2e56939547d        2014-05-24T00:08:15Z
+github.com/joyent/gosign       git     0da0d5f1342065321c97812b1f4ac0c2b0bab56c        2014-05-24T00:07:34Z
+github.com/juju/ansiterm       git     b99631de12cf04a906c1d4e4ec54fb86eae5863d        2016-09-07T23:45:32Z
+github.com/juju/blobstore      git     06056004b3d7b54bbb7984d830c537bad00fec21        2015-07-29T11:18:58Z
+github.com/juju/bundlechanges  git     7725027b95e0d54635e0fb11efc2debdcdf19f75        2016-12-15T16:06:52Z
+github.com/juju/cmd    git     9425a576247f348b9b40afe3b60085de63470de5        2017-03-20T01:37:09Z
+github.com/juju/description    git     d3742c23561884cd7d759ef7142340af1d22cab0        2017-03-20T07:46:40Z
+github.com/juju/errors git     1b5e39b83d1835fa480e0c2ddefb040ee82d58b3        2015-09-16T12:56:42Z
+github.com/juju/gnuflag        git     4e76c56581859c14d9d87e1ddbe29e1c0f10195f        2016-08-09T16:52:14Z
+github.com/juju/go4    git     40d72ab9641a2a8c36a9c46a51e28367115c8e59        2016-02-22T16:32:58Z
+github.com/juju/gojsonpointer  git     afe8b77aa08f272b49e01b82de78510c11f61500        2015-02-04T19:46:29Z
+github.com/juju/gojsonreference        git     f0d24ac5ee330baa21721cdff56d45e4ee42628e        2015-02-04T19:46:33Z
+github.com/juju/gojsonschema   git     e1ad140384f254c82f89450d9a7c8dd38a632838        2015-03-12T17:00:16Z
+github.com/juju/gomaasapi      git     cfbc096bd45f276c17a391efc4db710b60ae3ad7        2017-02-27T07:51:07Z
+github.com/juju/httpprof       git     14bf14c307672fd2456bdbf35d19cf0ccd3cf565        2014-12-17T16:00:36Z
+github.com/juju/httprequest    git     266fd1e9debf09c037a63f074d099a2da4559ece        2016-10-06T15:09:09Z
+github.com/juju/idmclient      git     4dc25171f675da4206b71695d3fd80e519ad05c1        2017-02-09T16:27:49Z
+github.com/juju/jsonschema     git     a0ef8b74ebcffeeff9fc374854deb4af388f037e        2016-11-02T18:19:19Z
+github.com/juju/loggo  git     21bc4c63e8b435779a080e39e592969b7b90b889        2017-02-22T12:20:47Z
+github.com/juju/mempool        git     24974d6c264fe5a29716e7d56ea24c4bd904b7cc        2016-02-05T10:49:27Z
+github.com/juju/mutex  git     59c26ee163447c5c57f63ff71610d433862013de        2016-06-17T01:09:07Z
+github.com/juju/persistent-cookiejar   git     5243747bf8f2d0897f6c7a52799327dc97d585e8        2016-11-15T13:33:28Z
+github.com/juju/pubsub git     9dcaca7eb4340dbf685aa7b3ad4cc4f8691a33d4        2016-07-28T03:00:34Z
+github.com/juju/replicaset     git     6b5becf2232ce76656ea765d8d915d41755a1513        2016-11-25T16:08:49Z
+github.com/juju/retry  git     62c62032529169c7ec02fa48f93349604c345e1f        2015-10-29T02:48:21Z
+github.com/juju/rfc    git     ebdbbdb950cd039a531d15cdc2ac2cbd94f068ee        2016-07-11T02:42:13Z
+github.com/juju/romulus        git     98d6700423d63971f10ca14afea9ecf2b9b99f0f        2017-01-23T14:29:29Z
+github.com/juju/schema git     075de04f9b7d7580d60a1e12a0b3f50bb18e6998        2016-04-20T04:42:03Z
+github.com/juju/terms-client   git     9b925afd677234e4146dde3cb1a11e187cbed64e        2016-08-09T13:19:00Z
+github.com/juju/testing        git     fce9bc4ebf7a77310c262ac4884e03b778eae06a        2017-02-22T09:01:19Z
+github.com/juju/txn    git     28898197906200d603394d8e4ce537436529f1c5        2016-11-16T04:07:55Z
+github.com/juju/usso   git     68a59c96c178fbbad65926e7f93db50a2cd14f33        2016-04-01T10:44:24Z
+github.com/juju/utils  git     9f8aeb9b09e2d8c769be8317ccfa23f7eec62c26        2017-02-15T08:19:00Z
+github.com/juju/version        git     1f41e27e54f21acccf9b2dddae063a782a8a7ceb        2016-10-31T05:19:06Z
+github.com/juju/webbrowser     git     54b8c57083b4afb7dc75da7f13e2967b2606a507        2016-03-09T14:36:29Z
+github.com/juju/xml    git     eb759a627588d35166bc505fceb51b88500e291e        2015-04-13T13:11:21Z
+github.com/juju/zip    git     f6b1e93fa2e29a1d7d49b566b2b51efb060c982a        2016-02-05T10:52:21Z
+github.com/julienschmidt/httprouter    git     77a895ad01ebc98a4dc95d8355bc825ce80a56f6        2015-10-13T22:55:20Z
+github.com/lestrrat/go-jspointer       git     f4881e611bdbe9fb413a7780721ef8400a1f2341        2016-02-29T02:13:54Z
+github.com/lestrrat/go-jsref   git     e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1        2016-06-01T01:32:40Z
+github.com/lestrrat/go-jsschema        git     b09d7650b822d2ea3dc83d5091a5e2acd8330051        2016-09-03T13:19:57Z
+github.com/lestrrat/go-jsval   git     b1258a10419fe0693f7b35ad65cd5074bc0ba1e5        2016-10-12T04:57:17Z
+github.com/lestrrat/go-pdebug  git     2e6eaaa5717f81bda41d27070d3c966f40a1e75f        2016-08-17T06:33:33Z
+github.com/lestrrat/go-structinfo      git     f74c056fe41f860aa6264478c664a6fff8a64298        2016-03-08T13:11:05Z
+github.com/lunixbochs/vtclean  git     4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36        2016-01-25T03:51:06Z
+github.com/lxc/lxd     git     23da0234979fa6299565b91b529a6dbeb42ee36d        2017-02-16T05:29:42Z
+github.com/masterzen/azure-sdk-for-go  git     ee4f0065d00cd12b542f18f5bc45799e88163b12        2016-10-14T13:56:28Z
+github.com/masterzen/simplexml git     4572e39b1ab9fe03ee513ce6fc7e289e98482190        2016-06-08T18:30:07Z
+github.com/masterzen/winrm     git     7a535cd943fccaeed196718896beec3fb51aff41        2016-10-14T15:10:40Z
+github.com/masterzen/xmlpath   git     13f4951698adc0fa9c1dda3e275d489a24201161        2014-02-18T18:59:01Z
+github.com/mattn/go-colorable  git     ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8        2016-07-31T23:54:17Z
+github.com/mattn/go-isatty     git     66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8        2016-08-06T12:27:52Z
+github.com/mattn/go-runewidth  git     d96d1bd051f2bd9e7e43d602782b37b93b1b5666        2015-11-18T07:21:59Z
+github.com/matttproud/golang_protobuf_extensions       git     c12348ce28de40eed0136aa2b644d0ee0650e56c        2016-04-24T11:30:07Z
+github.com/nu7hatch/gouuid     git     179d4d0c4d8d407a32af483c2354df1d2c91e6c3        2013-12-21T20:05:32Z
+github.com/pkg/errors  git     839d9e913e063e28dfd0e6c7b7512793e0a48be9        2016-10-02T05:25:12Z
+github.com/prometheus/client_golang    git     575f371f7862609249a1be4c9145f429fe065e32        2016-11-24T15:57:32Z
+github.com/prometheus/client_model     git     fa8ad6fec33561be4280a8f0514318c79d7f6cb6        2015-02-12T10:17:44Z
+github.com/prometheus/common   git     dd586c1c5abb0be59e60f942c22af711a2008cb4        2016-05-03T22:05:32Z
+github.com/prometheus/procfs   git     abf152e5f3e97f2fafac028d2cc06c1feb87ffa5        2016-04-11T19:08:41Z
+github.com/rogpeppe/fastuuid   git     6724a57986aff9bff1a1770e9347036def7c89f6        2015-01-06T09:32:20Z
+github.com/vmware/govmomi      git     c0c7ce63df7edd78e713257b924c89d9a2dac119        2016-06-30T15:37:42Z
+golang.org/x/crypto    git     8e06e8ddd9629eb88639aba897641bff8031f1d3        2016-09-22T17:06:29Z
+golang.org/x/net       git     ea47fc708ee3e20177f3ca3716217c4ab75942cb        2015-08-29T23:03:18Z
+golang.org/x/oauth2    git     11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a        2015-03-25T02:00:22Z
+golang.org/x/sys       git     7a6e5648d140666db5d920909e082ca00a87ba2c        2017-02-01T05:12:45Z
+golang.org/x/text      git     2910a502d2bf9e43193af9d68ca516529614eed3        2016-07-26T16:48:57Z
+google.golang.org/api  git     0d3983fb069cb6651353fc44c5cb604e263f2a93        2014-12-10T23:51:26Z
+google.golang.org/cloud        git     f20d6dcccb44ed49de45ae3703312cb46e627db1        2015-03-19T22:36:35Z
+gopkg.in/amz.v3        git     8c3190dff075bf5442c9eedbf8f8ed6144a099e7        2016-12-15T13:08:49Z
+gopkg.in/check.v1      git     4f90aeace3a26ad7021961c297b22c42160c7b25        2016-01-05T16:49:36Z
+gopkg.in/errgo.v1      git     442357a80af5c6bf9b6d51ae791a39c3421004f3        2016-12-22T12:58:16Z
+gopkg.in/goose.v1      git     ac43167b647feacdd9a1e34ee81e574551bc748d        2017-02-15T01:56:23Z
+gopkg.in/ini.v1        git     776aa739ce9373377cd16f526cdf06cb4c89b40f        2016-02-22T23:24:41Z
+gopkg.in/juju/blobstore.v2     git     51fa6e26128d74e445c72d3a91af555151cc3654        2016-01-25T02:37:03Z
+gopkg.in/juju/charm.v6-unstable        git     83771c4919d6810bce5b7e63f46bea5fbfed0b93        2016-10-03T20:31:18Z
+gopkg.in/juju/charmrepo.v2-unstable    git     e79aa298df89ea887c9bffec46063c24bfb730f7        2016-11-17T15:25:28Z
+gopkg.in/juju/charmstore.v5-unstable   git     fd1eef3002fc6b6daff5e97efab6f5056d22dcc7        2016-09-16T10:09:07Z
+gopkg.in/juju/environschema.v1 git     7359fc7857abe2b11b5b3e23811a9c64cb6b01e0        2015-11-04T11:58:10Z
+gopkg.in/juju/jujusvg.v2       git     d82160011935ef79fc7aca84aba2c6f74700fe75        2016-06-09T10:52:15Z
+gopkg.in/juju/names.v2 git     0847c26d322a121e52614f969fb82eae2820c715        2016-11-02T13:43:03Z
+gopkg.in/juju/worker.v1        git     6965b9d826717287bb002e02d1fd4d079978083e        2017-03-08T00:24:58Z
+gopkg.in/macaroon-bakery.v1    git     469b44e6f1f9479e115c8ae879ef80695be624d5        2016-06-22T12:14:21Z
+gopkg.in/macaroon.v1   git     ab3940c6c16510a850e1c2dd628b919f0f3f1464        2015-01-21T11:42:31Z
+gopkg.in/mgo.v2        git     f2b6f6c918c452ad107eec89615f074e3bd80e33        2016-08-18T01:52:18Z
+gopkg.in/natefinch/lumberjack.v2       git     514cbda263a734ae8caac038dadf05f8f3f9f738        2016-01-25T11:17:49Z
+gopkg.in/natefinch/npipe.v2    git     c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6        2016-06-21T03:49:01Z
+gopkg.in/retry.v1      git     c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db        2016-10-25T18:14:30Z
+gopkg.in/tomb.v1       git     dd632973f1e7218eb1089048e0798ec9ae7dceb8        2014-10-24T13:56:13Z
+gopkg.in/yaml.v2       git     a3f3340b5840cee44f372bddb5880fcbc419b46a        2017-02-08T14:18:51Z
diff --git a/src/cmd/go/internal/modconv/testdata/moby.out b/src/cmd/go/internal/modconv/testdata/moby.out
new file mode 100644 (file)
index 0000000..2cb2e05
--- /dev/null
@@ -0,0 +1,105 @@
+github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
+github.com/Microsoft/hcsshim v0.6.5
+github.com/Microsoft/go-winio v0.4.5
+github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
+github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
+github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609
+github.com/gorilla/context v1.1
+github.com/gorilla/mux v1.1
+github.com/Microsoft/opengcs v0.3.4
+github.com/kr/pty 5cf931ef8f
+github.com/mattn/go-shellwords v1.0.3
+github.com/sirupsen/logrus v1.0.3
+github.com/tchap/go-patricia v2.2.6
+github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
+golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
+golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
+github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
+github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
+golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
+github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
+github.com/pmezard/go-difflib v1.0.0
+github.com/gotestyourself/gotestyourself v1.1.0
+github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
+github.com/imdario/mergo 0.2.1
+golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
+github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
+github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8
+github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
+github.com/docker/libnetwork 68f1039f172434709a4550fe92e3e058406c74ce
+github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
+github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
+github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
+github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
+github.com/hashicorp/memberlist v0.1.0
+github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
+github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
+github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
+github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
+github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
+github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
+github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969
+github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
+github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
+github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
+github.com/coreos/etcd v3.2.1
+github.com/coreos/go-semver v0.2.0
+github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
+github.com/hashicorp/consul v0.5.2
+github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
+github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
+github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
+github.com/vbatts/tar-split v0.10.1
+github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
+github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa
+github.com/pborman/uuid v1.0
+google.golang.org/grpc v1.3.0
+github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
+github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
+github.com/opencontainers/runtime-spec v1.0.0
+github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
+github.com/coreos/go-systemd v4
+github.com/godbus/dbus v4.0.0
+github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
+github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
+github.com/Graylog2/go-gelf v2
+github.com/fluent/fluent-logger-golang v1.2.1
+github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972
+github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c
+github.com/fsnotify/fsnotify v1.4.2
+github.com/aws/aws-sdk-go v1.4.22
+github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0
+github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
+github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf
+golang.org/x/oauth2 96382aa079b72d8c014eb0c50f6c223d1e6a2de0
+google.golang.org/api 3cc2e591b550923a2c5f0ab5a803feda924d5823
+cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525
+github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
+google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
+github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0
+github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
+github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0
+github.com/gogo/protobuf v0.4
+github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
+github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
+golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
+golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
+github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad
+github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990
+github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
+github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
+github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0
+github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e
+github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
+github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/matttproud/golang_protobuf_extensions v1.0.0
+github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
+github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
+github.com/spf13/cobra v1.5.1
+github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c
+github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
+github.com/opencontainers/selinux v1.0.0-rc1
diff --git a/src/cmd/go/internal/modconv/testdata/moby.vconf b/src/cmd/go/internal/modconv/testdata/moby.vconf
new file mode 100644 (file)
index 0000000..53b90d1
--- /dev/null
@@ -0,0 +1,149 @@
+# the following lines are in sorted order, FYI
+github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
+github.com/Microsoft/hcsshim v0.6.5
+github.com/Microsoft/go-winio v0.4.5
+github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
+github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
+github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
+github.com/gorilla/context v1.1
+github.com/gorilla/mux v1.1
+github.com/Microsoft/opengcs v0.3.4
+github.com/kr/pty 5cf931ef8f
+github.com/mattn/go-shellwords v1.0.3
+github.com/sirupsen/logrus v1.0.3
+github.com/tchap/go-patricia v2.2.6
+github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
+golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
+golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
+github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
+github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
+golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
+github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
+github.com/pmezard/go-difflib v1.0.0
+github.com/gotestyourself/gotestyourself v1.1.0
+
+github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
+github.com/imdario/mergo 0.2.1
+golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
+
+github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
+github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8
+github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
+
+#get libnetwork packages
+github.com/docker/libnetwork 68f1039f172434709a4550fe92e3e058406c74ce 
+github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
+github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
+github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
+github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
+github.com/hashicorp/memberlist v0.1.0
+github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
+github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
+github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
+github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
+github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
+github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
+github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969
+github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
+github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
+github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
+github.com/coreos/etcd v3.2.1
+github.com/coreos/go-semver v0.2.0
+github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
+github.com/hashicorp/consul v0.5.2
+github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
+github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
+
+# get graph and distribution packages
+github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
+github.com/vbatts/tar-split v0.10.1
+github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
+
+# get go-zfs packages
+github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa
+github.com/pborman/uuid v1.0
+
+google.golang.org/grpc v1.3.0
+
+# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
+github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
+github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
+github.com/opencontainers/runtime-spec v1.0.0
+
+github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
+
+# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json)
+github.com/coreos/go-systemd v4
+github.com/godbus/dbus v4.0.0
+github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
+github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
+
+# gelf logging driver deps
+github.com/Graylog2/go-gelf v2
+
+github.com/fluent/fluent-logger-golang v1.2.1
+# fluent-logger-golang deps
+github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972
+github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c
+
+# fsnotify
+github.com/fsnotify/fsnotify v1.4.2
+
+# awslogs deps
+github.com/aws/aws-sdk-go v1.4.22
+github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0
+github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
+
+# logentries
+github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf
+
+# gcplogs deps
+golang.org/x/oauth2 96382aa079b72d8c014eb0c50f6c223d1e6a2de0
+google.golang.org/api 3cc2e591b550923a2c5f0ab5a803feda924d5823
+cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525
+github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
+google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
+
+# containerd
+github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0
+github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
+
+# cluster
+github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0
+github.com/gogo/protobuf v0.4
+github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
+github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
+golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
+golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
+github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad
+github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990
+github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
+github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
+github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0
+github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e
+github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
+github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/matttproud/golang_protobuf_extensions v1.0.0
+github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
+github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
+
+# cli
+github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git
+github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty
+
+# metrics
+github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
+
+github.com/opencontainers/selinux v1.0.0-rc1
+
+# archive/tar
+# mkdir -p ./vendor/archive
+# git clone git://github.com/tonistiigi/go-1.git ./go
+# git --git-dir ./go/.git --work-tree ./go checkout revert-prefix-ignore
+# cp -a go/src/archive/tar ./vendor/archive/tar
+# rm -rf ./go
+# vndr
diff --git a/src/cmd/go/internal/modconv/testdata/panicparse.out b/src/cmd/go/internal/modconv/testdata/panicparse.out
new file mode 100644 (file)
index 0000000..8830033
--- /dev/null
@@ -0,0 +1,8 @@
+github.com/kr/pretty 737b74a46c4bf788349f72cb256fed10aea4d0ac
+github.com/kr/text 7cafcd837844e784b526369c9bce262804aebc60
+github.com/maruel/ut a9c9f15ccfa6f8b90182a53df32f4745586fbae3
+github.com/mattn/go-colorable 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
+github.com/mattn/go-isatty 56b76bdf51f7708750eac80fa38b952bb9f32639
+github.com/mgutz/ansi c286dcecd19ff979eeb73ea444e479b903f2cfcb
+github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
+golang.org/x/sys a646d33e2ee3172a661fc09bca23bb4889a41bc8
diff --git a/src/cmd/go/internal/modconv/testdata/panicparse.vyml b/src/cmd/go/internal/modconv/testdata/panicparse.vyml
new file mode 100644 (file)
index 0000000..ff3d43f
--- /dev/null
@@ -0,0 +1,17 @@
+vendors:
+- path: github.com/kr/pretty
+  rev: 737b74a46c4bf788349f72cb256fed10aea4d0ac
+- path: github.com/kr/text
+  rev: 7cafcd837844e784b526369c9bce262804aebc60
+- path: github.com/maruel/ut
+  rev: a9c9f15ccfa6f8b90182a53df32f4745586fbae3
+- path: github.com/mattn/go-colorable
+  rev: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
+- path: github.com/mattn/go-isatty
+  rev: 56b76bdf51f7708750eac80fa38b952bb9f32639
+- path: github.com/mgutz/ansi
+  rev: c286dcecd19ff979eeb73ea444e479b903f2cfcb
+- path: github.com/pmezard/go-difflib
+  rev: 792786c7400a136282c1664665ae0a8db921c6c2
+- path: golang.org/x/sys
+  rev: a646d33e2ee3172a661fc09bca23bb4889a41bc8
diff --git a/src/cmd/go/internal/modconv/testdata/prometheus.out b/src/cmd/go/internal/modconv/testdata/prometheus.out
new file mode 100644 (file)
index 0000000..d11b8ec
--- /dev/null
@@ -0,0 +1,258 @@
+cloud.google.com/go/compute/metadata c589d0c9f0d81640c518354c7bcae77d99820aa3
+cloud.google.com/go/internal c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/Azure/azure-sdk-for-go/arm/compute bd73d950fa4440dae889bd9917bff7cef539f86e
+github.com/Azure/azure-sdk-for-go/arm/network bd73d950fa4440dae889bd9917bff7cef539f86e
+github.com/Azure/go-autorest/autorest 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/azure 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/date 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/to 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/validation 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/PuerkitoBio/purell c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/PuerkitoBio/urlesc c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/asaskevich/govalidator 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877
+github.com/aws/aws-sdk-go/aws 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/awserr 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/awsutil 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/client 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/client/metadata 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/corehandlers 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials/endpointcreds 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials/stscreds 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/defaults 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/ec2metadata 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/request 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/session 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/signer/v4 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/endpoints 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/ec2query 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/query 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/query/queryutil 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/rest 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/waiter 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/service/ec2 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/service/sts 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/beorn7/perks/quantile 3ac7bf7a47d159a033b107610db8a1b6575507a4
+github.com/blang/semver c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/http c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/jose c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/key c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/oauth2 c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/oidc c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/pkg/health c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/pkg/httputil c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/pkg/timeutil c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/davecgh/go-spew/spew c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/dgrijalva/jwt-go 9ed569b5d1ac936e6494082958d63a6aa4fff99a
+github.com/docker/distribution/digest c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/docker/distribution/reference c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/emicklei/go-restful c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/emicklei/go-restful/log c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/emicklei/go-restful/swagger c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/ghodss/yaml c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-ini/ini 6e4869b434bd001f6983749881c7ead3545887d8
+github.com/go-openapi/jsonpointer c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-openapi/jsonreference c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-openapi/spec c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-openapi/swag c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/gogo/protobuf/proto c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/gogo/protobuf/sortkeys c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/golang/glog c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/golang/protobuf/proto 98fa357170587e470c5f27d3c3ea0947b71eb455
+github.com/golang/snappy d9eb7a3d35ec988b8585d4a0068e462c27d28380
+github.com/google/gofuzz c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/hashicorp/consul/api daacc4be8bee214e3fc4b32a6dd385f5ef1b4c36
+github.com/hashicorp/go-cleanhttp ad28ea4487f05916463e2423a55166280e8254b5
+github.com/hashicorp/serf/coordinate 1d4fa605f6ff3ed628d7ae5eda7c0e56803e72a5
+github.com/influxdb/influxdb/client 291aaeb9485b43b16875c238482b2f7d0a22a13b
+github.com/influxdb/influxdb/tsdb 291aaeb9485b43b16875c238482b2f7d0a22a13b
+github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
+github.com/jonboulle/clockwork c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/juju/ratelimit c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/julienschmidt/httprouter 109e267447e95ad1bb48b758e40dd7453eb7b039
+github.com/mailru/easyjson/buffer c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/mailru/easyjson/jlexer c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/mailru/easyjson/jwriter c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/matttproud/golang_protobuf_extensions/pbutil fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
+github.com/miekg/dns 58f52c57ce9df13460ac68200cef30a008b9c468
+github.com/pborman/uuid c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/pmezard/go-difflib/difflib d77da356e56a7428ad25149ca77381849a6a5232
+github.com/prometheus/client_golang/prometheus c5b7fccd204277076155f10851dad72b76a49317
+github.com/prometheus/client_model/go fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common/expfmt 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/log 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/model 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/route 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/version 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/samuel/go-zookeeper/zk 177002e16a0061912f02377e2dd8951a8b3551bc
+github.com/spf13/pflag c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/stretchr/testify/assert d77da356e56a7428ad25149ca77381849a6a5232
+github.com/stretchr/testify/require d77da356e56a7428ad25149ca77381849a6a5232
+github.com/syndtr/goleveldb/leveldb 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/cache 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/comparer 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/errors 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/filter 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/iterator 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/journal 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/memdb 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/opt 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/storage 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/table 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/util 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/ugorji/go/codec c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/vaughan0/go-ini a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
+golang.org/x/net/context b336a971b799939dd16ae9b1df8334cb8b977c4d
+golang.org/x/net/context/ctxhttp b336a971b799939dd16ae9b1df8334cb8b977c4d
+golang.org/x/net/http2 c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/http2/hpack c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/idna c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/internal/timeseries 6250b412798208e6c90b03b7c4f226de5aa299e2
+golang.org/x/net/lex/httplex c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/netutil bc3663df0ac92f928d419e31e0d2af22e683a5a2
+golang.org/x/oauth2 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/google 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/internal 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/jws 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/jwt 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/sys/unix c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/sys/windows c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/sys/windows/registry c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/sys/windows/svc/eventlog c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/text/cases c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/internal/tag c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/language c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/runes c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/secure/bidirule c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/secure/precis c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/transform c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/unicode/bidi c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/unicode/norm c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/width c589d0c9f0d81640c518354c7bcae77d99820aa3
+google.golang.org/api/compute/v1 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/api/gensupport 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/api/googleapi 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/api/googleapi/internal/uritemplates 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/appengine 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/app_identity 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/base 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/datastore 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/log 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/modules 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/remote_api 4f7eeb5305a4ba1966344836ba4af9996b7b4e05
+google.golang.org/appengine/internal/urlfetch 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/urlfetch 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/cloud/compute/metadata 0a83eba2cadb60eb22123673c8fb6fca02b03c94
+google.golang.org/cloud/internal 0a83eba2cadb60eb22123673c8fb6fca02b03c94
+gopkg.in/fsnotify.v1 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
+gopkg.in/inf.v0 c589d0c9f0d81640c518354c7bcae77d99820aa3
+gopkg.in/yaml.v2 7ad95dd0798a40da1ccdff6dff35fd177b5edf40
+k8s.io/client-go/1.5/discovery c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/apps/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/authentication/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/authorization/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/autoscaling/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/batch/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/certificates/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/core/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/extensions/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/policy/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/rbac/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/storage/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/errors c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/meta c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/meta/metatypes c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/resource c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/unversioned c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/validation/path c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apimachinery c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apimachinery/announced c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apimachinery/registered c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/apps c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/apps/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/apps/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authentication c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authentication/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authentication/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authorization c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authorization/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authorization/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/autoscaling c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/autoscaling/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/autoscaling/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch/v2alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/certificates c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/certificates/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/certificates/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/extensions c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/extensions/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/policy c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/policy/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/policy/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/rbac c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/rbac/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/rbac/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/storage c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/storage/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/storage/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/auth/user c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/conversion c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/conversion/queryparams c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/fields c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/genericapiserver/openapi/common c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/labels c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/json c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/protobuf c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/recognizer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/streaming c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/versioning c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/selection c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/third_party/forked/golang/reflect c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/types c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/cert c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/clock c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/errors c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/flowcontrol c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/framer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/integer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/intstr c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/json c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/labels c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/net c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/parsers c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/rand c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/runtime c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/sets c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/uuid c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/validation c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/validation/field c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/wait c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/yaml c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/version c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/watch c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/watch/versioned c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/plugin/pkg/client/auth c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/plugin/pkg/client/auth/gcp c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/plugin/pkg/client/auth/oidc c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/rest c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/tools/cache c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/tools/clientcmd/api c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/tools/metrics c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/transport c589d0c9f0d81640c518354c7bcae77d99820aa3
diff --git a/src/cmd/go/internal/modconv/testdata/prometheus.vjson b/src/cmd/go/internal/modconv/testdata/prometheus.vjson
new file mode 100644 (file)
index 0000000..648bec4
--- /dev/null
@@ -0,0 +1,1605 @@
+{
+       "comment": "",
+       "ignore": "test appengine",
+       "package": [
+               {
+                       "checksumSHA1": "Cslv4/ITyQmgjSUhNXFu8q5bqOU=",
+                       "origin": "k8s.io/client-go/1.5/vendor/cloud.google.com/go/compute/metadata",
+                       "path": "cloud.google.com/go/compute/metadata",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "hiJXjkFEGy+sDFf6O58Ocdy9Rnk=",
+                       "origin": "k8s.io/client-go/1.5/vendor/cloud.google.com/go/internal",
+                       "path": "cloud.google.com/go/internal",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "oIt4tXgFYnZJBsCac1BQLnTWALM=",
+                       "path": "github.com/Azure/azure-sdk-for-go/arm/compute",
+                       "revision": "bd73d950fa4440dae889bd9917bff7cef539f86e",
+                       "revisionTime": "2016-10-28T18:31:11Z"
+               },
+               {
+                       "checksumSHA1": "QKi6LiSyD5GnRK8ExpMgZl4XiMI=",
+                       "path": "github.com/Azure/azure-sdk-for-go/arm/network",
+                       "revision": "bd73d950fa4440dae889bd9917bff7cef539f86e",
+                       "revisionTime": "2016-10-28T18:31:11Z"
+               },
+               {
+                       "checksumSHA1": "eVSHe6GIHj9/ziFrQLZ1SC7Nn6k=",
+                       "path": "github.com/Azure/go-autorest/autorest",
+                       "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+                       "revisionTime": "2016-10-25T18:07:34Z"
+               },
+               {
+                       "checksumSHA1": "0sYi0JprevG/PZjtMbOh8h0pt0g=",
+                       "path": "github.com/Azure/go-autorest/autorest/azure",
+                       "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+                       "revisionTime": "2016-10-25T18:07:34Z"
+               },
+               {
+                       "checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=",
+                       "path": "github.com/Azure/go-autorest/autorest/date",
+                       "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+                       "revisionTime": "2016-10-25T18:07:34Z"
+               },
+               {
+                       "checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=",
+                       "path": "github.com/Azure/go-autorest/autorest/to",
+                       "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+                       "revisionTime": "2016-10-25T18:07:34Z"
+               },
+               {
+                       "checksumSHA1": "oBixceM+55gdk47iff8DSEIh3po=",
+                       "path": "github.com/Azure/go-autorest/autorest/validation",
+                       "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+                       "revisionTime": "2016-10-25T18:07:34Z"
+               },
+               {
+                       "checksumSHA1": "IatnluZB5jTVUncMN134e4VOV34=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/PuerkitoBio/purell",
+                       "path": "github.com/PuerkitoBio/purell",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "E/Tz8z0B/gaR551g+XqPKAhcteM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/PuerkitoBio/urlesc",
+                       "path": "github.com/PuerkitoBio/urlesc",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "BdLdZP/C2uOO3lqk9X3NCKFpXa4=",
+                       "path": "github.com/asaskevich/govalidator",
+                       "revision": "7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877",
+                       "revisionTime": "2016-10-01T16:31:30Z"
+               },
+               {
+                       "checksumSHA1": "WNfR3yhLjRC5/uccgju/bwrdsxQ=",
+                       "path": "github.com/aws/aws-sdk-go/aws",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=",
+                       "path": "github.com/aws/aws-sdk-go/aws/awserr",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=",
+                       "path": "github.com/aws/aws-sdk-go/aws/awsutil",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=",
+                       "path": "github.com/aws/aws-sdk-go/aws/client",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=",
+                       "path": "github.com/aws/aws-sdk-go/aws/client/metadata",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=",
+                       "path": "github.com/aws/aws-sdk-go/aws/corehandlers",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=",
+                       "path": "github.com/aws/aws-sdk-go/aws/credentials",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "KQiUK/zr3mqnAXD7x/X55/iNme0=",
+                       "path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=",
+                       "path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=",
+                       "path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=",
+                       "path": "github.com/aws/aws-sdk-go/aws/defaults",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "8E0fEBUJY/1lJOyVxzTxMGQGInk=",
+                       "path": "github.com/aws/aws-sdk-go/aws/ec2metadata",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=",
+                       "path": "github.com/aws/aws-sdk-go/aws/request",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=",
+                       "path": "github.com/aws/aws-sdk-go/aws/session",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "diXvBs1LRC0RJ9WK6sllWKdzC04=",
+                       "path": "github.com/aws/aws-sdk-go/aws/signer/v4",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=",
+                       "path": "github.com/aws/aws-sdk-go/private/endpoints",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=",
+                       "path": "github.com/aws/aws-sdk-go/private/protocol",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=",
+                       "path": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=",
+                       "path": "github.com/aws/aws-sdk-go/private/protocol/query",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=",
+                       "path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "TW/7U+/8ormL7acf6z2rv2hDD+s=",
+                       "path": "github.com/aws/aws-sdk-go/private/protocol/rest",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=",
+                       "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=",
+                       "path": "github.com/aws/aws-sdk-go/private/waiter",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=",
+                       "path": "github.com/aws/aws-sdk-go/service/ec2",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=",
+                       "path": "github.com/aws/aws-sdk-go/service/sts",
+                       "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+                       "revisionTime": "2016-11-02T21:59:28Z"
+               },
+               {
+                       "checksumSHA1": "4QnLdmB1kG3N+KlDd1N+G9TWAGQ=",
+                       "path": "github.com/beorn7/perks/quantile",
+                       "revision": "3ac7bf7a47d159a033b107610db8a1b6575507a4",
+                       "revisionTime": "2016-02-29T21:34:45Z"
+               },
+               {
+                       "checksumSHA1": "n+s4YwtzpMWW5Rt0dEaQa7NHDGQ=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/blang/semver",
+                       "path": "github.com/blang/semver",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Z2AOGSmDKKvI6nuxa+UPjQWpIeM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/http",
+                       "path": "github.com/coreos/go-oidc/http",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "8yvt1xKCgNwuuavJdxRnvaIjrIc=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/jose",
+                       "path": "github.com/coreos/go-oidc/jose",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "zhXKrWBSSJLqZxVE/Xsw0M9ynFQ=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/key",
+                       "path": "github.com/coreos/go-oidc/key",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "bkW0mnXvmHQwHprW/6wrbpP7lAk=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/oauth2",
+                       "path": "github.com/coreos/go-oidc/oauth2",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "E1x2k5FdhJ+dzFrh3kCmC6aJfVw=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/oidc",
+                       "path": "github.com/coreos/go-oidc/oidc",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "O0UMBRCOD9ItMayDqLQ2MJEjkVE=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/health",
+                       "path": "github.com/coreos/pkg/health",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "74vyZz/d49FZXMbFaHOfCGvSLj0=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/httputil",
+                       "path": "github.com/coreos/pkg/httputil",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "etBdQ0LN6ojGunfvUt6B5C3FNrQ=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/timeutil",
+                       "path": "github.com/coreos/pkg/timeutil",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "SdSd7pyjONWWTHc5XE3AhglLo34=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/davecgh/go-spew/spew",
+                       "path": "github.com/davecgh/go-spew/spew",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=",
+                       "path": "github.com/dgrijalva/jwt-go",
+                       "revision": "9ed569b5d1ac936e6494082958d63a6aa4fff99a",
+                       "revisionTime": "2016-11-01T19:39:35Z"
+               },
+               {
+                       "checksumSHA1": "f1wARLDzsF/JoyN01yoxXEwFIp8=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/docker/distribution/digest",
+                       "path": "github.com/docker/distribution/digest",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "PzXRTLmmqWXxmDqdIXLcRYBma18=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/docker/distribution/reference",
+                       "path": "github.com/docker/distribution/reference",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "1vQR+ZyudsjKio6RNKmWhwzGTb0=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful",
+                       "path": "github.com/emicklei/go-restful",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "3xWz4fZ9xW+CfADpYoPFcZCYJ4E=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful/log",
+                       "path": "github.com/emicklei/go-restful/log",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "J7CtF9gIs2yH9A7lPQDDrhYxiRk=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful/swagger",
+                       "path": "github.com/emicklei/go-restful/swagger",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ww7LVo7jNJ1o6sfRcromEHKyY+o=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/ghodss/yaml",
+                       "path": "github.com/ghodss/yaml",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "cVyhKIRI2gQrgpn5qrBeAqErmWM=",
+                       "path": "github.com/go-ini/ini",
+                       "revision": "6e4869b434bd001f6983749881c7ead3545887d8",
+                       "revisionTime": "2016-08-27T06:11:18Z"
+               },
+               {
+                       "checksumSHA1": "NaZnW0tKj/b0k5WzcMD0twrLbrE=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/jsonpointer",
+                       "path": "github.com/go-openapi/jsonpointer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "3LJXjMDxPY+veIqzQtiAvK3hXnY=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/jsonreference",
+                       "path": "github.com/go-openapi/jsonreference",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "faeB3fny260hQ/gEfEXa1ZQTGtk=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/spec",
+                       "path": "github.com/go-openapi/spec",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "wGpZwJ5HZtReou8A3WEV1Gdxs6k=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/swag",
+                       "path": "github.com/go-openapi/swag",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "BIyZQL97iG7mzZ2UMR3XpiXbZdc=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/gogo/protobuf/proto",
+                       "path": "github.com/gogo/protobuf/proto",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "e6cMbpJj41MpihS5eP4SIliRBK4=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/gogo/protobuf/sortkeys",
+                       "path": "github.com/gogo/protobuf/sortkeys",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "URsJa4y/sUUw/STmbeYx9EKqaYE=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/golang/glog",
+                       "path": "github.com/golang/glog",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "yDh5kmmr0zEF1r+rvYqbZcR7iLs=",
+                       "path": "github.com/golang/protobuf/proto",
+                       "revision": "98fa357170587e470c5f27d3c3ea0947b71eb455",
+                       "revisionTime": "2016-10-12T20:53:35Z"
+               },
+               {
+                       "checksumSHA1": "2a/SsTUBMKtcM6VtpbdPGO+c6c8=",
+                       "path": "github.com/golang/snappy",
+                       "revision": "d9eb7a3d35ec988b8585d4a0068e462c27d28380",
+                       "revisionTime": "2016-05-29T05:00:41Z"
+               },
+               {
+                       "checksumSHA1": "/yFfUp3tGt6cK22UVzbq8SjPDCU=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/google/gofuzz",
+                       "path": "github.com/google/gofuzz",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "LclVLJYrBi03PBjsVPpgoMbUDQ8=",
+                       "path": "github.com/hashicorp/consul/api",
+                       "revision": "daacc4be8bee214e3fc4b32a6dd385f5ef1b4c36",
+                       "revisionTime": "2016-10-28T04:06:46Z"
+               },
+               {
+                       "checksumSHA1": "Uzyon2091lmwacNsl1hCytjhHtg=",
+                       "path": "github.com/hashicorp/go-cleanhttp",
+                       "revision": "ad28ea4487f05916463e2423a55166280e8254b5",
+                       "revisionTime": "2016-04-07T17:41:26Z"
+               },
+               {
+                       "checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
+                       "path": "github.com/hashicorp/serf/coordinate",
+                       "revision": "1d4fa605f6ff3ed628d7ae5eda7c0e56803e72a5",
+                       "revisionTime": "2016-10-07T00:41:22Z"
+               },
+               {
+                       "path": "github.com/influxdb/influxdb/client",
+                       "revision": "291aaeb9485b43b16875c238482b2f7d0a22a13b",
+                       "revisionTime": "2015-09-16T14:41:53+02:00"
+               },
+               {
+                       "path": "github.com/influxdb/influxdb/tsdb",
+                       "revision": "291aaeb9485b43b16875c238482b2f7d0a22a13b",
+                       "revisionTime": "2015-09-16T14:41:53+02:00"
+               },
+               {
+                       "checksumSHA1": "0ZrwvB6KoGPj2PoDNSEJwxQ6Mog=",
+                       "path": "github.com/jmespath/go-jmespath",
+                       "revision": "bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d",
+                       "revisionTime": "2016-08-03T19:07:31Z"
+               },
+               {
+                       "checksumSHA1": "9ZVOEbIXnTuYpVqce4en8rwlkPE=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/jonboulle/clockwork",
+                       "path": "github.com/jonboulle/clockwork",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "gA95N2LM2hEJLoqrTPaFsSWDJ2Y=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/juju/ratelimit",
+                       "path": "github.com/juju/ratelimit",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Farach1xcmsQYrhiUfkwF2rbIaE=",
+                       "path": "github.com/julienschmidt/httprouter",
+                       "revision": "109e267447e95ad1bb48b758e40dd7453eb7b039",
+                       "revisionTime": "2015-09-05T19:25:33+02:00"
+               },
+               {
+                       "checksumSHA1": "urY45++NYCue4nh4k8OjUFnIGfU=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/buffer",
+                       "path": "github.com/mailru/easyjson/buffer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "yTDKAM4KBgOvXRsZC50zg0OChvM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/jlexer",
+                       "path": "github.com/mailru/easyjson/jlexer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "4+d+6rhM1pei6lBguhqSEW7LaXs=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/jwriter",
+                       "path": "github.com/mailru/easyjson/jwriter",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Q2vw4HZBbnU8BLFt8VrzStwqSJg=",
+                       "path": "github.com/matttproud/golang_protobuf_extensions/pbutil",
+                       "revision": "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a",
+                       "revisionTime": "2015-04-06T19:39:34+02:00"
+               },
+               {
+                       "checksumSHA1": "Wahi4g/9XiHhSLAJ+8jskg71PCU=",
+                       "path": "github.com/miekg/dns",
+                       "revision": "58f52c57ce9df13460ac68200cef30a008b9c468",
+                       "revisionTime": "2016-10-18T06:08:08Z"
+               },
+               {
+                       "checksumSHA1": "3YJklSuzSE1Rt8A+2dhiWSmf/fw=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/pborman/uuid",
+                       "path": "github.com/pborman/uuid",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "zKKp5SZ3d3ycKe4EKMNT0BqAWBw=",
+                       "origin": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib",
+                       "path": "github.com/pmezard/go-difflib/difflib",
+                       "revision": "d77da356e56a7428ad25149ca77381849a6a5232",
+                       "revisionTime": "2016-06-15T09:26:46Z"
+               },
+               {
+                       "checksumSHA1": "KkB+77Ziom7N6RzSbyUwYGrmDeU=",
+                       "path": "github.com/prometheus/client_golang/prometheus",
+                       "revision": "c5b7fccd204277076155f10851dad72b76a49317",
+                       "revisionTime": "2016-08-17T15:48:24Z"
+               },
+               {
+                       "checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=",
+                       "path": "github.com/prometheus/client_model/go",
+                       "revision": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6",
+                       "revisionTime": "2015-02-12T10:17:44Z"
+               },
+               {
+                       "checksumSHA1": "mHyjbJ3BWOfUV6q9f5PBt0gaY1k=",
+                       "path": "github.com/prometheus/common/expfmt",
+                       "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+                       "revisionTime": "2016-10-02T21:02:34Z"
+               },
+               {
+                       "checksumSHA1": "GWlM3d2vPYyNATtTFgftS10/A9w=",
+                       "path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
+                       "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+                       "revisionTime": "2016-10-02T21:02:34Z"
+               },
+               {
+                       "checksumSHA1": "UU6hIfhVjnAYDADQEfE/3T7Ddm8=",
+                       "path": "github.com/prometheus/common/log",
+                       "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+                       "revisionTime": "2016-10-02T21:02:34Z"
+               },
+               {
+                       "checksumSHA1": "nFie+rxcX5WdIv1diZ+fu3aj6lE=",
+                       "path": "github.com/prometheus/common/model",
+                       "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+                       "revisionTime": "2016-10-02T21:02:34Z"
+               },
+               {
+                       "checksumSHA1": "QQKJYoGcY10nIHxhBEHwjwUZQzk=",
+                       "path": "github.com/prometheus/common/route",
+                       "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+                       "revisionTime": "2016-10-02T21:02:34Z"
+               },
+               {
+                       "checksumSHA1": "91KYK0SpvkaMJJA2+BcxbVnyRO0=",
+                       "path": "github.com/prometheus/common/version",
+                       "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+                       "revisionTime": "2016-10-02T21:02:34Z"
+               },
+               {
+                       "checksumSHA1": "W218eJZPXJG783fUr/z6IaAZyes=",
+                       "path": "github.com/prometheus/procfs",
+                       "revision": "abf152e5f3e97f2fafac028d2cc06c1feb87ffa5",
+                       "revisionTime": "2016-04-11T19:08:41Z"
+               },
+               {
+                       "checksumSHA1": "+49Vr4Me28p3cR+gxX5SUQHbbas=",
+                       "path": "github.com/samuel/go-zookeeper/zk",
+                       "revision": "177002e16a0061912f02377e2dd8951a8b3551bc",
+                       "revisionTime": "2015-08-17T10:50:50-07:00"
+               },
+               {
+                       "checksumSHA1": "YuPBOVkkE3uuBh4RcRUTF0n+frs=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/spf13/pflag",
+                       "path": "github.com/spf13/pflag",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=",
+                       "path": "github.com/stretchr/testify/assert",
+                       "revision": "d77da356e56a7428ad25149ca77381849a6a5232",
+                       "revisionTime": "2016-06-15T09:26:46Z"
+               },
+               {
+                       "checksumSHA1": "P9FJpir2c4G5PA46qEkaWy3l60U=",
+                       "path": "github.com/stretchr/testify/require",
+                       "revision": "d77da356e56a7428ad25149ca77381849a6a5232",
+                       "revisionTime": "2016-06-15T09:26:46Z"
+               },
+               {
+                       "checksumSHA1": "VhcnDY37sYAnL8WjfYQN9YYl+W4=",
+                       "path": "github.com/syndtr/goleveldb/leveldb",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/cache",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/comparer",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/errors",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/filter",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "8dXuAVIsbtaMiGGuHjzGR6Ny/5c=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/iterator",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/journal",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "j+uaQ6DwJ50dkIdfMQu1TXdlQcY=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/memdb",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/opt",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "/Wvv9HeJTN9UUjdjwUlz7X4ioIo=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/storage",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "JTJA+u8zk7EXy1UUmpFPNGvtO2A=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/table",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "4zil8Gwg8VPkDn1YzlgCvtukJFU=",
+                       "path": "github.com/syndtr/goleveldb/leveldb/util",
+                       "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+                       "revisionTime": "2016-10-11T05:00:08Z"
+               },
+               {
+                       "checksumSHA1": "f6Aew+ZA+HBAXCw6/xTST3mB0Lw=",
+                       "origin": "k8s.io/client-go/1.5/vendor/github.com/ugorji/go/codec",
+                       "path": "github.com/ugorji/go/codec",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "sFD8LpJPQtWLwGda3edjf5mNUbs=",
+                       "path": "github.com/vaughan0/go-ini",
+                       "revision": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1",
+                       "revisionTime": "2013-09-23T16:52:12+02:00"
+               },
+               {
+                       "checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
+                       "path": "golang.org/x/net/context",
+                       "revision": "b336a971b799939dd16ae9b1df8334cb8b977c4d",
+                       "revisionTime": "2016-10-27T19:58:04Z"
+               },
+               {
+                       "checksumSHA1": "WHc3uByvGaMcnSoI21fhzYgbOgg=",
+                       "path": "golang.org/x/net/context/ctxhttp",
+                       "revision": "b336a971b799939dd16ae9b1df8334cb8b977c4d",
+                       "revisionTime": "2016-10-27T19:58:04Z"
+               },
+               {
+                       "checksumSHA1": "SPYGC6DQrH9jICccUsOfbvvhB4g=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/http2",
+                       "path": "golang.org/x/net/http2",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "EYNaHp7XdLWRydUCE0amEkKAtgk=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/http2/hpack",
+                       "path": "golang.org/x/net/http2/hpack",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "gXiSniT8fevWOVPVKopYgrdzi60=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/idna",
+                       "path": "golang.org/x/net/idna",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=",
+                       "path": "golang.org/x/net/internal/timeseries",
+                       "revision": "6250b412798208e6c90b03b7c4f226de5aa299e2",
+                       "revisionTime": "2016-08-24T22:20:41Z"
+               },
+               {
+                       "checksumSHA1": "yhndhWXMs/VSEDLks4dNyFMQStA=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/lex/httplex",
+                       "path": "golang.org/x/net/lex/httplex",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "7WASrg0PEueWDDRHkFhEEN6Qrms=",
+                       "path": "golang.org/x/net/netutil",
+                       "revision": "bc3663df0ac92f928d419e31e0d2af22e683a5a2",
+                       "revisionTime": "2016-06-21T20:48:10Z"
+               },
+               {
+                       "checksumSHA1": "mktBVED98G2vv+OKcSgtnFVZC1Y=",
+                       "path": "golang.org/x/oauth2",
+                       "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+                       "revisionTime": "2016-06-07T03:33:14Z"
+               },
+               {
+                       "checksumSHA1": "2rk6lthfQa5Rfydj8j7+dilKGbo=",
+                       "path": "golang.org/x/oauth2/google",
+                       "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+                       "revisionTime": "2016-06-07T03:33:14Z"
+               },
+               {
+                       "checksumSHA1": "W/GiDqzsagBnR7/yEvxatMhUDBs=",
+                       "path": "golang.org/x/oauth2/internal",
+                       "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+                       "revisionTime": "2016-06-07T03:33:14Z"
+               },
+               {
+                       "checksumSHA1": "CPTYHWrVL4jA0B1IuC0hvgcE2AQ=",
+                       "path": "golang.org/x/oauth2/jws",
+                       "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+                       "revisionTime": "2016-06-07T03:33:14Z"
+               },
+               {
+                       "checksumSHA1": "xifBSq0Pn6pIoPA/o3tyzq8X4Ds=",
+                       "path": "golang.org/x/oauth2/jwt",
+                       "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+                       "revisionTime": "2016-06-07T03:33:14Z"
+               },
+               {
+                       "checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=",
+                       "path": "golang.org/x/sys/unix",
+                       "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+                       "revisionTime": "2016-10-22T18:22:21Z"
+               },
+               {
+                       "checksumSHA1": "fpW2dhGFC6SrVzipJx7fjg2DIH8=",
+                       "path": "golang.org/x/sys/windows",
+                       "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+                       "revisionTime": "2016-10-22T18:22:21Z"
+               },
+               {
+                       "checksumSHA1": "PjYlbMS0ttyZYlaevvjA/gV3g1c=",
+                       "path": "golang.org/x/sys/windows/registry",
+                       "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+                       "revisionTime": "2016-10-22T18:22:21Z"
+               },
+               {
+                       "checksumSHA1": "uVlUSSKplihZG7N+QJ6fzDZ4Kh8=",
+                       "path": "golang.org/x/sys/windows/svc/eventlog",
+                       "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+                       "revisionTime": "2016-10-22T18:22:21Z"
+               },
+               {
+                       "checksumSHA1": "QQpKbWuqvhmxVr/hfEYdWzzcXRM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/cases",
+                       "path": "golang.org/x/text/cases",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "iAsGo/kxvnwILbJVUCd0ZcqZO/Q=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/internal/tag",
+                       "path": "golang.org/x/text/internal/tag",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "mQ6PCGHY7K0oPjKbYD8wsTjm/P8=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/language",
+                       "path": "golang.org/x/text/language",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "WpeH2TweiuiZAQVTJNO5vyZAQQA=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/runes",
+                       "path": "golang.org/x/text/runes",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "1VjEPyjdi0xOiIN/Alkqiad/B/c=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/secure/bidirule",
+                       "path": "golang.org/x/text/secure/bidirule",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "FcK7VslktIAWj5jnWVnU2SesBq0=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/secure/precis",
+                       "path": "golang.org/x/text/secure/precis",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "nwlu7UTwYbCj9l5f3a7t2ROwNzM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/transform",
+                       "path": "golang.org/x/text/transform",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "nWJ9R1+Xw41f/mM3b7BYtv77CfI=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/unicode/bidi",
+                       "path": "golang.org/x/text/unicode/bidi",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "BAZ96wCGUj6HdY9sG60Yw09KWA4=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/unicode/norm",
+                       "path": "golang.org/x/text/unicode/norm",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "AZMILKWqLP99UilLgbGZ+uzIVrM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/width",
+                       "path": "golang.org/x/text/width",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "AjdmRXf0fiy6Bec9mNlsGsmZi1k=",
+                       "path": "google.golang.org/api/compute/v1",
+                       "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+                       "revisionTime": "2016-05-31T06:42:46Z"
+               },
+               {
+                       "checksumSHA1": "OtsMVXY89Hc/bBXdDp84atFQawM=",
+                       "path": "google.golang.org/api/gensupport",
+                       "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+                       "revisionTime": "2016-05-31T06:42:46Z"
+               },
+               {
+                       "checksumSHA1": "yQREK/OWrz9PLljbr127+xFk6J0=",
+                       "path": "google.golang.org/api/googleapi",
+                       "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+                       "revisionTime": "2016-05-31T06:42:46Z"
+               },
+               {
+                       "checksumSHA1": "ii4ET3JHk3vkMUEcg+9t/1RZSUU=",
+                       "path": "google.golang.org/api/googleapi/internal/uritemplates",
+                       "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+                       "revisionTime": "2016-05-31T06:42:46Z"
+               },
+               {
+                       "checksumSHA1": "N3KZEuQ9O1QwJXcCJbe7Czwroo4=",
+                       "path": "google.golang.org/appengine",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "G9Xp1ScdsfcKsw+PcWunivRRP3o=",
+                       "path": "google.golang.org/appengine/internal",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "x6Thdfyasqd68dWZWqzWWeIfAfI=",
+                       "path": "google.golang.org/appengine/internal/app_identity",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=",
+                       "path": "google.golang.org/appengine/internal/base",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=",
+                       "path": "google.golang.org/appengine/internal/datastore",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=",
+                       "path": "google.golang.org/appengine/internal/log",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "eLZVX1EHLclFtQnjDIszsdyWRHo=",
+                       "path": "google.golang.org/appengine/internal/modules",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=",
+                       "path": "google.golang.org/appengine/internal/remote_api",
+                       "revision": "4f7eeb5305a4ba1966344836ba4af9996b7b4e05",
+                       "revisionTime": "2016-08-19T23:33:10Z"
+               },
+               {
+                       "checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=",
+                       "path": "google.golang.org/appengine/internal/urlfetch",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=",
+                       "path": "google.golang.org/appengine/urlfetch",
+                       "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+                       "revisionTime": "2016-06-21T05:59:22Z"
+               },
+               {
+                       "checksumSHA1": "Wp8g9MHRmK8SwcyGVCoGtPx+5Lo=",
+                       "path": "google.golang.org/cloud/compute/metadata",
+                       "revision": "0a83eba2cadb60eb22123673c8fb6fca02b03c94",
+                       "revisionTime": "2016-06-21T15:59:29Z"
+               },
+               {
+                       "checksumSHA1": "U7dGDNwEHORvJFMoNSXErKE7ITg=",
+                       "path": "google.golang.org/cloud/internal",
+                       "revision": "0a83eba2cadb60eb22123673c8fb6fca02b03c94",
+                       "revisionTime": "2016-06-21T15:59:29Z"
+               },
+               {
+                       "checksumSHA1": "JfVmsMwyeeepbdw4q4wpN07BuFg=",
+                       "path": "gopkg.in/fsnotify.v1",
+                       "revision": "30411dbcefb7a1da7e84f75530ad3abe4011b4f8",
+                       "revisionTime": "2016-04-12T13:37:56Z"
+               },
+               {
+                       "checksumSHA1": "pfQwQtWlFezJq0Viroa/L+v+yDM=",
+                       "origin": "k8s.io/client-go/1.5/vendor/gopkg.in/inf.v0",
+                       "path": "gopkg.in/inf.v0",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "KgT+peLCcuh0/m2mpoOZXuxXmwc=",
+                       "path": "gopkg.in/yaml.v2",
+                       "revision": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40",
+                       "revisionTime": "2015-06-24T11:29:02+01:00"
+               },
+               {
+                       "checksumSHA1": "st0Nbu4zwLcP3mz03lDOJVZtn8Y=",
+                       "path": "k8s.io/client-go/1.5/discovery",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "S+OzpkipMb46LGZoWuveqSLAcoM=",
+                       "path": "k8s.io/client-go/1.5/kubernetes",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "yCBn8ig1TUMrk+ljtK0nDr7E5Vo=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/apps/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ZRnUz5NrpvJsXAjtnRdEv5UYhSI=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/authentication/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "TY55Np20olmPMzXgfVlIUIyqv04=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/authorization/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "FRByJsFff/6lPH20FtJPaK1NPWI=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/autoscaling/v1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "3Cy2as7HnQ2FDcvpNbatpFWx0P4=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/batch/v1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "RUKywApIbSLLsfkYxXzifh7HIvs=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/certificates/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "4+Lsxu+sYgzsS2JOHP7CdrZLSKc=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/core/v1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "H8jzevN03YUfmf2krJt0qj2P9sU=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/extensions/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "hrpA6xxtwj3oMcQbFxI2cDhO2ZA=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/policy/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "B2+F12NeMwrOHvHK2ALyEcr3UGA=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/rbac/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "h2eSNUym87RWPlez7UKujShwrUQ=",
+                       "path": "k8s.io/client-go/1.5/kubernetes/typed/storage/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "+oIykJ3A0wYjAWbbrGo0jNnMLXw=",
+                       "path": "k8s.io/client-go/1.5/pkg/api",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "UsUsIdhuy5Ej2vI0hbmSsrimoaQ=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/errors",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Eo6LLHFqG6YznIAKr2mVjuqUj6k=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "dYznkLcCEai21z1dX8kZY7uDsck=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/meta",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "b06esG4xMj/YNFD85Lqq00cx+Yo=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/meta/metatypes",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "L9svak1yut0Mx8r9VLDOwpqZzBk=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/resource",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "m7jGshKDLH9kdokfa6MwAqzxRQk=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/unversioned",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "iI6s5WAexr1PEfqrbvuscB+oVik=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/v1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ikac34qI/IkTWHnfi8pPl9irPyo=",
+                       "path": "k8s.io/client-go/1.5/pkg/api/validation/path",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "MJyygSPp8N6z+7SPtcROz4PEwas=",
+                       "path": "k8s.io/client-go/1.5/pkg/apimachinery",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "EGb4IcSTQ1VXCmX0xcyG5GpWId8=",
+                       "path": "k8s.io/client-go/1.5/pkg/apimachinery/announced",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "vhSyuINHQhCsDKTyBmvJT1HzDHI=",
+                       "path": "k8s.io/client-go/1.5/pkg/apimachinery/registered",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "rXeBnwLg8ZFe6m5/Ki7tELVBYDk=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/apps",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "KzHaG858KV1tBh5cuLInNcm+G5s=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/apps/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "fynWdchlRbPaxuST2oGDKiKLTqE=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/apps/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "hreIYssoH4Ef/+Aglpitn3GNLR4=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/authentication",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "EgUqJH4CqB9vXVg6T8II2OEt5LE=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/authentication/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Z3DKgomzRPGcBv/8hlL6pfnIpXI=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/authentication/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "GpuScB2Z+NOT4WIQg1mVvVSDUts=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/authorization",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "+u3UD+HY9lBH+PFi/2B4W564JEw=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/authorization/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "zIFzgWjmlWNLHGHMpCpDCvoLtKY=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/authorization/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "tdpzQFQyVkt5kCLTvtKTVqT+maE=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "nb6LbYGS5tv8H8Ovptg6M7XuDZ4=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "DNb1/nl/5RDdckRrJoXBRagzJXs=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling/v1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "4bLhH2vNl5l4Qp6MjLhWyWVAPE0=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/batch",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "RpAAEynmxlvOlLLZK1KEUQRnYzk=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/batch/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "uWJ2BHmjL/Gq4FFlNkqiN6vvPyM=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/batch/v1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "mHWt/p724dKeP1vqLtWQCye7zaE=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/batch/v2alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "6dJ1dGfXkB3A42TOtMaY/rvv4N8=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/certificates",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Bkrhm6HbFYANwtzUE8eza9SWBk0=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/certificates/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "nRRPIBQ5O3Ad24kscNtK+gPC+fk=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/certificates/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "KUMhoaOg9GXHN/aAVvSLO18SgqU=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/extensions",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "eSo2VhNAYtesvmpEPqn05goW4LY=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/extensions/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "DunWIPrCC5iGMWzkaaugMOxD+hg=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "rVGYi2ko0E7vL5OZSMYX+NAGPYw=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/policy",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "llJHd2H0LzABGB6BcletzIHnexo=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/policy/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "j44bqyY13ldnuCtysYE8nRkMD7o=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/policy/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "vT7rFxowcKMTYc55mddePqUFRgE=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/rbac",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "r1MzUXsG+Zyn30aU8I5R5dgrJPA=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/rbac/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "aNfO8xn8VDO3fM9CpVCe6EIB+GA=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/rbac/v1alpha1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "rQCxrbisCXmj2wymlYG63kcTL9I=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/storage",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "wZyxh5nt5Eh6kF7YNAIYukKWWy0=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/storage/install",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "P8ANOt/I4Cs3QtjVXWmDA/gpQdg=",
+                       "path": "k8s.io/client-go/1.5/pkg/apis/storage/v1beta1",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "qnVPwzvNLz2mmr3BXdU9qIhQXXU=",
+                       "path": "k8s.io/client-go/1.5/pkg/auth/user",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "KrIchxhapSs242yAy8yrTS1XlZo=",
+                       "path": "k8s.io/client-go/1.5/pkg/conversion",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "weZqKFcOhcnF47eDDHXzluCKSF0=",
+                       "path": "k8s.io/client-go/1.5/pkg/conversion/queryparams",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "T3EMfyXZX5939/OOQ1JU+Nmbk4k=",
+                       "path": "k8s.io/client-go/1.5/pkg/fields",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "2v11s3EBH8UBl2qfImT29tQN2kM=",
+                       "path": "k8s.io/client-go/1.5/pkg/genericapiserver/openapi/common",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "GvBlph6PywK3zguou/T9kKNNdoQ=",
+                       "path": "k8s.io/client-go/1.5/pkg/labels",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Vtrgy827r0rWzIAgvIWY4flu740=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "SEcZqRATexhgHvDn+eHvMc07UJs=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime/serializer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "qzYKG9YZSj8l/W1QVTOrGAry/BM=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/json",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "F7h+8zZ0JPLYkac4KgSVljguBE4=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/protobuf",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "CvySOL8C85e3y7EWQ+Au4cwUZJM=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/recognizer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "eCitoKeIun+lJzYFhAfdSIIicSM=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/streaming",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "kVWvZuLGltJ4YqQsiaCLRRLDDK0=",
+                       "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/versioning",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "m51+LAeQ9RK1KHX+l2iGcwbVCKs=",
+                       "path": "k8s.io/client-go/1.5/pkg/selection",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "dp4IWcC3U6a0HeOdVCDQWODWCbw=",
+                       "path": "k8s.io/client-go/1.5/pkg/third_party/forked/golang/reflect",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ER898XJD1ox4d71gKZD8TLtTSpM=",
+                       "path": "k8s.io/client-go/1.5/pkg/types",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "BVdXtnLDlmBQksRPfHOIG+qdeVg=",
+                       "path": "k8s.io/client-go/1.5/pkg/util",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "nnh8Sa4dCupxRI4bbKaozGp1d/A=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/cert",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "S32d5uduNlwouM8+mIz+ALpliUQ=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/clock",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Y6rWC0TUw2/uUeUjJ7kazyEUzBQ=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/errors",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "C7IfEAdCOePw3/IraaZCNXuYXLw=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/flowcontrol",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "EuslQHnhBSRXaWimYqLEqhMPV48=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/framer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ByO18NbZwiifFr8qtLyfJAHXguA=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/integer",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ww+RfsoIlUBDwThg2oqC5QVz33Y=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/intstr",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "7E8f8dLlXW7u6r9sggMjvB4HEiw=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/json",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "d0pFZxMJG9j95acNmaIM1l+X+QU=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/labels",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "wCN7u1lE+25neM9jXeI7aE8EAfk=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/net",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "g+kBkxcb+tYmFtRRly+VE+JAIfw=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/parsers",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "S4wUnE5VkaWWrkLbgPL/1oNLJ4g=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/rand",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "8j9c2PqTKybtnymXbStNYRexRj8=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/runtime",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "aAz4e8hLGs0+ZAz1TdA5tY/9e1A=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/sets",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "P/fwh6QZ5tsjVyHTaASDWL3WaGs=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/uuid",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "P9Bq/1qbF4SvnN9HyCTRpbUz7sQ=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/validation",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "D0JIEjlP69cuPOZEdsSKeFgsnI8=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/validation/field",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "T7ba8t8i+BtgClMgL+aMZM94fcI=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/wait",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "6RCTv/KDiw7as4KeyrgU3XrUSQI=",
+                       "path": "k8s.io/client-go/1.5/pkg/util/yaml",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "OwKlsSeKtz1FBVC9cQ5gWRL5pKc=",
+                       "path": "k8s.io/client-go/1.5/pkg/version",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Oil9WGw/dODbpBopn6LWQGS3DYg=",
+                       "path": "k8s.io/client-go/1.5/pkg/watch",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "r5alnRCbLaPsbTeJjjTVn/bt6uw=",
+                       "path": "k8s.io/client-go/1.5/pkg/watch/versioned",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "X1+ltyfHui/XCwDupXIf39+9gWQ=",
+                       "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "KYy+js37AS0ZT08g5uBr1ZoMPmE=",
+                       "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth/gcp",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "wQ9G5++lbQpejqCzGHo037N3YcY=",
+                       "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth/oidc",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "ABe8YfZVEDoRpAUqp2BKP8o1VIA=",
+                       "path": "k8s.io/client-go/1.5/rest",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "Gbe0Vs9hkI7X5hhbXUuWdRFffSI=",
+                       "path": "k8s.io/client-go/1.5/tools/cache",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "K/oOznXABjqSS1c2Fs407c5F8KA=",
+                       "path": "k8s.io/client-go/1.5/tools/clientcmd/api",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "c1PQ4WJRfpA9BYcFHW2+46hu5IE=",
+                       "path": "k8s.io/client-go/1.5/tools/metrics",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               },
+               {
+                       "checksumSHA1": "e4W2q+6wvjejv3V0UCI1mewTTro=",
+                       "path": "k8s.io/client-go/1.5/transport",
+                       "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+                       "revisionTime": "2016-09-30T00:14:02Z"
+               }
+       ],
+       "rootPath": "github.com/prometheus/prometheus"
+}
diff --git a/src/cmd/go/internal/modconv/testdata/upspin.dep b/src/cmd/go/internal/modconv/testdata/upspin.dep
new file mode 100644 (file)
index 0000000..be77bcb
--- /dev/null
@@ -0,0 +1,57 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  branch = "master"
+  name = "bazil.org/fuse"
+  packages = [".","fs","fuseutil"]
+  revision = "371fbbdaa8987b715bdd21d6adc4c9b20155f748"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/NYTimes/gziphandler"
+  packages = ["."]
+  revision = "97ae7fbaf81620fe97840685304a78a306a39c64"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/golang/protobuf"
+  packages = ["proto"]
+  revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/russross/blackfriday"
+  packages = ["."]
+  revision = "6d1ef893fcb01b4f50cb6e57ed7df3e2e627b6b2"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/crypto"
+  packages = ["acme","acme/autocert","hkdf"]
+  revision = "13931e22f9e72ea58bb73048bc752b48c6d4d4ac"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/net"
+  packages = ["context"]
+  revision = "4b14673ba32bee7f5ac0f990a48f033919fd418b"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/text"
+  packages = ["cases","internal","internal/gen","internal/tag","internal/triegen","internal/ucd","language","runes","secure/bidirule","secure/precis","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable","width"]
+  revision = "6eab0e8f74e86c598ec3b6fad4888e0c11482d48"
+
+[[projects]]
+  branch = "v2"
+  name = "gopkg.in/yaml.v2"
+  packages = ["."]
+  revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  inputs-digest = "2246e647ba1c78b0b9f948f9fb072fff1467284fb138709c063e99736f646b90"
+  solver-name = "gps-cdcl"
+  solver-version = 1
diff --git a/src/cmd/go/internal/modconv/testdata/upspin.out b/src/cmd/go/internal/modconv/testdata/upspin.out
new file mode 100644 (file)
index 0000000..00597db
--- /dev/null
@@ -0,0 +1,8 @@
+bazil.org/fuse 371fbbdaa8987b715bdd21d6adc4c9b20155f748
+github.com/NYTimes/gziphandler 97ae7fbaf81620fe97840685304a78a306a39c64
+github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
+github.com/russross/blackfriday 6d1ef893fcb01b4f50cb6e57ed7df3e2e627b6b2
+golang.org/x/crypto 13931e22f9e72ea58bb73048bc752b48c6d4d4ac
+golang.org/x/net 4b14673ba32bee7f5ac0f990a48f033919fd418b
+golang.org/x/text 6eab0e8f74e86c598ec3b6fad4888e0c11482d48
+gopkg.in/yaml.v2 eb3733d160e74a9c7e442f435eb3bea458e1d19f
diff --git a/src/cmd/go/internal/modconv/tsv.go b/src/cmd/go/internal/modconv/tsv.go
new file mode 100644 (file)
index 0000000..fd33649
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+package modconv
+
+import (
+       "strings"
+
+       "cmd/go/internal/module"
+)
+
+func ParseDependenciesTSV(file string, data []byte) ([]module.Version, error) {
+       var list []module.Version
+       for lineno, line := range strings.Split(string(data), "\n") {
+               lineno++
+               f := strings.Split(line, "\t")
+               if len(f) >= 3 {
+                       list = append(list, module.Version{Path: f[0], Version: f[2]})
+               }
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/vconf.go b/src/cmd/go/internal/modconv/vconf.go
new file mode 100644 (file)
index 0000000..5d3cd3c
--- /dev/null
@@ -0,0 +1,26 @@
+// 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.
+
+package modconv
+
+import (
+       "strings"
+
+       "cmd/go/internal/module"
+)
+
+func ParseVendorConf(file string, data []byte) ([]module.Version, error) {
+       var list []module.Version
+       for lineno, line := range strings.Split(string(data), "\n") {
+               lineno++
+               if i := strings.Index(line, "#"); i >= 0 {
+                       line = line[:i]
+               }
+               f := strings.Fields(line)
+               if len(f) >= 2 {
+                       list = append(list, module.Version{Path: f[0], Version: f[1]})
+               }
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/vjson.go b/src/cmd/go/internal/modconv/vjson.go
new file mode 100644 (file)
index 0000000..38b0a68
--- /dev/null
@@ -0,0 +1,28 @@
+// 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.
+
+package modconv
+
+import (
+       "encoding/json"
+
+       "cmd/go/internal/module"
+)
+
+func ParseVendorJSON(file string, data []byte) ([]module.Version, error) {
+       var cfg struct {
+               Package []struct {
+                       Path     string
+                       Revision string
+               }
+       }
+       if err := json.Unmarshal(data, &cfg); err != nil {
+               return nil, err
+       }
+       var list []module.Version
+       for _, d := range cfg.Package {
+               list = append(list, module.Version{Path: d.Path, Version: d.Revision})
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/vmanifest.go b/src/cmd/go/internal/modconv/vmanifest.go
new file mode 100644 (file)
index 0000000..f2cf0f5
--- /dev/null
@@ -0,0 +1,28 @@
+// 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.
+
+package modconv
+
+import (
+       "encoding/json"
+
+       "cmd/go/internal/module"
+)
+
+func ParseVendorManifest(file string, data []byte) ([]module.Version, error) {
+       var cfg struct {
+               Dependencies []struct {
+                       ImportPath string
+                       Revision   string
+               }
+       }
+       if err := json.Unmarshal(data, &cfg); err != nil {
+               return nil, err
+       }
+       var list []module.Version
+       for _, d := range cfg.Dependencies {
+               list = append(list, module.Version{Path: d.ImportPath, Version: d.Revision})
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modconv/vyml.go b/src/cmd/go/internal/modconv/vyml.go
new file mode 100644 (file)
index 0000000..e2ea9e3
--- /dev/null
@@ -0,0 +1,40 @@
+// 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.
+
+package modconv
+
+import (
+       "cmd/go/internal/module"
+       "strings"
+)
+
+func ParseVendorYML(file string, data []byte) ([]module.Version, error) {
+       var list []module.Version
+       vendors := false
+       path := ""
+       for lineno, line := range strings.Split(string(data), "\n") {
+               lineno++
+               if line == "" {
+                       continue
+               }
+               if strings.HasPrefix(line, "vendors:") {
+                       vendors = true
+               } else if line[0] != '-' && line[0] != ' ' && line[0] != '\t' {
+                       vendors = false
+               }
+               if !vendors {
+                       continue
+               }
+               if strings.HasPrefix(line, "- path:") {
+                       path = strings.TrimSpace(line[len("- path:"):])
+               }
+               if strings.HasPrefix(line, "  rev:") {
+                       rev := strings.TrimSpace(line[len("  rev:"):])
+                       if path != "" && rev != "" {
+                               list = append(list, module.Version{Path: path, Version: rev})
+                       }
+               }
+       }
+       return list, nil
+}
diff --git a/src/cmd/go/internal/modfetch/bitbucket/fetch.go b/src/cmd/go/internal/modfetch/bitbucket/fetch.go
new file mode 100644 (file)
index 0000000..c077a3e
--- /dev/null
@@ -0,0 +1,22 @@
+// 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.
+
+package bitbucket
+
+import (
+       "fmt"
+       "strings"
+
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfetch/gitrepo"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+       f := strings.Split(path, "/")
+       if len(f) < 3 || f[0] != "bitbucket.org" {
+               return nil, fmt.Errorf("bitbucket repo must be bitbucket.org/org/project")
+       }
+       path = f[0] + "/" + f[1] + "/" + f[2]
+       return gitrepo.Repo("https://"+path, path)
+}
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
new file mode 100644 (file)
index 0000000..0e3bb7d
--- /dev/null
@@ -0,0 +1,187 @@
+// 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.
+
+// Package codehost defines the interface implemented by a code hosting source,
+// along with support code for use by implementations.
+package codehost
+
+import (
+       "bytes"
+       "crypto/sha256"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "strings"
+       "time"
+
+       "cmd/go/internal/cfg"
+       "cmd/go/internal/str"
+)
+
+// Downloaded size limits.
+const (
+       MaxGoMod   = 16 << 20  // maximum size of go.mod file
+       MaxLICENSE = 16 << 20  // maximum size of LICENSE file
+       MaxZipFile = 500 << 20 // maximum size of downloaded zip file
+)
+
+// A Repo represents a code hosting source.
+// Typical implementations include local version control repositories,
+// remote version control servers, and code hosting sites.
+type Repo interface {
+       // Root returns the import path of the root directory of the repository.
+       Root() string
+
+       // List lists all tags with the given prefix.
+       Tags(prefix string) (tags []string, err error)
+
+       // Stat returns information about the revision rev.
+       // A revision can be any identifier known to the underlying service:
+       // commit hash, branch, tag, and so on.
+       Stat(rev string) (*RevInfo, error)
+
+       // Latest returns the latest revision on the default branch,
+       // whatever that means in the underlying implementation.
+       Latest() (*RevInfo, error)
+
+       // ReadFile reads the given file in the file tree corresponding to revision rev.
+       // It should refuse to read more than maxSize bytes.
+       ReadFile(rev, file string, maxSize int64) (data []byte, err error)
+
+       // ReadZip downloads a zip file for the subdir subdirectory
+       // of the given revision to a new file in a given temporary directory.
+       // It should refuse to read more than maxSize bytes.
+       // It returns a ReadCloser for a streamed copy of the zip file,
+       // along with the actual subdirectory (possibly shorter than subdir)
+       // contained in the zip file. All files in the zip file are expected to be
+       // nested in a single top-level directory, whose name is not specified.
+       ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error)
+}
+
+// A Rev describes a single revision in a source code repository.
+type RevInfo struct {
+       Name    string    // complete ID in underlying repository
+       Short   string    // shortened ID, for use in pseudo-version
+       Version string    // TODO what is this?
+       Time    time.Time // commit time
+}
+
+// AllHex reports whether the revision rev is entirely lower-case hexadecimal digits.
+func AllHex(rev string) bool {
+       for i := 0; i < len(rev); i++ {
+               c := rev[i]
+               if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' {
+                       continue
+               }
+               return false
+       }
+       return true
+}
+
+// ShortenSHA1 shortens a SHA1 hash (40 hex digits) to the canonical length
+// used in pseudo-versions (12 hex digits).
+func ShortenSHA1(rev string) string {
+       if AllHex(rev) && len(rev) == 40 {
+               return rev[:12]
+       }
+       return rev
+}
+
+// WorkRoot is the root of the cached work directory.
+// It is set by cmd/go/internal/vgo.InitMod.
+var WorkRoot string
+
+// WorkDir returns the name of the cached work directory to use for the
+// given repository type and name.
+func WorkDir(typ, name string) (string, error) {
+       if WorkRoot == "" {
+               return "", fmt.Errorf("codehost.WorkRoot not set")
+       }
+
+       // We name the work directory for the SHA256 hash of the type and name.
+       // We intentionally avoid the actual name both because of possible
+       // conflicts with valid file system paths and because we want to ensure
+       // that one checkout is never nested inside another. That nesting has
+       // led to security problems in the past.
+       if strings.Contains(typ, ":") {
+               return "", fmt.Errorf("codehost.WorkDir: type cannot contain colon")
+       }
+       key := typ + ":" + name
+       dir := filepath.Join(WorkRoot, fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
+       data, err := ioutil.ReadFile(dir + ".info")
+       if err == nil {
+               have := strings.TrimSuffix(string(data), "\n")
+               if have != key {
+                       return "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key)
+               }
+               _, err := os.Stat(dir)
+               if err != nil {
+                       return "", fmt.Errorf("%s exists but %s does not", dir+".info", dir)
+               }
+               if cfg.BuildX {
+                       fmt.Fprintf(os.Stderr, "# %s for %s %s\n", dir, typ, name)
+               }
+               return dir, nil
+       }
+
+       if cfg.BuildX {
+               fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", dir, typ, name)
+       }
+       os.RemoveAll(dir)
+       if err := os.MkdirAll(dir, 0777); err != nil {
+               return "", err
+       }
+       if err := ioutil.WriteFile(dir+".info", []byte(key), 0666); err != nil {
+               os.RemoveAll(dir)
+               return "", err
+       }
+       return dir, nil
+}
+
+type RunError struct {
+       Cmd    string
+       Err    error
+       Stderr []byte
+}
+
+func (e *RunError) Error() string {
+       text := e.Cmd + ": " + e.Err.Error()
+       stderr := bytes.TrimRight(e.Stderr, "\n")
+       if len(stderr) > 0 {
+               text += ":\n\t" + strings.Replace(string(stderr), "\n", "\n\t", -1)
+       }
+       return text
+}
+
+// Run runs the command line in the given directory
+// (an empty dir means the current directory).
+// It returns the standard output and, for a non-zero exit,
+// a *RunError indicating the command, exit status, and standard error.
+// Standard error is unavailable for commands that exit successfully.
+func Run(dir string, cmdline ...interface{}) ([]byte, error) {
+       cmd := str.StringList(cmdline...)
+       if cfg.BuildX {
+               var cd string
+               if dir != "" {
+                       cd = "cd " + dir + "; "
+               }
+               fmt.Fprintf(os.Stderr, "%s%s\n", cd, strings.Join(cmd, " "))
+       }
+       // TODO: Impose limits on command output size.
+       // TODO: Set environment to get English error messages.
+       var stderr bytes.Buffer
+       var stdout bytes.Buffer
+       c := exec.Command(cmd[0], cmd[1:]...)
+       c.Dir = dir
+       c.Stderr = &stderr
+       c.Stdout = &stdout
+       err := c.Run()
+       if err != nil {
+               err = &RunError{Cmd: strings.Join(cmd, " ") + " in " + dir, Stderr: stderr.Bytes(), Err: err}
+       }
+       return stdout.Bytes(), err
+}
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
new file mode 100644 (file)
index 0000000..bb6e8ac
--- /dev/null
@@ -0,0 +1,576 @@
+// 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.
+
+package modfetch
+
+import (
+       "archive/zip"
+       "bytes"
+       "errors"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path"
+       "path/filepath"
+       "regexp"
+       "strconv"
+       "strings"
+       "time"
+
+       "cmd/go/internal/modconv"
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfile"
+       "cmd/go/internal/module"
+       "cmd/go/internal/semver"
+)
+
+// A codeRepo implements modfetch.Repo using an underlying codehost.Repo.
+type codeRepo struct {
+       modPath  string
+       code     codehost.Repo
+       codeRoot string
+       codeDir  string
+
+       path        string
+       pathPrefix  string
+       pathMajor   string
+       pseudoMajor string
+}
+
+func newCodeRepo(code codehost.Repo, path string) (Repo, error) {
+       codeRoot := code.Root()
+       if !hasPathPrefix(path, codeRoot) {
+               return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
+       }
+       pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
+       if !ok {
+               return nil, fmt.Errorf("invalid module path %q", path)
+       }
+       pseudoMajor := "v0"
+       if pathMajor != "" {
+               pseudoMajor = pathMajor[1:]
+       }
+
+       // At this point we might have:
+       //      codeRoot = github.com/rsc/foo
+       //      path = github.com/rsc/foo/bar/v2
+       //      pathPrefix = github.com/rsc/foo/bar
+       //      pathMajor = /v2
+       //      pseudoMajor = v2
+       //
+       // Compute codeDir = bar, the subdirectory within the repo
+       // corresponding to the module root.
+       codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, codeRoot), "/")
+       if strings.HasPrefix(path, "gopkg.in/") {
+               // But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot.
+               // For example we might have:
+               //      codeRoot = gopkg.in/yaml.v2
+               //      pathPrefix = gopkg.in/yaml
+               //      pathMajor = .v2
+               //      pseudoMajor = v2
+               //      codeDir = pathPrefix (because codeRoot is not a prefix of pathPrefix)
+               // Clear codeDir - the module root is the repo root for gopkg.in repos.
+               codeDir = ""
+       }
+
+       r := &codeRepo{
+               modPath:     path,
+               code:        code,
+               codeRoot:    codeRoot,
+               codeDir:     codeDir,
+               pathPrefix:  pathPrefix,
+               pathMajor:   pathMajor,
+               pseudoMajor: pseudoMajor,
+       }
+
+       return r, nil
+}
+
+func (r *codeRepo) ModulePath() string {
+       return r.modPath
+}
+
+func (r *codeRepo) Versions(prefix string) ([]string, error) {
+       p := prefix
+       if r.codeDir != "" {
+               p = r.codeDir + "/" + p
+       }
+       tags, err := r.code.Tags(p)
+       if err != nil {
+               return nil, err
+       }
+       list := []string{}
+       for _, tag := range tags {
+               if !strings.HasPrefix(tag, p) {
+                       continue
+               }
+               v := tag
+               if r.codeDir != "" {
+                       v = v[len(r.codeDir)+1:]
+               }
+               if !semver.IsValid(v) || v != semver.Canonical(v) || isPseudoVersion(v) || !module.MatchPathMajor(v, r.pathMajor) {
+                       continue
+               }
+               list = append(list, v)
+       }
+       SortVersions(list)
+       return list, nil
+}
+
+func (r *codeRepo) Stat(rev string) (*RevInfo, error) {
+       if rev == "latest" {
+               return r.Latest()
+       }
+       codeRev := r.revToRev(rev)
+       if semver.IsValid(codeRev) && r.codeDir != "" {
+               codeRev = r.codeDir + "/" + codeRev
+       }
+       info, err := r.code.Stat(codeRev)
+       if err != nil {
+               return nil, err
+       }
+       return r.convert(info)
+}
+
+func (r *codeRepo) Latest() (*RevInfo, error) {
+       info, err := r.code.Latest()
+       if err != nil {
+               return nil, err
+       }
+       return r.convert(info)
+}
+
+func (r *codeRepo) convert(info *codehost.RevInfo) (*RevInfo, error) {
+       versionOK := func(v string) bool {
+               return semver.IsValid(v) && v == semver.Canonical(v) && !isPseudoVersion(v) && module.MatchPathMajor(v, r.pathMajor)
+       }
+       v := info.Version
+       if r.codeDir == "" {
+               if !versionOK(v) {
+                       v = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
+               }
+       } else {
+               p := r.codeDir + "/"
+               if strings.HasPrefix(v, p) && versionOK(v[len(p):]) {
+                       v = v[len(p):]
+               } else {
+                       v = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
+               }
+       }
+
+       info2 := &RevInfo{
+               Name:    info.Name,
+               Short:   info.Short,
+               Time:    info.Time,
+               Version: v,
+       }
+       return info2, nil
+}
+
+func (r *codeRepo) revToRev(rev string) string {
+       if semver.IsValid(rev) {
+               if isPseudoVersion(rev) {
+                       i := strings.Index(rev, "-")
+                       j := strings.Index(rev[i+1:], "-")
+                       return rev[i+1+j+1:]
+               }
+               if r.codeDir == "" {
+                       return rev
+               }
+               return r.codeDir + "/" + rev
+       }
+       return rev
+}
+
+func (r *codeRepo) versionToRev(version string) (rev string, err error) {
+       if !semver.IsValid(version) {
+               return "", fmt.Errorf("malformed semantic version %q", version)
+       }
+       return r.revToRev(version), nil
+}
+
+func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err error) {
+       rev, err = r.versionToRev(version)
+       if err != nil {
+               return "", "", nil, err
+       }
+       if r.pathMajor == "" || strings.HasPrefix(r.pathMajor, ".") {
+               if r.codeDir == "" {
+                       return rev, "", nil, nil
+               }
+               file1 := path.Join(r.codeDir, "go.mod")
+               gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
+               if err1 != nil {
+                       return "", "", nil, fmt.Errorf("missing go.mod")
+               }
+               return rev, r.codeDir, gomod1, nil
+       }
+
+       // Suppose pathMajor is "/v2".
+       // Either go.mod should claim v2 and v2/go.mod should not exist,
+       // or v2/go.mod should exist and claim v2. Not both.
+       // Note that we don't check the full path, just the major suffix,
+       // because of replacement modules. This might be a fork of
+       // the real module, found at a different path, usable only in
+       // a replace directive.
+       file1 := path.Join(r.codeDir, "go.mod")
+       file2 := path.Join(r.codeDir, r.pathMajor[1:], "go.mod")
+       gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
+       gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
+       found1 := err1 == nil && isMajor(gomod1, r.pathMajor)
+       found2 := err2 == nil && isMajor(gomod2, r.pathMajor)
+
+       if err2 == nil && !found2 {
+               return "", "", nil, fmt.Errorf("%s has non-...%s module path", file2, r.pathMajor)
+       }
+       if found1 && found2 {
+               return "", "", nil, fmt.Errorf("both %s and %s claim ...%s module", file1, file2, r.pathMajor)
+       }
+       if found2 {
+               return rev, filepath.Join(r.codeDir, r.pathMajor), gomod2, nil
+       }
+       if found1 {
+               return rev, r.codeDir, gomod1, nil
+       }
+       return "", "", nil, fmt.Errorf("missing or invalid go.mod")
+}
+
+func isMajor(gomod []byte, pathMajor string) bool {
+       return strings.HasSuffix(modPath(gomod), pathMajor)
+}
+
+var moduleStr = []byte("module")
+
+func modPath(mod []byte) string {
+       for len(mod) > 0 {
+               line := mod
+               mod = nil
+               if i := bytes.IndexByte(line, '\n'); i >= 0 {
+                       line, mod = line[:i], line[i+1:]
+               }
+               line = bytes.TrimSpace(line)
+               if !bytes.HasPrefix(line, moduleStr) {
+                       continue
+               }
+               line = line[len(moduleStr):]
+               n := len(line)
+               line = bytes.TrimSpace(line)
+               if len(line) == n || len(line) == 0 {
+                       continue
+               }
+
+               if line[0] == '"' || line[0] == '`' {
+                       p, err := strconv.Unquote(string(line))
+                       if err != nil {
+                               return "" // malformed quoted string or multiline module path
+                       }
+                       return p
+               }
+
+               return string(line)
+       }
+       return "" // missing module path
+}
+
+func (r *codeRepo) GoMod(version string) (data []byte, err error) {
+       rev, dir, gomod, err := r.findDir(version)
+       if err != nil {
+               return nil, err
+       }
+       if gomod != nil {
+               return gomod, nil
+       }
+       data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
+       if err != nil {
+               if e := strings.ToLower(err.Error()); strings.Contains(e, "not found") || strings.Contains(e, "404") { // TODO
+                       return r.legacyGoMod(rev, dir), nil
+               }
+               return nil, err
+       }
+       return data, nil
+}
+
+var altConfigs = []string{
+       "Gopkg.lock",
+
+       "GLOCKFILE",
+       "Godeps/Godeps.json",
+       "dependencies.tsv",
+       "glide.lock",
+       "vendor.conf",
+       "vendor.yml",
+       "vendor/manifest",
+       "vendor/vendor.json",
+}
+
+func (r *codeRepo) legacyGoMod(rev, dir string) []byte {
+       mf := new(modfile.File)
+       mf.AddModuleStmt(r.modPath)
+       for _, file := range altConfigs {
+               data, err := r.code.ReadFile(rev, path.Join(dir, file), codehost.MaxGoMod)
+               if err != nil {
+                       continue
+               }
+               convert := modconv.Converters[file]
+               if convert == nil {
+                       continue
+               }
+               if err := ConvertLegacyConfig(mf, file, data); err != nil {
+                       continue
+               }
+               break
+       }
+       data, err := mf.Format()
+       if err != nil {
+               return []byte(fmt.Sprintf("%s\nmodule %q\n", modconv.Prefix, r.modPath))
+       }
+       return append([]byte(modconv.Prefix+"\n"), data...)
+}
+
+func (r *codeRepo) modPrefix(rev string) string {
+       return r.modPath + "@" + rev
+}
+
+func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
+       rev, dir, _, err := r.findDir(version)
+       if err != nil {
+               return "", err
+       }
+       dl, actualDir, err := r.code.ReadZip(rev, dir, codehost.MaxZipFile)
+       if err != nil {
+               return "", err
+       }
+       if actualDir != "" && !hasPathPrefix(dir, actualDir) {
+               return "", fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir)
+       }
+       subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
+
+       // Spool to local file.
+       f, err := ioutil.TempFile(tmpdir, "vgo-codehost-")
+       if err != nil {
+               dl.Close()
+               return "", err
+       }
+       defer os.Remove(f.Name())
+       defer f.Close()
+       maxSize := int64(codehost.MaxZipFile)
+       lr := &io.LimitedReader{R: dl, N: maxSize + 1}
+       if _, err := io.Copy(f, lr); err != nil {
+               dl.Close()
+               return "", err
+       }
+       dl.Close()
+       if lr.N <= 0 {
+               return "", fmt.Errorf("downloaded zip file too large")
+       }
+       size := (maxSize + 1) - lr.N
+       if _, err := f.Seek(0, 0); err != nil {
+               return "", err
+       }
+
+       // Translate from zip file we have to zip file we want.
+       zr, err := zip.NewReader(f, size)
+       if err != nil {
+               return "", err
+       }
+       f2, err := ioutil.TempFile(tmpdir, "vgo-")
+       if err != nil {
+               return "", err
+       }
+
+       zw := zip.NewWriter(f2)
+       newName := f2.Name()
+       defer func() {
+               f2.Close()
+               if err != nil {
+                       os.Remove(newName)
+               }
+       }()
+       if subdir != "" {
+               subdir += "/"
+       }
+       haveLICENSE := false
+       topPrefix := ""
+       haveGoMod := make(map[string]bool)
+       for _, zf := range zr.File {
+               if topPrefix == "" {
+                       i := strings.Index(zf.Name, "/")
+                       if i < 0 {
+                               return "", fmt.Errorf("missing top-level directory prefix")
+                       }
+                       topPrefix = zf.Name[:i+1]
+               }
+               if !strings.HasPrefix(zf.Name, topPrefix) {
+                       return "", fmt.Errorf("zip file contains more than one top-level directory")
+               }
+               dir, file := path.Split(zf.Name)
+               if file == "go.mod" {
+                       haveGoMod[dir] = true
+               }
+       }
+       root := topPrefix + subdir
+       inSubmodule := func(name string) bool {
+               for {
+                       dir, _ := path.Split(name)
+                       if len(dir) <= len(root) {
+                               return false
+                       }
+                       if haveGoMod[dir] {
+                               return true
+                       }
+                       name = dir[:len(dir)-1]
+               }
+       }
+       for _, zf := range zr.File {
+               if topPrefix == "" {
+                       i := strings.Index(zf.Name, "/")
+                       if i < 0 {
+                               return "", fmt.Errorf("missing top-level directory prefix")
+                       }
+                       topPrefix = zf.Name[:i+1]
+               }
+               if strings.HasSuffix(zf.Name, "/") { // drop directory dummy entries
+                       continue
+               }
+               if !strings.HasPrefix(zf.Name, topPrefix) {
+                       return "", fmt.Errorf("zip file contains more than one top-level directory")
+               }
+               name := strings.TrimPrefix(zf.Name, topPrefix)
+               if !strings.HasPrefix(name, subdir) {
+                       continue
+               }
+               name = strings.TrimPrefix(name, subdir)
+               if isVendoredPackage(name) {
+                       continue
+               }
+               if inSubmodule(zf.Name) {
+                       continue
+               }
+               base := path.Base(name)
+               if strings.ToLower(base) == "go.mod" && base != "go.mod" {
+                       return "", fmt.Errorf("zip file contains %s, want all lower-case go.mod", zf.Name)
+               }
+               if name == "LICENSE" {
+                       haveLICENSE = true
+               }
+               size := int64(zf.UncompressedSize)
+               if size < 0 || maxSize < size {
+                       return "", fmt.Errorf("module source tree too big")
+               }
+               maxSize -= size
+
+               rc, err := zf.Open()
+               if err != nil {
+                       return "", err
+               }
+               w, err := zw.Create(r.modPrefix(version) + "/" + name)
+               lr := &io.LimitedReader{R: rc, N: size + 1}
+               if _, err := io.Copy(w, lr); err != nil {
+                       return "", err
+               }
+               if lr.N <= 0 {
+                       return "", fmt.Errorf("individual file too large")
+               }
+       }
+
+       if !haveLICENSE && subdir != "" {
+               if data, err := r.code.ReadFile(rev, "LICENSE", codehost.MaxLICENSE); err == nil {
+                       w, err := zw.Create(r.modPrefix(version) + "/LICENSE")
+                       if err != nil {
+                               return "", err
+                       }
+                       if _, err := w.Write(data); err != nil {
+                               return "", err
+                       }
+               }
+       }
+       if err := zw.Close(); err != nil {
+               return "", err
+       }
+       if err := f2.Close(); err != nil {
+               return "", err
+       }
+
+       return f2.Name(), nil
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+       switch {
+       default:
+               return false
+       case len(s) == len(prefix):
+               return s == prefix
+       case len(s) > len(prefix):
+               if prefix != "" && prefix[len(prefix)-1] == '/' {
+                       return strings.HasPrefix(s, prefix)
+               }
+               return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+       }
+}
+
+func isVendoredPackage(name string) bool {
+       var i int
+       if strings.HasPrefix(name, "vendor/") {
+               i += len("vendor/")
+       } else if j := strings.Index(name, "/vendor/"); j >= 0 {
+               i += len("/vendor/")
+       } else {
+               return false
+       }
+       return strings.Contains(name[i:], "/")
+}
+
+func PseudoVersion(major string, t time.Time, rev string) string {
+       if major == "" {
+               major = "v0"
+       }
+       return fmt.Sprintf("%s.0.0-%s-%s", major, t.UTC().Format("20060102150405"), rev)
+}
+
+var ErrNotPseudoVersion = errors.New("not a pseudo-version")
+
+/*
+func ParsePseudoVersion(repo Repo, version string) (rev string, err error) {
+       major := semver.Major(version)
+       if major == "" {
+               return "", ErrNotPseudoVersion
+       }
+       majorPrefix := major + ".0.0-"
+       if !strings.HasPrefix(version, majorPrefix) || !strings.Contains(version[len(majorPrefix):], "-") {
+               return "", ErrNotPseudoVersion
+       }
+       versionSuffix := version[len(majorPrefix):]
+       for i := 0; versionSuffix[i] != '-'; i++ {
+               c := versionSuffix[i]
+               if c < '0' || '9' < c {
+                       return "", ErrNotPseudoVersion
+               }
+       }
+       rev = versionSuffix[strings.Index(versionSuffix, "-")+1:]
+       if rev == "" {
+               return "", ErrNotPseudoVersion
+       }
+       if proxyURL != "" {
+               return version, nil
+       }
+       fullRev, t, err := repo.CommitInfo(rev)
+       if err != nil {
+               return "", fmt.Errorf("unknown pseudo-version %s: loading %v: %v", version, rev, err)
+       }
+       v := PseudoVersion(major, t, repo.ShortRev(fullRev))
+       if v != version {
+               return "", fmt.Errorf("unknown pseudo-version %s: %v is %v", version, rev, v)
+       }
+       return fullRev, nil
+}
+*/
+
+var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
+
+func isPseudoVersion(v string) bool {
+       return pseudoVersionRE.MatchString(v)
+}
diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go
new file mode 100644 (file)
index 0000000..e3b106e
--- /dev/null
@@ -0,0 +1,688 @@
+// 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.
+
+package modfetch
+
+import (
+       "archive/zip"
+       "internal/testenv"
+       "io"
+       "io/ioutil"
+       "log"
+       "os"
+       "reflect"
+       "strings"
+       "testing"
+       "time"
+
+       "cmd/go/internal/modfetch/codehost"
+)
+
+func init() {
+       isTest = true
+}
+
+func TestMain(m *testing.M) {
+       os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+       dir, err := ioutil.TempDir("", "gitrepo-test-")
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       codehost.WorkRoot = dir
+       return m.Run()
+}
+
+var codeRepoTests = []struct {
+       path     string
+       lookerr  string
+       mpath    string
+       rev      string
+       err      string
+       version  string
+       name     string
+       short    string
+       time     time.Time
+       gomod    string
+       gomoderr string
+       zip      []string
+       ziperr   string
+}{
+       {
+               path:    "github.com/rsc/vgotest1",
+               rev:     "v0.0.0",
+               version: "v0.0.0",
+               name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:   "80d85c5d4d17",
+               time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               zip: []string{
+                       "LICENSE",
+                       "README.md",
+                       "pkg/p.go",
+               },
+       },
+       {
+               path:    "github.com/rsc/vgotest1",
+               rev:     "v1.0.0",
+               version: "v1.0.0",
+               name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:   "80d85c5d4d17",
+               time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               zip: []string{
+                       "LICENSE",
+                       "README.md",
+                       "pkg/p.go",
+               },
+       },
+       {
+               path:    "github.com/rsc/vgotest1/v2",
+               rev:     "v2.0.0",
+               version: "v2.0.0",
+               name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:   "80d85c5d4d17",
+               time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               ziperr:  "missing go.mod",
+       },
+       {
+               path:    "github.com/rsc/vgotest1",
+               rev:     "80d85",
+               version: "v0.0.0-20180219231006-80d85c5d4d17",
+               name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:   "80d85c5d4d17",
+               time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               zip: []string{
+                       "LICENSE",
+                       "README.md",
+                       "pkg/p.go",
+               },
+       },
+       {
+               path:    "github.com/rsc/vgotest1",
+               rev:     "mytag",
+               version: "v0.0.0-20180219231006-80d85c5d4d17",
+               name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:   "80d85c5d4d17",
+               time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               zip: []string{
+                       "LICENSE",
+                       "README.md",
+                       "pkg/p.go",
+               },
+       },
+       {
+               path:     "github.com/rsc/vgotest1/v2",
+               rev:      "80d85",
+               version:  "v2.0.0-20180219231006-80d85c5d4d17",
+               name:     "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:    "80d85c5d4d17",
+               time:     time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               gomoderr: "missing go.mod",
+               ziperr:   "missing go.mod",
+       },
+       {
+               path:    "github.com/rsc/vgotest1/v54321",
+               rev:     "80d85",
+               version: "v54321.0.0-20180219231006-80d85c5d4d17",
+               name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+               short:   "80d85c5d4d17",
+               time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+               ziperr:  "missing go.mod",
+       },
+       {
+               path: "github.com/rsc/vgotest1/submod",
+               rev:  "v1.0.0",
+               err:  "unknown revision \"submod/v1.0.0\"",
+       },
+       {
+               path: "github.com/rsc/vgotest1/submod",
+               rev:  "v1.0.3",
+               err:  "unknown revision \"submod/v1.0.3\"",
+       },
+       {
+               path:    "github.com/rsc/vgotest1/submod",
+               rev:     "v1.0.4",
+               version: "v1.0.4",
+               name:    "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6",
+               short:   "8afe2b2efed9",
+               time:    time.Date(2018, 2, 19, 23, 12, 7, 0, time.UTC),
+               gomod:   "module \"github.com/vgotest1/submod\" // submod/go.mod\n",
+               zip: []string{
+                       "go.mod",
+                       "pkg/p.go",
+                       "LICENSE",
+               },
+       },
+       {
+               path:    "github.com/rsc/vgotest1",
+               rev:     "v1.1.0",
+               version: "v1.1.0",
+               name:    "b769f2de407a4db81af9c5de0a06016d60d2ea09",
+               short:   "b769f2de407a",
+               time:    time.Date(2018, 2, 19, 23, 13, 36, 0, time.UTC),
+               gomod:   "module \"github.com/rsc/vgotest1\" // root go.mod\nrequire \"github.com/rsc/vgotest1/submod\" v1.0.5\n",
+               zip: []string{
+                       "LICENSE",
+                       "README.md",
+                       "go.mod",
+                       "pkg/p.go",
+               },
+       },
+       {
+               path:    "github.com/rsc/vgotest1/v2",
+               rev:     "v2.0.1",
+               version: "v2.0.1",
+               name:    "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9",
+               short:   "ea65f87c8f52",
+               time:    time.Date(2018, 2, 19, 23, 14, 23, 0, time.UTC),
+               gomod:   "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n",
+       },
+       {
+               path:     "github.com/rsc/vgotest1/v2",
+               rev:      "v2.0.3",
+               version:  "v2.0.3",
+               name:     "f18795870fb14388a21ef3ebc1d75911c8694f31",
+               short:    "f18795870fb1",
+               time:     time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC),
+               gomoderr: "v2/go.mod has non-.../v2 module path",
+       },
+       {
+               path:     "github.com/rsc/vgotest1/v2",
+               rev:      "v2.0.4",
+               version:  "v2.0.4",
+               name:     "1f863feb76bc7029b78b21c5375644838962f88d",
+               short:    "1f863feb76bc",
+               time:     time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC),
+               gomoderr: "both go.mod and v2/go.mod claim .../v2 module",
+       },
+       {
+               path:    "github.com/rsc/vgotest1/v2",
+               rev:     "v2.0.5",
+               version: "v2.0.5",
+               name:    "2f615117ce481c8efef46e0cc0b4b4dccfac8fea",
+               short:   "2f615117ce48",
+               time:    time.Date(2018, 2, 20, 0, 3, 59, 0, time.UTC),
+               gomod:   "module \"github.com/rsc/vgotest1/v2\" // v2/go.mod\n",
+       },
+       {
+               path:    "go.googlesource.com/scratch",
+               rev:     "0f302529858",
+               version: "v0.0.0-20180220024720-0f3025298580",
+               name:    "0f30252985809011f026b5a2d5cf456e021623da",
+               short:   "0f3025298580",
+               time:    time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
+               gomod:   "//vgo 0.0.4\n\nmodule go.googlesource.com/scratch\n",
+       },
+       {
+               path:    "go.googlesource.com/scratch/rsc",
+               rev:     "0f302529858",
+               version: "v0.0.0-20180220024720-0f3025298580",
+               name:    "0f30252985809011f026b5a2d5cf456e021623da",
+               short:   "0f3025298580",
+               time:    time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
+               gomod:   "",
+       },
+       {
+               path:     "go.googlesource.com/scratch/cbro",
+               rev:      "0f302529858",
+               version:  "v0.0.0-20180220024720-0f3025298580",
+               name:     "0f30252985809011f026b5a2d5cf456e021623da",
+               short:    "0f3025298580",
+               time:     time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
+               gomoderr: "missing go.mod",
+       },
+       {
+               // redirect to github
+               path:    "rsc.io/quote",
+               rev:     "v1.0.0",
+               version: "v1.0.0",
+               name:    "f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6",
+               short:   "f488df80bcdb",
+               time:    time.Date(2018, 2, 14, 0, 45, 20, 0, time.UTC),
+               gomod:   "module \"rsc.io/quote\"\n",
+       },
+       {
+               // redirect to static hosting proxy
+               path:    "swtch.com/testmod",
+               rev:     "v1.0.0",
+               version: "v1.0.0",
+               name:    "v1.0.0",
+               short:   "v1.0.0",
+               time:    time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC),
+               gomod:   "module \"swtch.com/testmod\"\n",
+       },
+       {
+               // redirect to googlesource
+               path:    "golang.org/x/text",
+               rev:     "4e4a3210bb",
+               version: "v0.0.0-20180208041248-4e4a3210bb54",
+               name:    "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1",
+               short:   "4e4a3210bb54",
+               time:    time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC),
+       },
+       {
+               path:    "github.com/pkg/errors",
+               rev:     "v0.8.0",
+               version: "v0.8.0",
+               name:    "645ef00459ed84a119197bfb8d8205042c6df63d",
+               short:   "645ef00459ed",
+               time:    time.Date(2016, 9, 29, 1, 48, 1, 0, time.UTC),
+       },
+       {
+               // package in subdirectory - custom domain
+               path:    "golang.org/x/net/context",
+               lookerr: "module root is \"golang.org/x/net\"",
+       },
+       {
+               // package in subdirectory - github
+               path:     "github.com/rsc/quote/buggy",
+               rev:      "c4d4236f",
+               version:  "v0.0.0-20180214154420-c4d4236f9242",
+               name:     "c4d4236f92427c64bfbcf1cc3f8142ab18f30b22",
+               short:    "c4d4236f9242",
+               time:     time.Date(2018, 2, 14, 15, 44, 20, 0, time.UTC),
+               gomoderr: "missing go.mod",
+       },
+       {
+               path:    "gopkg.in/yaml.v2",
+               rev:     "d670f940",
+               version: "v2.0.0-20180109114331-d670f9405373",
+               name:    "d670f9405373e636a5a2765eea47fac0c9bc91a4",
+               short:   "d670f9405373",
+               time:    time.Date(2018, 1, 9, 11, 43, 31, 0, time.UTC),
+               gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/yaml.v2\n",
+       },
+       {
+               path:    "gopkg.in/check.v1",
+               rev:     "20d25e280405",
+               version: "v1.0.0-20161208181325-20d25e280405",
+               name:    "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
+               short:   "20d25e280405",
+               time:    time.Date(2016, 12, 8, 18, 13, 25, 0, time.UTC),
+               gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/check.v1\n",
+       },
+       {
+               path:    "gopkg.in/yaml.v2",
+               rev:     "v2",
+               version: "v2.0.0-20180328195020-5420a8b6744d",
+               name:    "5420a8b6744d3b0345ab293f6fcba19c978f1183",
+               short:   "5420a8b6744d",
+               time:    time.Date(2018, 3, 28, 19, 50, 20, 0, time.UTC),
+               gomod:   "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
+       },
+       {
+               path:    "vcs-test.golang.org/go/mod/gitrepo1",
+               rev:     "master",
+               version: "v0.0.0-20180417194322-ede458df7cd0",
+               name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+               short:   "ede458df7cd0",
+               time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+               gomod:   "//vgo 0.0.4\n\nmodule vcs-test.golang.org/go/mod/gitrepo1\n",
+       },
+       {
+               path:    "gopkg.in/natefinch/lumberjack.v2",
+               rev:     "latest",
+               version: "v2.0.0-20170531160350-a96e63847dc3",
+               name:    "a96e63847dc3c67d17befa69c303767e2f84e54f",
+               short:   "a96e63847dc3",
+               time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
+               gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/natefinch/lumberjack.v2\n",
+       },
+       {
+               path: "gopkg.in/natefinch/lumberjack.v2",
+               // This repo has a v2.1 tag.
+               // We only allow semver references to tags that are fully qualified, as in v2.1.0.
+               // Because we can't record v2.1.0 (the actual tag is v2.1), we record a pseudo-version
+               // instead, same as if the tag were any other non-version-looking string.
+               // We use a v2 pseudo-version here because of the .v2 in the path, not because
+               // of the v2 in the rev.
+               rev:     "v2.1", // non-canonical semantic version turns into pseudo-version
+               version: "v2.0.0-20170531160350-a96e63847dc3",
+               name:    "a96e63847dc3c67d17befa69c303767e2f84e54f",
+               short:   "a96e63847dc3",
+               time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
+               gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/natefinch/lumberjack.v2\n",
+       },
+}
+
+func TestCodeRepo(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+
+       tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+       for _, tt := range codeRepoTests {
+               t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, func(t *testing.T) {
+                       repo, err := Lookup(tt.path)
+                       if err != nil {
+                               if tt.lookerr != "" {
+                                       if err.Error() == tt.lookerr {
+                                               return
+                                       }
+                                       t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr)
+                               }
+                               t.Fatalf("Lookup(%q): %v", tt.path, err)
+                       }
+                       if tt.mpath == "" {
+                               tt.mpath = tt.path
+                       }
+                       if mpath := repo.ModulePath(); mpath != tt.mpath {
+                               t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
+                       }
+                       info, err := repo.Stat(tt.rev)
+                       if err != nil {
+                               if tt.err != "" {
+                                       if !strings.Contains(err.Error(), tt.err) {
+                                               t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
+                                       }
+                                       return
+                               }
+                               t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
+                       }
+                       if tt.err != "" {
+                               t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
+                       }
+                       if info.Version != tt.version {
+                               t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
+                       }
+                       if info.Name != tt.name {
+                               t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
+                       }
+                       if info.Short != tt.short {
+                               t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
+                       }
+                       if !info.Time.Equal(tt.time) {
+                               t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
+                       }
+                       if tt.gomod != "" || tt.gomoderr != "" {
+                               data, err := repo.GoMod(tt.version)
+                               if err != nil && tt.gomoderr == "" {
+                                       t.Errorf("repo.GoMod(%q): %v", tt.version, err)
+                               } else if err != nil && tt.gomoderr != "" {
+                                       if err.Error() != tt.gomoderr {
+                                               t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomoderr)
+                                       }
+                               } else if tt.gomoderr != "" {
+                                       t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomoderr)
+                               } else if string(data) != tt.gomod {
+                                       t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
+                               }
+                       }
+                       if tt.zip != nil || tt.ziperr != "" {
+                               zipfile, err := repo.Zip(tt.version, tmpdir)
+                               if err != nil {
+                                       if tt.ziperr != "" {
+                                               if err.Error() == tt.ziperr {
+                                                       return
+                                               }
+                                               t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.ziperr)
+                                       }
+                                       t.Fatalf("repo.Zip(%q): %v", tt.version, err)
+                               }
+                               if tt.ziperr != "" {
+                                       t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.ziperr)
+                               }
+                               prefix := tt.path + "@" + tt.version + "/"
+                               z, err := zip.OpenReader(zipfile)
+                               if err != nil {
+                                       t.Fatalf("open zip %s: %v", zipfile, err)
+                               }
+                               var names []string
+                               for _, file := range z.File {
+                                       if !strings.HasPrefix(file.Name, prefix) {
+                                               t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
+                                               continue
+                                       }
+                                       names = append(names, file.Name[len(prefix):])
+                               }
+                               z.Close()
+                               if !reflect.DeepEqual(names, tt.zip) {
+                                       t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
+                               }
+                       }
+               })
+       }
+}
+
+var importTests = []struct {
+       path  string
+       mpath string
+       err   string
+}{
+       {
+               path:  "golang.org/x/net/context",
+               mpath: "golang.org/x/net",
+       },
+       {
+               path:  "github.com/rsc/quote/buggy",
+               mpath: "github.com/rsc/quote",
+       },
+       {
+               path:  "golang.org/x/net",
+               mpath: "golang.org/x/net",
+       },
+       {
+               path:  "github.com/rsc/quote",
+               mpath: "github.com/rsc/quote",
+       },
+       {
+               path: "golang.org/x/foo/bar",
+               err:  "unknown module golang.org/x/foo/bar: no go-import tags",
+       },
+}
+
+func TestImport(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+
+       for _, tt := range importTests {
+               t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
+                       repo, info, err := Import(tt.path, nil)
+                       if err != nil {
+                               if tt.err != "" {
+                                       if err.Error() == tt.err {
+                                               return
+                                       }
+                                       t.Errorf("Import(%q): %v, want error %q", tt.path, err, tt.err)
+                               }
+                               t.Fatalf("Lookup(%q): %v", tt.path, err)
+                       }
+                       if mpath := repo.ModulePath(); mpath != tt.mpath {
+                               t.Errorf("repo.ModulePath() = %q (%v), want %q", mpath, info.Version, tt.mpath)
+                       }
+               })
+       }
+}
+
+var codeRepoVersionsTests = []struct {
+       path     string
+       prefix   string
+       versions []string
+}{
+       // TODO: Why do we allow a prefix here at all?
+       {
+               path:     "github.com/rsc/vgotest1",
+               versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0"},
+       },
+       {
+               path:     "github.com/rsc/vgotest1",
+               prefix:   "v1.0",
+               versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"},
+       },
+       {
+               path:     "github.com/rsc/vgotest1/v2",
+               versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"},
+       },
+       {
+               path:     "swtch.com/testmod",
+               versions: []string{"v1.0.0", "v1.1.1"},
+       },
+       {
+               path:     "gopkg.in/russross/blackfriday.v2",
+               versions: []string{"v2.0.0"},
+       },
+       {
+               path:     "gopkg.in/natefinch/lumberjack.v2",
+               versions: []string{},
+       },
+}
+
+func TestCodeRepoVersions(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+
+       tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+       for _, tt := range codeRepoVersionsTests {
+               t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
+                       repo, err := Lookup(tt.path)
+                       if err != nil {
+                               t.Fatalf("Lookup(%q): %v", tt.path, err)
+                       }
+                       list, err := repo.Versions(tt.prefix)
+                       if err != nil {
+                               t.Fatalf("Versions(%q): %v", tt.prefix, err)
+                       }
+                       if !reflect.DeepEqual(list, tt.versions) {
+                               t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions)
+                       }
+               })
+       }
+}
+
+var latestTests = []struct {
+       path    string
+       version string
+       err     string
+}{
+       {
+               path: "github.com/rsc/empty",
+               err:  "no commits",
+       },
+       {
+               path:    "github.com/rsc/vgotest1",
+               version: "v0.0.0-20180219223237-a08abb797a67",
+       },
+       {
+               path:    "swtch.com/testmod",
+               version: "v1.1.1",
+       },
+}
+
+func TestLatest(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+
+       tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+       for _, tt := range latestTests {
+               name := strings.Replace(tt.path, "/", "_", -1)
+               t.Run(name, func(t *testing.T) {
+                       repo, err := Lookup(tt.path)
+                       if err != nil {
+                               t.Fatalf("Lookup(%q): %v", tt.path, err)
+                       }
+                       info, err := repo.Latest()
+                       if err != nil {
+                               if tt.err != "" {
+                                       if err.Error() == tt.err {
+                                               return
+                                       }
+                                       t.Fatalf("Latest(): %v, want %q", err, tt.err)
+                               }
+                               t.Fatalf("Latest(): %v", err)
+                       }
+                       if info.Version != tt.version {
+                               t.Fatalf("Latest() = %v, want %v", info.Version, tt.version)
+                       }
+               })
+       }
+}
+
+// fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags
+type fixedTagsRepo struct {
+       root string
+       tags []string
+}
+
+func (ch *fixedTagsRepo) Tags(string) ([]string, error)                  { return ch.tags, nil }
+func (ch *fixedTagsRepo) Root() string                                   { return ch.root }
+func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error)             { panic("not impl") }
+func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") }
+func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) {
+       panic("not impl")
+}
+func (ch *fixedTagsRepo) Stat(string) (*codehost.RevInfo, error) { panic("not impl") }
+
+func TestNonCanonicalSemver(t *testing.T) {
+       root := "golang.org/x/issue24476"
+       ch := &fixedTagsRepo{
+               root: root,
+               tags: []string{
+                       "", "huh?", "1.0.1",
+                       // what about "version 1 dot dogcow"?
+                       "v1.🐕.🐄",
+                       "v1", "v0.1",
+                       // and one normal one that should pass through
+                       "v1.0.1",
+               },
+       }
+
+       cr, err := newCodeRepo(ch, root)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       v, err := cr.Versions("")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(v) != 1 || v[0] != "v1.0.1" {
+               t.Fatal("unexpected versions returned:", v)
+       }
+}
+
+var modPathTests = []struct {
+       input    []byte
+       expected string
+}{
+       {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
+       {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
+       {input: []byte("module  \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
+       {input: []byte("module  github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
+       {input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"},
+       {input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"},
+       {input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"},
+       {input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"},
+       {input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"},
+       {input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"},
+       {input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""},
+       {input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"},
+       {input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"},
+       {input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"},
+       {input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"},
+       {input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"},
+       {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
+       {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
+       {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
+}
+
+func TestModPath(t *testing.T) {
+       for _, test := range modPathTests {
+               t.Run(string(test.input), func(t *testing.T) {
+                       result := modPath(test.input)
+                       if result != test.expected {
+                               t.Fatalf("modPath(%s): %s, want %s", string(test.input), result, test.expected)
+                       }
+               })
+       }
+}
diff --git a/src/cmd/go/internal/modfetch/convert.go b/src/cmd/go/internal/modfetch/convert.go
new file mode 100644 (file)
index 0000000..5c482be
--- /dev/null
@@ -0,0 +1,78 @@
+// 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.
+
+package modfetch
+
+import (
+       "fmt"
+       "os"
+       "sort"
+       "strings"
+
+       "cmd/go/internal/modconv"
+       "cmd/go/internal/modfile"
+       "cmd/go/internal/semver"
+)
+
+// ConvertLegacyConfig converts legacy config to modfile.
+// The file argument is slash-delimited.
+func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
+       i := strings.LastIndex(file, "/")
+       j := -2
+       if i >= 0 {
+               j = strings.LastIndex(file[:i], "/")
+       }
+       convert := modconv.Converters[file[i+1:]]
+       if convert == nil && j != -2 {
+               convert = modconv.Converters[file[j+1:]]
+       }
+       if convert == nil {
+               return fmt.Errorf("unknown legacy config file %s", file)
+       }
+       require, err := convert(file, data)
+       if err != nil {
+               return fmt.Errorf("parsing %s: %v", file, err)
+       }
+
+       // Convert requirements block, which may use raw SHA1 hashes as versions,
+       // to valid semver requirement list, respecting major versions.
+       need := make(map[string]string)
+       for _, r := range require {
+               if r.Path == "" {
+                       continue
+               }
+
+               // TODO: Something better here.
+               if strings.HasPrefix(r.Path, "github.com/") || strings.HasPrefix(r.Path, "golang.org/x/") {
+                       f := strings.Split(r.Path, "/")
+                       if len(f) > 3 {
+                               r.Path = strings.Join(f[:3], "/")
+                       }
+               }
+
+               repo, err := Lookup(r.Path)
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "vgo: lookup %s: %v\n", r.Path, err)
+                       continue
+               }
+               info, err := repo.Stat(r.Version)
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "vgo: stat %s@%s: %v\n", r.Path, r.Version, err)
+                       continue
+               }
+               path := repo.ModulePath()
+               need[path] = semver.Max(need[path], info.Version)
+       }
+
+       var paths []string
+       for path := range need {
+               paths = append(paths, path)
+       }
+       sort.Strings(paths)
+       for _, path := range paths {
+               f.AddRequire(path, need[path])
+       }
+
+       return nil
+}
diff --git a/src/cmd/go/internal/modfetch/convert_test.go b/src/cmd/go/internal/modfetch/convert_test.go
new file mode 100644 (file)
index 0000000..03ae156
--- /dev/null
@@ -0,0 +1,139 @@
+// 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.
+
+package modfetch
+
+import (
+       "bytes"
+       "internal/testenv"
+       "strings"
+       "testing"
+
+       "cmd/go/internal/cfg"
+       "cmd/go/internal/modconv"
+       "cmd/go/internal/modfile"
+)
+
+func TestConvertLegacyConfig(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+
+       if testing.Verbose() {
+               old := cfg.BuildX
+               defer func() {
+                       cfg.BuildX = old
+               }()
+               cfg.BuildX = true
+       }
+
+       var tests = []struct {
+               path  string
+               vers  string
+               gomod string
+       }{
+               {
+                       // Gopkg.lock parsing.
+                       "github.com/golang/dep", "v0.4.0",
+                       `module github.com/golang/dep
+
+                       require (
+                               github.com/Masterminds/semver v0.0.0-20170726230514-a93e51b5a57e
+                               github.com/Masterminds/vcs v1.11.1
+                               github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
+                               github.com/boltdb/bolt v1.3.1
+                               github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
+                               github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
+                               github.com/jmank88/nuts v0.3.0
+                               github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
+                               github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
+                               github.com/pkg/errors v0.8.0
+                               github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
+                               golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
+                               golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
+                               golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
+                       )`,
+               },
+
+               // TODO: https://github.com/docker/distribution uses vendor.conf
+
+               {
+                       // Godeps.json parsing.
+                       // TODO: Should v2.0.0 work here too?
+                       "github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93",
+                       `module github.com/docker/distribution
+
+                       require (
+                               github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
+                               github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
+                               github.com/Sirupsen/logrus v0.0.0-20150409230825-55eb11d21d2a
+                               github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
+                               github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
+                               github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
+                               github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
+                               github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
+                               github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
+                               github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
+                               github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
+                               github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1
+                               github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
+                               github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
+                               github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
+                               github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
+                               github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
+                               golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
+                               gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
+                               gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76
+                       )`,
+               },
+
+               {
+                       // golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module
+                       "github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0",
+                       `module github.com/fishy/gcsbucket
+
+                       require (
+                               cloud.google.com/go v0.18.0
+                               github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
+                               github.com/golang/protobuf v1.0.0
+                               github.com/googleapis/gax-go v0.0.0-20170915024731-317e0006254c
+                               golang.org/x/net v0.0.0-20180216171745-136a25c244d3
+                               golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
+                               golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
+                               google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
+                               google.golang.org/appengine v1.0.0
+                               google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b
+                               google.golang.org/grpc v1.10.0
+                       )`,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
+                       f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       want, err := f.Format()
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       repo, err := Lookup(tt.path)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+
+                       out, err := repo.GoMod(tt.vers)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       prefix := modconv.Prefix + "\n"
+                       if !bytes.HasPrefix(out, []byte(prefix)) {
+                               t.Fatalf("go.mod missing prefix %q:\n%s", prefix, out)
+                       }
+                       out = out[len(prefix):]
+                       if !bytes.Equal(out, want) {
+                               t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
+                       }
+               })
+       }
+}
diff --git a/src/cmd/go/internal/modfetch/domain.go b/src/cmd/go/internal/modfetch/domain.go
new file mode 100644 (file)
index 0000000..2494f80
--- /dev/null
@@ -0,0 +1,178 @@
+// 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.
+
+// Support for custom domains.
+
+package modfetch
+
+import (
+       "encoding/xml"
+       "fmt"
+       "io"
+       "net/url"
+       "os"
+       "strings"
+
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfetch/gitrepo"
+)
+
+// metaImport represents the parsed <meta name="go-import"
+// content="prefix vcs reporoot" /> tags from HTML files.
+type metaImport struct {
+       Prefix, VCS, RepoRoot string
+}
+
+func lookupCustomDomain(path string) (Repo, error) {
+       dom := path
+       if i := strings.Index(dom, "/"); i >= 0 {
+               dom = dom[:i]
+       }
+       if !strings.Contains(dom, ".") {
+               return nil, fmt.Errorf("unknown module %s: not a domain name", path)
+       }
+       var body io.ReadCloser
+       err := webGetGoGet("https://"+path+"?go-get=1", &body)
+       if body != nil {
+               defer body.Close()
+       }
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "FindRepo: %v\n", err)
+               return nil, err
+       }
+       // Note: accepting a non-200 OK here, so people can serve a
+       // meta import in their http 404 page.
+       imports, err := parseMetaGoImports(body)
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "findRepo: %v\n", err)
+               return nil, err
+       }
+       if len(imports) == 0 {
+               return nil, fmt.Errorf("unknown module %s: no go-import tags", path)
+       }
+
+       // First look for new module definition.
+       for _, imp := range imports {
+               if path == imp.Prefix || strings.HasPrefix(path, imp.Prefix+"/") {
+                       if imp.VCS == "mod" {
+                               u, err := url.Parse(imp.RepoRoot)
+                               if err != nil {
+                                       return nil, fmt.Errorf("invalid module URL %q", imp.RepoRoot)
+                               } else if u.Scheme != "https" {
+                                       // TODO: Allow -insecure flag as a build flag?
+                                       return nil, fmt.Errorf("invalid module URL %q: must be HTTPS", imp.RepoRoot)
+                               }
+                               return newProxyRepo(imp.RepoRoot, imp.Prefix), nil
+                       }
+               }
+       }
+
+       // Fall back to redirections to known version control systems.
+       for _, imp := range imports {
+               if path == imp.Prefix {
+                       if !strings.HasPrefix(imp.RepoRoot, "https://") {
+                               // TODO: Allow -insecure flag as a build flag?
+                               return nil, fmt.Errorf("invalid server URL %q: must be HTTPS", imp.RepoRoot)
+                       }
+                       if imp.VCS == "git" {
+                               code, err := gitrepo.Repo(imp.RepoRoot, imp.Prefix)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               return newCodeRepo(code, path)
+                       }
+                       return nil, fmt.Errorf("unknown VCS, Repo: %s, %s", imp.VCS, imp.RepoRoot)
+               }
+       }
+
+       // Check for redirect to repo root.
+       for _, imp := range imports {
+               if strings.HasPrefix(path, imp.Prefix+"/") {
+                       return nil, &ModuleSubdirError{imp.Prefix}
+               }
+       }
+
+       return nil, fmt.Errorf("unknown module %s: no matching go-import tags", path)
+}
+
+type ModuleSubdirError struct {
+       ModulePath string
+}
+
+func (e *ModuleSubdirError) Error() string {
+       return fmt.Sprintf("module root is %q", e.ModulePath)
+}
+
+type customPrefix struct {
+       codehost.Repo
+       root string
+}
+
+func (c *customPrefix) Root() string {
+       return c.root
+}
+
+// parseMetaGoImports returns meta imports from the HTML in r.
+// Parsing ends at the end of the <head> section or the beginning of the <body>.
+func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
+       d := xml.NewDecoder(r)
+       d.CharsetReader = charsetReader
+       d.Strict = false
+       var t xml.Token
+       for {
+               t, err = d.RawToken()
+               if err != nil {
+                       if err == io.EOF || len(imports) > 0 {
+                               err = nil
+                       }
+                       return
+               }
+               if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
+                       return
+               }
+               if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
+                       return
+               }
+               e, ok := t.(xml.StartElement)
+               if !ok || !strings.EqualFold(e.Name.Local, "meta") {
+                       continue
+               }
+               if attrValue(e.Attr, "name") != "go-import" {
+                       continue
+               }
+               if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
+                       imports = append(imports, metaImport{
+                               Prefix:   f[0],
+                               VCS:      f[1],
+                               RepoRoot: f[2],
+                       })
+               }
+       }
+}
+
+// attrValue returns the attribute value for the case-insensitive key
+// `name', or the empty string if nothing is found.
+func attrValue(attrs []xml.Attr, name string) string {
+       for _, a := range attrs {
+               if strings.EqualFold(a.Name.Local, name) {
+                       return a.Value
+               }
+       }
+       return ""
+}
+
+// charsetReader returns a reader for the given charset. Currently
+// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful
+// error which is printed by go get, so the user can find why the package
+// wasn't downloaded if the encoding is not supported. Note that, in
+// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters
+// greater than 0x7f are not rejected).
+func charsetReader(charset string, input io.Reader) (io.Reader, error) {
+       switch strings.ToLower(charset) {
+       case "ascii":
+               return input, nil
+       default:
+               return nil, fmt.Errorf("can't decode XML document using charset %q", charset)
+       }
+}
diff --git a/src/cmd/go/internal/modfetch/github/fetch.go b/src/cmd/go/internal/modfetch/github/fetch.go
new file mode 100644 (file)
index 0000000..a2a90f1
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+
+package github
+
+import (
+       "fmt"
+       "strings"
+
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfetch/gitrepo"
+)
+
+// Lookup returns the code repository enclosing the given module path,
+// which must begin with github.com/.
+func Lookup(path string) (codehost.Repo, error) {
+       f := strings.Split(path, "/")
+       if len(f) < 3 || f[0] != "github.com" {
+               return nil, fmt.Errorf("github repo must be github.com/org/project")
+       }
+       path = f[0] + "/" + f[1] + "/" + f[2]
+       return gitrepo.Repo("https://"+path, path)
+}
diff --git a/src/cmd/go/internal/modfetch/gitrepo/fetch.go b/src/cmd/go/internal/modfetch/gitrepo/fetch.go
new file mode 100644 (file)
index 0000000..0d6eb4b
--- /dev/null
@@ -0,0 +1,445 @@
+// 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.
+
+// Package gitrepo provides a Git-based implementation of codehost.Repo.
+package gitrepo
+
+import (
+       "archive/zip"
+       "bytes"
+       "cmd/go/internal/modfetch/codehost"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "sort"
+       "strconv"
+       "strings"
+       "sync"
+       "time"
+)
+
+// Repo returns the code repository at the given Git remote reference.
+// The returned repo reports the given root as its module root.
+func Repo(remote, root string) (codehost.Repo, error) {
+       return newRepo(remote, root, false)
+}
+
+// LocalRepo is like Repo but accepts both Git remote references
+// and paths to repositories on the local file system.
+// The returned repo reports the given root as its module root.
+func LocalRepo(remote, root string) (codehost.Repo, error) {
+       return newRepo(remote, root, true)
+}
+
+const workDirType = "git2"
+
+func newRepo(remote, root string, localOK bool) (codehost.Repo, error) {
+       r := &repo{remote: remote, root: root, canArchive: true}
+       if strings.Contains(remote, "://") {
+               // This is a remote path.
+               dir, err := codehost.WorkDir(workDirType, r.remote)
+               if err != nil {
+                       return nil, err
+               }
+               r.dir = dir
+               if _, err := os.Stat(filepath.Join(dir, "objects")); err != nil {
+                       if _, err := codehost.Run(dir, "git", "init", "--bare"); err != nil {
+                               os.RemoveAll(dir)
+                               return nil, err
+                       }
+                       // We could just say git fetch https://whatever later,
+                       // but this lets us say git fetch origin instead, which
+                       // is a little nicer. More importantly, using a named remote
+                       // avoids a problem with Git LFS. See golang.org/issue/25605.
+                       if _, err := codehost.Run(dir, "git", "remote", "add", "origin", r.remote); err != nil {
+                               os.RemoveAll(dir)
+                               return nil, err
+                       }
+                       r.remote = "origin"
+               }
+       } else {
+               // Local path.
+               // Disallow colon (not in ://) because sometimes
+               // that's rcp-style host:path syntax and sometimes it's not (c:\work).
+               // The go command has always insisted on URL syntax for ssh.
+               if strings.Contains(remote, ":") {
+                       return nil, fmt.Errorf("git remote cannot use host:path syntax")
+               }
+               if !localOK {
+                       return nil, fmt.Errorf("git remote must not be local directory")
+               }
+               r.local = true
+               info, err := os.Stat(remote)
+               if err != nil {
+                       return nil, err
+               }
+               if !info.IsDir() {
+                       return nil, fmt.Errorf("%s exists but is not a directory", remote)
+               }
+               r.dir = remote
+       }
+       return r, nil
+}
+
+type repo struct {
+       remote     string
+       local      bool
+       root       string
+       dir        string
+       canArchive bool
+
+       refsOnce sync.Once
+       refs     map[string]string
+       refsErr  error
+}
+
+func (r *repo) Root() string {
+       return r.root
+}
+
+// loadRefs loads heads and tags references from the remote into the map r.refs.
+// Should only be called as r.refsOnce.Do(r.loadRefs).
+func (r *repo) loadRefs() {
+       // The git protocol sends all known refs and ls-remote filters them on the client side,
+       // so we might as well record both heads and tags in one shot.
+       // Most of the time we only care about tags but sometimes we care about heads too.
+       out, err := codehost.Run(r.dir, "git", "ls-remote", "-q", r.remote)
+       if err != nil {
+               r.refsErr = err
+               return
+       }
+
+       r.refs = make(map[string]string)
+       for _, line := range strings.Split(string(out), "\n") {
+               f := strings.Fields(line)
+               if len(f) != 2 {
+                       continue
+               }
+               if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") {
+                       r.refs[f[1]] = f[0]
+               }
+       }
+       for ref, hash := range r.refs {
+               if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag
+                       r.refs[strings.TrimSuffix(ref, "^{}")] = hash
+                       delete(r.refs, ref)
+               }
+       }
+}
+
+func (r *repo) Tags(prefix string) ([]string, error) {
+       r.refsOnce.Do(r.loadRefs)
+       if r.refsErr != nil {
+               return nil, r.refsErr
+       }
+
+       tags := []string{}
+       for ref := range r.refs {
+               if !strings.HasPrefix(ref, "refs/tags/") {
+                       continue
+               }
+               tag := ref[len("refs/tags/"):]
+               if !strings.HasPrefix(tag, prefix) {
+                       continue
+               }
+               tags = append(tags, tag)
+       }
+       sort.Strings(tags)
+       return tags, nil
+}
+
+func (r *repo) Latest() (*codehost.RevInfo, error) {
+       r.refsOnce.Do(r.loadRefs)
+       if r.refsErr != nil {
+               return nil, r.refsErr
+       }
+       if r.refs["HEAD"] == "" {
+               return nil, fmt.Errorf("no commits")
+       }
+       return r.Stat(r.refs["HEAD"])
+}
+
+// findRef finds some ref name for the given hash,
+// for use when the server requires giving a ref instead of a hash.
+// There may be multiple ref names for a given hash,
+// in which case this returns some name - it doesn't matter which.
+func (r *repo) findRef(hash string) (ref string, ok bool) {
+       r.refsOnce.Do(r.loadRefs)
+       for ref, h := range r.refs {
+               if h == hash {
+                       return ref, true
+               }
+       }
+       return "", false
+}
+
+func unshallow(gitDir string) []string {
+       if _, err := os.Stat(filepath.Join(gitDir, "shallow")); err == nil {
+               return []string{"--unshallow"}
+       }
+       return []string{}
+}
+
+// statOrArchive tries to stat the given rev in the local repository,
+// or else it tries to obtain an archive at the rev with the given arguments,
+// or else it falls back to aggressive fetching and then a local stat.
+// The archive step is an optimization for servers that support it
+// (most do not, but maybe that will change), to let us minimize
+// the amount of code downloaded.
+func (r *repo) statOrArchive(rev string, archiveArgs ...string) (info *codehost.RevInfo, archive []byte, err error) {
+       // Do we have this rev?
+       r.refsOnce.Do(r.loadRefs)
+       var hash string
+       if k := "refs/tags/" + rev; r.refs[k] != "" {
+               hash = r.refs[k]
+       } else if k := "refs/heads/" + rev; r.refs[k] != "" {
+               hash = r.refs[k]
+               rev = hash
+       } else if rev == "HEAD" && r.refs["HEAD"] != "" {
+               hash = r.refs["HEAD"]
+               rev = hash
+       } else if len(rev) >= 5 && len(rev) <= 40 && codehost.AllHex(rev) {
+               hash = rev
+       } else {
+               return nil, nil, fmt.Errorf("unknown revision %q", rev)
+       }
+
+       out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash)
+       if err == nil {
+               hash = strings.TrimSpace(string(out))
+               goto Found
+       }
+
+       // We don't have the rev. Can we fetch it?
+       if r.local {
+               return nil, nil, fmt.Errorf("unknown revision %q", rev)
+       }
+
+       if r.canArchive {
+               // git archive with --remote requires a ref, not a hash.
+               // Proceed only if we know a ref for this hash.
+               if ref, ok := r.findRef(hash); ok {
+                       out, err := codehost.Run(r.dir, "git", "archive", "--format=zip", "--remote="+r.remote, "--prefix=prefix/", ref, archiveArgs)
+                       if err == nil {
+                               return &codehost.RevInfo{Version: rev}, out, nil
+                       }
+                       if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("did not match any files")) {
+                               return nil, nil, fmt.Errorf("file not found")
+                       }
+                       if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("Operation not supported by protocol")) {
+                               r.canArchive = false
+                       }
+               }
+       }
+
+       // Maybe it's a prefix of a ref we know.
+       // Iterating through all the refs is faster than doing unnecessary fetches.
+       // This is not strictly correct, in that the short ref might be ambiguous
+       // in the git repo as a whole, but not ambiguous in the list of named refs,
+       // so that we will resolve it where the git server would not.
+       // But this check avoids great expense, and preferring a known ref does
+       // not seem like such a bad failure mode.
+       if len(hash) >= 5 && len(hash) < 40 {
+               var full string
+               for _, h := range r.refs {
+                       if strings.HasPrefix(h, hash) {
+                               if full != "" {
+                                       // Prefix is ambiguous even in the ref list!
+                                       full = ""
+                                       break
+                               }
+                               full = h
+                       }
+               }
+               if full != "" {
+                       hash = full
+               }
+       }
+
+       // Fetch it.
+       if len(hash) == 40 {
+               name := hash
+               if ref, ok := r.findRef(hash); ok {
+                       name = ref
+               }
+               if _, err = codehost.Run(r.dir, "git", "fetch", "--depth=1", r.remote, name); err == nil {
+                       goto Found
+               }
+               if !strings.Contains(err.Error(), "unadvertised object") && !strings.Contains(err.Error(), "no such remote ref") && !strings.Contains(err.Error(), "does not support shallow") {
+                       return nil, nil, err
+               }
+       }
+
+       // It's a prefix, and we don't have a way to make the server resolve the prefix for us,
+       // or it's a full hash but also an unadvertised object.
+       // Download progressively more of the repo to look for it.
+
+       // Fetch the main branch (non-shallow).
+       if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), r.remote); err != nil {
+               return nil, nil, err
+       }
+       if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+               hash = strings.TrimSpace(string(out))
+               goto Found
+       }
+
+       // Fetch all tags (non-shallow).
+       if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", "--tags", r.remote); err != nil {
+               return nil, nil, err
+       }
+       if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+               hash = strings.TrimSpace(string(out))
+               goto Found
+       }
+
+       // Fetch all branches (non-shallow).
+       if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", r.remote, "refs/heads/*:refs/heads/*"); err != nil {
+               return nil, nil, err
+       }
+       if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+               hash = strings.TrimSpace(string(out))
+               goto Found
+       }
+
+       // Fetch all refs (non-shallow).
+       if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", r.remote, "refs/*:refs/*"); err != nil {
+               return nil, nil, err
+       }
+       if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+               hash = strings.TrimSpace(string(out))
+               goto Found
+       }
+       return nil, nil, fmt.Errorf("cannot find hash %s", hash)
+Found:
+
+       if strings.HasPrefix(hash, rev) {
+               rev = hash
+       }
+
+       out, err = codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%ct", hash)
+       if err != nil {
+               return nil, nil, err
+       }
+       t, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64)
+       if err != nil {
+               return nil, nil, fmt.Errorf("invalid time from git log: %q", out)
+       }
+
+       info = &codehost.RevInfo{
+               Name:    hash,
+               Short:   codehost.ShortenSHA1(hash),
+               Time:    time.Unix(t, 0).UTC(),
+               Version: rev,
+       }
+       return info, nil, nil
+}
+
+func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
+       // If the server will give us a git archive, we can pull the
+       // commit ID and the commit time out of the archive.
+       // We want an archive as small as possible (for speed),
+       // but we have to specify a pattern that matches at least one file name.
+       // The pattern here matches README, .gitignore, .gitattributes,
+       // and go.mod (and some other incidental file names);
+       // hopefully most repos will have at least one of these.
+       info, archive, err := r.statOrArchive(rev, "[Rg.][Ego][A.i][Dmt][Miao][Edgt]*")
+       if err != nil {
+               return nil, err
+       }
+       if archive != nil {
+               return zip2info(archive, info.Version)
+       }
+       return info, nil
+}
+
+func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+       info, archive, err := r.statOrArchive(rev, file)
+       if err != nil {
+               return nil, err
+       }
+       if archive != nil {
+               return zip2file(archive, file, maxSize)
+       }
+       out, err := codehost.Run(r.dir, "git", "cat-file", "blob", info.Name+":"+file)
+       if err != nil {
+               return nil, fmt.Errorf("file not found")
+       }
+       return out, nil
+}
+
+func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+       // TODO: Use maxSize or drop it.
+       args := []string{}
+       if subdir != "" {
+               args = append(args, "--", subdir)
+       }
+       info, archive, err := r.statOrArchive(rev, args...)
+       if err != nil {
+               return nil, "", err
+       }
+       if archive == nil {
+               archive, err = codehost.Run(r.dir, "git", "archive", "--format=zip", "--prefix=prefix/", info.Name, args)
+               if err != nil {
+                       if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("did not match any files")) {
+                               return nil, "", fmt.Errorf("file not found")
+                       }
+                       return nil, "", err
+               }
+       }
+
+       return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
+}
+
+func zip2info(archive []byte, rev string) (*codehost.RevInfo, error) {
+       r, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+       if err != nil {
+               return nil, err
+       }
+       if r.Comment == "" {
+               return nil, fmt.Errorf("missing commit ID in git zip comment")
+       }
+       hash := r.Comment
+       if len(hash) != 40 || !codehost.AllHex(hash) {
+               return nil, fmt.Errorf("invalid commit ID in git zip comment")
+       }
+       if len(r.File) == 0 {
+               return nil, fmt.Errorf("git zip has no files")
+       }
+       info := &codehost.RevInfo{
+               Name:    hash,
+               Short:   codehost.ShortenSHA1(hash),
+               Time:    r.File[0].Modified.UTC(),
+               Version: rev,
+       }
+       return info, nil
+}
+
+func zip2file(archive []byte, file string, maxSize int64) ([]byte, error) {
+       r, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+       if err != nil {
+               return nil, err
+       }
+       for _, f := range r.File {
+               if f.Name != "prefix/"+file {
+                       continue
+               }
+               rc, err := f.Open()
+               if err != nil {
+                       return nil, err
+               }
+               defer rc.Close()
+               l := &io.LimitedReader{R: rc, N: maxSize + 1}
+               data, err := ioutil.ReadAll(l)
+               if err != nil {
+                       return nil, err
+               }
+               if l.N <= 0 {
+                       return nil, fmt.Errorf("file %s too large", file)
+               }
+               return data, nil
+       }
+       return nil, fmt.Errorf("incomplete git zip archive: cannot find %s", file)
+}
diff --git a/src/cmd/go/internal/modfetch/gitrepo/fetch_test.go b/src/cmd/go/internal/modfetch/gitrepo/fetch_test.go
new file mode 100644 (file)
index 0000000..ca93280
--- /dev/null
@@ -0,0 +1,500 @@
+// 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.
+
+package gitrepo
+
+import (
+       "archive/zip"
+       "bytes"
+       "fmt"
+       "internal/testenv"
+       "io/ioutil"
+       "log"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "reflect"
+       "strings"
+       "testing"
+       "time"
+
+       "cmd/go/internal/modfetch/codehost"
+)
+
+func TestMain(m *testing.M) {
+       os.Exit(testMain(m))
+}
+
+const gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1"
+
+// localGitRepo is like gitrepo1 but allows archive access.
+var localGitRepo string
+
+func testMain(m *testing.M) int {
+       if _, err := exec.LookPath("git"); err != nil {
+               fmt.Fprintln(os.Stderr, "skipping because git binary not found")
+               fmt.Println("PASS")
+               return 0
+       }
+
+       dir, err := ioutil.TempDir("", "gitrepo-test-")
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+       codehost.WorkRoot = dir
+
+       if testenv.HasExternalNetwork() && testenv.HasExec() {
+               // Clone gitrepo1 into a local directory.
+               // If we use a file:// URL to access the local directory,
+               // then git starts up all the usual protocol machinery,
+               // which will let us test remote git archive invocations.
+               localGitRepo = filepath.Join(dir, "gitrepo2")
+               if _, err := codehost.Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
+                       log.Fatal(err)
+               }
+               if _, err := codehost.Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
+                       log.Fatal(err)
+               }
+       }
+
+       return m.Run()
+}
+
+func testRepo(remote string) (codehost.Repo, error) {
+       if remote == "localGitRepo" {
+               remote = "file://" + filepath.ToSlash(localGitRepo)
+       }
+       // Re ?root: nothing should care about the second argument,
+       // so use a string that will be distinctive if it does show up.
+       return LocalRepo(remote, "?root")
+}
+
+var tagsTests = []struct {
+       repo   string
+       prefix string
+       tags   []string
+}{
+       {gitrepo1, "xxx", []string{}},
+       {gitrepo1, "", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}},
+       {gitrepo1, "v", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}},
+       {gitrepo1, "v1", []string{"v1.2.3", "v1.2.4-annotated"}},
+       {gitrepo1, "2", []string{}},
+}
+
+func TestTags(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+       testenv.MustHaveExec(t)
+
+       for _, tt := range tagsTests {
+               f := func(t *testing.T) {
+                       r, err := testRepo(tt.repo)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       tags, err := r.Tags(tt.prefix)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if !reflect.DeepEqual(tags, tt.tags) {
+                               t.Errorf("Tags: incorrect tags\nhave %v\nwant %v", tags, tt.tags)
+                       }
+               }
+               t.Run(tt.repo+"/"+tt.prefix, f)
+               if tt.repo == gitrepo1 {
+                       tt.repo = "localGitRepo"
+                       t.Run(tt.repo+"/"+tt.prefix, f)
+               }
+       }
+}
+
+var latestTests = []struct {
+       repo string
+       info *codehost.RevInfo
+}{
+       {
+               gitrepo1,
+               &codehost.RevInfo{
+                       Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Short:   "ede458df7cd0",
+                       Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+               },
+       },
+}
+
+func TestLatest(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+       testenv.MustHaveExec(t)
+
+       for _, tt := range latestTests {
+               f := func(t *testing.T) {
+                       r, err := testRepo(tt.repo)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       info, err := r.Latest()
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if *info != *tt.info {
+                               t.Errorf("Latest: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
+                       }
+               }
+               t.Run(tt.repo, f)
+               if tt.repo == gitrepo1 {
+                       tt.repo = "localGitRepo"
+                       t.Run(tt.repo, f)
+               }
+       }
+}
+
+var readFileTests = []struct {
+       repo string
+       rev  string
+       file string
+       err  string
+       data string
+}{
+       {
+               repo: gitrepo1,
+               rev:  "HEAD",
+               file: "README",
+               data: "",
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v2",
+               file: "another.txt",
+               data: "another\n",
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v2.3.4",
+               file: "another.txt",
+               err:  "file not found",
+       },
+}
+
+func TestReadFile(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+       testenv.MustHaveExec(t)
+
+       for _, tt := range readFileTests {
+               f := func(t *testing.T) {
+                       r, err := testRepo(tt.repo)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       data, err := r.ReadFile(tt.rev, tt.file, 100)
+                       if err != nil {
+                               if tt.err == "" {
+                                       t.Fatalf("ReadFile: unexpected error %v", err)
+                               }
+                               if !strings.Contains(err.Error(), tt.err) {
+                                       t.Fatalf("ReadFile: wrong error %q, want %q", err, tt.err)
+                               }
+                               if len(data) != 0 {
+                                       t.Errorf("ReadFile: non-empty data %q with error %v", data, err)
+                               }
+                               return
+                       }
+                       if tt.err != "" {
+                               t.Fatalf("ReadFile: no error, wanted %v", tt.err)
+                       }
+                       if string(data) != tt.data {
+                               t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data)
+                       }
+               }
+               t.Run(tt.repo+"/"+tt.rev+"/"+tt.file, f)
+               if tt.repo == gitrepo1 {
+                       tt.repo = "localGitRepo"
+                       t.Run(tt.repo+"/"+tt.rev+"/"+tt.file, f)
+               }
+       }
+}
+
+var readZipTests = []struct {
+       repo         string
+       rev          string
+       subdir       string
+       actualSubdir string
+       err          string
+       files        map[string]uint64
+}{
+       {
+               repo:   gitrepo1,
+               rev:    "v2.3.4",
+               subdir: "",
+               files: map[string]uint64{
+                       "prefix/":       0,
+                       "prefix/README": 0,
+                       "prefix/v2":     3,
+               },
+       },
+       {
+               repo:   gitrepo1,
+               rev:    "v2",
+               subdir: "",
+               files: map[string]uint64{
+                       "prefix/":            0,
+                       "prefix/README":      0,
+                       "prefix/v2":          3,
+                       "prefix/another.txt": 8,
+                       "prefix/foo.txt":     13,
+               },
+       },
+       {
+               repo:   gitrepo1,
+               rev:    "v3",
+               subdir: "",
+               files: map[string]uint64{
+                       "prefix/":                    0,
+                       "prefix/v3/":                 0,
+                       "prefix/v3/sub/":             0,
+                       "prefix/v3/sub/dir/":         0,
+                       "prefix/v3/sub/dir/file.txt": 16,
+                       "prefix/README":              0,
+               },
+       },
+       {
+               repo:   gitrepo1,
+               rev:    "v3",
+               subdir: "v3/sub/dir",
+               files: map[string]uint64{
+                       "prefix/":                    0,
+                       "prefix/v3/":                 0,
+                       "prefix/v3/sub/":             0,
+                       "prefix/v3/sub/dir/":         0,
+                       "prefix/v3/sub/dir/file.txt": 16,
+               },
+       },
+       {
+               repo:   gitrepo1,
+               rev:    "v3",
+               subdir: "v3/sub",
+               files: map[string]uint64{
+                       "prefix/":                    0,
+                       "prefix/v3/":                 0,
+                       "prefix/v3/sub/":             0,
+                       "prefix/v3/sub/dir/":         0,
+                       "prefix/v3/sub/dir/file.txt": 16,
+               },
+       },
+       {
+               repo:   gitrepo1,
+               rev:    "aaaaaaaaab",
+               subdir: "",
+               err:    "cannot find hash",
+       },
+       {
+               repo:   "https://github.com/rsc/vgotest1",
+               rev:    "submod/v1.0.4",
+               subdir: "submod",
+               files: map[string]uint64{
+                       "prefix/":                0,
+                       "prefix/submod/":         0,
+                       "prefix/submod/go.mod":   53,
+                       "prefix/submod/pkg/":     0,
+                       "prefix/submod/pkg/p.go": 31,
+               },
+       },
+}
+
+type zipFile struct {
+       name string
+       size int64
+}
+
+func TestReadZip(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+       testenv.MustHaveExec(t)
+
+       for _, tt := range readZipTests {
+               f := func(t *testing.T) {
+                       r, err := testRepo(tt.repo)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       rc, actualSubdir, err := r.ReadZip(tt.rev, tt.subdir, 100000)
+                       if err != nil {
+                               if tt.err == "" {
+                                       t.Fatalf("ReadZip: unexpected error %v", err)
+                               }
+                               if !strings.Contains(err.Error(), tt.err) {
+                                       t.Fatalf("ReadZip: wrong error %q, want %q", err, tt.err)
+                               }
+                               if rc != nil {
+                                       t.Errorf("ReadZip: non-nil io.ReadCloser with error %v", err)
+                               }
+                               return
+                       }
+                       defer rc.Close()
+                       if tt.err != "" {
+                               t.Fatalf("ReadZip: no error, wanted %v", tt.err)
+                       }
+                       if actualSubdir != tt.actualSubdir {
+                               t.Fatalf("ReadZip: actualSubdir = %q, want %q", actualSubdir, tt.actualSubdir)
+                       }
+                       zipdata, err := ioutil.ReadAll(rc)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       z, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata)))
+                       if err != nil {
+                               t.Fatalf("ReadZip: cannot read zip file: %v", err)
+                       }
+                       have := make(map[string]bool)
+                       for _, f := range z.File {
+                               size, ok := tt.files[f.Name]
+                               if !ok {
+                                       t.Errorf("ReadZip: unexpected file %s", f.Name)
+                                       continue
+                               }
+                               have[f.Name] = true
+                               if f.UncompressedSize64 != size {
+                                       t.Errorf("ReadZip: file %s has unexpected size %d != %d", f.Name, f.UncompressedSize64, size)
+                               }
+                       }
+                       for name := range tt.files {
+                               if !have[name] {
+                                       t.Errorf("ReadZip: missing file %s", name)
+                               }
+                       }
+               }
+               t.Run(tt.repo+"/"+tt.rev+"/"+tt.subdir, f)
+               if tt.repo == gitrepo1 {
+                       tt.repo = "localGitRepo"
+                       t.Run(tt.repo+"/"+tt.rev+"/"+tt.subdir, f)
+               }
+       }
+}
+
+var statTests = []struct {
+       repo string
+       rev  string
+       err  string
+       info *codehost.RevInfo
+}{
+       {
+               repo: gitrepo1,
+               rev:  "HEAD",
+               info: &codehost.RevInfo{
+                       Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Short:   "ede458df7cd0",
+                       Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v2", // branch
+               info: &codehost.RevInfo{
+                       Name:    "9d02800338b8a55be062c838d1f02e0c5780b9eb",
+                       Short:   "9d02800338b8",
+                       Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
+                       Time:    time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v2.3.4", // badly-named branch (semver should be a tag)
+               info: &codehost.RevInfo{
+                       Name:    "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
+                       Short:   "76a00fb249b7",
+                       Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
+                       Time:    time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v2.3", // badly-named tag (we only respect full semver v2.3.0)
+               info: &codehost.RevInfo{
+                       Name:    "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
+                       Short:   "76a00fb249b7",
+                       Version: "v2.3",
+                       Time:    time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v1.2.3", // tag
+               info: &codehost.RevInfo{
+                       Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Short:   "ede458df7cd0",
+                       Version: "v1.2.3",
+                       Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "ede458df", // hash prefix in refs
+               info: &codehost.RevInfo{
+                       Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Short:   "ede458df7cd0",
+                       Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "97f6aa59", // hash prefix not in refs
+               info: &codehost.RevInfo{
+                       Name:    "97f6aa59c81c623494825b43d39e445566e429a4",
+                       Short:   "97f6aa59c81c",
+                       Version: "97f6aa59c81c623494825b43d39e445566e429a4",
+                       Time:    time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "v1.2.4-annotated", // annotated tag uses unwrapped commit hash
+               info: &codehost.RevInfo{
+                       Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+                       Short:   "ede458df7cd0",
+                       Version: "v1.2.4-annotated",
+                       Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+               },
+       },
+       {
+               repo: gitrepo1,
+               rev:  "aaaaaaaaab",
+               err:  "cannot find hash",
+       },
+}
+
+func TestStat(t *testing.T) {
+       testenv.MustHaveExternalNetwork(t)
+       testenv.MustHaveExec(t)
+
+       for _, tt := range statTests {
+               f := func(t *testing.T) {
+                       r, err := testRepo(tt.repo)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       info, err := r.Stat(tt.rev)
+                       if err != nil {
+                               if tt.err == "" {
+                                       t.Fatalf("Stat: unexpected error %v", err)
+                               }
+                               if !strings.Contains(err.Error(), tt.err) {
+                                       t.Fatalf("Stat: wrong error %q, want %q", err, tt.err)
+                               }
+                               if info != nil {
+                                       t.Errorf("Stat: non-nil info with error %q", err)
+                               }
+                               return
+                       }
+                       if *info != *tt.info {
+                               t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
+                       }
+               }
+               t.Run(filepath.Base(tt.repo)+"/"+tt.rev, f)
+               if tt.repo == gitrepo1 {
+                       tt.repo = "localGitRepo"
+                       t.Run(filepath.Base(tt.repo)+"/"+tt.rev, f)
+               }
+       }
+}
diff --git a/src/cmd/go/internal/modfetch/googlesource/fetch.go b/src/cmd/go/internal/modfetch/googlesource/fetch.go
new file mode 100644 (file)
index 0000000..8317ac3
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.
+
+package googlesource
+
+import (
+       "fmt"
+       "strings"
+
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfetch/gitrepo"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+       i := strings.Index(path, "/")
+       if i+1 == len(path) || !strings.HasSuffix(path[:i+1], ".googlesource.com/") {
+               return nil, fmt.Errorf("not *.googlesource.com/*")
+       }
+       j := strings.Index(path[i+1:], "/")
+       if j >= 0 {
+               path = path[:i+1+j]
+       }
+       return gitrepo.Repo("https://"+path, path)
+}
diff --git a/src/cmd/go/internal/modfetch/gopkgin.go b/src/cmd/go/internal/modfetch/gopkgin.go
new file mode 100644 (file)
index 0000000..d49b60b
--- /dev/null
@@ -0,0 +1,22 @@
+// 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 modfetch
+
+import (
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfetch/gitrepo"
+       "cmd/go/internal/modfile"
+       "fmt"
+)
+
+func gopkginLookup(path string) (codehost.Repo, error) {
+       root, _, _, _, ok := modfile.ParseGopkgIn(path)
+       if !ok {
+               return nil, fmt.Errorf("invalid gopkg.in/ path: %q", path)
+       }
+       return gitrepo.Repo("https://"+root, root)
+}
diff --git a/src/cmd/go/internal/modfetch/noweb.go b/src/cmd/go/internal/modfetch/noweb.go
new file mode 100644 (file)
index 0000000..9d713dc
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+
+// +build cmd_go_bootstrap
+
+package modfetch
+
+import (
+       "fmt"
+       "io"
+)
+
+func webGetGoGet(url string, body *io.ReadCloser) error {
+       return fmt.Errorf("no network in go_bootstrap")
+}
+
+func webGetBytes(url string, body *[]byte) error {
+       return fmt.Errorf("no network in go_bootstrap")
+}
+
+func webGetBody(url string, body *io.ReadCloser) error {
+       return fmt.Errorf("no network in go_bootstrap")
+}
diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go
new file mode 100644 (file)
index 0000000..419323b
--- /dev/null
@@ -0,0 +1,164 @@
+// 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.
+
+package modfetch
+
+import (
+       "encoding/json"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "net/url"
+       "os"
+       "strings"
+       "time"
+
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/semver"
+)
+
+var proxyURL = os.Getenv("GOPROXY")
+
+func lookupProxy(path string) (Repo, error) {
+       u, err := url.Parse(proxyURL)
+       if err != nil || u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "file" {
+               // Don't echo $GOPROXY back in case it has user:password in it (sigh).
+               return nil, fmt.Errorf("invalid $GOPROXY setting")
+       }
+       return newProxyRepo(u.String(), path), nil
+}
+
+type proxyRepo struct {
+       url  string
+       path string
+}
+
+func newProxyRepo(baseURL, path string) Repo {
+       return &proxyRepo{strings.TrimSuffix(baseURL, "/") + "/" + pathEscape(path), path}
+}
+
+func (p *proxyRepo) ModulePath() string {
+       return p.path
+}
+
+func (p *proxyRepo) Versions(prefix string) ([]string, error) {
+       var data []byte
+       err := webGetBytes(p.url+"/@v/list", &data)
+       if err != nil {
+               return nil, err
+       }
+       var list []string
+       for _, line := range strings.Split(string(data), "\n") {
+               f := strings.Fields(line)
+               if len(f) >= 1 && semver.IsValid(f[0]) && strings.HasPrefix(f[0], prefix) {
+                       list = append(list, f[0])
+               }
+       }
+       return list, nil
+}
+
+func (p *proxyRepo) latest() (*RevInfo, error) {
+       var data []byte
+       err := webGetBytes(p.url+"/@v/list", &data)
+       if err != nil {
+               return nil, err
+       }
+       var best time.Time
+       var bestVersion string
+       for _, line := range strings.Split(string(data), "\n") {
+               f := strings.Fields(line)
+               if len(f) >= 2 && semver.IsValid(f[0]) {
+                       ft, err := time.Parse(time.RFC3339, f[1])
+                       if err == nil && best.Before(ft) {
+                               best = ft
+                               bestVersion = f[0]
+                       }
+               }
+       }
+       if bestVersion == "" {
+               return nil, fmt.Errorf("no commits")
+       }
+       info := &RevInfo{
+               Version: bestVersion,
+               Name:    bestVersion,
+               Short:   bestVersion,
+               Time:    best,
+       }
+       return info, nil
+}
+
+func (p *proxyRepo) Stat(rev string) (*RevInfo, error) {
+       var data []byte
+       err := webGetBytes(p.url+"/@v/"+pathEscape(rev)+".info", &data)
+       if err != nil {
+               return nil, err
+       }
+       info := new(RevInfo)
+       if err := json.Unmarshal(data, info); err != nil {
+               return nil, err
+       }
+       return info, nil
+}
+
+func (p *proxyRepo) Latest() (*RevInfo, error) {
+       var data []byte
+       u := p.url + "/@latest"
+       err := webGetBytes(u, &data)
+       if err != nil {
+               // TODO return err if not 404
+               return p.latest()
+       }
+       info := new(RevInfo)
+       if err := json.Unmarshal(data, info); err != nil {
+               return nil, err
+       }
+       return info, nil
+}
+
+func (p *proxyRepo) GoMod(version string) ([]byte, error) {
+       var data []byte
+       err := webGetBytes(p.url+"/@v/"+pathEscape(version)+".mod", &data)
+       if err != nil {
+               return nil, err
+       }
+       return data, nil
+}
+
+func (p *proxyRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
+       var body io.ReadCloser
+       err = webGetBody(p.url+"/@v/"+pathEscape(version)+".zip", &body)
+       if err != nil {
+               return "", err
+       }
+       defer body.Close()
+
+       // Spool to local file.
+       f, err := ioutil.TempFile(tmpdir, "vgo-proxy-download-")
+       if err != nil {
+               return "", err
+       }
+       defer f.Close()
+       maxSize := int64(codehost.MaxZipFile)
+       lr := &io.LimitedReader{R: body, N: maxSize + 1}
+       if _, err := io.Copy(f, lr); err != nil {
+               os.Remove(f.Name())
+               return "", err
+       }
+       if lr.N <= 0 {
+               os.Remove(f.Name())
+               return "", fmt.Errorf("downloaded zip file too large")
+       }
+       if err := f.Close(); err != nil {
+               os.Remove(f.Name())
+               return "", err
+       }
+       return f.Name(), nil
+}
+
+// pathEscape escapes s so it can be used in a path.
+// That is, it escapes things like ? and # (which really shouldn't appear anyway).
+// It does not escape / to %2F: our REST API is designed so that / can be left as is.
+func pathEscape(s string) string {
+       return strings.Replace(url.PathEscape(s), "%2F", "/", -1)
+}
diff --git a/src/cmd/go/internal/modfetch/query.go b/src/cmd/go/internal/modfetch/query.go
new file mode 100644 (file)
index 0000000..5e9d86c
--- /dev/null
@@ -0,0 +1,84 @@
+// 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.
+
+package modfetch
+
+import (
+       "cmd/go/internal/module"
+       "cmd/go/internal/semver"
+       "fmt"
+       "strings"
+)
+
+// Query looks up a revision of a given module given a version query string.
+// The module must be a complete module path.
+// The version must take one of the following forms:
+//
+//     - the literal string "latest", denoting the latest available tagged version
+//     - v1.2.3, a semantic version string
+//     - v1 or v1.2, an abbreviated semantic version string completed by adding zeroes (v1.0.0 or v1.2.0);
+//     - >v1.2.3, denoting the earliest available version after v1.2.3
+//     - <v1.2.3, denoting the latest available version before v1.2.3
+//     - an RFC 3339 time stamp, denoting the latest available version at that time
+//     - a Unix time expressed as seconds since 1970, denoting the latest available version at that time
+//     - a repository commit identifier, denoting that version
+//
+// The time stamps can be followed by an optional @branch suffix to limit the
+// result to revisions on a particular branch name.
+//
+func Query(path, vers string, allowed func(module.Version) bool) (*RevInfo, error) {
+       repo, err := Lookup(path)
+       if err != nil {
+               return nil, err
+       }
+
+       if strings.HasPrefix(vers, "v") && semver.IsValid(vers) {
+               // TODO: This turns query for "v2" into Stat "v2.0.0",
+               // but probably it should allow checking for a branch named "v2".
+               vers = semver.Canonical(vers)
+               if allowed != nil && !allowed(module.Version{Path: path, Version: vers}) {
+                       return nil, fmt.Errorf("%s@%s excluded", path, vers)
+               }
+               return repo.Stat(vers)
+       }
+       if strings.HasPrefix(vers, ">") || strings.HasPrefix(vers, "<") || vers == "latest" {
+               var op string
+               if vers != "latest" {
+                       if !semver.IsValid(vers[1:]) {
+                               return nil, fmt.Errorf("invalid semantic version in range %s", vers)
+                       }
+                       op, vers = vers[:1], vers[1:]
+               }
+               versions, err := repo.Versions("")
+               if err != nil {
+                       return nil, err
+               }
+               if len(versions) == 0 && vers == "latest" {
+                       return repo.Latest()
+               }
+               if vers == "latest" {
+                       for i := len(versions) - 1; i >= 0; i-- {
+                               if allowed == nil || allowed(module.Version{Path: path, Version: versions[i]}) {
+                                       return repo.Stat(versions[i])
+                               }
+                       }
+               } else if op == "<" {
+                       for i := len(versions) - 1; i >= 0; i-- {
+                               if semver.Compare(versions[i], vers) < 0 && (allowed == nil || allowed(module.Version{Path: path, Version: versions[i]})) {
+                                       return repo.Stat(versions[i])
+                               }
+                       }
+               } else {
+                       for i := 0; i < len(versions); i++ {
+                               if semver.Compare(versions[i], vers) > 0 && (allowed == nil || allowed(module.Version{Path: path, Version: versions[i]})) {
+                                       return repo.Stat(versions[i])
+                               }
+                       }
+               }
+               return nil, fmt.Errorf("no matching versions for %s%s", op, vers)
+       }
+       // TODO: Time queries, maybe.
+
+       return repo.Stat(vers)
+}
diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go
new file mode 100644 (file)
index 0000000..6e21a41
--- /dev/null
@@ -0,0 +1,137 @@
+// 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.
+
+package modfetch
+
+import (
+       "errors"
+       pathpkg "path"
+       "sort"
+       "strings"
+       "time"
+
+       "cmd/go/internal/modfetch/bitbucket"
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfetch/github"
+       "cmd/go/internal/modfetch/googlesource"
+       "cmd/go/internal/module"
+       "cmd/go/internal/semver"
+)
+
+// A Repo represents a repository storing all versions of a single module.
+type Repo interface {
+       // ModulePath returns the module path.
+       ModulePath() string
+
+       // Versions lists all known versions with the given prefix.
+       // Pseudo-versions are not included.
+       // Versions should be returned sorted in semver order
+       // (implementations can use SortVersions).
+       Versions(prefix string) (tags []string, err error)
+
+       // Stat returns information about the revision rev.
+       // A revision can be any identifier known to the underlying service:
+       // commit hash, branch, tag, and so on.
+       Stat(rev string) (*RevInfo, error)
+
+       // Latest returns the latest revision on the default branch,
+       // whatever that means in the underlying source code repository.
+       // It is only used when there are no tagged versions.
+       Latest() (*RevInfo, error)
+
+       // GoMod returns the go.mod file for the given version.
+       GoMod(version string) (data []byte, err error)
+
+       // Zip downloads a zip file for the given version
+       // to a new file in a given temporary directory.
+       // It returns the name of the new file.
+       // The caller should remove the file when finished with it.
+       Zip(version, tmpdir string) (tmpfile string, err error)
+}
+
+// A Rev describes a single revision in a module repository.
+type RevInfo struct {
+       Version string    // version string
+       Name    string    // complete ID in underlying repository
+       Short   string    // shortened ID, for use in pseudo-version
+       Time    time.Time // commit time
+}
+
+// Lookup returns the module with the given module path.
+func Lookup(path string) (Repo, error) {
+       if proxyURL != "" {
+               return lookupProxy(path)
+       }
+       if code, err := lookupCodeHost(path, false); err != errNotHosted {
+               if err != nil {
+                       return nil, err
+               }
+               return newCodeRepo(code, path)
+       }
+       return lookupCustomDomain(path)
+}
+
+func Import(path string, allowed func(module.Version) bool) (Repo, *RevInfo, error) {
+       try := func(path string) (Repo, *RevInfo, error) {
+               r, err := Lookup(path)
+               if err != nil {
+                       return nil, nil, err
+               }
+               info, err := Query(path, "latest", allowed)
+               if err != nil {
+                       return nil, nil, err
+               }
+               _, err = r.GoMod(info.Version)
+               if err != nil {
+                       return nil, nil, err
+               }
+               return r, info, nil
+       }
+
+       var firstErr error
+       for {
+               r, info, err := try(path)
+               if err == nil {
+                       return r, info, nil
+               }
+               if firstErr == nil {
+                       firstErr = err
+               }
+               p := pathpkg.Dir(path)
+               if p == "." {
+                       break
+               }
+               path = p
+       }
+       return nil, nil, firstErr
+}
+
+var errNotHosted = errors.New("not hosted")
+
+var isTest bool
+
+func lookupCodeHost(path string, customDomain bool) (codehost.Repo, error) {
+       switch {
+       case strings.HasPrefix(path, "github.com/"):
+               return github.Lookup(path)
+       case strings.HasPrefix(path, "bitbucket.org/"):
+               return bitbucket.Lookup(path)
+       case customDomain && strings.HasSuffix(path[:strings.Index(path, "/")+1], ".googlesource.com/") ||
+               isTest && strings.HasPrefix(path, "go.googlesource.com/scratch"):
+               return googlesource.Lookup(path)
+       case strings.HasPrefix(path, "gopkg.in/"):
+               return gopkginLookup(path)
+       }
+       return nil, errNotHosted
+}
+
+func SortVersions(list []string) {
+       sort.Slice(list, func(i, j int) bool {
+               cmp := semver.Compare(list[i], list[j])
+               if cmp != 0 {
+                       return cmp < 0
+               }
+               return list[i] < list[j]
+       })
+}
diff --git a/src/cmd/go/internal/modfetch/unzip.go b/src/cmd/go/internal/modfetch/unzip.go
new file mode 100644 (file)
index 0000000..9d9e298
--- /dev/null
@@ -0,0 +1,100 @@
+// 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.
+
+package modfetch
+
+import (
+       "archive/zip"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "strings"
+
+       "cmd/go/internal/modfetch/codehost"
+)
+
+func Unzip(dir, zipfile, prefix string, maxSize int64) error {
+       if maxSize == 0 {
+               maxSize = codehost.MaxZipFile
+       }
+
+       // Directory can exist, but must be empty.
+       // except maybe
+       files, _ := ioutil.ReadDir(dir)
+       if len(files) > 0 {
+               return fmt.Errorf("target directory %v exists and is not empty", dir)
+       }
+       if err := os.MkdirAll(dir, 0777); err != nil {
+               return err
+       }
+
+       f, err := os.Open(zipfile)
+       if err != nil {
+               return err
+       }
+       defer f.Close()
+       info, err := f.Stat()
+       if err != nil {
+               return err
+       }
+
+       z, err := zip.NewReader(f, info.Size())
+       if err != nil {
+               return fmt.Errorf("unzip %v: %s", zipfile, err)
+       }
+
+       // Check total size.
+       var size int64
+       for _, zf := range z.File {
+               if !strings.HasPrefix(zf.Name, prefix) {
+                       return fmt.Errorf("unzip %v: unexpected file name %s", zipfile, zf.Name)
+               }
+               if strings.HasSuffix(zf.Name, "/") {
+                       continue
+               }
+               s := int64(zf.UncompressedSize64)
+               if s < 0 || maxSize-size < s {
+                       return fmt.Errorf("unzip %v: content too large", zipfile)
+               }
+               size += s
+       }
+
+       // Unzip, enforcing sizes checked earlier.
+       for _, zf := range z.File {
+               if strings.HasSuffix(zf.Name, "/") {
+                       continue
+               }
+               dst := filepath.Join(dir, zf.Name[len(prefix):])
+               if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
+                       return err
+               }
+               w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444)
+               if err != nil {
+                       return fmt.Errorf("unzip %v: %v", zipfile, err)
+               }
+               r, err := zf.Open()
+               if err != nil {
+                       r.Close()
+                       w.Close()
+                       return fmt.Errorf("unzip %v: %v", zipfile, err)
+               }
+               lr := &io.LimitedReader{R: r, N: int64(zf.UncompressedSize64) + 1}
+               _, err = io.Copy(w, lr)
+               r.Close()
+               if err != nil {
+                       w.Close()
+                       return fmt.Errorf("unzip %v: %v", zipfile, err)
+               }
+               if err := w.Close(); err != nil {
+                       return fmt.Errorf("unzip %v: %v", zipfile, err)
+               }
+               if lr.N <= 0 {
+                       return fmt.Errorf("unzip %v: content too large", zipfile)
+               }
+       }
+
+       return nil
+}
diff --git a/src/cmd/go/internal/modfetch/web.go b/src/cmd/go/internal/modfetch/web.go
new file mode 100644 (file)
index 0000000..b327bf2
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.
+
+// +build !cmd_go_bootstrap
+
+package modfetch
+
+import (
+       "io"
+
+       web "cmd/go/internal/web2"
+)
+
+// webGetGoGet fetches a go-get=1 URL and returns the body in *body.
+// It allows non-200 responses, as usual for these URLs.
+func webGetGoGet(url string, body *io.ReadCloser) error {
+       return web.Get(url, web.Non200OK(), web.Body(body))
+}
+
+// webGetBytes returns the body returned by an HTTP GET, as a []byte.
+// It insists on a 200 response.
+func webGetBytes(url string, body *[]byte) error {
+       return web.Get(url, web.ReadAllBody(body))
+}
+
+// webGetBody returns the body returned by an HTTP GET, as a io.ReadCloser.
+// It insists on a 200 response.
+func webGetBody(url string, body *io.ReadCloser) error {
+       return web.Get(url, web.Body(body))
+}
diff --git a/src/cmd/go/internal/modfile/gopkgin.go b/src/cmd/go/internal/modfile/gopkgin.go
new file mode 100644 (file)
index 0000000..c94b384
--- /dev/null
@@ -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/src/cmd/go/internal/modfile/print.go b/src/cmd/go/internal/modfile/print.go
new file mode 100644 (file)
index 0000000..cefc43b
--- /dev/null
@@ -0,0 +1,164 @@
+// 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.
+
+// Module file printer.
+
+package modfile
+
+import (
+       "bytes"
+       "fmt"
+       "strings"
+)
+
+func Format(f *FileSyntax) []byte {
+       pr := &printer{}
+       pr.file(f)
+       return pr.Bytes()
+}
+
+// A printer collects the state during printing of a file or expression.
+type printer struct {
+       bytes.Buffer           // output buffer
+       comment      []Comment // pending end-of-line comments
+       margin       int       // left margin (indent), a number of tabs
+}
+
+// printf prints to the buffer.
+func (p *printer) printf(format string, args ...interface{}) {
+       fmt.Fprintf(p, format, args...)
+}
+
+// indent returns the position on the current line, in bytes, 0-indexed.
+func (p *printer) indent() int {
+       b := p.Bytes()
+       n := 0
+       for n < len(b) && b[len(b)-1-n] != '\n' {
+               n++
+       }
+       return n
+}
+
+// newline ends the current line, flushing end-of-line comments.
+func (p *printer) newline() {
+       if len(p.comment) > 0 {
+               p.printf(" ")
+               for i, com := range p.comment {
+                       if i > 0 {
+                               p.trim()
+                               p.printf("\n")
+                               for i := 0; i < p.margin; i++ {
+                                       p.printf("\t")
+                               }
+                       }
+                       p.printf("%s", strings.TrimSpace(com.Token))
+               }
+               p.comment = p.comment[:0]
+       }
+
+       p.trim()
+       p.printf("\n")
+       for i := 0; i < p.margin; i++ {
+               p.printf("\t")
+       }
+}
+
+// trim removes trailing spaces and tabs from the current line.
+func (p *printer) trim() {
+       // Remove trailing spaces and tabs from line we're about to end.
+       b := p.Bytes()
+       n := len(b)
+       for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') {
+               n--
+       }
+       p.Truncate(n)
+}
+
+// file formats the given file into the print buffer.
+func (p *printer) file(f *FileSyntax) {
+       for _, com := range f.Before {
+               p.printf("%s", strings.TrimSpace(com.Token))
+               p.newline()
+       }
+
+       for i, stmt := range f.Stmt {
+               switch x := stmt.(type) {
+               case *CommentBlock:
+                       // comments already handled
+                       p.expr(x)
+
+               default:
+                       p.expr(x)
+                       p.newline()
+               }
+
+               for _, com := range stmt.Comment().After {
+                       p.printf("%s", strings.TrimSpace(com.Token))
+                       p.newline()
+               }
+
+               if i+1 < len(f.Stmt) {
+                       p.newline()
+               }
+       }
+}
+
+func (p *printer) expr(x Expr) {
+       // Emit line-comments preceding this expression.
+       if before := x.Comment().Before; len(before) > 0 {
+               // Want to print a line comment.
+               // Line comments must be at the current margin.
+               p.trim()
+               if p.indent() > 0 {
+                       // There's other text on the line. Start a new line.
+                       p.printf("\n")
+               }
+               // Re-indent to margin.
+               for i := 0; i < p.margin; i++ {
+                       p.printf("\t")
+               }
+               for _, com := range before {
+                       p.printf("%s", strings.TrimSpace(com.Token))
+                       p.newline()
+               }
+       }
+
+       switch x := x.(type) {
+       default:
+               panic(fmt.Errorf("printer: unexpected type %T", x))
+
+       case *CommentBlock:
+               // done
+
+       case *LParen:
+               p.printf("(")
+       case *RParen:
+               p.printf(")")
+
+       case *Line:
+               sep := ""
+               for _, tok := range x.Token {
+                       p.printf("%s%s", sep, tok)
+                       sep = " "
+               }
+
+       case *LineBlock:
+               for _, tok := range x.Token {
+                       p.printf("%s ", tok)
+               }
+               p.expr(&x.LParen)
+               p.margin++
+               for _, l := range x.Line {
+                       p.newline()
+                       p.expr(l)
+               }
+               p.margin--
+               p.newline()
+               p.expr(&x.RParen)
+       }
+
+       // Queue end-of-line comments for printing when we
+       // reach the end of the line.
+       p.comment = append(p.comment, x.Comment().Suffix...)
+}
diff --git a/src/cmd/go/internal/modfile/read.go b/src/cmd/go/internal/modfile/read.go
new file mode 100644 (file)
index 0000000..a0c88d6
--- /dev/null
@@ -0,0 +1,699 @@
+// 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.
+
+// Module file parser.
+// This is a simplified copy of Google's buildifier parser.
+
+package modfile
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "strings"
+       "unicode"
+       "unicode/utf8"
+)
+
+// A Position describes the position between two bytes of input.
+type Position struct {
+       Line     int // line in input (starting at 1)
+       LineRune int // rune in line (starting at 1)
+       Byte     int // byte in input (starting at 0)
+}
+
+// add returns the position at the end of s, assuming it starts at p.
+func (p Position) add(s string) Position {
+       p.Byte += len(s)
+       if n := strings.Count(s, "\n"); n > 0 {
+               p.Line += n
+               s = s[strings.LastIndex(s, "\n")+1:]
+               p.LineRune = 1
+       }
+       p.LineRune += utf8.RuneCountInString(s)
+       return p
+}
+
+// An Expr represents an input element.
+type Expr interface {
+       // Span returns the start and end position of the expression,
+       // excluding leading or trailing comments.
+       Span() (start, end Position)
+
+       // Comment returns the comments attached to the expression.
+       // This method would normally be named 'Comments' but that
+       // would interfere with embedding a type of the same name.
+       Comment() *Comments
+}
+
+// A Comment represents a single // comment.
+type Comment struct {
+       Start  Position
+       Token  string // without trailing newline
+       Suffix bool   // an end of line (not whole line) comment
+}
+
+// Comments collects the comments associated with an expression.
+type Comments struct {
+       Before []Comment // whole-line comments before this expression
+       Suffix []Comment // end-of-line comments after this expression
+
+       // For top-level expressions only, After lists whole-line
+       // comments following the expression.
+       After []Comment
+}
+
+// Comment returns the receiver. This isn't useful by itself, but
+// a Comments struct is embedded into all the expression
+// implementation types, and this gives each of those a Comment
+// method to satisfy the Expr interface.
+func (c *Comments) Comment() *Comments {
+       return c
+}
+
+// A FileSyntax represents an entire go.mod file.
+type FileSyntax struct {
+       Name string // file path
+       Comments
+       Stmt []Expr
+}
+
+func (x *FileSyntax) Span() (start, end Position) {
+       if len(x.Stmt) == 0 {
+               return
+       }
+       start, _ = x.Stmt[0].Span()
+       _, end = x.Stmt[len(x.Stmt)-1].Span()
+       return start, end
+}
+
+// A CommentBlock represents a top-level block of comments separate
+// from any rule.
+type CommentBlock struct {
+       Comments
+       Start Position
+}
+
+func (x *CommentBlock) Span() (start, end Position) {
+       return x.Start, x.Start
+}
+
+// A Line is a single line of tokens.
+type Line struct {
+       Comments
+       Start Position
+       Token []string
+       End   Position
+}
+
+func (x *Line) Span() (start, end Position) {
+       return x.Start, x.End
+}
+
+// A LineBlock is a factored block of lines, like
+//
+//     require (
+//             "x"
+//             "y"
+//     )
+//
+type LineBlock struct {
+       Comments
+       Start  Position
+       LParen LParen
+       Token  []string
+       Line   []*Line
+       RParen RParen
+}
+
+func (x *LineBlock) Span() (start, end Position) {
+       return x.Start, x.RParen.Pos.add(")")
+}
+
+// An LParen represents the beginning of a parenthesized line block.
+// It is a place to store suffix comments.
+type LParen struct {
+       Comments
+       Pos Position
+}
+
+func (x *LParen) Span() (start, end Position) {
+       return x.Pos, x.Pos.add(")")
+}
+
+// An RParen represents the end of a parenthesized line block.
+// It is a place to store whole-line (before) comments.
+type RParen struct {
+       Comments
+       Pos Position
+}
+
+func (x *RParen) Span() (start, end Position) {
+       return x.Pos, x.Pos.add(")")
+}
+
+// An input represents a single input file being parsed.
+type input struct {
+       // Lexing state.
+       filename  string    // name of input file, for errors
+       complete  []byte    // entire input
+       remaining []byte    // remaining input
+       token     []byte    // token being scanned
+       lastToken string    // most recently returned token, for error messages
+       pos       Position  // current input position
+       comments  []Comment // accumulated comments
+       endRule   int       // position of end of current rule
+
+       // Parser state.
+       file       *FileSyntax // returned top-level syntax tree
+       parseError error       // error encountered during parsing
+
+       // Comment assignment state.
+       pre  []Expr // all expressions, in preorder traversal
+       post []Expr // all expressions, in postorder traversal
+}
+
+func newInput(filename string, data []byte) *input {
+       return &input{
+               filename:  filename,
+               complete:  data,
+               remaining: data,
+               pos:       Position{Line: 1, LineRune: 1, Byte: 0},
+       }
+}
+
+// parse parses the input file.
+func parse(file string, data []byte) (f *FileSyntax, err error) {
+       in := newInput(file, data)
+       // The parser panics for both routine errors like syntax errors
+       // and for programmer bugs like array index errors.
+       // Turn both into error returns. Catching bug panics is
+       // especially important when processing many files.
+       defer func() {
+               if e := recover(); e != nil {
+                       if e == in.parseError {
+                               err = in.parseError
+                       } else {
+                               err = fmt.Errorf("%s:%d:%d: internal error: %v", in.filename, in.pos.Line, in.pos.LineRune, e)
+                       }
+               }
+       }()
+
+       // Invoke the parser.
+       in.parseFile()
+       if in.parseError != nil {
+               return nil, in.parseError
+       }
+       in.file.Name = in.filename
+
+       // Assign comments to nearby syntax.
+       in.assignComments()
+
+       return in.file, nil
+}
+
+// Error is called to report an error.
+// The reason s is often "syntax error".
+// Error does not return: it panics.
+func (in *input) Error(s string) {
+       if s == "syntax error" && in.lastToken != "" {
+               s += " near " + in.lastToken
+       }
+       in.parseError = fmt.Errorf("%s:%d:%d: %v", in.filename, in.pos.Line, in.pos.LineRune, s)
+       panic(in.parseError)
+}
+
+// eof reports whether the input has reached end of file.
+func (in *input) eof() bool {
+       return len(in.remaining) == 0
+}
+
+// peekRune returns the next rune in the input without consuming it.
+func (in *input) peekRune() int {
+       if len(in.remaining) == 0 {
+               return 0
+       }
+       r, _ := utf8.DecodeRune(in.remaining)
+       return int(r)
+}
+
+// peekPrefix reports whether the remaining input begins with the given prefix.
+func (in *input) peekPrefix(prefix string) bool {
+       // This is like bytes.HasPrefix(in.remaining, []byte(prefix))
+       // but without the allocation of the []byte copy of prefix.
+       for i := 0; i < len(prefix); i++ {
+               if i >= len(in.remaining) || in.remaining[i] != prefix[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+// readRune consumes and returns the next rune in the input.
+func (in *input) readRune() int {
+       if len(in.remaining) == 0 {
+               in.Error("internal lexer error: readRune at EOF")
+       }
+       r, size := utf8.DecodeRune(in.remaining)
+       in.remaining = in.remaining[size:]
+       if r == '\n' {
+               in.pos.Line++
+               in.pos.LineRune = 1
+       } else {
+               in.pos.LineRune++
+       }
+       in.pos.Byte += size
+       return int(r)
+}
+
+type symType struct {
+       pos    Position
+       endPos Position
+       text   string
+}
+
+// startToken marks the beginning of the next input token.
+// It must be followed by a call to endToken, once the token has
+// been consumed using readRune.
+func (in *input) startToken(sym *symType) {
+       in.token = in.remaining
+       sym.text = ""
+       sym.pos = in.pos
+}
+
+// endToken marks the end of an input token.
+// It records the actual token string in sym.text if the caller
+// has not done that already.
+func (in *input) endToken(sym *symType) {
+       if sym.text == "" {
+               tok := string(in.token[:len(in.token)-len(in.remaining)])
+               sym.text = tok
+               in.lastToken = sym.text
+       }
+       sym.endPos = in.pos
+}
+
+// lex is called from the parser to obtain the next input token.
+// It returns the token value (either a rune like '+' or a symbolic token _FOR)
+// and sets val to the data associated with the token.
+// For all our input tokens, the associated data is
+// val.Pos (the position where the token begins)
+// and val.Token (the input string corresponding to the token).
+func (in *input) lex(sym *symType) int {
+       // Skip past spaces, stopping at non-space or EOF.
+       countNL := 0 // number of newlines we've skipped past
+       for !in.eof() {
+               // Skip over spaces. Count newlines so we can give the parser
+               // information about where top-level blank lines are,
+               // for top-level comment assignment.
+               c := in.peekRune()
+               if c == ' ' || c == '\t' || c == '\r' {
+                       in.readRune()
+                       continue
+               }
+
+               // Comment runs to end of line.
+               if in.peekPrefix("//") {
+                       in.startToken(sym)
+
+                       // Is this comment the only thing on its line?
+                       // Find the last \n before this // and see if it's all
+                       // spaces from there to here.
+                       i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n"))
+                       suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0
+                       in.readRune()
+                       in.readRune()
+
+                       // Consume comment.
+                       for len(in.remaining) > 0 && in.readRune() != '\n' {
+                       }
+                       in.endToken(sym)
+
+                       sym.text = strings.TrimRight(sym.text, "\n")
+                       in.lastToken = "comment"
+
+                       // If we are at top level (not in a statement), hand the comment to
+                       // the parser as a _COMMENT token. The grammar is written
+                       // to handle top-level comments itself.
+                       if !suffix {
+                               // Not in a statement. Tell parser about top-level comment.
+                               return _COMMENT
+                       }
+
+                       // Otherwise, save comment for later attachment to syntax tree.
+                       if countNL > 1 {
+                               in.comments = append(in.comments, Comment{sym.pos, "", false})
+                       }
+                       in.comments = append(in.comments, Comment{sym.pos, sym.text, suffix})
+                       countNL = 1
+                       return _EOL
+               }
+
+               if in.peekPrefix("/*") {
+                       in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)"))
+               }
+
+               // Found non-space non-comment.
+               break
+       }
+
+       // Found the beginning of the next token.
+       in.startToken(sym)
+       defer in.endToken(sym)
+
+       // End of file.
+       if in.eof() {
+               in.lastToken = "EOF"
+               return _EOF
+       }
+
+       // Punctuation tokens.
+       switch c := in.peekRune(); c {
+       case '\n':
+               in.readRune()
+               return c
+
+       case '(':
+               in.readRune()
+               return c
+
+       case ')':
+               in.readRune()
+               return c
+
+       case '"', '`': // quoted string
+               quote := c
+               in.readRune()
+               for {
+                       if in.eof() {
+                               in.pos = sym.pos
+                               in.Error("unexpected EOF in string")
+                       }
+                       if in.peekRune() == '\n' {
+                               in.Error("unexpected newline in string")
+                       }
+                       c := in.readRune()
+                       if c == quote {
+                               break
+                       }
+                       if c == '\\' && quote != '`' {
+                               if in.eof() {
+                                       in.pos = sym.pos
+                                       in.Error("unexpected EOF in string")
+                               }
+                               in.readRune()
+                       }
+               }
+               in.endToken(sym)
+               return _STRING
+       }
+
+       // Checked all punctuation. Must be identifier token.
+       if c := in.peekRune(); !isIdent(c) {
+               in.Error(fmt.Sprintf("unexpected input character %#q", c))
+       }
+
+       // Scan over identifier.
+       for isIdent(in.peekRune()) {
+               if in.peekPrefix("//") {
+                       break
+               }
+               if in.peekPrefix("/*") {
+                       in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)"))
+               }
+               in.readRune()
+       }
+       return _IDENT
+}
+
+// isIdent reports whether c is an identifier rune.
+// We treat nearly all runes as identifier runes.
+func isIdent(c int) bool {
+       return c != 0 && !unicode.IsSpace(rune(c))
+}
+
+// Comment assignment.
+// We build two lists of all subexpressions, preorder and postorder.
+// The preorder list is ordered by start location, with outer expressions first.
+// The postorder list is ordered by end location, with outer expressions last.
+// We use the preorder list to assign each whole-line comment to the syntax
+// immediately following it, and we use the postorder list to assign each
+// end-of-line comment to the syntax immediately preceding it.
+
+// order walks the expression adding it and its subexpressions to the
+// preorder and postorder lists.
+func (in *input) order(x Expr) {
+       if x != nil {
+               in.pre = append(in.pre, x)
+       }
+       switch x := x.(type) {
+       default:
+               panic(fmt.Errorf("order: unexpected type %T", x))
+       case nil:
+               // nothing
+       case *LParen, *RParen:
+               // nothing
+       case *CommentBlock:
+               // nothing
+       case *Line:
+               // nothing
+       case *FileSyntax:
+               for _, stmt := range x.Stmt {
+                       in.order(stmt)
+               }
+       case *LineBlock:
+               in.order(&x.LParen)
+               for _, l := range x.Line {
+                       in.order(l)
+               }
+               in.order(&x.RParen)
+       }
+       if x != nil {
+               in.post = append(in.post, x)
+       }
+}
+
+// assignComments attaches comments to nearby syntax.
+func (in *input) assignComments() {
+       const debug = false
+
+       // Generate preorder and postorder lists.
+       in.order(in.file)
+
+       // Split into whole-line comments and suffix comments.
+       var line, suffix []Comment
+       for _, com := range in.comments {
+               if com.Suffix {
+                       suffix = append(suffix, com)
+               } else {
+                       line = append(line, com)
+               }
+       }
+
+       if debug {
+               for _, c := range line {
+                       fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
+               }
+       }
+
+       // Assign line comments to syntax immediately following.
+       for _, x := range in.pre {
+               start, _ := x.Span()
+               if debug {
+                       fmt.Printf("pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte)
+               }
+               xcom := x.Comment()
+               for len(line) > 0 && start.Byte >= line[0].Start.Byte {
+                       if debug {
+                               fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte)
+                       }
+                       xcom.Before = append(xcom.Before, line[0])
+                       line = line[1:]
+               }
+       }
+
+       // Remaining line comments go at end of file.
+       in.file.After = append(in.file.After, line...)
+
+       if debug {
+               for _, c := range suffix {
+                       fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
+               }
+       }
+
+       // Assign suffix comments to syntax immediately before.
+       for i := len(in.post) - 1; i >= 0; i-- {
+               x := in.post[i]
+
+               start, end := x.Span()
+               if debug {
+                       fmt.Printf("post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
+               }
+
+               // Do not assign suffix comments to end of line block or whole file.
+               // Instead assign them to the last element inside.
+               switch x.(type) {
+               case *FileSyntax:
+                       continue
+               }
+
+               // Do not assign suffix comments to something that starts
+               // on an earlier line, so that in
+               //
+               //      x ( y
+               //              z ) // comment
+               //
+               // we assign the comment to z and not to x ( ... ).
+               if start.Line != end.Line {
+                       continue
+               }
+               xcom := x.Comment()
+               for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte {
+                       if debug {
+                               fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte)
+                       }
+                       xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1])
+                       suffix = suffix[:len(suffix)-1]
+               }
+       }
+
+       // We assigned suffix comments in reverse.
+       // If multiple suffix comments were appended to the same
+       // expression node, they are now in reverse. Fix that.
+       for _, x := range in.post {
+               reverseComments(x.Comment().Suffix)
+       }
+
+       // Remaining suffix comments go at beginning of file.
+       in.file.Before = append(in.file.Before, suffix...)
+}
+
+// reverseComments reverses the []Comment list.
+func reverseComments(list []Comment) {
+       for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
+               list[i], list[j] = list[j], list[i]
+       }
+}
+
+func (in *input) parseFile() {
+       in.file = new(FileSyntax)
+       var sym symType
+       var cb *CommentBlock
+       for {
+               tok := in.lex(&sym)
+               switch tok {
+               case '\n':
+                       if cb != nil {
+                               in.file.Stmt = append(in.file.Stmt, cb)
+                               cb = nil
+                       }
+               case _COMMENT:
+                       if cb == nil {
+                               cb = &CommentBlock{Start: sym.pos}
+                       }
+                       com := cb.Comment()
+                       com.Before = append(com.Before, Comment{Start: sym.pos, Token: sym.text})
+               case _EOF:
+                       if cb != nil {
+                               in.file.Stmt = append(in.file.Stmt, cb)
+                       }
+                       return
+               default:
+                       in.parseStmt(&sym)
+                       if cb != nil {
+                               in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before
+                               cb = nil
+                       }
+               }
+       }
+}
+
+func (in *input) parseStmt(sym *symType) {
+       start := sym.pos
+       end := sym.endPos
+       token := []string{sym.text}
+       for {
+               tok := in.lex(sym)
+               switch tok {
+               case '\n', _EOF, _EOL:
+                       in.file.Stmt = append(in.file.Stmt, &Line{
+                               Start: start,
+                               Token: token,
+                               End:   end,
+                       })
+                       return
+               case '(':
+                       in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, token, sym))
+                       return
+               default:
+                       token = append(token, sym.text)
+                       end = sym.endPos
+               }
+       }
+}
+
+func (in *input) parseLineBlock(start Position, token []string, sym *symType) *LineBlock {
+       x := &LineBlock{
+               Start:  start,
+               Token:  token,
+               LParen: LParen{Pos: sym.pos},
+       }
+       var comments []Comment
+       for {
+               tok := in.lex(sym)
+               switch tok {
+               case _EOL:
+                       // ignore
+               case '\n':
+                       if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" {
+                               comments = append(comments, Comment{})
+                       }
+               case _COMMENT:
+                       comments = append(comments, Comment{Start: sym.pos, Token: sym.text})
+               case _EOF:
+                       in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune))
+               case ')':
+                       x.RParen.Before = comments
+                       x.RParen.Pos = sym.pos
+                       tok = in.lex(sym)
+                       if tok != '\n' && tok != _EOF && tok != _EOL {
+                               in.Error("syntax error (expected newline after closing paren)")
+                       }
+                       return x
+               default:
+                       l := in.parseLine(sym)
+                       x.Line = append(x.Line, l)
+                       l.Comment().Before = comments
+                       comments = nil
+               }
+       }
+}
+
+func (in *input) parseLine(sym *symType) *Line {
+       start := sym.pos
+       end := sym.endPos
+       token := []string{sym.text}
+       for {
+               tok := in.lex(sym)
+               switch tok {
+               case '\n', _EOF, _EOL:
+                       return &Line{
+                               Start: start,
+                               Token: token,
+                               End:   end,
+                       }
+               default:
+                       token = append(token, sym.text)
+                       end = sym.endPos
+               }
+       }
+}
+
+const (
+       _EOF = -(1 + iota)
+       _EOL
+       _IDENT
+       _STRING
+       _COMMENT
+)
diff --git a/src/cmd/go/internal/modfile/read_test.go b/src/cmd/go/internal/modfile/read_test.go
new file mode 100644 (file)
index 0000000..2c617b8
--- /dev/null
@@ -0,0 +1,306 @@
+// 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.
+
+package modfile
+
+import (
+       "bytes"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "reflect"
+       "strings"
+       "testing"
+)
+
+// exists reports whether the named file exists.
+func exists(name string) bool {
+       _, err := os.Stat(name)
+       return err == nil
+}
+
+// Test that reading and then writing the golden files
+// does not change their output.
+func TestPrintGolden(t *testing.T) {
+       outs, err := filepath.Glob("testdata/*.golden")
+       if err != nil {
+               t.Fatal(err)
+       }
+       for _, out := range outs {
+               testPrint(t, out, out)
+       }
+}
+
+// testPrint is a helper for testing the printer.
+// It reads the file named in, reformats it, and compares
+// the result to the file named out.
+func testPrint(t *testing.T, in, out string) {
+       data, err := ioutil.ReadFile(in)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       golden, err := ioutil.ReadFile(out)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       base := "testdata/" + filepath.Base(in)
+       f, err := parse(in, data)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       ndata := Format(f)
+
+       if !bytes.Equal(ndata, golden) {
+               t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base)
+               tdiff(t, string(golden), string(ndata))
+               return
+       }
+}
+
+// Test that when files in the testdata directory are parsed
+// and printed and parsed again, we get the same parse tree
+// both times.
+func TestPrintParse(t *testing.T) {
+       outs, err := filepath.Glob("testdata/*")
+       if err != nil {
+               t.Fatal(err)
+       }
+       for _, out := range outs {
+               data, err := ioutil.ReadFile(out)
+               if err != nil {
+                       t.Error(err)
+                       continue
+               }
+
+               base := "testdata/" + filepath.Base(out)
+               f, err := parse(base, data)
+               if err != nil {
+                       t.Errorf("parsing original: %v", err)
+                       continue
+               }
+
+               ndata := Format(f)
+               f2, err := parse(base, ndata)
+               if err != nil {
+                       t.Errorf("parsing reformatted: %v", err)
+                       continue
+               }
+
+               eq := eqchecker{file: base}
+               if err := eq.check(f, f2); err != nil {
+                       t.Errorf("not equal (parse/Format/parse): %v", err)
+               }
+
+               pf1, err := Parse(base, data, nil)
+               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)
+                       if err != nil {
+                               t.Errorf("Parsing reformatted: %v", err)
+                               continue
+                       }
+                       eq := eqchecker{file: base}
+                       if err := eq.check(pf1, pf2); err != nil {
+                               t.Errorf("not equal (parse/Format/Parse): %v", err)
+                       }
+
+                       ndata2, err := pf1.Format()
+                       if err != nil {
+                               t.Errorf("reformat: %v", err)
+                       }
+                       pf3, err := Parse(base, ndata2, nil)
+                       if err != nil {
+                               t.Errorf("Parsing reformatted2: %v", err)
+                               continue
+                       }
+                       eq = eqchecker{file: base}
+                       if err := eq.check(pf1, pf3); err != nil {
+                               t.Errorf("not equal (Parse/Format/Parse): %v", err)
+                       }
+                       ndata = ndata2
+               }
+
+               if strings.HasSuffix(out, ".in") {
+                       golden, err := ioutil.ReadFile(strings.TrimSuffix(out, ".in") + ".golden")
+                       if err != nil {
+                               t.Error(err)
+                               continue
+                       }
+                       if !bytes.Equal(ndata, golden) {
+                               t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base)
+                               tdiff(t, string(golden), string(ndata))
+                               return
+                       }
+               }
+       }
+}
+
+// An eqchecker holds state for checking the equality of two parse trees.
+type eqchecker struct {
+       file string
+       pos  Position
+}
+
+// errorf returns an error described by the printf-style format and arguments,
+// inserting the current file position before the error text.
+func (eq *eqchecker) errorf(format string, args ...interface{}) error {
+       return fmt.Errorf("%s:%d: %s", eq.file, eq.pos.Line,
+               fmt.Sprintf(format, args...))
+}
+
+// check checks that v and w represent the same parse tree.
+// If not, it returns an error describing the first difference.
+func (eq *eqchecker) check(v, w interface{}) error {
+       return eq.checkValue(reflect.ValueOf(v), reflect.ValueOf(w))
+}
+
+var (
+       posType      = reflect.TypeOf(Position{})
+       commentsType = reflect.TypeOf(Comments{})
+)
+
+// checkValue checks that v and w represent the same parse tree.
+// If not, it returns an error describing the first difference.
+func (eq *eqchecker) checkValue(v, w reflect.Value) error {
+       // inner returns the innermost expression for v.
+       // if v is a non-nil interface value, it returns the concrete
+       // value in the interface.
+       inner := func(v reflect.Value) reflect.Value {
+               for {
+                       if v.Kind() == reflect.Interface && !v.IsNil() {
+                               v = v.Elem()
+                               continue
+                       }
+                       break
+               }
+               return v
+       }
+
+       v = inner(v)
+       w = inner(w)
+       if v.Kind() == reflect.Invalid && w.Kind() == reflect.Invalid {
+               return nil
+       }
+       if v.Kind() == reflect.Invalid {
+               return eq.errorf("nil interface became %s", w.Type())
+       }
+       if w.Kind() == reflect.Invalid {
+               return eq.errorf("%s became nil interface", v.Type())
+       }
+
+       if v.Type() != w.Type() {
+               return eq.errorf("%s became %s", v.Type(), w.Type())
+       }
+
+       if p, ok := v.Interface().(Expr); ok {
+               eq.pos, _ = p.Span()
+       }
+
+       switch v.Kind() {
+       default:
+               return eq.errorf("unexpected type %s", v.Type())
+
+       case reflect.Bool, reflect.Int, reflect.String:
+               vi := v.Interface()
+               wi := w.Interface()
+               if vi != wi {
+                       return eq.errorf("%v became %v", vi, wi)
+               }
+
+       case reflect.Slice:
+               vl := v.Len()
+               wl := w.Len()
+               for i := 0; i < vl || i < wl; i++ {
+                       if i >= vl {
+                               return eq.errorf("unexpected %s", w.Index(i).Type())
+                       }
+                       if i >= wl {
+                               return eq.errorf("missing %s", v.Index(i).Type())
+                       }
+                       if err := eq.checkValue(v.Index(i), w.Index(i)); err != nil {
+                               return err
+                       }
+               }
+
+       case reflect.Struct:
+               // Fields in struct must match.
+               t := v.Type()
+               n := t.NumField()
+               for i := 0; i < n; i++ {
+                       tf := t.Field(i)
+                       switch {
+                       default:
+                               if err := eq.checkValue(v.Field(i), w.Field(i)); err != nil {
+                                       return err
+                               }
+
+                       case tf.Type == posType: // ignore positions
+                       case tf.Type == commentsType: // ignore comment assignment
+                       }
+               }
+
+       case reflect.Ptr, reflect.Interface:
+               if v.IsNil() != w.IsNil() {
+                       if v.IsNil() {
+                               return eq.errorf("unexpected %s", w.Elem().Type())
+                       }
+                       return eq.errorf("missing %s", v.Elem().Type())
+               }
+               if err := eq.checkValue(v.Elem(), w.Elem()); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// diff returns the output of running diff on b1 and b2.
+func diff(b1, b2 []byte) (data []byte, err error) {
+       f1, err := ioutil.TempFile("", "testdiff")
+       if err != nil {
+               return nil, err
+       }
+       defer os.Remove(f1.Name())
+       defer f1.Close()
+
+       f2, err := ioutil.TempFile("", "testdiff")
+       if err != nil {
+               return nil, err
+       }
+       defer os.Remove(f2.Name())
+       defer f2.Close()
+
+       f1.Write(b1)
+       f2.Write(b2)
+
+       data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
+       if len(data) > 0 {
+               // diff exits with a non-zero status when the files don't match.
+               // Ignore that failure as long as we get output.
+               err = nil
+       }
+       return
+}
+
+// tdiff logs the diff output to t.Error.
+func tdiff(t *testing.T, a, b string) {
+       data, err := diff([]byte(a), []byte(b))
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       t.Error(string(data))
+}
diff --git a/src/cmd/go/internal/modfile/rule.go b/src/cmd/go/internal/modfile/rule.go
new file mode 100644 (file)
index 0000000..5a784a3
--- /dev/null
@@ -0,0 +1,507 @@
+// 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.
+
+package modfile
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+       "path/filepath"
+       "sort"
+       "strconv"
+       "strings"
+       "unicode"
+
+       "cmd/go/internal/module"
+       "cmd/go/internal/semver"
+)
+
+type File struct {
+       Module  *Module
+       Require []*Require
+       Exclude []*Exclude
+       Replace []*Replace
+
+       Syntax *FileSyntax
+}
+
+type Module struct {
+       Mod   module.Version
+       Major string
+}
+
+type Require struct {
+       Mod    module.Version
+       Syntax *Line
+}
+
+type Exclude struct {
+       Mod    module.Version
+       Syntax *Line
+}
+
+type Replace struct {
+       Old module.Version
+       New module.Version
+
+       Syntax *Line
+}
+
+func (f *File) AddModuleStmt(path string) {
+       f.Module = &Module{
+               Mod: module.Version{Path: path},
+       }
+       if f.Syntax == nil {
+               f.Syntax = new(FileSyntax)
+       }
+       f.Syntax.Stmt = append(f.Syntax.Stmt, &Line{
+               Token: []string{"module", AutoQuote(path)},
+       })
+}
+
+func (f *File) AddComment(text string) {
+       if f.Syntax == nil {
+               f.Syntax = new(FileSyntax)
+       }
+       f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{
+               Comments: Comments{
+                       Before: []Comment{
+                               {
+                                       Token: text,
+                               },
+                       },
+               },
+       })
+}
+
+type VersionFixer func(path, version string) (string, error)
+
+func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
+       fs, err := parse(file, data)
+       if err != nil {
+               return nil, err
+       }
+       f := &File{
+               Syntax: fs,
+       }
+
+       var errs bytes.Buffer
+       for _, x := range fs.Stmt {
+               switch x := x.(type) {
+               case *Line:
+                       f.add(&errs, x, x.Token[0], x.Token[1:], fix)
+
+               case *LineBlock:
+                       if len(x.Token) > 1 {
+                               fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
+                               continue
+                       }
+                       switch x.Token[0] {
+                       default:
+                               fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
+                               continue
+                       case "module", "require", "exclude", "replace":
+                               for _, l := range x.Line {
+                                       f.add(&errs, l, x.Token[0], l.Token, fix)
+                               }
+                       }
+               }
+       }
+
+       if errs.Len() > 0 {
+               return nil, errors.New(strings.TrimRight(errs.String(), "\n"))
+       }
+       return f, nil
+}
+
+func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer) {
+       // TODO: We should pass in a flag saying whether this module is a dependency.
+       // If so, we should ignore all unknown directives and not attempt to parse
+       // replace and exclude either. They don't matter, and it will work better for
+       // forward compatibility if we can depend on modules that have local changes.
+
+       // TODO: For the target module (not dependencies), maybe we should
+       // relax the semver requirement and rewrite the file with updated info
+       // after resolving any versions. That would let people type commit hashes
+       // or tags or branch names, and then vgo would fix them.
+
+       switch verb {
+       default:
+               fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb)
+       case "module":
+               if f.Module != nil {
+                       fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)
+                       return
+               }
+               f.Module = new(Module)
+               if len(args) != 1 {
+
+                       fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line)
+                       return
+               }
+               s, err := parseString(&args[0])
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+                       return
+               }
+               f.Module.Mod = module.Version{Path: s}
+       case "require", "exclude":
+               if len(args) != 2 {
+                       fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3\n", f.Syntax.Name, line.Start.Line, verb)
+                       return
+               }
+               s, err := parseString(&args[0])
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+                       return
+               }
+               old := args[1]
+               v, err := parseVersion(s, &args[1], fix)
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: invalid module version %q: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+                       return
+               }
+               v1, err := moduleMajorVersion(s)
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
+                       return
+               }
+               if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
+                       fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
+                       return
+               }
+               if verb == "require" {
+                       f.Require = append(f.Require, &Require{
+                               Mod:    module.Version{Path: s, Version: v},
+                               Syntax: line,
+                       })
+               } else {
+                       f.Exclude = append(f.Exclude, &Exclude{
+                               Mod:    module.Version{Path: s, Version: v},
+                               Syntax: line,
+                       })
+               }
+       case "replace":
+               if len(args) < 4 || len(args) > 5 || args[2] != "=>" {
+                       fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3 => other/module v1.4\n\t or %s module/path v1.2.3 => ../local/directory", f.Syntax.Name, line.Start.Line, verb, verb)
+                       return
+               }
+               s, err := parseString(&args[0])
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+                       return
+               }
+               old := args[1]
+               v, err := parseVersion(s, &args[1], fix)
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+                       return
+               }
+               v1, err := moduleMajorVersion(s)
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
+                       return
+               }
+               if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
+                       fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
+                       return
+               }
+               ns, err := parseString(&args[3])
+               if err != nil {
+                       fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+                       return
+               }
+               nv := ""
+               if len(args) == 4 {
+                       if !isDirectoryPath(ns) {
+                               fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)", f.Syntax.Name, line.Start.Line)
+                               return
+                       }
+                       if filepath.Separator == '/' && strings.Contains(ns, `\`) {
+                               fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)", f.Syntax.Name, line.Start.Line)
+                               return
+                       }
+               }
+               if len(args) == 5 {
+                       old := args[4]
+                       nv, err = parseVersion(ns, &args[4], fix)
+                       if err != nil {
+                               fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+                               return
+                       }
+                       if isDirectoryPath(ns) {
+                               fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version", f.Syntax.Name, line.Start.Line, ns)
+                               return
+                       }
+               }
+               // TODO: More sanity checks about directories vs module paths.
+               f.Replace = append(f.Replace, &Replace{
+                       Old:    module.Version{Path: s, Version: v},
+                       New:    module.Version{Path: ns, Version: nv},
+                       Syntax: line,
+               })
+       }
+}
+
+func isDirectoryPath(ns string) bool {
+       // Because go.mod files can move from one system to another,
+       // we check all known path syntaxes, both Unix and Windows.
+       return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") ||
+               strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) ||
+               len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
+}
+
+func mustQuote(t string) bool {
+       for _, r := range t {
+               if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' {
+                       return true
+               }
+       }
+       return t == "" || strings.Contains(t, "//") || strings.Contains(t, "/*")
+}
+
+// AutoQuote returns s or, if quoting is required for s to appear in a go.mod,
+// the quotation of s.
+func AutoQuote(s string) string {
+       if mustQuote(s) {
+               return strconv.Quote(s)
+       }
+       return s
+}
+
+func parseString(s *string) (string, error) {
+       t := *s
+       if strings.HasPrefix(t, `"`) {
+               var err error
+               if t, err = strconv.Unquote(t); err != nil {
+                       return "", err
+               }
+       } else if strings.ContainsAny(t, "\"'`") {
+               // Other quotes are reserved both for possible future expansion
+               // and to avoid confusion. For example if someone types 'x'
+               // we want that to be a syntax error and not a literal x in literal quotation marks.
+               return "", fmt.Errorf("unquoted string cannot contain quote")
+       }
+       *s = AutoQuote(t)
+       return t, nil
+}
+
+func parseVersion(path string, s *string, fix VersionFixer) (string, error) {
+       t, err := parseString(s)
+       if err != nil {
+               return "", err
+       }
+       if fix != nil {
+               var err error
+               t, err = fix(path, t)
+               if err != nil {
+                       return "", err
+               }
+       }
+       if semver.IsValid(t) {
+               *s = semver.Canonical(t)
+               return *s, nil
+       }
+       return "", fmt.Errorf("version must be of the form v1.2.3")
+}
+
+func moduleMajorVersion(p string) (string, error) {
+       if _, _, major, _, ok := ParseGopkgIn(p); ok {
+               return major, nil
+       }
+
+       start := strings.LastIndex(p, "/") + 1
+       v := p[start:]
+       if !isMajorVersion(v) {
+               return "v1", nil
+       }
+       if v[1] == '0' || v == "v1" {
+               return "", fmt.Errorf("module path has invalid version number %s", v)
+       }
+       return v, nil
+}
+
+func isMajorVersion(v string) bool {
+       if len(v) < 2 || v[0] != 'v' {
+               return false
+       }
+       for i := 1; i < len(v); i++ {
+               if v[i] < '0' || '9' < v[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (f *File) Format() ([]byte, error) {
+       return Format(f.Syntax), nil
+}
+
+func (x *File) AddRequire(path, vers string) {
+       var syntax *Line
+
+       for i, stmt := range x.Syntax.Stmt {
+               switch stmt := stmt.(type) {
+               case *LineBlock:
+                       if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+                               syntax = &Line{Token: []string{AutoQuote(path), vers}}
+                               stmt.Line = append(stmt.Line, syntax)
+                               goto End
+                       }
+               case *Line:
+                       if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+                               stmt.Token = stmt.Token[1:]
+                               syntax = &Line{Token: []string{AutoQuote(path), vers}}
+                               x.Syntax.Stmt[i] = &LineBlock{
+                                       Comments: stmt.Comments,
+                                       Token:    []string{"require"},
+                                       Line: []*Line{
+                                               stmt,
+                                               syntax,
+                                       },
+                               }
+                               goto End
+                       }
+               }
+       }
+
+       syntax = &Line{Token: []string{"require", AutoQuote(path), vers}}
+       x.Syntax.Stmt = append(x.Syntax.Stmt, syntax)
+
+End:
+       x.Require = append(x.Require, &Require{module.Version{Path: path, Version: vers}, syntax})
+}
+
+func (f *File) SetRequire(req []module.Version) {
+       need := make(map[string]string)
+       for _, m := range req {
+               need[m.Path] = m.Version
+       }
+
+       for _, r := range f.Require {
+               if v, ok := need[r.Mod.Path]; ok {
+                       r.Mod.Version = v
+               }
+       }
+
+       var newStmts []Expr
+       for _, stmt := range f.Syntax.Stmt {
+               switch stmt := stmt.(type) {
+               case *LineBlock:
+                       if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+                               var newLines []*Line
+                               for _, line := range stmt.Line {
+                                       if p, err := strconv.Unquote(line.Token[0]); err == nil && need[p] != "" {
+                                               line.Token[1] = need[p]
+                                               delete(need, p)
+                                               newLines = append(newLines, line)
+                                       }
+                               }
+                               if len(newLines) == 0 {
+                                       continue // drop stmt
+                               }
+                               stmt.Line = newLines
+                       }
+
+               case *Line:
+                       if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+                               if p, err := strconv.Unquote(stmt.Token[1]); err == nil && need[p] != "" {
+                                       stmt.Token[2] = need[p]
+                                       delete(need, p)
+                               } else {
+                                       continue // drop stmt
+                               }
+                       }
+               }
+               newStmts = append(newStmts, stmt)
+       }
+       f.Syntax.Stmt = newStmts
+
+       for path, vers := range need {
+               f.AddRequire(path, vers)
+       }
+       f.SortBlocks()
+}
+
+func (f *File) SortBlocks() {
+       f.removeDups() // otherwise sorting is unsafe
+
+       for _, stmt := range f.Syntax.Stmt {
+               block, ok := stmt.(*LineBlock)
+               if !ok {
+                       continue
+               }
+               sort.Slice(block.Line, func(i, j int) bool {
+                       li := block.Line[i]
+                       lj := block.Line[j]
+                       for k := 0; k < len(li.Token) && k < len(lj.Token); k++ {
+                               if li.Token[k] != lj.Token[k] {
+                                       return li.Token[k] < lj.Token[k]
+                               }
+                       }
+                       return len(li.Token) < len(lj.Token)
+               })
+       }
+}
+
+func (f *File) removeDups() {
+       have := make(map[module.Version]bool)
+       kill := make(map[*Line]bool)
+       for _, x := range f.Exclude {
+               if have[x.Mod] {
+                       kill[x.Syntax] = true
+                       continue
+               }
+               have[x.Mod] = true
+       }
+       var excl []*Exclude
+       for _, x := range f.Exclude {
+               if !kill[x.Syntax] {
+                       excl = append(excl, x)
+               }
+       }
+       f.Exclude = excl
+
+       have = make(map[module.Version]bool)
+       // Later replacements take priority over earlier ones.
+       for i := len(f.Replace) - 1; i >= 0; i-- {
+               x := f.Replace[i]
+               if have[x.Old] {
+                       kill[x.Syntax] = true
+                       continue
+               }
+               have[x.Old] = true
+       }
+       var repl []*Replace
+       for _, x := range f.Replace {
+               if !kill[x.Syntax] {
+                       repl = append(repl, x)
+               }
+       }
+       f.Replace = repl
+
+       var stmts []Expr
+       for _, stmt := range f.Syntax.Stmt {
+               switch stmt := stmt.(type) {
+               case *Line:
+                       if kill[stmt] {
+                               continue
+                       }
+               case *LineBlock:
+                       var lines []*Line
+                       for _, line := range stmt.Line {
+                               if !kill[line] {
+                                       lines = append(lines, line)
+                               }
+                       }
+                       stmt.Line = lines
+                       if len(lines) == 0 {
+                               continue
+                       }
+               }
+               stmts = append(stmts, stmt)
+       }
+       f.Syntax.Stmt = stmts
+}
diff --git a/src/cmd/go/internal/modfile/testdata/block.golden b/src/cmd/go/internal/modfile/testdata/block.golden
new file mode 100644 (file)
index 0000000..4aa2d63
--- /dev/null
@@ -0,0 +1,29 @@
+// comment
+x "y" z
+
+// block
+block ( // block-eol
+       // x-before-line
+
+       "x" ( y // x-eol
+       "x1"
+       "x2"
+       // line
+       "x3"
+       "x4"
+
+       "x5"
+
+       // y-line
+       "y" //  y-eol
+
+       "z" // z-eol
+) // block-eol2
+
+block2 (
+       x
+       y
+       z
+)
+
+// eof
diff --git a/src/cmd/go/internal/modfile/testdata/block.in b/src/cmd/go/internal/modfile/testdata/block.in
new file mode 100644 (file)
index 0000000..1dfae65
--- /dev/null
@@ -0,0 +1,29 @@
+// comment
+x "y" z
+
+// block
+block ( // block-eol
+       // x-before-line
+       
+       "x" ( y // x-eol
+       "x1"
+       "x2"
+       // line
+       "x3"
+       "x4"
+       
+       "x5"
+       
+       // y-line
+       "y" //  y-eol
+       
+       "z" // z-eol
+) // block-eol2
+
+
+block2 (x
+       y
+       z
+)
+
+// eof
diff --git a/src/cmd/go/internal/modfile/testdata/comment.golden b/src/cmd/go/internal/modfile/testdata/comment.golden
new file mode 100644 (file)
index 0000000..75f3b84
--- /dev/null
@@ -0,0 +1,10 @@
+// comment
+module "x" // eol
+
+// mid comment
+
+// comment 2
+// comment 2 line 2
+module "y" // eoy
+
+// comment 3
diff --git a/src/cmd/go/internal/modfile/testdata/comment.in b/src/cmd/go/internal/modfile/testdata/comment.in
new file mode 100644 (file)
index 0000000..bfc2492
--- /dev/null
@@ -0,0 +1,8 @@
+// comment
+module "x" // eol
+// mid comment
+
+// comment 2
+// comment 2 line 2
+module "y" // eoy
+// comment 3
diff --git a/src/cmd/go/internal/modfile/testdata/empty.golden b/src/cmd/go/internal/modfile/testdata/empty.golden
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/cmd/go/internal/modfile/testdata/empty.in b/src/cmd/go/internal/modfile/testdata/empty.in
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/cmd/go/internal/modfile/testdata/gopkg.in.golden b/src/cmd/go/internal/modfile/testdata/gopkg.in.golden
new file mode 100644 (file)
index 0000000..41669b3
--- /dev/null
@@ -0,0 +1,6 @@
+module x
+
+require (
+       gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528
+       gopkg.in/yaml.v2 v2.2.1
+)
diff --git a/src/cmd/go/internal/modfile/testdata/module.golden b/src/cmd/go/internal/modfile/testdata/module.golden
new file mode 100644 (file)
index 0000000..78ba943
--- /dev/null
@@ -0,0 +1 @@
+module abc
diff --git a/src/cmd/go/internal/modfile/testdata/module.in b/src/cmd/go/internal/modfile/testdata/module.in
new file mode 100644 (file)
index 0000000..08f3836
--- /dev/null
@@ -0,0 +1 @@
+module "abc"
diff --git a/src/cmd/go/internal/modfile/testdata/replace.golden b/src/cmd/go/internal/modfile/testdata/replace.golden
new file mode 100644 (file)
index 0000000..5d6abcf
--- /dev/null
@@ -0,0 +1,5 @@
+module abc
+
+replace xyz v1.2.3 => /tmp/z
+
+replace xyz v1.3.4 => my/xyz v1.3.4-me
diff --git a/src/cmd/go/internal/modfile/testdata/replace.in b/src/cmd/go/internal/modfile/testdata/replace.in
new file mode 100644 (file)
index 0000000..6852499
--- /dev/null
@@ -0,0 +1,5 @@
+module "abc"
+
+replace "xyz" v1.2.3 => "/tmp/z"
+
+replace "xyz" v1.3.4 => "my/xyz" v1.3.4-me
diff --git a/src/cmd/go/internal/modfile/testdata/replace2.golden b/src/cmd/go/internal/modfile/testdata/replace2.golden
new file mode 100644 (file)
index 0000000..1d18a3b
--- /dev/null
@@ -0,0 +1,8 @@
+module abc
+
+replace (
+       xyz v1.2.3 => /tmp/z
+       xyz v1.3.4 => my/xyz v1.3.4-me
+       xyz v1.4.5 => "/tmp/my dir"
+       xyz v1.5.6 => my/xyz v1.5.6
+)
diff --git a/src/cmd/go/internal/modfile/testdata/replace2.in b/src/cmd/go/internal/modfile/testdata/replace2.in
new file mode 100644 (file)
index 0000000..78c4669
--- /dev/null
@@ -0,0 +1,8 @@
+module "abc"
+
+replace (
+       "xyz" v1.2.3 => "/tmp/z"
+       "xyz" v1.3.4 => "my/xyz" "v1.3.4-me"
+       xyz "v1.4.5" => "/tmp/my dir"
+       xyz v1.5.6 => my/xyz v1.5.6
+)
diff --git a/src/cmd/go/internal/modfile/testdata/rule1.golden b/src/cmd/go/internal/modfile/testdata/rule1.golden
new file mode 100644 (file)
index 0000000..8a5c725
--- /dev/null
@@ -0,0 +1,7 @@
+module "x"
+
+module "y"
+
+require "x"
+
+require x
diff --git a/src/cmd/go/internal/modinfo/info.go b/src/cmd/go/internal/modinfo/info.go
new file mode 100644 (file)
index 0000000..6dff2d2
--- /dev/null
@@ -0,0 +1,11 @@
+// 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.
+
+package modinfo
+
+type ModulePublic struct {
+       Top     bool
+       Path    string
+       Version string
+}
diff --git a/src/cmd/go/internal/module/module.go b/src/cmd/go/internal/module/module.go
new file mode 100644 (file)
index 0000000..1e8f74c
--- /dev/null
@@ -0,0 +1,195 @@
+// 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.
+
+// Package module defines the module.Version type
+// along with support code.
+package module
+
+import (
+       "fmt"
+       "strings"
+       "unicode"
+       "unicode/utf8"
+
+       "cmd/go/internal/semver"
+)
+
+// A Version is defined by a module path and version pair.
+type Version struct {
+       Path    string
+       Version string
+}
+
+// Check checks that a given module path, version pair is valid.
+// In addition to the path being a valid module path
+// and the version being a valid semantic version,
+// the two must correspond.
+// For example, the path "yaml/v2" only corresponds to
+// semantic versions beginning with "v2.".
+func Check(path, version string) error {
+       if err := CheckPath(path); err != nil {
+               return err
+       }
+       if !semver.IsValid(version) {
+               return fmt.Errorf("malformed semantic version %v", version)
+       }
+       vm := semver.Major(version)
+       _, pathVersion, _ := SplitPathVersion(path)
+
+       if strings.HasPrefix(pathVersion, ".") {
+               // Special-case gopkg.in path requirements.
+               pathVersion = pathVersion[1:] // cut .
+               if vm == pathVersion {
+                       return nil
+               }
+       } else {
+               // Standard path requirements.
+               if pathVersion != "" {
+                       pathVersion = pathVersion[1:] // cut /
+               }
+               if vm == "v0" || vm == "v1" {
+                       vm = ""
+               }
+               if vm == pathVersion {
+                       return nil
+               }
+               if pathVersion == "" {
+                       pathVersion = "v0 or v1"
+               }
+       }
+       return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathVersion)
+}
+
+// firstPathOK reports whether r can appear in the first element of a module path.
+// The first element of the path must be an LDH domain name, at least for now.
+func firstPathOK(r rune) bool {
+       return r == '-' || r == '.' ||
+               '0' <= r && r <= '9' ||
+               'A' <= r && r <= 'Z' ||
+               'a' <= r && r <= 'z'
+}
+
+// pathOK reports whether r can appear in a module path.
+// Paths must avoid potentially problematic ASCII punctuation
+// and control characters but otherwise can be any Unicode printable character,
+// as defined by Go's IsPrint.
+func pathOK(r rune) bool {
+       if r < utf8.RuneSelf {
+               return r == '+' || r == ',' || r == '-' || r == '.' || r == '/' || r == '_' || r == '~' ||
+                       '0' <= r && r <= '9' ||
+                       'A' <= r && r <= 'Z' ||
+                       'a' <= r && r <= 'z'
+       }
+       return unicode.IsPrint(r)
+}
+
+// CheckPath checks that a module path is valid.
+func CheckPath(path string) error {
+       if !utf8.ValidString(path) {
+               return fmt.Errorf("malformed module path %q: invalid UTF-8", path)
+       }
+       if path == "" {
+               return fmt.Errorf("malformed module path %q: empty string", path)
+       }
+
+       i := strings.Index(path, "/")
+       if i < 0 {
+               i = len(path)
+       }
+       if i == 0 {
+               return fmt.Errorf("malformed module path %q: leading slash", path)
+       }
+       if !strings.Contains(path[:i], ".") {
+               return fmt.Errorf("malformed module path %q: missing dot in first path element", path)
+       }
+       if path[i-1] == '.' {
+               return fmt.Errorf("malformed module path %q: trailing dot in first path element", path)
+       }
+       if path[0] == '.' {
+               return fmt.Errorf("malformed module path %q: leading dot in first path element", path)
+       }
+       if path[0] == '-' {
+               return fmt.Errorf("malformed module path %q: leading dash in first path element", path)
+       }
+       if strings.Contains(path, "..") {
+               return fmt.Errorf("malformed module path %q: double dot", path)
+       }
+       if strings.Contains(path, "//") {
+               return fmt.Errorf("malformed module path %q: double slash", path)
+       }
+       for _, r := range path[:i] {
+               if !firstPathOK(r) {
+                       return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r)
+               }
+       }
+       if path[len(path)-1] == '/' {
+               return fmt.Errorf("malformed module path %q: trailing slash", path)
+       }
+       for _, r := range path {
+               if !pathOK(r) {
+                       return fmt.Errorf("malformed module path %q: invalid char %q", path, r)
+               }
+       }
+       if _, _, ok := SplitPathVersion(path); !ok {
+               return fmt.Errorf("malformed module path %q: invalid version %s", path, path[strings.LastIndex(path, "/")+1:])
+       }
+       return nil
+}
+
+// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path
+// and version is either empty or "/vN" for N >= 2.
+// As a special case, gopkg.in paths are recognized directly;
+// they require ".vN" instead of "/vN", and for all N, not just N >= 2.
+func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) {
+       if strings.HasPrefix(path, "gopkg.in/") {
+               return splitGopkgIn(path)
+       }
+
+       i := len(path)
+       dot := false
+       for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
+               if path[i-1] == '.' {
+                       dot = true
+               }
+               i--
+       }
+       if i <= 1 || path[i-1] != 'v' || path[i-2] != '/' {
+               return path, "", true
+       }
+       prefix, pathMajor = path[:i-2], path[i-2:]
+       if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" {
+               return path, "", false
+       }
+       return prefix, pathMajor, true
+}
+
+// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths.
+func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) {
+       if !strings.HasPrefix(path, "gopkg.in/") {
+               return path, "", false
+       }
+       i := len(path)
+       for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') {
+               i--
+       }
+       if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' {
+               // All gopkg.in paths must end in vN for some N.
+               return path, "", false
+       }
+       prefix, pathMajor = path[:i-2], path[i-2:]
+       if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" {
+               return path, "", false
+       }
+       return prefix, pathMajor, true
+}
+
+// MatchPathMajor reports whether the semantic version v
+// matches the path major version pathMajor.
+func MatchPathMajor(v, pathMajor string) bool {
+       m := semver.Major(v)
+       if pathMajor == "" {
+               return m == "v0" || m == "v1"
+       }
+       return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:]
+}
diff --git a/src/cmd/go/internal/module/module_test.go b/src/cmd/go/internal/module/module_test.go
new file mode 100644 (file)
index 0000000..6142a9e
--- /dev/null
@@ -0,0 +1,184 @@
+// 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.
+
+package module
+
+import "testing"
+
+var checkTests = []struct {
+       path    string
+       version string
+       ok      bool
+}{
+       {"rsc.io/quote", "0.1.0", false},
+       {"rsc io/quote", "v1.0.0", false},
+
+       {"github.com/go-yaml/yaml", "v0.8.0", true},
+       {"github.com/go-yaml/yaml", "v1.0.0", true},
+       {"github.com/go-yaml/yaml", "v2.0.0", false},
+       {"github.com/go-yaml/yaml", "v2.1.5", false},
+       {"github.com/go-yaml/yaml", "v3.0.0", false},
+
+       {"github.com/go-yaml/yaml/v2", "v1.0.0", false},
+       {"github.com/go-yaml/yaml/v2", "v2.0.0", true},
+       {"github.com/go-yaml/yaml/v2", "v2.1.5", true},
+       {"github.com/go-yaml/yaml/v2", "v3.0.0", false},
+
+       {"gopkg.in/yaml.v0", "v0.8.0", true},
+       {"gopkg.in/yaml.v0", "v1.0.0", false},
+       {"gopkg.in/yaml.v0", "v2.0.0", false},
+       {"gopkg.in/yaml.v0", "v2.1.5", false},
+       {"gopkg.in/yaml.v0", "v3.0.0", false},
+
+       {"gopkg.in/yaml.v1", "v0.8.0", false},
+       {"gopkg.in/yaml.v1", "v1.0.0", true},
+       {"gopkg.in/yaml.v1", "v2.0.0", false},
+       {"gopkg.in/yaml.v1", "v2.1.5", false},
+       {"gopkg.in/yaml.v1", "v3.0.0", false},
+
+       {"gopkg.in/yaml.v2", "v1.0.0", false},
+       {"gopkg.in/yaml.v2", "v2.0.0", true},
+       {"gopkg.in/yaml.v2", "v2.1.5", true},
+       {"gopkg.in/yaml.v2", "v3.0.0", false},
+}
+
+func TestCheck(t *testing.T) {
+       for _, tt := range checkTests {
+               err := Check(tt.path, tt.version)
+               if tt.ok && err != nil {
+                       t.Errorf("Check(%q, %q) = %v, wanted nil error", tt.path, tt.version, err)
+               } else if !tt.ok && err == nil {
+                       t.Errorf("Check(%q, %q) succeeded, wanted error", tt.path, tt.version)
+               }
+       }
+}
+
+var checkPathTests = []struct {
+       path string
+       ok   bool
+}{
+       {"x.y/z", true},
+       {"x.y", true},
+
+       {"", false},
+       {"x.y/\xFFz", false},
+       {"/x.y/z", false},
+       {"x./z", false},
+       {".x/z", false},
+       {"-x/z", false},
+       {"x..y/z", false},
+       {"x.y/z/../../w", false},
+       {"x.y//z", false},
+       {"x.y/z//w", false},
+       {"x.y/z/", false},
+
+       {"x.y/z/v0", false},
+       {"x.y/z/v1", false},
+       {"x.y/z/v2", true},
+       {"x.y/z/v2.0", false},
+
+       {"!x.y/z", false},
+       {"_x.y/z", false},
+       {"x.y!/z", false},
+       {"x.y\"/z", false},
+       {"x.y#/z", false},
+       {"x.y$/z", false},
+       {"x.y%/z", false},
+       {"x.y&/z", false},
+       {"x.y'/z", false},
+       {"x.y(/z", false},
+       {"x.y)/z", false},
+       {"x.y*/z", false},
+       {"x.y+/z", false},
+       {"x.y,/z", false},
+       {"x.y-/z", true},
+       {"x.y./zt", false},
+       {"x.y:/z", false},
+       {"x.y;/z", false},
+       {"x.y</z", false},
+       {"x.y=/z", false},
+       {"x.y>/z", false},
+       {"x.y?/z", false},
+       {"x.y@/z", false},
+       {"x.y[/z", false},
+       {"x.y\\/z", false},
+       {"x.y]/z", false},
+       {"x.y^/z", false},
+       {"x.y_/z", false},
+       {"x.y`/z", false},
+       {"x.y{/z", false},
+       {"x.y}/z", false},
+       {"x.y~/z", false},
+       {"x.y/z!", false},
+       {"x.y/z\"", false},
+       {"x.y/z#", false},
+       {"x.y/z$", false},
+       {"x.y/z%", false},
+       {"x.y/z&", false},
+       {"x.y/z'", false},
+       {"x.y/z(", false},
+       {"x.y/z)", false},
+       {"x.y/z*", false},
+       {"x.y/z+", true},
+       {"x.y/z,", true},
+       {"x.y/z-", true},
+       {"x.y/z.t", true},
+       {"x.y/z/t", true},
+       {"x.y/z:", false},
+       {"x.y/z;", false},
+       {"x.y/z<", false},
+       {"x.y/z=", false},
+       {"x.y/z>", false},
+       {"x.y/z?", false},
+       {"x.y/z@", false},
+       {"x.y/z[", false},
+       {"x.y/z\\", false},
+       {"x.y/z]", false},
+       {"x.y/z^", false},
+       {"x.y/z_", true},
+       {"x.y/z`", false},
+       {"x.y/z{", false},
+       {"x.y/z}", false},
+       {"x.y/z~", true},
+}
+
+func TestCheckPath(t *testing.T) {
+       for _, tt := range checkPathTests {
+               err := CheckPath(tt.path)
+               if tt.ok && err != nil {
+                       t.Errorf("CheckPath(%q) = %v, wanted nil error", tt.path, err)
+               } else if !tt.ok && err == nil {
+                       t.Errorf("CheckPath(%q) succeeded, wanted error", tt.path)
+               }
+       }
+}
+
+var splitPathVersionTests = []struct {
+       pathPrefix string
+       version    string
+}{
+       {"x.y/z", ""},
+       {"x.y/z", "/v2"},
+       {"x.y/z", "/v3"},
+       {"gopkg.in/yaml", ".v0"},
+       {"gopkg.in/yaml", ".v1"},
+       {"gopkg.in/yaml", ".v2"},
+       {"gopkg.in/yaml", ".v3"},
+}
+
+func TestSplitPathVersion(t *testing.T) {
+       for _, tt := range splitPathVersionTests {
+               pathPrefix, version, ok := SplitPathVersion(tt.pathPrefix + tt.version)
+               if pathPrefix != tt.pathPrefix || version != tt.version || !ok {
+                       t.Errorf("SplitPathVersion(%q) = %q, %q, %v, want %q, %q, true", tt.pathPrefix+tt.version, pathPrefix, version, ok, tt.pathPrefix, tt.version)
+               }
+       }
+
+       for _, tt := range checkPathTests {
+               pathPrefix, version, ok := SplitPathVersion(tt.path)
+               if pathPrefix+version != tt.path {
+                       t.Errorf("SplitPathVersion(%q) = %q, %q, %v, doesn't add to input", tt.path, pathPrefix, version, ok)
+               }
+       }
+}
diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go
new file mode 100644 (file)
index 0000000..47670ff
--- /dev/null
@@ -0,0 +1,308 @@
+// 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.
+
+// Package mvs implements Minimal Version Selection.
+// See https://research.swtch.com/vgo-mvs.
+package mvs
+
+import (
+       "fmt"
+       "sort"
+
+       "cmd/go/internal/module"
+)
+
+type Reqs interface {
+       Required(m module.Version) ([]module.Version, error)
+       Max(v1, v2 string) string
+       Latest(path string) (module.Version, error)
+       Previous(m module.Version) (module.Version, error)
+}
+
+type MissingModuleError struct {
+       Module module.Version
+}
+
+func (e *MissingModuleError) Error() string {
+       return fmt.Sprintf("missing module: %v", e.Module)
+}
+
+// BuildList returns the build list for the target module.
+func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) {
+       return buildList(target, reqs, nil, nil)
+}
+
+func buildList(target module.Version, reqs Reqs, uses map[module.Version][]module.Version, vers map[string]string) ([]module.Version, error) {
+       var (
+               min  = map[string]string{target.Path: target.Version}
+               todo = []module.Version{target}
+               seen = map[module.Version]bool{target: true}
+       )
+       for len(todo) > 0 {
+               m := todo[len(todo)-1]
+               todo = todo[:len(todo)-1]
+               required, _ := reqs.Required(m)
+               for _, r := range required {
+                       if uses != nil {
+                               uses[r] = append(uses[r], m)
+                       }
+                       if !seen[r] {
+                               if v, ok := min[r.Path]; !ok {
+                                       min[r.Path] = r.Version
+                               } else if max := reqs.Max(v, r.Version); max != v {
+                                       min[r.Path] = max
+                               }
+                               todo = append(todo, r)
+                               seen[r] = true
+                       }
+               }
+       }
+
+       if min[target.Path] != target.Version {
+               panic("unbuildable") // TODO
+       }
+
+       if vers == nil {
+               vers = make(map[string]string)
+       }
+       list := []module.Version{target}
+       for i := 0; i < len(list); i++ {
+               m := list[i]
+               required, err := reqs.Required(m)
+               if err != nil {
+                       // TODO: Check error is decent.
+                       return nil, err
+               }
+               for _, r := range required {
+                       v := min[r.Path]
+                       if reqs.Max(v, r.Version) != v {
+                               panic("mistake") // TODO
+                       }
+                       if _, ok := vers[r.Path]; !ok {
+                               vers[r.Path] = v
+                               list = append(list, module.Version{Path: r.Path, Version: v})
+                       }
+               }
+       }
+       tail := list[1:]
+       sort.Slice(tail, func(i, j int) bool {
+               return tail[i].Path < tail[j].Path
+       })
+       return list, nil
+}
+
+// Req returns the minimal requirement list for the target module
+// that result in the given build list.
+func Req(target module.Version, list []module.Version, reqs Reqs) ([]module.Version, error) {
+       // Compute postorder, cache requirements.
+       var postorder []module.Version
+       reqCache := map[module.Version][]module.Version{}
+       reqCache[target] = nil
+       var walk func(module.Version) error
+       walk = func(m module.Version) error {
+               _, ok := reqCache[m]
+               if ok {
+                       return nil
+               }
+               required, err := reqs.Required(m)
+               if err != nil {
+                       return err
+               }
+               reqCache[m] = required
+               for _, m1 := range required {
+                       if err := walk(m1); err != nil {
+                               return err
+                       }
+               }
+               postorder = append(postorder, m)
+               return nil
+       }
+       for _, m := range list {
+               if err := walk(m); err != nil {
+                       return nil, err
+               }
+       }
+
+       // Walk modules in reverse post-order, only adding those not implied already.
+       have := map[string]string{}
+       walk = func(m module.Version) error {
+               if v, ok := have[m.Path]; ok && reqs.Max(m.Version, v) == v {
+                       return nil
+               }
+               have[m.Path] = m.Version
+               for _, m1 := range reqCache[m] {
+                       walk(m1)
+               }
+               return nil
+       }
+       max := map[string]string{}
+       for _, m := range list {
+               if max[m.Path] == "" {
+                       max[m.Path] = m.Version
+               } else {
+                       max[m.Path] = reqs.Max(m.Version, max[m.Path])
+               }
+       }
+       var min []module.Version
+       for i := len(postorder) - 1; i >= 0; i-- {
+               m := postorder[i]
+               if max[m.Path] != m.Version {
+                       // Older version.
+                       continue
+               }
+               if have[m.Path] != m.Version {
+                       min = append(min, m)
+                       walk(m)
+               }
+       }
+       sort.Slice(min, func(i, j int) bool {
+               return min[i].Path < min[j].Path
+       })
+       return min, nil
+}
+
+// UpgradeAll returns a build list for the target module
+// in which every module is upgraded to its latest version.
+func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) {
+       have := map[string]bool{target.Path: true}
+       list := []module.Version{target}
+       for i := 0; i < len(list); i++ {
+               m := list[i]
+               required, err := reqs.Required(m)
+               if err != nil {
+                       panic(err) // TODO
+               }
+               for _, r := range required {
+                       latest, err := reqs.Latest(r.Path)
+                       if err != nil {
+                               panic(err) // TODO
+                       }
+                       if reqs.Max(latest.Version, r.Version) != latest.Version {
+                               panic("mistake") // TODO
+                       }
+                       if !have[r.Path] {
+                               have[r.Path] = true
+                               list = append(list, module.Version{Path: r.Path, Version: latest.Version})
+                       }
+               }
+       }
+       tail := list[1:]
+       sort.Slice(tail, func(i, j int) bool {
+               return tail[i].Path < tail[j].Path
+       })
+       return list, nil
+}
+
+// Upgrade returns a build list for the target module
+// in which the given additional modules are upgraded.
+func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]module.Version, error) {
+       list, err := reqs.Required(target)
+       if err != nil {
+               panic(err) // TODO
+       }
+       // TODO: Maybe if an error is given,
+       // rerun with BuildList(upgrade[0], reqs) etc
+       // to find which ones are the buggy ones.
+       list = append([]module.Version(nil), list...)
+       list = append(list, upgrade...)
+       return BuildList(target, &override{target, list, reqs})
+}
+
+// Downgrade returns a build list for the target module
+// in which the given additional modules are downgraded.
+func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) {
+       list, err := reqs.Required(target)
+       if err != nil {
+               panic(err) // TODO
+       }
+       max := make(map[string]string)
+       for _, r := range list {
+               max[r.Path] = r.Version
+       }
+       for _, d := range downgrade {
+               if v, ok := max[d.Path]; !ok || reqs.Max(v, d.Version) != d.Version {
+                       max[d.Path] = d.Version
+               }
+       }
+
+       var (
+               added    = make(map[module.Version]bool)
+               rdeps    = make(map[module.Version][]module.Version)
+               excluded = make(map[module.Version]bool)
+       )
+       var exclude func(module.Version)
+       exclude = func(m module.Version) {
+               if excluded[m] {
+                       return
+               }
+               excluded[m] = true
+               for _, p := range rdeps[m] {
+                       exclude(p)
+               }
+       }
+       var add func(module.Version)
+       add = func(m module.Version) {
+               if added[m] {
+                       return
+               }
+               added[m] = true
+               if v, ok := max[m.Path]; ok && reqs.Max(m.Version, v) != v {
+                       exclude(m)
+                       return
+               }
+               list, err := reqs.Required(m)
+               if err != nil {
+                       panic(err) // TODO
+               }
+               for _, r := range list {
+                       add(r)
+                       if excluded[r] {
+                               exclude(m)
+                               return
+                       }
+                       rdeps[r] = append(rdeps[r], m)
+               }
+       }
+
+       var out []module.Version
+       out = append(out, target)
+List:
+       for _, r := range list {
+               add(r)
+               for excluded[r] {
+                       p, err := reqs.Previous(r)
+                       if err != nil {
+                               return nil, err // TODO
+                       }
+                       // If the target version is a pseudo-version, it may not be
+                       // included when iterating over prior versions using reqs.Previous.
+                       // Insert it into the right place in the iteration.
+                       // If v is excluded, p should be returned again by reqs.Previous on the next iteration.
+                       if v := max[r.Path]; reqs.Max(v, r.Version) != v && reqs.Max(p.Version, v) != p.Version {
+                               p.Version = v
+                       }
+                       if p.Version == "none" {
+                               continue List
+                       }
+                       add(p)
+                       r = p
+               }
+               out = append(out, r)
+       }
+
+       return out, nil
+}
+
+type override struct {
+       target module.Version
+       list   []module.Version
+       Reqs
+}
+
+func (r *override) Required(m module.Version) ([]module.Version, error) {
+       if m == r.target {
+               return r.list, nil
+       }
+       return r.Reqs.Required(m)
+}
diff --git a/src/cmd/go/internal/mvs/mvs_test.go b/src/cmd/go/internal/mvs/mvs_test.go
new file mode 100644 (file)
index 0000000..0fd55e4
--- /dev/null
@@ -0,0 +1,377 @@
+// 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.
+
+package mvs
+
+import (
+       "reflect"
+       "strings"
+       "testing"
+
+       "cmd/go/internal/module"
+)
+
+var tests = `
+# Scenario from blog.
+name: blog
+A: B1 C2
+B1: D3
+C1: D2
+C2: D4
+C3: D5
+C4: G1
+D2: E1
+D3: E2
+D4: E2 F1
+D5: E2
+G1: C4
+A2: B1 C4 D4
+build A: A B1 C2 D4 E2 F1
+upgrade* A: A B1 C4 D5 E2 G1
+upgrade A C4: A B1 C4 D4 E2 F1 G1
+downgrade A2 D2: A2 C4 D2
+
+name: trim
+A: B1 C2
+B1: D3
+C2: B2
+B2:
+build A: A B2 C2
+
+# Cross-dependency between D and E.
+# No matter how it arises, should get result of merging all build lists via max,
+# which leads to including both D2 and E2.
+
+name: cross1
+A: B C
+B: D1
+C: D2
+D1: E2
+D2: E1
+build A: A B C D2 E2
+
+name: cross1V
+A: B2 C D2 E1
+B1: 
+B2: D1
+C: D2
+D1: E2
+D2: E1
+build A: A B2 C D2 E2
+
+name: cross1U
+A: B1 C
+B1: 
+B2: D1
+C: D2
+D1: E2
+D2: E1
+build A: A B1 C D2 E1
+upgrade A B2: A B2 C D2 E2
+
+name: cross1R
+A: B C 
+B: D2
+C: D1
+D1: E2
+D2: E1
+build A: A B C D2 E2
+
+name: cross1X
+A: B C
+B: D1 E2
+C: D2
+D1: E2
+D2: E1
+build A: A B C D2 E2
+
+name: cross2
+A: B D2
+B: D1
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+name: cross2X
+A: B D2
+B: D1 E2
+C: D2
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+name: cross3
+A: B D2 E1
+B: D1
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+name: cross3X
+A: B D2 E1
+B: D1 E2
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+# Should not get E2 here, because B has been updated
+# not to depend on D1 anymore.
+name: cross4
+A1: B1 D2
+A2: B2 D2
+B1: D1
+B2: D2
+D1: E2
+D2: E1
+build A1: A1 B1 D2 E2
+build A2: A2 B2 D2 E1
+
+# But the upgrade from A1 preserves the E2 dep explicitly.
+upgrade A1 B2: A1 B2 D2 E2
+upgradereq A1 B2: B2 E2
+
+name: cross5
+A: D1
+D1: E2
+D2: E1
+build A: A D1 E2
+upgrade* A: A D2 E2
+upgrade A D2: A D2 E2
+upgradereq A D2: D2 E2
+
+name: cross6
+A: D2
+D1: E2
+D2: E1
+build A: A D2 E1
+upgrade* A: A D2 E2
+upgrade A E2: A D2 E2
+
+name: cross7
+A: B C
+B: D1
+C: E1
+D1: E2
+E1: D2
+build A: A B C D2 E2
+
+name: down1
+A: B2
+B1: C1
+B2: C2
+build A: A B2 C2
+downgrade A C1: A B1
+
+name: down2
+A: B2 E2
+B1:
+B2: C2 F2
+C1:
+D1:
+C2: D2 E2
+D2: B2
+E2: D2
+E1:
+F1:
+downgrade A F1: A B1 E1
+
+name: down3
+A: 
+
+# golang.org/issue/25542.
+name: noprev1
+A: B4 C2
+B2.hidden: 
+C2: 
+downgrade A B2.hidden: A B2.hidden C2
+
+name: noprev2
+A: B4 C2
+B2.hidden: 
+B1: 
+C2: 
+downgrade A B2.hidden: A B2.hidden C2
+
+name: noprev3
+A: B4 C2
+B3: 
+B2.hidden: 
+C2: 
+downgrade A B2.hidden: A B2.hidden C2
+`
+
+func Test(t *testing.T) {
+       var (
+               name string
+               reqs reqsMap
+               fns  []func(*testing.T)
+       )
+       flush := func() {
+               if name != "" {
+                       t.Run(name, func(t *testing.T) {
+                               for _, fn := range fns {
+                                       fn(t)
+                               }
+                       })
+               }
+       }
+       m := func(s string) module.Version {
+               return module.Version{Path: s[:1], Version: s[1:]}
+       }
+       ms := func(list []string) []module.Version {
+               var mlist []module.Version
+               for _, s := range list {
+                       mlist = append(mlist, m(s))
+               }
+               return mlist
+       }
+       checkList := func(t *testing.T, desc string, list []module.Version, err error, val string) {
+               if err != nil {
+                       t.Fatalf("%s: %v", desc, err)
+               }
+               vs := ms(strings.Fields(val))
+               if !reflect.DeepEqual(list, vs) {
+                       t.Errorf("%s = %v, want %v", desc, list, vs)
+               }
+       }
+
+       for _, line := range strings.Split(tests, "\n") {
+               line = strings.TrimSpace(line)
+               if strings.HasPrefix(line, "#") || line == "" {
+                       continue
+               }
+               i := strings.Index(line, ":")
+               if i < 0 {
+                       t.Fatalf("missing colon: %q", line)
+               }
+               key := strings.TrimSpace(line[:i])
+               val := strings.TrimSpace(line[i+1:])
+               if key == "" {
+                       t.Fatalf("missing key: %q", line)
+               }
+               kf := strings.Fields(key)
+               switch kf[0] {
+               case "name":
+                       if len(kf) != 1 {
+                               t.Fatalf("name takes no arguments: %q", line)
+                       }
+                       flush()
+                       reqs = make(reqsMap)
+                       fns = nil
+                       name = val
+                       continue
+               case "build":
+                       if len(kf) != 2 {
+                               t.Fatalf("build takes one argument: %q", line)
+                       }
+                       fns = append(fns, func(t *testing.T) {
+                               list, err := BuildList(m(kf[1]), reqs)
+                               checkList(t, key, list, err, val)
+                       })
+                       continue
+               case "upgrade*":
+                       if len(kf) != 2 {
+                               t.Fatalf("upgrade* takes one argument: %q", line)
+                       }
+                       fns = append(fns, func(t *testing.T) {
+                               list, err := UpgradeAll(m(kf[1]), reqs)
+                               checkList(t, key, list, err, val)
+                       })
+                       continue
+               case "upgradereq":
+                       if len(kf) < 2 {
+                               t.Fatalf("upgrade takes at least one arguments: %q", line)
+                       }
+                       fns = append(fns, func(t *testing.T) {
+                               list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
+                               if err == nil {
+                                       list, err = Req(m(kf[1]), list, reqs)
+                               }
+                               checkList(t, key, list, err, val)
+                       })
+                       continue
+               case "upgrade":
+                       if len(kf) < 2 {
+                               t.Fatalf("upgrade takes at least one arguments: %q", line)
+                       }
+                       fns = append(fns, func(t *testing.T) {
+                               list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
+                               checkList(t, key, list, err, val)
+                       })
+                       continue
+               case "downgrade":
+                       if len(kf) < 2 {
+                               t.Fatalf("downgrade takes at least one arguments: %q", line)
+                       }
+                       fns = append(fns, func(t *testing.T) {
+                               list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...)
+                               checkList(t, key, list, err, val)
+                       })
+                       continue
+               }
+               if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' {
+                       var rs []module.Version
+                       for _, f := range strings.Fields(val) {
+                               r := m(f)
+                               if reqs[r] == nil {
+                                       reqs[r] = []module.Version{}
+                               }
+                               rs = append(rs, r)
+                       }
+                       reqs[m(key)] = rs
+                       continue
+               }
+               t.Fatalf("bad line: %q", line)
+       }
+       flush()
+}
+
+type reqsMap map[module.Version][]module.Version
+
+func (r reqsMap) Max(v1, v2 string) string {
+       if v1 == "none" {
+               return v2
+       }
+       if v2 == "none" {
+               return v1
+       }
+       if v1 < v2 {
+               return v2
+       }
+       return v1
+}
+
+func (r reqsMap) Latest(path string) (module.Version, error) {
+       var m module.Version
+       for k := range r {
+               if k.Path == path && m.Version < k.Version {
+                       m = k
+               }
+       }
+       if m.Path == "" {
+               return module.Version{}, &MissingModuleError{module.Version{Path: path, Version: ""}}
+       }
+       return m, nil
+}
+
+func (r reqsMap) Previous(m module.Version) (module.Version, error) {
+       var p module.Version
+       for k := range r {
+               if k.Path == m.Path && p.Version < k.Version && k.Version < m.Version && !strings.HasSuffix(k.Version, ".hidden") {
+                       p = k
+               }
+       }
+       if p.Path == "" {
+               return module.Version{Path: m.Path, Version: "none"}, nil
+       }
+       return p, nil
+}
+
+func (r reqsMap) Required(m module.Version) ([]module.Version, error) {
+       rr, ok := r[m]
+       if !ok {
+               return nil, &MissingModuleError{m}
+       }
+       return rr, nil
+}
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
new file mode 100644 (file)
index 0000000..42a6be0
--- /dev/null
@@ -0,0 +1,423 @@
+// Copyright 2017 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 search
+
+import (
+       "cmd/go/internal/base"
+       "cmd/go/internal/cfg"
+       "fmt"
+       "go/build"
+       "log"
+       "os"
+       "path"
+       "path/filepath"
+       "regexp"
+       "strings"
+)
+
+// AllPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func AllPackages(pattern string) []string {
+       pkgs := MatchPackages(pattern)
+       if len(pkgs) == 0 {
+               fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+       }
+       return pkgs
+}
+
+// AllPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+func AllPackagesInFS(pattern string) []string {
+       pkgs := MatchPackagesInFS(pattern)
+       if len(pkgs) == 0 {
+               fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+       }
+       return pkgs
+}
+
+// MatchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func MatchPackages(pattern string) []string {
+       match := func(string) bool { return true }
+       treeCanMatch := func(string) bool { return true }
+       if !IsMetaPackage(pattern) {
+               match = MatchPattern(pattern)
+               treeCanMatch = TreeCanMatchPattern(pattern)
+       }
+
+       have := map[string]bool{
+               "builtin": true, // ignore pseudo-package that exists only for documentation
+       }
+       if !cfg.BuildContext.CgoEnabled {
+               have["runtime/cgo"] = true // ignore during walk
+       }
+       var pkgs []string
+
+       for _, src := range cfg.BuildContext.SrcDirs() {
+               if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
+                       continue
+               }
+               src = filepath.Clean(src) + string(filepath.Separator)
+               root := src
+               if pattern == "cmd" {
+                       root += "cmd" + string(filepath.Separator)
+               }
+               filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+                       if err != nil || path == src {
+                               return nil
+                       }
+
+                       want := true
+                       // Avoid .foo, _foo, and testdata directory trees.
+                       _, elem := filepath.Split(path)
+                       if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+                               want = false
+                       }
+
+                       name := filepath.ToSlash(path[len(src):])
+                       if pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") {
+                               // The name "std" is only the standard library.
+                               // If the name is cmd, it's the root of the command tree.
+                               want = false
+                       }
+                       if !treeCanMatch(name) {
+                               want = false
+                       }
+
+                       if !fi.IsDir() {
+                               if fi.Mode()&os.ModeSymlink != 0 && want {
+                                       if target, err := os.Stat(path); err == nil && target.IsDir() {
+                                               fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+                                       }
+                               }
+                               return nil
+                       }
+                       if !want {
+                               return filepath.SkipDir
+                       }
+
+                       if have[name] {
+                               return nil
+                       }
+                       have[name] = true
+                       if !match(name) {
+                               return nil
+                       }
+                       pkg, err := cfg.BuildContext.ImportDir(path, 0)
+                       if err != nil {
+                               if _, noGo := err.(*build.NoGoError); noGo {
+                                       return nil
+                               }
+                       }
+
+                       // If we are expanding "cmd", skip main
+                       // packages under cmd/vendor. At least as of
+                       // March, 2017, there is one there for the
+                       // vendored pprof tool.
+                       if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
+                               return nil
+                       }
+
+                       pkgs = append(pkgs, name)
+                       return nil
+               })
+       }
+       return pkgs
+}
+
+var modRoot string
+
+func SetModRoot(dir string) {
+       modRoot = dir
+}
+
+// MatchPackagesInFS returns a list of package paths matching pattern,
+// which must begin with ./ or ../
+// (see go help packages for pattern syntax).
+func MatchPackagesInFS(pattern string) []string {
+       // Find directory to begin the scan.
+       // Could be smarter but this one optimization
+       // is enough for now, since ... is usually at the
+       // end of a path.
+       i := strings.Index(pattern, "...")
+       dir, _ := path.Split(pattern[:i])
+
+       // pattern begins with ./ or ../.
+       // path.Clean will discard the ./ but not the ../.
+       // We need to preserve the ./ for pattern matching
+       // and in the returned import paths.
+       prefix := ""
+       if strings.HasPrefix(pattern, "./") {
+               prefix = "./"
+       }
+       match := MatchPattern(pattern)
+
+       if modRoot != "" {
+               abs, err := filepath.Abs(dir)
+               if err != nil {
+                       base.Fatalf("go: %v", err)
+               }
+               if !hasFilepathPrefix(abs, modRoot) {
+                       base.Fatalf("go: pattern %s refers to dir %s, outside module root %s", pattern, abs, modRoot)
+                       return nil
+               }
+       }
+
+       var pkgs []string
+       filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+               if err != nil || !fi.IsDir() {
+                       return nil
+               }
+               if path == dir {
+                       // filepath.Walk starts at dir and recurses. For the recursive case,
+                       // the path is the result of filepath.Join, which calls filepath.Clean.
+                       // The initial case is not Cleaned, though, so we do this explicitly.
+                       //
+                       // This converts a path like "./io/" to "io". Without this step, running
+                       // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
+                       // package, because prepending the prefix "./" to the unclean path would
+                       // result in "././io", and match("././io") returns false.
+                       path = filepath.Clean(path)
+               }
+
+               // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
+               _, elem := filepath.Split(path)
+               dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
+               if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
+                       return filepath.SkipDir
+               }
+
+               name := prefix + filepath.ToSlash(path)
+               if !match(name) {
+                       return nil
+               }
+
+               // We keep the directory if we can import it, or if we can't import it
+               // due to invalid Go source files. This means that directories containing
+               // parse errors will be built (and fail) instead of being silently skipped
+               // as not matching the pattern. Go 1.5 and earlier skipped, but that
+               // behavior means people miss serious mistakes.
+               // See golang.org/issue/11407.
+               if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
+                       if _, noGo := err.(*build.NoGoError); !noGo {
+                               log.Print(err)
+                       }
+                       return nil
+               }
+               pkgs = append(pkgs, name)
+               return nil
+       })
+       return pkgs
+}
+
+// TreeCanMatchPattern(pattern)(name) reports whether
+// name or children of name can possibly match pattern.
+// Pattern is the same limited glob accepted by matchPattern.
+func TreeCanMatchPattern(pattern string) func(name string) bool {
+       wildCard := false
+       if i := strings.Index(pattern, "..."); i >= 0 {
+               wildCard = true
+               pattern = pattern[:i]
+       }
+       return func(name string) bool {
+               return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
+                       wildCard && strings.HasPrefix(name, pattern)
+       }
+}
+
+// MatchPattern(pattern)(name) reports whether
+// name matches pattern. Pattern is a limited glob
+// pattern in which '...' means 'any string' and there
+// is no other special syntax.
+// Unfortunately, there are two special cases. Quoting "go help packages":
+//
+// First, /... at the end of the pattern can match an empty string,
+// so that net/... matches both net and packages in its subdirectories, like net/http.
+// Second, any slash-separted pattern element containing a wildcard never
+// participates in a match of the "vendor" element in the path of a vendored
+// package, so that ./... does not match packages in subdirectories of
+// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+// Note, however, that a directory named vendor that itself contains code
+// is not a vendored package: cmd/vendor would be a command named vendor,
+// and the pattern cmd/... matches it.
+func MatchPattern(pattern string) func(name string) bool {
+       // Convert pattern to regular expression.
+       // The strategy for the trailing /... is to nest it in an explicit ? expression.
+       // The strategy for the vendor exclusion is to change the unmatchable
+       // vendor strings to a disallowed code point (vendorChar) and to use
+       // "(anything but that codepoint)*" as the implementation of the ... wildcard.
+       // This is a bit complicated but the obvious alternative,
+       // namely a hand-written search like in most shell glob matchers,
+       // is too easy to make accidentally exponential.
+       // Using package regexp guarantees linear-time matching.
+
+       const vendorChar = "\x00"
+
+       if strings.Contains(pattern, vendorChar) {
+               return func(name string) bool { return false }
+       }
+
+       re := regexp.QuoteMeta(pattern)
+       re = replaceVendor(re, vendorChar)
+       switch {
+       case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
+               re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
+       case re == vendorChar+`/\.\.\.`:
+               re = `(/vendor|/` + vendorChar + `/\.\.\.)`
+       case strings.HasSuffix(re, `/\.\.\.`):
+               re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
+       }
+       re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
+
+       reg := regexp.MustCompile(`^` + re + `$`)
+
+       return func(name string) bool {
+               if strings.Contains(name, vendorChar) {
+                       return false
+               }
+               return reg.MatchString(replaceVendor(name, vendorChar))
+       }
+}
+
+// replaceVendor returns the result of replacing
+// non-trailing vendor path elements in x with repl.
+func replaceVendor(x, repl string) string {
+       if !strings.Contains(x, "vendor") {
+               return x
+       }
+       elem := strings.Split(x, "/")
+       for i := 0; i < len(elem)-1; i++ {
+               if elem[i] == "vendor" {
+                       elem[i] = repl
+               }
+       }
+       return strings.Join(elem, "/")
+}
+
+// ImportPaths returns the import paths to use for the given command line.
+func ImportPaths(args []string) []string {
+       args = CleanImportPaths(args)
+       var out []string
+       for _, a := range args {
+               if IsMetaPackage(a) {
+                       out = append(out, AllPackages(a)...)
+                       continue
+               }
+               if strings.Contains(a, "...") {
+                       if build.IsLocalImport(a) {
+                               out = append(out, AllPackagesInFS(a)...)
+                       } else {
+                               out = append(out, AllPackages(a)...)
+                       }
+                       continue
+               }
+               out = append(out, a)
+       }
+       return out
+}
+
+// CleanImportPaths returns the import paths to use for the given
+// command line, but it does no wildcard expansion.
+func CleanImportPaths(args []string) []string {
+       if len(args) == 0 {
+               return []string{"."}
+       }
+       var out []string
+       for _, a := range args {
+               // Arguments are supposed to be import paths, but
+               // as a courtesy to Windows developers, rewrite \ to /
+               // in command-line arguments. Handles .\... and so on.
+               if filepath.Separator == '\\' {
+                       a = strings.Replace(a, `\`, `/`, -1)
+               }
+
+               // Put argument in canonical form, but preserve leading ./.
+               if strings.HasPrefix(a, "./") {
+                       a = "./" + path.Clean(a)
+                       if a == "./." {
+                               a = "."
+                       }
+               } else {
+                       a = path.Clean(a)
+               }
+               out = append(out, a)
+       }
+       return out
+}
+
+// ImportPathsNoDotExpansion returns the import paths to use for the given
+// command line, but it does no ... expansion.
+// TODO(vgo): Delete once old go get is gone.
+func ImportPathsNoDotExpansion(args []string) []string {
+       args = CleanImportPaths(args)
+       var out []string
+       for _, a := range args {
+               if IsMetaPackage(a) {
+                       out = append(out, AllPackages(a)...)
+                       continue
+               }
+               out = append(out, a)
+       }
+       return out
+}
+
+// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
+func IsMetaPackage(name string) bool {
+       return name == "std" || name == "cmd" || name == "all"
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+       switch {
+       default:
+               return false
+       case len(s) == len(prefix):
+               return s == prefix
+       case len(s) > len(prefix):
+               if prefix != "" && prefix[len(prefix)-1] == '/' {
+                       return strings.HasPrefix(s, prefix)
+               }
+               return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+       }
+}
+
+// hasFilepathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasFilepathPrefix(s, prefix string) bool {
+       switch {
+       default:
+               return false
+       case len(s) == len(prefix):
+               return s == prefix
+       case len(s) > len(prefix):
+               if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
+                       return strings.HasPrefix(s, prefix)
+               }
+               return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
+       }
+}
+
+// IsStandardImportPath reports whether $GOROOT/src/path should be considered
+// part of the standard distribution. For historical reasons we allow people to add
+// their own code to $GOROOT instead of using $GOPATH, but we assume that
+// code will start with a domain name (dot in the first element).
+//
+// Note that this function is meant to evaluate whether a directory found in GOROOT
+// should be treated as part of the standard library. It should not be used to decide
+// that a directory found in GOPATH should be rejected: directories in GOPATH
+// need not have dots in the first element, and they just take their chances
+// with future collisions in the standard library.
+func IsStandardImportPath(path string) bool {
+       i := strings.Index(path, "/")
+       if i < 0 {
+               i = len(path)
+       }
+       elem := path[:i]
+       return !strings.Contains(elem, ".")
+}
similarity index 94%
rename from src/cmd/go/internal/load/match_test.go
rename to src/cmd/go/internal/search/search_test.go
index b8d67dac742e5a53a3db1eebaab0ef532a527d06..0bef765fa452b586038f3e4829d519aaf501bf07 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package load
+package search
 
 import (
        "strings"
@@ -65,8 +65,8 @@ var matchPatternTests = `
 `
 
 func TestMatchPattern(t *testing.T) {
-       testPatterns(t, "matchPattern", matchPatternTests, func(pattern, name string) bool {
-               return matchPattern(pattern)(name)
+       testPatterns(t, "MatchPattern", matchPatternTests, func(pattern, name string) bool {
+               return MatchPattern(pattern)(name)
        })
 }
 
@@ -106,8 +106,8 @@ var treeCanMatchPatternTests = `
 `
 
 func TestTreeCanMatchPattern(t *testing.T) {
-       testPatterns(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
-               return treeCanMatchPattern(pattern)(name)
+       testPatterns(t, "TreeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
+               return TreeCanMatchPattern(pattern)(name)
        })
 }
 
diff --git a/src/cmd/go/internal/semver/semver.go b/src/cmd/go/internal/semver/semver.go
new file mode 100644 (file)
index 0000000..ecc5300
--- /dev/null
@@ -0,0 +1,351 @@
+// 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.
+
+// Package semver implements comparison of semantic version strings.
+// In this package, semantic version strings must begin with a leading "v",
+// as in "v1.0.0".
+//
+// The general form of a semantic version string accepted by this package is
+//
+//     vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
+//
+// where square brackets indicate optional parts of the syntax;
+// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
+// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
+// using only alphanumeric characters and hyphens; and
+// all-numeric PRERELEASE identifiers must not have leading zeros.
+//
+// This package follows Semantic Versioning 2.0.0 (see semver.org)
+// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
+// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
+// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
+package semver
+
+// parsed returns the parsed form of a semantic version string.
+type parsed struct {
+       major      string
+       minor      string
+       patch      string
+       short      string
+       prerelease string
+       build      string
+       err        string
+}
+
+// IsValid reports whether v is a valid semantic version string.
+func IsValid(v string) bool {
+       _, ok := parse(v)
+       return ok
+}
+
+// Canonical returns the canonical formatting of the semantic version v.
+// It fills in any missing .MINOR or .PATCH and discards build metadata.
+// Two semantic versions compare equal only if their canonical formattings
+// are identical strings.
+// The canonical invalid semantic version is the empty string.
+func Canonical(v string) string {
+       p, ok := parse(v)
+       if !ok {
+               return ""
+       }
+       if p.build != "" {
+               return v[:len(v)-len(p.build)]
+       }
+       if p.short != "" {
+               return v + p.short
+       }
+       return v
+}
+
+// Major returns the major version prefix of the semantic version v.
+// For example, Major("v2.1.0") == "v2".
+// If v is an invalid semantic version string, Major returns the empty string.
+func Major(v string) string {
+       pv, ok := parse(v)
+       if !ok {
+               return ""
+       }
+       return v[:1+len(pv.major)]
+}
+
+// Compare returns an integer comparing two versions according to
+// according to semantic version precedence.
+// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
+//
+// An invalid semantic version string is considered less than a valid one.
+// All invalid semantic version strings compare equal to each other.
+func Compare(v, w string) int {
+       pv, ok1 := parse(v)
+       pw, ok2 := parse(w)
+       if !ok1 && !ok2 {
+               return 0
+       }
+       if !ok1 {
+               return -1
+       }
+       if !ok2 {
+               return +1
+       }
+       if c := compareInt(pv.major, pw.major); c != 0 {
+               return c
+       }
+       if c := compareInt(pv.minor, pw.minor); c != 0 {
+               return c
+       }
+       if c := compareInt(pv.patch, pw.patch); c != 0 {
+               return c
+       }
+       return comparePrerelease(pv.prerelease, pw.prerelease)
+}
+
+// Max canonicalizes its arguments and then returns the version string
+// that compares greater.
+func Max(v, w string) string {
+       v = Canonical(v)
+       w = Canonical(w)
+       if Compare(v, w) > 0 {
+               return v
+       }
+       return w
+}
+
+func parse(v string) (p parsed, ok bool) {
+       if v == "" || v[0] != 'v' {
+               p.err = "missing v prefix"
+               return
+       }
+       p.major, v, ok = parseInt(v[1:])
+       if !ok {
+               p.err = "bad major version"
+               return
+       }
+       if v == "" {
+               p.minor = "0"
+               p.patch = "0"
+               p.short = ".0.0"
+               return
+       }
+       if v[0] != '.' {
+               p.err = "bad minor prefix"
+               ok = false
+               return
+       }
+       p.minor, v, ok = parseInt(v[1:])
+       if !ok {
+               p.err = "bad minor version"
+               return
+       }
+       if v == "" {
+               p.patch = "0"
+               p.short = ".0"
+               return
+       }
+       if v[0] != '.' {
+               p.err = "bad patch prefix"
+               ok = false
+               return
+       }
+       p.patch, v, ok = parseInt(v[1:])
+       if !ok {
+               p.err = "bad patch version"
+               return
+       }
+       if len(v) > 0 && v[0] == '-' {
+               p.prerelease, v, ok = parsePrerelease(v)
+               if !ok {
+                       p.err = "bad prerelease"
+                       return
+               }
+       }
+       if len(v) > 0 && v[0] == '+' {
+               p.build, v, ok = parseBuild(v)
+               if !ok {
+                       p.err = "bad build"
+                       return
+               }
+       }
+       if v != "" {
+               p.err = "junk on end"
+               ok = false
+               return
+       }
+       ok = true
+       return
+}
+
+func parseInt(v string) (t, rest string, ok bool) {
+       if v == "" {
+               return
+       }
+       if v[0] < '0' || '9' < v[0] {
+               return
+       }
+       i := 1
+       for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+               i++
+       }
+       if v[0] == '0' && i != 1 {
+               return
+       }
+       return v[:i], v[i:], true
+}
+
+func parsePrerelease(v string) (t, rest string, ok bool) {
+       // "A pre-release version MAY be denoted by appending a hyphen and
+       // a series of dot separated identifiers immediately following the patch version.
+       // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
+       // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
+       if v == "" || v[0] != '-' {
+               return
+       }
+       i := 1
+       start := 1
+       for i < len(v) && v[i] != '+' {
+               if !isIdentChar(v[i]) && v[i] != '.' {
+                       return
+               }
+               if v[i] == '.' {
+                       if start == i || isBadNum(v[start:i]) {
+                               return
+                       }
+                       start = i + 1
+               }
+               i++
+       }
+       if start == i || isBadNum(v[start:i]) {
+               return
+       }
+       return v[:i], v[i:], true
+}
+
+func parseBuild(v string) (t, rest string, ok bool) {
+       if v == "" || v[0] != '+' {
+               return
+       }
+       i := 1
+       start := 1
+       for i < len(v) {
+               if !isIdentChar(v[i]) {
+                       return
+               }
+               if v[i] == '.' {
+                       if start == i {
+                               return
+                       }
+                       start = i + 1
+               }
+               i++
+       }
+       if start == i {
+               return
+       }
+       return v[:i], v[i:], true
+}
+
+func isIdentChar(c byte) bool {
+       return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
+}
+
+func isBadNum(v string) bool {
+       i := 0
+       for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+               i++
+       }
+       return i == len(v) && i > 1 && v[0] == '0'
+}
+
+func isNum(v string) bool {
+       i := 0
+       for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+               i++
+       }
+       return i == len(v)
+}
+
+func compareInt(x, y string) int {
+       if x == y {
+               return 0
+       }
+       if len(x) < len(y) {
+               return -1
+       }
+       if len(x) > len(y) {
+               return +1
+       }
+       if x < y {
+               return -1
+       } else {
+               return +1
+       }
+}
+
+func comparePrerelease(x, y string) int {
+       // "When major, minor, and patch are equal, a pre-release version has
+       // lower precedence than a normal version.
+       // Example: 1.0.0-alpha < 1.0.0.
+       // Precedence for two pre-release versions with the same major, minor,
+       // and patch version MUST be determined by comparing each dot separated
+       // identifier from left to right until a difference is found as follows:
+       // identifiers consisting of only digits are compared numerically and
+       // identifiers with letters or hyphens are compared lexically in ASCII
+       // sort order. Numeric identifiers always have lower precedence than
+       // non-numeric identifiers. A larger set of pre-release fields has a
+       // higher precedence than a smaller set, if all of the preceding
+       // identifiers are equal.
+       // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
+       // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
+       if x == y {
+               return 0
+       }
+       if x == "" {
+               return +1
+       }
+       if y == "" {
+               return -1
+       }
+       for x != "" && y != "" {
+               x = x[1:] // skip - or .
+               y = y[1:] // skip - or .
+               var dx, dy string
+               dx, x = nextIdent(x)
+               dy, y = nextIdent(y)
+               if dx != dy {
+                       ix := isNum(dx)
+                       iy := isNum(dy)
+                       if ix != iy {
+                               if ix {
+                                       return -1
+                               } else {
+                                       return +1
+                               }
+                       }
+                       if ix {
+                               if len(dx) < len(dy) {
+                                       return -1
+                               }
+                               if len(dx) > len(dy) {
+                                       return +1
+                               }
+                       }
+                       if dx < dy {
+                               return -1
+                       } else {
+                               return +1
+                       }
+               }
+       }
+       if x == "" {
+               return -1
+       } else {
+               return +1
+       }
+}
+
+func nextIdent(x string) (dx, rest string) {
+       i := 0
+       for i < len(x) && x[i] != '.' {
+               i++
+       }
+       return x[:i], x[i:]
+}
diff --git a/src/cmd/go/internal/semver/semver_test.go b/src/cmd/go/internal/semver/semver_test.go
new file mode 100644 (file)
index 0000000..7a697f6
--- /dev/null
@@ -0,0 +1,123 @@
+// 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.
+
+package semver
+
+import (
+       "strings"
+       "testing"
+)
+
+var tests = []struct {
+       in  string
+       out string
+}{
+       {"bad", ""},
+       {"v1-pre", ""},
+       {"v1+meta", ""},
+       {"v1-pre+meta", ""},
+       {"v1.2-pre", ""},
+       {"v1.2+meta", ""},
+       {"v1.2-pre+meta", ""},
+       {"v1.0.0-alpha", "v1.0.0-alpha"},
+       {"v1.0.0-alpha.1", "v1.0.0-alpha.1"},
+       {"v1.0.0-alpha.beta", "v1.0.0-alpha.beta"},
+       {"v1.0.0-beta", "v1.0.0-beta"},
+       {"v1.0.0-beta.2", "v1.0.0-beta.2"},
+       {"v1.0.0-beta.11", "v1.0.0-beta.11"},
+       {"v1.0.0-rc.1", "v1.0.0-rc.1"},
+       {"v1", "v1.0.0"},
+       {"v1.0", "v1.0.0"},
+       {"v1.0.0", "v1.0.0"},
+       {"v1.2", "v1.2.0"},
+       {"v1.2.0", "v1.2.0"},
+       {"v1.2.3-456", "v1.2.3-456"},
+       {"v1.2.3-456.789", "v1.2.3-456.789"},
+       {"v1.2.3-456-789", "v1.2.3-456-789"},
+       {"v1.2.3-456a", "v1.2.3-456a"},
+       {"v1.2.3-pre", "v1.2.3-pre"},
+       {"v1.2.3-pre+meta", "v1.2.3-pre"},
+       {"v1.2.3-pre.1", "v1.2.3-pre.1"},
+       {"v1.2.3-zzz", "v1.2.3-zzz"},
+       {"v1.2.3", "v1.2.3"},
+       {"v1.2.3+meta", "v1.2.3"},
+}
+
+func TestIsValid(t *testing.T) {
+       for _, tt := range tests {
+               ok := IsValid(tt.in)
+               if ok != (tt.out != "") {
+                       t.Errorf("IsValid(%q) = %v, want %v", tt.in, ok, !ok)
+               }
+       }
+}
+
+func TestCanonical(t *testing.T) {
+       for _, tt := range tests {
+               out := Canonical(tt.in)
+               if out != tt.out {
+                       t.Errorf("Canonical(%q) = %q, want %q", tt.in, out, tt.out)
+               }
+       }
+}
+
+func TestMajor(t *testing.T) {
+       for _, tt := range tests {
+               out := Major(tt.in)
+               want := ""
+               if i := strings.Index(tt.out, "."); i >= 0 {
+                       want = tt.out[:i]
+               }
+               if out != want {
+                       t.Errorf("Major(%q) = %q, want %q", tt.in, out, want)
+               }
+       }
+}
+
+func TestCompare(t *testing.T) {
+       for i, ti := range tests {
+               for j, tj := range tests {
+                       cmp := Compare(ti.in, tj.in)
+                       var want int
+                       if ti.out == tj.out {
+                               want = 0
+                       } else if i < j {
+                               want = -1
+                       } else {
+                               want = +1
+                       }
+                       if cmp != want {
+                               t.Errorf("Compare(%q, %q) = %d, want %d", ti.in, tj.in, cmp, want)
+                       }
+               }
+       }
+}
+
+func TestMax(t *testing.T) {
+       for i, ti := range tests {
+               for j, tj := range tests {
+                       max := Max(ti.in, tj.in)
+                       want := Canonical(ti.in)
+                       if i < j {
+                               want = Canonical(tj.in)
+                       }
+                       if max != want {
+                               t.Errorf("Max(%q, %q) = %q, want %q", ti.in, tj.in, max, want)
+                       }
+               }
+       }
+}
+
+var (
+       v1 = "v1.0.0+metadata-dash"
+       v2 = "v1.0.0+metadata-dash1"
+)
+
+func BenchmarkCompare(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               if Compare(v1, v2) != 0 {
+                       b.Fatalf("bad compare")
+               }
+       }
+}
diff --git a/src/cmd/go/internal/vgo/build.go b/src/cmd/go/internal/vgo/build.go
new file mode 100644 (file)
index 0000000..ba10309
--- /dev/null
@@ -0,0 +1,115 @@
+// 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.
+
+package vgo
+
+import (
+       "bytes"
+       "cmd/go/internal/base"
+       "cmd/go/internal/cfg"
+       "cmd/go/internal/modinfo"
+       "cmd/go/internal/module"
+       "cmd/go/internal/search"
+       "encoding/hex"
+       "fmt"
+       "os"
+       "path/filepath"
+)
+
+var (
+       infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
+       infoEnd, _   = hex.DecodeString("f932433186182072008242104116d8f2")
+)
+
+func isStandardImportPath(path string) bool {
+       if search.IsStandardImportPath(path) {
+               if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src", path)); err == nil {
+                       return true
+               }
+               if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src/vendor", path)); err == nil {
+                       return true
+               }
+       }
+       return false
+}
+
+func PackageModuleInfo(path string) *modinfo.ModulePublic {
+       var info modinfo.ModulePublic
+       if isStandardImportPath(path) || !Enabled() {
+               return nil
+       }
+       target := findModule(path, path)
+       info.Top = target.Path == buildList[0].Path
+       info.Path = target.Path
+       info.Version = target.Version
+       return &info
+}
+
+func PackageBuildInfo(path string, deps []string) string {
+       if isStandardImportPath(path) || !Enabled() {
+               return ""
+       }
+       target := findModule(path, path)
+       mdeps := make(map[module.Version]bool)
+       for _, dep := range deps {
+               if !isStandardImportPath(dep) {
+                       mdeps[findModule(path, dep)] = true
+               }
+       }
+       var mods []module.Version
+       delete(mdeps, target)
+       for mod := range mdeps {
+               mods = append(mods, mod)
+       }
+       sortModules(mods)
+
+       var buf bytes.Buffer
+       fmt.Fprintf(&buf, "path\t%s\n", path)
+       tv := target.Version
+       if tv == "" {
+               tv = "(devel)"
+       }
+       fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, findModHash(target))
+       for _, mod := range mods {
+               mv := mod.Version
+               if mv == "" {
+                       mv = "(devel)"
+               }
+               r := replaced(mod)
+               h := ""
+               if r == nil {
+                       h = "\t" + findModHash(mod)
+               }
+               fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mod.Version, h)
+               if r := replaced(mod); r != nil {
+                       fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.New.Path, r.New.Version, findModHash(r.New))
+               }
+       }
+       return buf.String()
+}
+
+func findModule(target, path string) module.Version {
+       if path == "." {
+               return buildList[0]
+       }
+       for _, mod := range buildList {
+               if importPathInModule(path, mod.Path) {
+                       return mod
+               }
+       }
+       base.Fatalf("build %v: cannot find module for path %v", target, path)
+       panic("unreachable")
+}
+
+func ModInfoProg(info string) []byte {
+       return []byte(fmt.Sprintf(`
+               package main
+               import _ "unsafe"
+               //go:linkname __debug_modinfo__ runtime/debug.modinfo
+               var __debug_modinfo__ string
+               func init() {
+                       __debug_modinfo__ = %q
+               }
+       `, string(infoStart)+info+string(infoEnd)))
+}
diff --git a/src/cmd/go/internal/vgo/fetch.go b/src/cmd/go/internal/vgo/fetch.go
new file mode 100644 (file)
index 0000000..c28353c
--- /dev/null
@@ -0,0 +1,232 @@
+// 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.
+
+package vgo
+
+import (
+       "archive/zip"
+       "bytes"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/dirhash"
+       "cmd/go/internal/modfetch"
+       "cmd/go/internal/module"
+       "cmd/go/internal/semver"
+)
+
+// fetch returns the directory in the local download cache
+// holding the root of mod's source tree.
+// It downloads the module if needed.
+func fetch(mod module.Version) (dir string, err error) {
+       if r := replaced(mod); r != nil {
+               if r.New.Version == "" {
+                       dir = r.New.Path
+                       if !filepath.IsAbs(dir) {
+                               dir = filepath.Join(ModRoot, dir)
+                       }
+                       return dir, nil
+               }
+               mod = r.New
+       }
+
+       modpath := mod.Path + "@" + mod.Version
+       dir = filepath.Join(srcV, modpath)
+       if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
+               zipfile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".zip")
+               if _, err := os.Stat(zipfile); err == nil {
+                       // Use it.
+                       // This should only happen if the v/cache directory is preinitialized
+                       // or if src/v/modpath was removed but not src/v/cache.
+                       fmt.Fprintf(os.Stderr, "vgo: extracting %s %s\n", mod.Path, mod.Version)
+               } else {
+                       if err := os.MkdirAll(filepath.Join(srcV, "cache", mod.Path, "@v"), 0777); err != nil {
+                               return "", err
+                       }
+                       fmt.Fprintf(os.Stderr, "vgo: downloading %s %s\n", mod.Path, mod.Version)
+                       if err := downloadZip(mod, zipfile); err != nil {
+                               return "", err
+                       }
+               }
+               if err := modfetch.Unzip(dir, zipfile, modpath, 0); err != nil {
+                       fmt.Fprintf(os.Stderr, "-> %s\n", err)
+                       return "", err
+               }
+       }
+       checkModHash(mod)
+       return dir, nil
+}
+
+func downloadZip(mod module.Version, target string) error {
+       repo, err := modfetch.Lookup(mod.Path)
+       if err != nil {
+               return err
+       }
+       tmpfile, err := repo.Zip(mod.Version, os.TempDir())
+       if err != nil {
+               return err
+       }
+       defer os.Remove(tmpfile)
+
+       // Double-check zip file looks OK.
+       z, err := zip.OpenReader(tmpfile)
+       if err != nil {
+               z.Close()
+               return err
+       }
+       prefix := mod.Path + "@" + mod.Version
+       for _, f := range z.File {
+               if !strings.HasPrefix(f.Name, prefix) {
+                       z.Close()
+                       return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
+               }
+       }
+       z.Close()
+
+       hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash)
+       if err != nil {
+               return err
+       }
+       r, err := os.Open(tmpfile)
+       if err != nil {
+               return err
+       }
+       defer r.Close()
+       w, err := os.Create(target)
+       if err != nil {
+               return err
+       }
+       if _, err := io.Copy(w, r); err != nil {
+               w.Close()
+               return fmt.Errorf("copying: %v", err)
+       }
+       if err := w.Close(); err != nil {
+               return err
+       }
+       return ioutil.WriteFile(target+"hash", []byte(hash), 0666)
+}
+
+var useModHash = false
+var modHash map[module.Version][]string
+
+func initModHash() {
+       if modHash != nil {
+               return
+       }
+       modHash = make(map[module.Version][]string)
+       file := filepath.Join(ModRoot, "go.modverify")
+       data, err := ioutil.ReadFile(file)
+       if err != nil && os.IsNotExist(err) {
+               return
+       }
+       if err != nil {
+               base.Fatalf("vgo: %v", err)
+       }
+       useModHash = true
+       lineno := 0
+       for len(data) > 0 {
+               var line []byte
+               lineno++
+               i := bytes.IndexByte(data, '\n')
+               if i < 0 {
+                       line, data = data, nil
+               } else {
+                       line, data = data[:i], data[i+1:]
+               }
+               f := strings.Fields(string(line))
+               if len(f) == 0 {
+                       // blank line; skip it
+                       continue
+               }
+               if len(f) != 3 {
+                       base.Fatalf("vgo: malformed go.modverify:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
+               }
+               mod := module.Version{Path: f[0], Version: f[1]}
+               modHash[mod] = append(modHash[mod], f[2])
+       }
+}
+
+func checkModHash(mod module.Version) {
+       initModHash()
+       if !useModHash {
+               return
+       }
+
+       data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash"))
+       if err != nil {
+               base.Fatalf("vgo: verifying %s %s: %v", mod.Path, mod.Version, err)
+       }
+       h := strings.TrimSpace(string(data))
+       if !strings.HasPrefix(h, "h1:") {
+               base.Fatalf("vgo: verifying %s %s: unexpected ziphash: %q", mod.Path, mod.Version, h)
+       }
+
+       for _, vh := range modHash[mod] {
+               if h == vh {
+                       return
+               }
+               if strings.HasPrefix(vh, "h1:") {
+                       base.Fatalf("vgo: verifying %s %s: module hash mismatch\n\tdownloaded:   %v\n\tgo.modverify: %v", mod.Path, mod.Version, h, vh)
+               }
+       }
+       if len(modHash[mod]) > 0 {
+               fmt.Fprintf(os.Stderr, "warning: verifying %s %s: unknown hashes in go.modverify: %v; adding %v", mod.Path, mod.Version, strings.Join(modHash[mod], ", "), h)
+       }
+       modHash[mod] = append(modHash[mod], h)
+}
+
+func findModHash(mod module.Version) string {
+       data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash"))
+       if err != nil {
+               return ""
+       }
+       return strings.TrimSpace(string(data))
+}
+
+func writeModHash() {
+       if !useModHash {
+               return
+       }
+
+       var mods []module.Version
+       for m := range modHash {
+               mods = append(mods, m)
+       }
+       sortModules(mods)
+       var buf bytes.Buffer
+       for _, m := range mods {
+               list := modHash[m]
+               sort.Strings(list)
+               for _, h := range list {
+                       fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
+               }
+       }
+
+       file := filepath.Join(ModRoot, "go.modverify")
+       data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "go.modverify"))
+       if bytes.Equal(data, buf.Bytes()) {
+               return
+       }
+
+       if err := ioutil.WriteFile(file, buf.Bytes(), 0666); err != nil {
+               base.Fatalf("vgo: writing go.modverify: %v", err)
+       }
+}
+
+func sortModules(mods []module.Version) {
+       sort.Slice(mods, func(i, j int) bool {
+               mi := mods[i]
+               mj := mods[j]
+               if mi.Path != mj.Path {
+                       return mi.Path < mj.Path
+               }
+               return semver.Compare(mi.Version, mj.Version) < 0
+       })
+}
diff --git a/src/cmd/go/internal/vgo/get.go b/src/cmd/go/internal/vgo/get.go
new file mode 100644 (file)
index 0000000..9fd8497
--- /dev/null
@@ -0,0 +1,152 @@
+// 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.
+
+package vgo
+
+import (
+       "strings"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/modfetch"
+       "cmd/go/internal/module"
+       "cmd/go/internal/mvs"
+       "cmd/go/internal/semver"
+)
+
+var CmdGet = &base.Command{
+       UsageLine: "get [build flags] [packages]",
+       Short:     "download and install versioned modules and dependencies",
+       Long: `
+Get downloads the latest versions of modules containing the named packages,
+along with the versions of the dependencies required by those modules
+(not necessarily the latest ones).
+
+It then installs the named packages, like 'go install'.
+
+The -u flag causes get to download the latest version of dependencies as well.
+
+Each package being updated can be suffixed with @version to specify
+the desired version. Specifying a version older than the one currently
+in use causes a downgrade, which may in turn downgrade other
+modules using that one, to keep everything consistent.
+
+TODO: Make this documentation better once the semantic dust settles.
+       `,
+}
+
+var getU = CmdGet.Flag.Bool("u", false, "")
+
+func init() {
+       CmdGet.Run = runGet // break init loop
+}
+
+func runGet(cmd *base.Command, args []string) {
+       if *getU && len(args) > 0 {
+               base.Fatalf("vgo get: -u not supported with argument list")
+       }
+       if !*getU && len(args) == 0 {
+               base.Fatalf("vgo get: need arguments or -u")
+       }
+
+       if *getU {
+               isGetU = true
+               ImportPaths([]string{"."})
+               return
+       }
+
+       Init()
+       InitMod()
+       var upgrade []module.Version
+       var downgrade []module.Version
+       var newPkgs []string
+       for _, pkg := range args {
+               var path, vers string
+               /* OLD CODE
+               if n := strings.Count(pkg, "(") + strings.Count(pkg, ")"); n > 0 {
+                       i := strings.Index(pkg, "(")
+                       j := strings.Index(pkg, ")")
+                       if n != 2 || i < 0 || j <= i+1 || j != len(pkg)-1 && pkg[j+1] != '/' {
+                               base.Errorf("vgo get: invalid module version syntax: %s", pkg)
+                               continue
+                       }
+                       path, vers = pkg[:i], pkg[i+1:j]
+                       pkg = pkg[:i] + pkg[j+1:]
+               */
+               if i := strings.Index(pkg, "@"); i >= 0 {
+                       path, pkg, vers = pkg[:i], pkg[:i], pkg[i+1:]
+                       if strings.Contains(vers, "@") {
+                               base.Errorf("vgo get: invalid module version syntax: %s", pkg)
+                               continue
+                       }
+               } else {
+                       path = pkg
+                       vers = "latest"
+               }
+               if vers == "none" {
+                       downgrade = append(downgrade, module.Version{Path: path, Version: ""})
+               } else {
+                       info, err := modfetch.Query(path, vers, allowed)
+                       if err != nil {
+                               base.Errorf("vgo get %v: %v", pkg, err)
+                               continue
+                       }
+                       upgrade = append(upgrade, module.Version{Path: path, Version: info.Version})
+                       newPkgs = append(newPkgs, pkg)
+               }
+       }
+       args = newPkgs
+
+       // Upgrade.
+       var err error
+       buildList, err = mvs.Upgrade(Target, newReqs(), upgrade...)
+       if err != nil {
+               base.Fatalf("vgo get: %v", err)
+       }
+
+       importPaths([]string{"."})
+
+       // Downgrade anything that went too far.
+       version := make(map[string]string)
+       for _, mod := range buildList {
+               version[mod.Path] = mod.Version
+       }
+       for _, mod := range upgrade {
+               if semver.Compare(mod.Version, version[mod.Path]) < 0 {
+                       downgrade = append(downgrade, mod)
+               }
+       }
+
+       if len(downgrade) > 0 {
+               buildList, err = mvs.Downgrade(Target, newReqs(buildList[1:]...), downgrade...)
+               if err != nil {
+                       base.Fatalf("vgo get: %v", err)
+               }
+
+               // TODO: Check that everything we need to import is still available.
+               /*
+                       local := v.matchPackages("all", v.Reqs[:1])
+                       for _, path := range local {
+                               dir, err := v.importDir(path)
+                               if err != nil {
+                                       return err // TODO
+                               }
+                               imports, testImports, err := imports.ScanDir(dir, v.Tags)
+                               for _, path := range imports {
+                                       xxx
+                               }
+                               for _, path := range testImports {
+                                       xxx
+                               }
+                       }
+               */
+       }
+       writeGoMod()
+
+       if len(args) > 0 {
+               InstallHook(args)
+       }
+}
+
+// Call into "go install". Set by internal/work, which imports us.
+var InstallHook func([]string)
diff --git a/src/cmd/go/internal/vgo/init.go b/src/cmd/go/internal/vgo/init.go
new file mode 100644 (file)
index 0000000..b307b6b
--- /dev/null
@@ -0,0 +1,411 @@
+// 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.
+
+package vgo
+
+import (
+       "bytes"
+       "cmd/go/internal/base"
+       "cmd/go/internal/cfg"
+       "cmd/go/internal/modconv"
+       "cmd/go/internal/modfetch"
+       "cmd/go/internal/modfetch/codehost"
+       "cmd/go/internal/modfile"
+       "cmd/go/internal/module"
+       "cmd/go/internal/mvs"
+       "cmd/go/internal/search"
+       "cmd/go/internal/semver"
+       "encoding/json"
+       "flag"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path"
+       "path/filepath"
+       "regexp"
+       "strconv"
+       "strings"
+)
+
+var (
+       cwd         string
+       enabled     = MustBeVgo
+       MustBeVgo   = mustBeVgo()
+       initialized bool
+
+       ModRoot  string
+       modFile  *modfile.File
+       excluded map[module.Version]bool
+       Target   module.Version
+
+       gopath string
+       srcV   string
+)
+
+func BinDir() string {
+       if !Enabled() {
+               panic("vgo.Bin")
+       }
+       return filepath.Join(gopath, "bin")
+}
+
+func init() {
+       flag.BoolVar(&MustBeVgo, "vgo", MustBeVgo, "require use of modules")
+}
+
+// mustBeVgo reports whether we are invoked as vgo
+// (as opposed to go).
+// If so, we only support builds with go.mod files.
+func mustBeVgo() bool {
+       name := os.Args[0]
+       name = name[strings.LastIndex(name, "/")+1:]
+       name = name[strings.LastIndex(name, `\`)+1:]
+       return strings.HasPrefix(name, "vgo")
+}
+
+func Init() {
+       if initialized {
+               return
+       }
+       initialized = true
+
+       // If this is testgo - the test binary during cmd/go tests - then
+       // do not let it look for a go.mod. Only use vgo support if the
+       // global -vgo flag has been passed on the command line.
+       if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && !MustBeVgo {
+               return
+       }
+
+       // Disable any prompting for passwords by Git.
+       // Only has an effect for 2.3.0 or later, but avoiding
+       // the prompt in earlier versions is just too hard.
+       // If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
+       // prompting.
+       // See golang.org/issue/9341 and golang.org/issue/12706.
+       if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
+               os.Setenv("GIT_TERMINAL_PROMPT", "0")
+       }
+
+       // Disable any ssh connection pooling by Git.
+       // If a Git subprocess forks a child into the background to cache a new connection,
+       // that child keeps stdout/stderr open. After the Git subprocess exits,
+       // os /exec expects to be able to read from the stdout/stderr pipe
+       // until EOF to get all the data that the Git subprocess wrote before exiting.
+       // The EOF doesn't come until the child exits too, because the child
+       // is holding the write end of the pipe.
+       // This is unfortunate, but it has come up at least twice
+       // (see golang.org/issue/13453 and golang.org/issue/16104)
+       // and confuses users when it does.
+       // If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
+       // assume they know what they are doing and don't step on it.
+       // But default to turning off ControlMaster.
+       if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
+               os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
+       }
+
+       var err error
+       cwd, err = os.Getwd()
+       if err != nil {
+               base.Fatalf("go: %v", err)
+       }
+
+       root, _ := FindModuleRoot(cwd, "", MustBeVgo)
+       if root == "" {
+               // If invoked as vgo, insist on a mod file.
+               if MustBeVgo {
+                       base.Fatalf("cannot determine module root; please create a go.mod file there")
+               }
+               return
+       }
+       enabled = true
+       ModRoot = root
+       search.SetModRoot(root)
+}
+
+func Enabled() bool {
+       return false // COMPLETELY OFF FOR NOW
+       /*
+               if !initialized {
+                       panic("vgo: Enabled called before Init")
+               }
+               return enabled
+       */
+}
+
+func InitMod() {
+       if Init(); !Enabled() || modFile != nil {
+               return
+       }
+
+       list := filepath.SplitList(cfg.BuildContext.GOPATH)
+       if len(list) == 0 || list[0] == "" {
+               base.Fatalf("missing $GOPATH")
+       }
+       gopath = list[0]
+       if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
+               base.Fatalf("$GOPATH/go.mod exists but should not")
+       }
+       srcV = filepath.Join(list[0], "src/v")
+       codehost.WorkRoot = filepath.Join(srcV, "cache/vcswork")
+
+       gomod := filepath.Join(ModRoot, "go.mod")
+       data, err := ioutil.ReadFile(gomod)
+       if err != nil {
+               legacyModInit()
+               return
+       }
+
+       f, err := modfile.Parse(gomod, data, fixVersion)
+       if err != nil {
+               // Errors returned by modfile.Parse begin with file:line.
+               base.Fatalf("vgo: errors parsing go.mod:\n%s\n", err)
+       }
+       modFile = f
+
+       if len(f.Syntax.Stmt) == 0 || f.Module == nil {
+               // Empty mod file. Must add module path.
+               path, err := FindModulePath(ModRoot)
+               if err != nil {
+                       base.Fatalf("vgo: %v", err)
+               }
+               f.AddModuleStmt(path)
+       }
+
+       if len(f.Syntax.Stmt) == 1 && f.Module != nil {
+               // Entire file is just a module statement.
+               // Populate require if possible.
+               legacyModInit()
+       }
+
+       excluded = make(map[module.Version]bool)
+       for _, x := range f.Exclude {
+               excluded[x.Mod] = true
+       }
+       Target = f.Module.Mod
+       writeGoMod()
+}
+
+func allowed(m module.Version) bool {
+       return !excluded[m]
+}
+
+func legacyModInit() {
+       if modFile == nil {
+               path, err := FindModulePath(ModRoot)
+               if err != nil {
+                       base.Fatalf("vgo: %v", err)
+               }
+               fmt.Fprintf(os.Stderr, "vgo: creating new go.mod: module %s\n", path)
+               modFile = new(modfile.File)
+               modFile.AddModuleStmt(path)
+       }
+
+       Target = modFile.Module.Mod
+       for _, name := range altConfigs {
+               cfg := filepath.Join(ModRoot, name)
+               data, err := ioutil.ReadFile(cfg)
+               if err == nil {
+                       convert := modconv.Converters[name]
+                       if convert == nil {
+                               return
+                       }
+                       fmt.Fprintf(os.Stderr, "vgo: copying requirements from %s\n", cfg)
+                       cfg = filepath.ToSlash(cfg)
+                       if err := modfetch.ConvertLegacyConfig(modFile, cfg, data); err != nil {
+                               base.Fatalf("vgo: %v", err)
+                       }
+                       if len(modFile.Syntax.Stmt) == 1 {
+                               // Add comment to prevent vgo from re-converting every time it runs.
+                               modFile.AddComment("// vgo: no requirements found in " + name)
+                       }
+                       return
+               }
+       }
+}
+
+var altConfigs = []string{
+       "Gopkg.lock",
+
+       "GLOCKFILE",
+       "Godeps/Godeps.json",
+       "dependencies.tsv",
+       "glide.lock",
+       "vendor.conf",
+       "vendor.yml",
+       "vendor/manifest",
+       "vendor/vendor.json",
+
+       ".git/config",
+}
+
+// Exported only for testing.
+func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) {
+       dir = filepath.Clean(dir)
+       dir1 := dir
+       limit = filepath.Clean(limit)
+
+       // Look for enclosing go.mod.
+       for {
+               if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
+                       return dir, "go.mod"
+               }
+               if dir == limit {
+                       break
+               }
+               d := filepath.Dir(dir)
+               if d == dir {
+                       break
+               }
+               dir = d
+       }
+
+       // Failing that, look for enclosing alternate version config.
+       if legacyConfigOK {
+               dir = dir1
+               for {
+                       for _, name := range altConfigs {
+                               if _, err := os.Stat(filepath.Join(dir, name)); err == nil {
+                                       return dir, name
+                               }
+                       }
+                       if dir == limit {
+                               break
+                       }
+                       d := filepath.Dir(dir)
+                       if d == dir {
+                               break
+                       }
+                       dir = d
+               }
+       }
+
+       return "", ""
+}
+
+// Exported only for testing.
+func FindModulePath(dir string) (string, error) {
+       for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
+               src := filepath.Join(gpdir, "src") + string(filepath.Separator)
+               if strings.HasPrefix(dir, src) {
+                       return filepath.ToSlash(dir[len(src):]), nil
+               }
+       }
+
+       // Cast about for import comments,
+       // first in top-level directory, then in subdirectories.
+       list, _ := ioutil.ReadDir(dir)
+       for _, info := range list {
+               if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
+                       if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
+                               return com, nil
+                       }
+               }
+       }
+       for _, info1 := range list {
+               if info1.IsDir() {
+                       files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name()))
+                       for _, info2 := range files {
+                               if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
+                                       if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
+                                               return path.Dir(com), nil
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // Look for Godeps.json declaring import path.
+       data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
+       var cfg struct{ ImportPath string }
+       json.Unmarshal(data, &cfg)
+       if cfg.ImportPath != "" {
+               return cfg.ImportPath, nil
+       }
+
+       // Look for vendor.json declaring import path.
+       data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
+       var cfg2 struct{ RootPath string }
+       json.Unmarshal(data, &cfg2)
+       if cfg2.RootPath != "" {
+               return cfg2.RootPath, nil
+       }
+
+       // Look for .git/config with github origin as last resort.
+       data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config"))
+       if m := gitOriginRE.FindSubmatch(data); m != nil {
+               return "github.com/" + string(m[1]), nil
+       }
+
+       return "", fmt.Errorf("cannot determine module path for source directory %s (outside GOPATH, no import comments)", dir)
+}
+
+var (
+       gitOriginRE     = regexp.MustCompile(`(?m)^\[remote "origin"\]\r?\n\turl = (?:https://github.com/|git@github.com:|gh:)([^/]+/[^/]+?)(\.git)?\r?\n`)
+       importCommentRE = regexp.MustCompile(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
+)
+
+func findImportComment(file string) string {
+       data, err := ioutil.ReadFile(file)
+       if err != nil {
+               return ""
+       }
+       m := importCommentRE.FindSubmatch(data)
+       if m == nil {
+               return ""
+       }
+       path, err := strconv.Unquote(string(m[1]))
+       if err != nil {
+               return ""
+       }
+       return path
+}
+
+func writeGoMod() {
+       writeModHash()
+
+       if buildList != nil {
+               min, err := mvs.Req(Target, buildList, newReqs())
+               if err != nil {
+                       base.Fatalf("vgo: %v", err)
+               }
+               modFile.SetRequire(min)
+       }
+
+       file := filepath.Join(ModRoot, "go.mod")
+       old, _ := ioutil.ReadFile(file)
+       new, err := modFile.Format()
+       if err != nil {
+               base.Fatalf("vgo: %v", err)
+       }
+       if bytes.Equal(old, new) {
+               return
+       }
+       if err := ioutil.WriteFile(file, new, 0666); err != nil {
+               base.Fatalf("vgo: %v", err)
+       }
+}
+
+func fixVersion(path, vers string) (string, error) {
+       // Special case: remove the old -gopkgin- hack.
+       if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
+               vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
+       }
+
+       // fixVersion is called speculatively on every
+       // module, version pair from every go.mod file.
+       // Avoid the query if it looks OK.
+       _, pathMajor, ok := module.SplitPathVersion(path)
+       if !ok {
+               return "", fmt.Errorf("malformed module path: %s", path)
+       }
+       if semver.IsValid(vers) && vers == semver.Canonical(vers) && module.MatchPathMajor(vers, pathMajor) {
+               return vers, nil
+       }
+
+       info, err := modfetch.Query(path, vers, nil)
+       if err != nil {
+               return "", err
+       }
+       return info.Version, nil
+}
diff --git a/src/cmd/go/internal/vgo/list.go b/src/cmd/go/internal/vgo/list.go
new file mode 100644 (file)
index 0000000..c6656c2
--- /dev/null
@@ -0,0 +1,153 @@
+// 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.
+
+package vgo
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "os"
+       "regexp"
+       "unicode/utf8"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/modfetch"
+       "cmd/go/internal/module"
+)
+
+func ListT(pkgs []string) {
+       if Init(); !Enabled() {
+               base.Fatalf("go list: cannot use -t outside module")
+       }
+       InitMod()
+
+       if len(pkgs) == 0 {
+               base.Fatalf("vgo list -t: need list of modules")
+       }
+
+       for _, pkg := range pkgs {
+               repo, err := modfetch.Lookup(pkg)
+               if err != nil {
+                       base.Errorf("vgo list -t: %v", err)
+                       continue
+               }
+               path := repo.ModulePath()
+               fmt.Printf("%s\n", path)
+               tags, err := repo.Versions("")
+               if err != nil {
+                       base.Errorf("vgo list -t: %v", err)
+                       continue
+               }
+               for _, t := range tags {
+                       if excluded[module.Version{Path: path, Version: t}] {
+                               t += " # excluded"
+                       }
+                       fmt.Printf("\t%s\n", t)
+               }
+       }
+}
+
+func ListM() {
+       if Init(); !Enabled() {
+               base.Fatalf("go list: cannot use -m outside module")
+       }
+       InitMod()
+       iterate(func(*loader) {})
+       printListM(os.Stdout)
+}
+
+func printListM(w io.Writer) {
+       var rows [][]string
+       rows = append(rows, []string{"MODULE", "VERSION"})
+       for _, mod := range buildList {
+               v := mod.Version
+               if v == "" {
+                       v = "-"
+               }
+               rows = append(rows, []string{mod.Path, v})
+               if r := replaced(mod); r != nil {
+                       rows = append(rows, []string{" => " + r.New.Path, r.New.Version})
+               }
+       }
+       printTable(w, rows)
+}
+
+func ListMU() {
+       if Init(); !Enabled() {
+               base.Fatalf("go list: cannot use -m outside module")
+       }
+       InitMod()
+
+       quietLookup = true // do not chatter in v.Lookup
+       iterate(func(*loader) {})
+
+       var rows [][]string
+       rows = append(rows, []string{"MODULE", "VERSION", "LATEST"})
+       for _, mod := range buildList {
+               var latest string
+               v := mod.Version
+               if v == "" {
+                       v = "-"
+                       latest = "-"
+               } else {
+                       info, err := modfetch.Query(mod.Path, "latest", allowed)
+                       if err != nil {
+                               latest = "ERR: " + err.Error()
+                       } else {
+                               latest = info.Version
+                               if !isPseudoVersion(latest) && !info.Time.IsZero() {
+                                       latest += info.Time.Local().Format(" (2006-01-02 15:04)")
+                               }
+                       }
+                       if !isPseudoVersion(mod.Version) {
+                               if info, err := modfetch.Query(mod.Path, mod.Version, nil); err == nil && !info.Time.IsZero() {
+                                       v += info.Time.Local().Format(" (2006-01-02 15:04)")
+                               }
+                       }
+               }
+               if latest == v {
+                       latest = "-"
+               }
+               rows = append(rows, []string{mod.Path, v, latest})
+       }
+       printTable(os.Stdout, rows)
+}
+
+var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
+
+func isPseudoVersion(v string) bool {
+       return pseudoVersionRE.MatchString(v)
+}
+
+func printTable(w io.Writer, rows [][]string) {
+       var max []int
+       for _, row := range rows {
+               for i, c := range row {
+                       n := utf8.RuneCountInString(c)
+                       if i >= len(max) {
+                               max = append(max, n)
+                       } else if max[i] < n {
+                               max[i] = n
+                       }
+               }
+       }
+
+       b := bufio.NewWriter(w)
+       for _, row := range rows {
+               for len(row) > 0 && row[len(row)-1] == "" {
+                       row = row[:len(row)-1]
+               }
+               for i, c := range row {
+                       b.WriteString(c)
+                       if i+1 < len(row) {
+                               for j := utf8.RuneCountInString(c); j < max[i]+2; j++ {
+                                       b.WriteRune(' ')
+                               }
+                       }
+               }
+               b.WriteRune('\n')
+       }
+       b.Flush()
+}
diff --git a/src/cmd/go/internal/vgo/load.go b/src/cmd/go/internal/vgo/load.go
new file mode 100644 (file)
index 0000000..3ebba06
--- /dev/null
@@ -0,0 +1,575 @@
+// 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.
+
+package vgo
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+       "go/build"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/cfg"
+       "cmd/go/internal/imports"
+       "cmd/go/internal/modconv"
+       "cmd/go/internal/modfetch"
+       "cmd/go/internal/modfile"
+       "cmd/go/internal/module"
+       "cmd/go/internal/mvs"
+       "cmd/go/internal/search"
+       "cmd/go/internal/semver"
+)
+
+type importLevel int
+
+const (
+       levelNone          importLevel = 0
+       levelBuild         importLevel = 1
+       levelTest          importLevel = 2
+       levelTestRecursive importLevel = 3
+)
+
+var (
+       buildList []module.Version
+       tags      map[string]bool
+       importmap map[string]string
+       pkgdir    map[string]string
+       pkgmod    map[string]module.Version
+       isGetU    bool
+)
+
+func AddImports(gofiles []string) {
+       if Init(); !Enabled() {
+               return
+       }
+       InitMod()
+
+       imports, testImports, err := imports.ScanFiles(gofiles, tags)
+       if err != nil {
+               base.Fatalf("vgo: %v", err)
+       }
+
+       iterate(func(ld *loader) {
+               ld.importList(imports, levelBuild)
+               ld.importList(testImports, levelBuild)
+       })
+       writeGoMod()
+}
+
+func ImportPaths(args []string) []string {
+       if Init(); !Enabled() {
+               return search.ImportPaths(args)
+       }
+       InitMod()
+
+       paths := importPaths(args)
+       writeGoMod()
+       return paths
+}
+
+func importPaths(args []string) []string {
+       level := levelBuild
+       switch cfg.CmdName {
+       case "test", "vet":
+               level = levelTest
+       }
+       cleaned := search.CleanImportPaths(args)
+       iterate(func(ld *loader) {
+               args = expandImportPaths(cleaned)
+               for i, pkg := range args {
+                       if pkg == "." || pkg == ".." || strings.HasPrefix(pkg, "./") || strings.HasPrefix(pkg, "../") {
+                               dir := filepath.Join(cwd, pkg)
+                               if dir == ModRoot {
+                                       pkg = Target.Path
+                               } else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
+                                       pkg = Target.Path + filepath.ToSlash(dir[len(ModRoot):])
+                               } else {
+                                       base.Errorf("vgo: package %s outside module root", pkg)
+                                       continue
+                               }
+                               args[i] = pkg
+                       }
+                       ld.importPkg(pkg, level)
+               }
+       })
+       return args
+}
+
+func Lookup(parentPath, path string) (dir, realPath string, err error) {
+       realPath = importmap[path]
+       if realPath == "" {
+               if isStandardImportPath(path) {
+                       dir := filepath.Join(cfg.GOROOT, "src", path)
+                       if _, err := os.Stat(dir); err == nil {
+                               return dir, path, nil
+                       }
+               }
+               return "", "", fmt.Errorf("no such package in module")
+       }
+       return pkgdir[realPath], realPath, nil
+}
+
+func iterate(doImports func(*loader)) {
+       var err error
+       mvsOp := mvs.BuildList
+       if isGetU {
+               mvsOp = mvs.UpgradeAll
+       }
+       buildList, err = mvsOp(Target, newReqs())
+       if err != nil {
+               base.Fatalf("vgo: %v", err)
+       }
+
+       var ld *loader
+       for {
+               ld = newLoader()
+               doImports(ld)
+               if len(ld.missing) == 0 {
+                       break
+               }
+               for _, m := range ld.missing {
+                       findMissing(m)
+               }
+               base.ExitIfErrors()
+               buildList, err = mvsOp(Target, newReqs())
+               if err != nil {
+                       base.Fatalf("vgo: %v", err)
+               }
+       }
+       base.ExitIfErrors()
+
+       importmap = ld.importmap
+       pkgdir = ld.pkgdir
+       pkgmod = ld.pkgmod
+}
+
+type loader struct {
+       imported  map[string]importLevel
+       importmap map[string]string
+       pkgdir    map[string]string
+       pkgmod    map[string]module.Version
+       tags      map[string]bool
+       missing   []missing
+       imports   []string
+       stack     []string
+}
+
+type missing struct {
+       path  string
+       stack string
+}
+
+func newLoader() *loader {
+       ld := &loader{
+               imported:  make(map[string]importLevel),
+               importmap: make(map[string]string),
+               pkgdir:    make(map[string]string),
+               pkgmod:    make(map[string]module.Version),
+               tags:      imports.Tags(),
+       }
+       ld.imported["C"] = 100
+       return ld
+}
+
+func (ld *loader) stackText() string {
+       var buf bytes.Buffer
+       for _, p := range ld.stack[:len(ld.stack)-1] {
+               fmt.Fprintf(&buf, "import %q ->\n\t", p)
+       }
+       fmt.Fprintf(&buf, "import %q", ld.stack[len(ld.stack)-1])
+       return buf.String()
+}
+
+func (ld *loader) importList(pkgs []string, level importLevel) {
+       for _, pkg := range pkgs {
+               ld.importPkg(pkg, level)
+       }
+}
+
+func (ld *loader) importPkg(path string, level importLevel) {
+       if ld.imported[path] >= level {
+               return
+       }
+
+       ld.stack = append(ld.stack, path)
+       defer func() {
+               ld.stack = ld.stack[:len(ld.stack)-1]
+       }()
+
+       // Any rewritings go here.
+       realPath := path
+
+       ld.imported[path] = level
+       ld.importmap[path] = realPath
+       if realPath != path && ld.imported[realPath] >= level {
+               // Already handled.
+               return
+       }
+
+       dir := ld.importDir(realPath)
+       if dir == "" {
+               return
+       }
+
+       ld.pkgdir[realPath] = dir
+
+       imports, testImports, err := imports.ScanDir(dir, ld.tags)
+       if err != nil {
+               base.Errorf("vgo: %s [%s]: %v", ld.stackText(), dir, err)
+               return
+       }
+       nextLevel := level
+       if level == levelTest {
+               nextLevel = levelBuild
+       }
+       for _, pkg := range imports {
+               ld.importPkg(pkg, nextLevel)
+       }
+       if level >= levelTest {
+               for _, pkg := range testImports {
+                       ld.importPkg(pkg, nextLevel)
+               }
+       }
+}
+
+func (ld *loader) importDir(path string) string {
+       if importPathInModule(path, Target.Path) {
+               dir := ModRoot
+               if len(path) > len(Target.Path) {
+                       dir = filepath.Join(dir, path[len(Target.Path)+1:])
+               }
+               ld.pkgmod[path] = Target
+               return dir
+       }
+
+       i := strings.Index(path, "/")
+       if i < 0 || !strings.Contains(path[:i], ".") {
+               if strings.HasPrefix(path, "golang_org/") {
+                       return filepath.Join(cfg.GOROOT, "src/vendor", path)
+               }
+               dir := filepath.Join(cfg.GOROOT, "src", path)
+               if _, err := os.Stat(dir); err == nil {
+                       return dir
+               }
+       }
+
+       var mod1 module.Version
+       var dir1 string
+       for _, mod := range buildList {
+               if !importPathInModule(path, mod.Path) {
+                       continue
+               }
+               dir, err := fetch(mod)
+               if err != nil {
+                       base.Errorf("vgo: %s: %v", ld.stackText(), err)
+                       return ""
+               }
+               if len(path) > len(mod.Path) {
+                       dir = filepath.Join(dir, path[len(mod.Path)+1:])
+               }
+               if dir1 != "" {
+                       base.Errorf("vgo: %s: found in both %v %v and %v %v", ld.stackText(),
+                               mod1.Path, mod1.Version, mod.Path, mod.Version)
+                       return ""
+               }
+               dir1 = dir
+               mod1 = mod
+       }
+       if dir1 != "" {
+               ld.pkgmod[path] = mod1
+               return dir1
+       }
+       ld.missing = append(ld.missing, missing{path, ld.stackText()})
+       return ""
+}
+
+func replaced(mod module.Version) *modfile.Replace {
+       var found *modfile.Replace
+       for _, r := range modFile.Replace {
+               if r.Old == mod {
+                       found = r // keep going
+               }
+       }
+       return found
+}
+
+func importPathInModule(path, mpath string) bool {
+       return mpath == path ||
+               len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath
+}
+
+var found = make(map[string]bool)
+
+func findMissing(m missing) {
+       for _, mod := range buildList {
+               if importPathInModule(m.path, mod.Path) {
+                       // Leave for ordinary build to complain about the missing import.
+                       return
+               }
+       }
+       if build.IsLocalImport(m.path) {
+               base.Errorf("vgo: relative import is not supported: %s", m.path)
+               return
+       }
+       fmt.Fprintf(os.Stderr, "vgo: resolving import %q\n", m.path)
+       repo, info, err := modfetch.Import(m.path, allowed)
+       if err != nil {
+               base.Errorf("vgo: %s: %v", m.stack, err)
+               return
+       }
+       root := repo.ModulePath()
+       fmt.Fprintf(os.Stderr, "vgo: finding %s (latest)\n", root)
+       if found[root] {
+               base.Fatalf("internal error: findmissing loop on %s", root)
+       }
+       found[root] = true
+       fmt.Fprintf(os.Stderr, "vgo: adding %s %s\n", root, info.Version)
+       buildList = append(buildList, module.Version{Path: root, Version: info.Version})
+       modFile.AddRequire(root, info.Version)
+}
+
+type mvsReqs struct {
+       extra []module.Version
+}
+
+func newReqs(extra ...module.Version) *mvsReqs {
+       r := &mvsReqs{
+               extra: extra,
+       }
+       return r
+}
+
+func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
+       list, err := r.required(mod)
+       if err != nil {
+               return nil, err
+       }
+       if *getU {
+               for i := range list {
+                       list[i].Version = "none"
+               }
+               return list, nil
+       }
+       for i, mv := range list {
+               for excluded[mv] {
+                       mv1, err := r.Next(mv)
+                       if err != nil {
+                               return nil, err
+                       }
+                       if mv1.Version == "" {
+                               return nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)
+                       }
+                       mv = mv1
+               }
+               list[i] = mv
+       }
+       return list, nil
+}
+
+var vgoVersion = []byte(modconv.Prefix)
+
+func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
+       if mod == Target {
+               var list []module.Version
+               if buildList != nil {
+                       list = append(list, buildList[1:]...)
+                       return list, nil
+               }
+               for _, r := range modFile.Require {
+                       list = append(list, r.Mod)
+               }
+               list = append(list, r.extra...)
+               return list, nil
+       }
+
+       origPath := mod.Path
+       if repl := replaced(mod); repl != nil {
+               if repl.New.Version == "" {
+                       // TODO: need to slip the new version into the tags list etc.
+                       dir := repl.New.Path
+                       if !filepath.IsAbs(dir) {
+                               dir = filepath.Join(ModRoot, dir)
+                       }
+                       gomod := filepath.Join(dir, "go.mod")
+                       data, err := ioutil.ReadFile(gomod)
+                       if err != nil {
+                               return nil, err
+                       }
+                       f, err := modfile.Parse(gomod, data, nil)
+                       if err != nil {
+                               return nil, err
+                       }
+                       var list []module.Version
+                       for _, r := range f.Require {
+                               list = append(list, r.Mod)
+                       }
+                       return list, nil
+               }
+               mod = repl.New
+       }
+
+       if mod.Version == "none" {
+               return nil, nil
+       }
+
+       if !semver.IsValid(mod.Version) {
+               // Disallow the broader queries supported by fetch.Lookup.
+               panic(fmt.Errorf("invalid semantic version %q for %s", mod.Version, mod.Path))
+               // TODO return nil, fmt.Errorf("invalid semantic version %q", mod.Version)
+       }
+
+       gomod := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".mod")
+       infofile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".info")
+       var f *modfile.File
+       if data, err := ioutil.ReadFile(gomod); err == nil {
+               // If go.mod has a //vgo comment at the start,
+               // it was auto-converted from a legacy lock file.
+               // The auto-conversion details may have bugs and
+               // may be fixed in newer versions of vgo.
+               // We ignore cached go.mod files if they do not match
+               // our own vgoVersion.
+               if !bytes.HasPrefix(data, vgoVersion[:len("//vgo")]) || bytes.HasPrefix(data, vgoVersion) {
+                       f, err := modfile.Parse(gomod, data, nil)
+                       if err != nil {
+                               return nil, err
+                       }
+                       var list []module.Version
+                       for _, r := range f.Require {
+                               list = append(list, r.Mod)
+                       }
+                       return list, nil
+               }
+               f, err = modfile.Parse("go.mod", data, nil)
+               if err != nil {
+                       return nil, fmt.Errorf("parsing downloaded go.mod: %v", err)
+               }
+       } else {
+               if !quietLookup {
+                       fmt.Fprintf(os.Stderr, "vgo: finding %s %s\n", mod.Path, mod.Version)
+               }
+               repo, err := modfetch.Lookup(mod.Path)
+               if err != nil {
+                       base.Errorf("vgo: %s: %v\n", mod.Path, err)
+                       return nil, err
+               }
+               info, err := repo.Stat(mod.Version)
+               if err != nil {
+                       base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err)
+                       return nil, err
+               }
+               data, err := repo.GoMod(info.Version)
+               if err != nil {
+                       base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err)
+                       return nil, err
+               }
+
+               f, err = modfile.Parse("go.mod", data, nil)
+               if err != nil {
+                       return nil, fmt.Errorf("parsing downloaded go.mod: %v", err)
+               }
+
+               dir := filepath.Dir(gomod)
+               if err := os.MkdirAll(dir, 0777); err != nil {
+                       return nil, fmt.Errorf("caching go.mod: %v", err)
+               }
+               js, err := json.Marshal(info)
+               if err != nil {
+                       return nil, fmt.Errorf("internal error: json failure: %v", err)
+               }
+               if err := ioutil.WriteFile(infofile, js, 0666); err != nil {
+                       return nil, fmt.Errorf("caching info: %v", err)
+               }
+               if err := ioutil.WriteFile(gomod, data, 0666); err != nil {
+                       return nil, fmt.Errorf("caching go.mod: %v", err)
+               }
+       }
+       if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
+               return nil, fmt.Errorf("downloaded %q and got module %q", mod.Path, mpath)
+       }
+
+       var list []module.Version
+       for _, req := range f.Require {
+               list = append(list, req.Mod)
+       }
+       if false {
+               fmt.Fprintf(os.Stderr, "REQLIST %v:\n", mod)
+               for _, req := range list {
+                       fmt.Fprintf(os.Stderr, "\t%v\n", req)
+               }
+       }
+       return list, nil
+}
+
+var quietLookup bool
+
+func (*mvsReqs) Max(v1, v2 string) string {
+       if semver.Compare(v1, v2) == -1 {
+               return v2
+       }
+       return v1
+}
+
+func (*mvsReqs) Latest(path string) (module.Version, error) {
+       // Note that query "latest" is not the same as
+       // using repo.Latest.
+       // The query only falls back to untagged versions
+       // if nothing is tagged. The Latest method
+       // only ever returns untagged versions,
+       // which is not what we want.
+       fmt.Fprintf(os.Stderr, "vgo: finding %s latest\n", path)
+       info, err := modfetch.Query(path, "latest", allowed)
+       if err != nil {
+               return module.Version{}, err
+       }
+       return module.Version{Path: path, Version: info.Version}, nil
+}
+
+var versionCache = make(map[string][]string)
+
+func versions(path string) ([]string, error) {
+       list, ok := versionCache[path]
+       if !ok {
+               var err error
+               repo, err := modfetch.Lookup(path)
+               if err != nil {
+                       return nil, err
+               }
+               list, err = repo.Versions("")
+               if err != nil {
+                       return nil, err
+               }
+               versionCache[path] = list
+       }
+       return list, nil
+}
+
+func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
+       list, err := versions(m.Path)
+       if err != nil {
+               return module.Version{}, err
+       }
+       i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
+       if i > 0 {
+               return module.Version{Path: m.Path, Version: list[i-1]}, nil
+       }
+       return module.Version{Path: m.Path, Version: "none"}, nil
+}
+
+func (*mvsReqs) Next(m module.Version) (module.Version, error) {
+       list, err := versions(m.Path)
+       if err != nil {
+               return module.Version{}, err
+       }
+       i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
+       if i < len(list) {
+               return module.Version{Path: m.Path, Version: list[i]}, nil
+       }
+       return module.Version{Path: m.Path, Version: "none"}, nil
+}
diff --git a/src/cmd/go/internal/vgo/search.go b/src/cmd/go/internal/vgo/search.go
new file mode 100644 (file)
index 0000000..c3f7ab1
--- /dev/null
@@ -0,0 +1,196 @@
+// 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.
+
+package vgo
+
+import (
+       "fmt"
+       "go/build"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/cfg"
+       "cmd/go/internal/imports"
+       "cmd/go/internal/module"
+       "cmd/go/internal/search"
+)
+
+func expandImportPaths(args []string) []string {
+       var out []string
+       for _, a := range args {
+               // TODO(rsc): Move a == "ALL" test into search.IsMetaPackage
+               // once we officially lock in all the module work (tentatively, Go 1.12).
+               if search.IsMetaPackage(a) || a == "ALL" {
+                       switch a {
+                       default:
+                               fmt.Fprintf(os.Stderr, "vgo: warning: %q matches no packages when using modules\n", a)
+                       case "all", "ALL":
+                               out = append(out, AllPackages(a)...)
+                       }
+                       continue
+               }
+               if strings.Contains(a, "...") {
+                       if build.IsLocalImport(a) {
+                               out = append(out, search.AllPackagesInFS(a)...)
+                       } else {
+                               out = append(out, AllPackages(a)...)
+                       }
+                       continue
+               }
+               out = append(out, a)
+       }
+       return out
+}
+
+// AllPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func AllPackages(pattern string) []string {
+       pkgs := MatchPackages(pattern)
+       if len(pkgs) == 0 {
+               fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+       }
+       return pkgs
+}
+
+// MatchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func MatchPackages(pattern string) []string {
+       if pattern == "std" || pattern == "cmd" {
+               return nil
+       }
+       if pattern == "all" {
+               return MatchAll()
+       }
+       if pattern == "ALL" {
+               return MatchALL()
+       }
+
+       return matchPackages(pattern, buildList)
+}
+
+func matchPackages(pattern string, buildList []module.Version) []string {
+       match := func(string) bool { return true }
+       treeCanMatch := func(string) bool { return true }
+       if !search.IsMetaPackage(pattern) && pattern != "ALL" {
+               match = search.MatchPattern(pattern)
+               treeCanMatch = search.TreeCanMatchPattern(pattern)
+       }
+
+       have := map[string]bool{
+               "builtin": true, // ignore pseudo-package that exists only for documentation
+       }
+       if !cfg.BuildContext.CgoEnabled {
+               have["runtime/cgo"] = true // ignore during walk
+       }
+       var pkgs []string
+
+       for _, mod := range buildList {
+               if !treeCanMatch(mod.Path) {
+                       continue
+               }
+               var root string
+               if mod.Version == "" {
+                       root = ModRoot
+               } else {
+                       var err error
+                       root, err = fetch(mod)
+                       if err != nil {
+                               base.Errorf("vgo: %v", err)
+                               continue
+                       }
+               }
+               root = filepath.Clean(root)
+
+               filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+                       if err != nil {
+                               return nil
+                       }
+
+                       want := true
+                       // Avoid .foo, _foo, and testdata directory trees.
+                       _, elem := filepath.Split(path)
+                       if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+                               want = false
+                       }
+
+                       name := mod.Path + filepath.ToSlash(path[len(root):])
+                       if !treeCanMatch(name) {
+                               want = false
+                       }
+
+                       if !fi.IsDir() {
+                               if fi.Mode()&os.ModeSymlink != 0 && want {
+                                       if target, err := os.Stat(path); err == nil && target.IsDir() {
+                                               fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+                                       }
+                               }
+                               return nil
+                       }
+
+                       if !want {
+                               return filepath.SkipDir
+                       }
+                       if path != root {
+                               if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil {
+                                       return filepath.SkipDir
+                               }
+                       }
+
+                       if !have[name] {
+                               have[name] = true
+                               if match(name) {
+                                       if _, _, err := imports.ScanDir(path, imports.Tags()); err != imports.ErrNoGo {
+                                               pkgs = append(pkgs, name)
+                                       }
+                               }
+                       }
+
+                       if elem == "vendor" {
+                               return filepath.SkipDir
+                       }
+                       return nil
+               })
+       }
+       return pkgs
+}
+
+// MatchAll returns a list of the packages matching the pattern "all".
+// We redefine "all" to mean start with the packages in the current module
+// and then follow imports into other modules to add packages imported
+// (directly or indirectly) as part of builds in this module.
+// It does not include packages in other modules that are not needed
+// by builds of this module.
+func MatchAll() []string {
+       return matchAll(imports.Tags())
+}
+
+// MatchALL returns a list of the packages matching the pattern "ALL".
+// The pattern "ALL" is like "all" but looks at all source files,
+// even ones that would be ignored by current build tag settings.
+// That's useful for identifying which packages to include in a vendor directory.
+func MatchALL() []string {
+       return matchAll(map[string]bool{"*": true})
+}
+
+// matchAll is the common implementation of MatchAll and MatchALL,
+// which differ only in the set of tags to apply to select files.
+func matchAll(tags map[string]bool) []string {
+       local := matchPackages("all", buildList[:1])
+       ld := newLoader()
+       ld.tags = tags
+       ld.importList(local, levelTestRecursive)
+       var all []string
+       for _, pkg := range ld.importmap {
+               if !isStandardImportPath(pkg) {
+                       all = append(all, pkg)
+               }
+       }
+       sort.Strings(all)
+       return all
+}
diff --git a/src/cmd/go/internal/vgo/vendor.go b/src/cmd/go/internal/vgo/vendor.go
new file mode 100644 (file)
index 0000000..acba4af
--- /dev/null
@@ -0,0 +1,156 @@
+// 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.
+
+package vgo
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "strings"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/module"
+)
+
+var CmdVendor = &base.Command{
+       UsageLine: "vendor [-v]",
+       Short:     "vendor dependencies of current module",
+       Long: `
+Vendor resets the module's vendor directory to include all
+packages needed to build and test all packages in the module
+and their dependencies.
+
+The -v flag causes vendor to print to standard error the
+module paths of the modules processed and the import paths
+of the packages copied.
+       `,
+}
+
+var vendorV = CmdVendor.Flag.Bool("v", false, "")
+
+func init() {
+       CmdVendor.Run = runVendor // break init cycle
+}
+
+func runVendor(cmd *base.Command, args []string) {
+       if Init(); !Enabled() {
+               base.Fatalf("vgo vendor: cannot use -m outside module")
+       }
+       if len(args) != 0 {
+               base.Fatalf("vgo vendor: vendor takes no arguments")
+       }
+       InitMod()
+       pkgs := ImportPaths([]string{"ALL"})
+
+       vdir := filepath.Join(ModRoot, "vendor")
+       if err := os.RemoveAll(vdir); err != nil {
+               base.Fatalf("vgo vendor: %v", err)
+       }
+
+       modpkgs := make(map[module.Version][]string)
+       for _, pkg := range pkgs {
+               m := pkgmod[pkg]
+               if m == Target {
+                       continue
+               }
+               modpkgs[m] = append(modpkgs[m], pkg)
+       }
+
+       var buf bytes.Buffer
+       for _, m := range buildList[1:] {
+               if pkgs := modpkgs[m]; len(pkgs) > 0 {
+                       repl := ""
+                       if r := replaced(m); r != nil {
+                               repl = " => " + r.New.Path
+                               if r.New.Version != "" {
+                                       repl += " " + r.New.Version
+                               }
+                       }
+                       fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
+                       if *vendorV {
+                               fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
+                       }
+                       for _, pkg := range pkgs {
+                               fmt.Fprintf(&buf, "%s\n", pkg)
+                               if *vendorV {
+                                       fmt.Fprintf(os.Stderr, "%s\n", pkg)
+                               }
+                               vendorPkg(vdir, pkg)
+                       }
+               }
+       }
+       if err := ioutil.WriteFile(filepath.Join(vdir, "vgo.list"), buf.Bytes(), 0666); err != nil {
+               base.Fatalf("vgo vendor: %v", err)
+       }
+}
+
+func vendorPkg(vdir, pkg string) {
+       realPath := importmap[pkg]
+       if realPath != pkg && importmap[realPath] != "" {
+               fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
+       }
+
+       dst := filepath.Join(vdir, pkg)
+       src := pkgdir[realPath]
+       if src == "" {
+               fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
+       }
+       copyDir(dst, src, false)
+}
+
+func copyDir(dst, src string, recursive bool) {
+       files, err := ioutil.ReadDir(src)
+       if err != nil {
+               base.Fatalf("vgo vendor: %v", err)
+       }
+       if err := os.MkdirAll(dst, 0777); err != nil {
+               base.Fatalf("vgo vendor: %v", err)
+       }
+       for _, file := range files {
+               if file.IsDir() {
+                       if recursive || file.Name() == "testdata" {
+                               copyDir(filepath.Join(dst, file.Name()), filepath.Join(src, file.Name()), true)
+                       }
+                       continue
+               }
+               if !file.Mode().IsRegular() {
+                       continue
+               }
+               r, err := os.Open(filepath.Join(src, file.Name()))
+               if err != nil {
+                       base.Fatalf("vgo vendor: %v", err)
+               }
+               w, err := os.Create(filepath.Join(dst, file.Name()))
+               if err != nil {
+                       base.Fatalf("vgo vendor: %v", err)
+               }
+               if _, err := io.Copy(w, r); err != nil {
+                       base.Fatalf("vgo vendor: %v", err)
+               }
+               r.Close()
+               if err := w.Close(); err != nil {
+                       base.Fatalf("vgo vendor: %v", err)
+               }
+       }
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+       switch {
+       default:
+               return false
+       case len(s) == len(prefix):
+               return s == prefix
+       case len(s) > len(prefix):
+               if prefix != "" && prefix[len(prefix)-1] == '/' {
+                       return strings.HasPrefix(s, prefix)
+               }
+               return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+       }
+}
diff --git a/src/cmd/go/internal/vgo/verify.go b/src/cmd/go/internal/vgo/verify.go
new file mode 100644 (file)
index 0000000..d21c9d7
--- /dev/null
@@ -0,0 +1,104 @@
+// 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.
+
+package vgo
+
+import (
+       "bytes"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+
+       "cmd/go/internal/base"
+       "cmd/go/internal/dirhash"
+       "cmd/go/internal/module"
+)
+
+var CmdVerify = &base.Command{
+       UsageLine: "verify",
+       Run:       runVerify,
+       Short:     "verify downloaded modules against expected hashes",
+       Long: `
+Verify checks that the dependencies of the current module,
+which are stored in a local downloaded source cache,
+have not been modified since being downloaded.
+
+If all the modules are unmodified, verify prints
+
+       all modules verified
+
+and exits successfully (status 0). Otherwise, verify reports
+which modules have been changed and exits with a non-zero status.
+       `,
+}
+
+func runVerify(cmd *base.Command, args []string) {
+       if Init(); !Enabled() {
+               base.Fatalf("vgo verify: cannot use outside module")
+       }
+       if len(args) != 0 {
+               // TODO: take arguments
+               base.Fatalf("vgo verify: verify takes no arguments")
+       }
+
+       // Make go.mod consistent but don't load any packages.
+       InitMod()
+       iterate(func(*loader) {})
+       writeGoMod()
+
+       ok := true
+       for _, mod := range buildList[1:] {
+               ok = verifyMod(mod) && ok
+       }
+       if ok {
+               fmt.Printf("all modules verified\n")
+       }
+}
+
+func verifyMod(mod module.Version) bool {
+       ok := true
+       zip := filepath.Join(srcV, "cache", mod.Path, "/@v/", mod.Version+".zip")
+       _, zipErr := os.Stat(zip)
+       dir := filepath.Join(srcV, mod.Path+"@"+mod.Version)
+       _, dirErr := os.Stat(dir)
+       data, err := ioutil.ReadFile(zip + "hash")
+       if err != nil {
+               if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
+                       // Nothing downloaded yet. Nothing to verify.
+                       return true
+               }
+               base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
+               return false
+       }
+       h := string(bytes.TrimSpace(data))
+
+       if zipErr != nil && os.IsNotExist(zipErr) {
+               // ok
+       } else {
+               hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
+               if err != nil {
+                       base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
+                       return false
+               } else if hZ != h {
+                       base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
+                       ok = false
+               }
+       }
+       if dirErr != nil && os.IsNotExist(dirErr) {
+               // ok
+       } else {
+               hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
+               if err != nil {
+
+                       base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
+                       return false
+               }
+               if hD != h {
+                       base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
+                       ok = false
+               }
+       }
+       return ok
+}
diff --git a/src/cmd/go/internal/web2/web.go b/src/cmd/go/internal/web2/web.go
new file mode 100644 (file)
index 0000000..d11ee6b
--- /dev/null
@@ -0,0 +1,297 @@
+// 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.
+
+package web2
+
+import (
+       "bytes"
+       "cmd/go/internal/base"
+       "encoding/json"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "net/http"
+       "os"
+       "path/filepath"
+       "runtime"
+       "runtime/debug"
+       "strings"
+       "sync"
+)
+
+var TraceGET = false
+var webstack = false
+
+func init() {
+       flag.BoolVar(&TraceGET, "webtrace", TraceGET, "trace GET requests")
+       flag.BoolVar(&webstack, "webstack", webstack, "print stack for GET requests")
+}
+
+type netrcLine struct {
+       machine  string
+       login    string
+       password string
+}
+
+var netrcOnce sync.Once
+var netrc []netrcLine
+
+func parseNetrc(data string) []netrcLine {
+       var nrc []netrcLine
+       var l netrcLine
+       for _, line := range strings.Split(data, "\n") {
+               f := strings.Fields(line)
+               for i := 0; i < len(f)-1; i += 2 {
+                       switch f[i] {
+                       case "machine":
+                               l.machine = f[i+1]
+                       case "login":
+                               l.login = f[i+1]
+                       case "password":
+                               l.password = f[i+1]
+                       }
+               }
+               if l.machine != "" && l.login != "" && l.password != "" {
+                       nrc = append(nrc, l)
+                       l = netrcLine{}
+               }
+       }
+       return nrc
+}
+
+func havePassword(machine string) bool {
+       netrcOnce.Do(readNetrc)
+       for _, line := range netrc {
+               if line.machine == machine {
+                       return true
+               }
+       }
+       return false
+}
+
+func netrcPath() string {
+       switch runtime.GOOS {
+       case "windows":
+               return filepath.Join(os.Getenv("USERPROFILE"), "_netrc")
+       case "plan9":
+               return filepath.Join(os.Getenv("home"), ".netrc")
+       default:
+               return filepath.Join(os.Getenv("HOME"), ".netrc")
+       }
+}
+
+func readNetrc() {
+       data, err := ioutil.ReadFile(netrcPath())
+       if err != nil {
+               return
+       }
+       netrc = parseNetrc(string(data))
+}
+
+type getState struct {
+       req      *http.Request
+       resp     *http.Response
+       body     io.ReadCloser
+       non200ok bool
+}
+
+type Option interface {
+       option(*getState) error
+}
+
+func Non200OK() Option {
+       return optionFunc(func(g *getState) error {
+               g.non200ok = true
+               return nil
+       })
+}
+
+type optionFunc func(*getState) error
+
+func (f optionFunc) option(g *getState) error {
+       return f(g)
+}
+
+func DecodeJSON(dst interface{}) Option {
+       return optionFunc(func(g *getState) error {
+               if g.resp != nil {
+                       return json.NewDecoder(g.body).Decode(dst)
+               }
+               return nil
+       })
+}
+
+func ReadAllBody(body *[]byte) Option {
+       return optionFunc(func(g *getState) error {
+               if g.resp != nil {
+                       var err error
+                       *body, err = ioutil.ReadAll(g.body)
+                       return err
+               }
+               return nil
+       })
+}
+
+func Body(body *io.ReadCloser) Option {
+       return optionFunc(func(g *getState) error {
+               if g.resp != nil {
+                       *body = g.body
+                       g.body = nil
+               }
+               return nil
+       })
+}
+
+func Header(hdr *http.Header) Option {
+       return optionFunc(func(g *getState) error {
+               if g.resp != nil {
+                       *hdr = CopyHeader(g.resp.Header)
+               }
+               return nil
+       })
+}
+
+func CopyHeader(hdr http.Header) http.Header {
+       if hdr == nil {
+               return nil
+       }
+       h2 := make(http.Header)
+       for k, v := range hdr {
+               v2 := make([]string, len(v))
+               copy(v2, v)
+               h2[k] = v2
+       }
+       return h2
+}
+
+var cache struct {
+       mu    sync.Mutex
+       byURL map[string]*cacheEntry
+}
+
+type cacheEntry struct {
+       mu   sync.Mutex
+       resp *http.Response
+       body []byte
+}
+
+var httpDo = http.DefaultClient.Do
+
+func SetHTTPDoForTesting(do func(*http.Request) (*http.Response, error)) {
+       if do == nil {
+               do = http.DefaultClient.Do
+       }
+       httpDo = do
+}
+
+func Get(url string, options ...Option) error {
+       if TraceGET || webstack {
+               println("GET", url)
+               if webstack {
+                       println(string(debug.Stack()))
+               }
+       }
+
+       req, err := http.NewRequest("GET", url, nil)
+       if err != nil {
+               return err
+       }
+
+       netrcOnce.Do(readNetrc)
+       for _, l := range netrc {
+               if l.machine == req.URL.Host {
+                       req.SetBasicAuth(l.login, l.password)
+                       break
+               }
+       }
+
+       g := &getState{req: req}
+       for _, o := range options {
+               if err := o.option(g); err != nil {
+                       return err
+               }
+       }
+
+       cache.mu.Lock()
+       e := cache.byURL[url]
+       if e == nil {
+               e = new(cacheEntry)
+               if !strings.HasPrefix(url, "file:") {
+                       if cache.byURL == nil {
+                               cache.byURL = make(map[string]*cacheEntry)
+                       }
+                       cache.byURL[url] = e
+               }
+       }
+       cache.mu.Unlock()
+
+       e.mu.Lock()
+       if strings.HasPrefix(url, "file:") {
+               body, err := ioutil.ReadFile(req.URL.Path)
+               if err != nil {
+                       e.mu.Unlock()
+                       return err
+               }
+               e.body = body
+               e.resp = &http.Response{
+                       StatusCode: 200,
+               }
+       } else if e.resp == nil {
+               resp, err := httpDo(req)
+               if err != nil {
+                       e.mu.Unlock()
+                       return err
+               }
+               e.resp = resp
+               // TODO: Spool to temp file.
+               body, err := ioutil.ReadAll(resp.Body)
+               resp.Body.Close()
+               resp.Body = nil
+               if err != nil {
+                       e.mu.Unlock()
+                       return err
+               }
+               e.body = body
+       }
+       g.resp = e.resp
+       g.body = ioutil.NopCloser(bytes.NewReader(e.body))
+       e.mu.Unlock()
+
+       defer func() {
+               if g.body != nil {
+                       g.body.Close()
+               }
+       }()
+
+       if g.resp.StatusCode == 403 && req.URL.Host == "api.github.com" && !havePassword("api.github.com") {
+               base.Errorf("%s", githubMessage)
+       }
+       if !g.non200ok && g.resp.StatusCode != 200 {
+               return fmt.Errorf("unexpected status (%s): %v", url, g.resp.Status)
+       }
+
+       for _, o := range options {
+               if err := o.option(g); err != nil {
+                       return err
+               }
+       }
+       return err
+}
+
+var githubMessage = `vgo: 403 response from api.github.com
+
+GitHub applies fairly small rate limits to unauthenticated users, and
+you appear to be hitting them. To authenticate, please visit
+https://github.com/settings/tokens and click "Generate New Token" to
+create a Personal Access Token. The token only needs "public_repo"
+scope, but you can add "repo" if you want to access private
+repositories too.
+
+Add the token to your $HOME/.netrc (%USERPROFILE%\_netrc on Windows):
+
+    machine api.github.com login YOU password TOKEN
+
+Sorry for the interruption.
+`
diff --git a/src/cmd/go/internal/web2/web_test.go b/src/cmd/go/internal/web2/web_test.go
new file mode 100644 (file)
index 0000000..c6f6b1e
--- /dev/null
@@ -0,0 +1,35 @@
+// 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.
+
+package web2
+
+import (
+       "reflect"
+       "testing"
+)
+
+var testNetrc = `
+machine api.github.com
+  login user
+  password pwd
+
+machine incomlete.host
+  login justlogin
+  
+machine test.host
+login user2
+password pwd2
+`
+
+func TestReadNetrc(t *testing.T) {
+       lines := parseNetrc(testNetrc)
+       want := []netrcLine{
+               {"api.github.com", "user", "pwd"},
+               {"test.host", "user2", "pwd2"},
+       }
+
+       if !reflect.DeepEqual(lines, want) {
+               t.Errorf("parseNetrc:\nhave %q\nwant %q", lines, want)
+       }
+}
diff --git a/src/cmd/go/internal/webtest/test.go b/src/cmd/go/internal/webtest/test.go
new file mode 100644 (file)
index 0000000..94b20a3
--- /dev/null
@@ -0,0 +1,314 @@
+// 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.
+
+package webtest
+
+import (
+       "bufio"
+       "bytes"
+       "encoding/hex"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "log"
+       "net/http"
+       "os"
+       "sort"
+       "strconv"
+       "strings"
+       "sync"
+       "unicode/utf8"
+
+       web "cmd/go/internal/web2"
+)
+
+var mode = flag.String("webtest", "replay", "set webtest `mode` - record, replay, bypass")
+
+func Hook() {
+       if *mode == "bypass" {
+               return
+       }
+       web.SetHTTPDoForTesting(Do)
+}
+
+func Unhook() {
+       web.SetHTTPDoForTesting(nil)
+}
+
+func Print() {
+       web.SetHTTPDoForTesting(DoPrint)
+}
+
+var responses struct {
+       mu    sync.Mutex
+       byURL map[string]*respEntry
+}
+
+type respEntry struct {
+       status string
+       code   int
+       hdr    http.Header
+       body   []byte
+}
+
+func Serve(url string, status string, hdr http.Header, body []byte) {
+       if status == "" {
+               status = "200 OK"
+       }
+       code, err := strconv.Atoi(strings.Fields(status)[0])
+       if err != nil {
+               panic("bad Serve status - " + status + " - " + err.Error())
+       }
+
+       responses.mu.Lock()
+       defer responses.mu.Unlock()
+
+       if responses.byURL == nil {
+               responses.byURL = make(map[string]*respEntry)
+       }
+       responses.byURL[url] = &respEntry{status: status, code: code, hdr: web.CopyHeader(hdr), body: body}
+}
+
+func Do(req *http.Request) (*http.Response, error) {
+       if req.Method != "GET" {
+               return nil, fmt.Errorf("bad method - must be GET")
+       }
+
+       responses.mu.Lock()
+       e := responses.byURL[req.URL.String()]
+       responses.mu.Unlock()
+
+       if e == nil {
+               if *mode == "record" {
+                       loaded.mu.Lock()
+                       if len(loaded.did) != 1 {
+                               loaded.mu.Unlock()
+                               return nil, fmt.Errorf("cannot use -webtest=record with multiple loaded response files")
+                       }
+                       var file string
+                       for file = range loaded.did {
+                               break
+                       }
+                       loaded.mu.Unlock()
+                       return doSave(file, req)
+               }
+               e = &respEntry{code: 599, status: "599 unexpected request (no canned response)"}
+       }
+       resp := &http.Response{
+               Status:     e.status,
+               StatusCode: e.code,
+               Header:     web.CopyHeader(e.hdr),
+               Body:       ioutil.NopCloser(bytes.NewReader(e.body)),
+       }
+       return resp, nil
+}
+
+func DoPrint(req *http.Request) (*http.Response, error) {
+       return doSave("", req)
+}
+
+func doSave(file string, req *http.Request) (*http.Response, error) {
+       resp, err := http.DefaultClient.Do(req)
+       if err != nil {
+               return nil, err
+       }
+       data, err := ioutil.ReadAll(resp.Body)
+       resp.Body.Close()
+       if err != nil {
+               return nil, err
+       }
+       resp.Body = ioutil.NopCloser(bytes.NewReader(data))
+
+       var f *os.File
+       if file == "" {
+               f = os.Stderr
+       } else {
+               f, err = os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               defer f.Close()
+       }
+
+       fmt.Fprintf(f, "GET %s\n", req.URL.String())
+       fmt.Fprintf(f, "%s\n", resp.Status)
+       var keys []string
+       for k := range resp.Header {
+               keys = append(keys, k)
+       }
+       sort.Strings(keys)
+       for _, k := range keys {
+               if k == "Set-Cookie" {
+                       continue
+               }
+               for _, v := range resp.Header[k] {
+                       fmt.Fprintf(f, "%s: %s\n", k, v)
+               }
+       }
+       fmt.Fprintf(f, "\n")
+       if utf8.Valid(data) && !bytes.Contains(data, []byte("\nGET")) && !isHexDump(data) {
+               fmt.Fprintf(f, "%s\n\n", data)
+       } else {
+               fmt.Fprintf(f, "%s\n", hex.Dump(data))
+       }
+       return resp, err
+}
+
+var loaded struct {
+       mu  sync.Mutex
+       did map[string]bool
+}
+
+func LoadOnce(file string) {
+       loaded.mu.Lock()
+       if loaded.did[file] {
+               loaded.mu.Unlock()
+               return
+       }
+       if loaded.did == nil {
+               loaded.did = make(map[string]bool)
+       }
+       loaded.did[file] = true
+       loaded.mu.Unlock()
+
+       f, err := os.Open(file)
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer f.Close()
+
+       b := bufio.NewReader(f)
+       var ungetLine string
+       nextLine := func() string {
+               if ungetLine != "" {
+                       l := ungetLine
+                       ungetLine = ""
+                       return l
+               }
+               line, err := b.ReadString('\n')
+               if err != nil {
+                       if err == io.EOF {
+                               return ""
+                       }
+                       log.Fatalf("%s: unexpected read error: %v", file, err)
+               }
+               return line
+       }
+
+       for {
+               line := nextLine()
+               if line == "" { // EOF
+                       break
+               }
+               line = strings.TrimSpace(line)
+               if strings.HasPrefix(line, "#") || line == "" {
+                       continue
+               }
+               if !strings.HasPrefix(line, "GET ") {
+                       log.Fatalf("%s: malformed GET line: %s", file, line)
+               }
+               url := line[len("GET "):]
+               status := nextLine()
+               if _, err := strconv.Atoi(strings.Fields(status)[0]); err != nil {
+                       log.Fatalf("%s: malformed status line (after GET %s): %s", file, url, status)
+               }
+               hdr := make(http.Header)
+               for {
+                       kv := strings.TrimSpace(nextLine())
+                       if kv == "" {
+                               break
+                       }
+                       i := strings.Index(kv, ":")
+                       if i < 0 {
+                               log.Fatalf("%s: malformed header line (after GET %s): %s", file, url, kv)
+                       }
+                       k, v := kv[:i], strings.TrimSpace(kv[i+1:])
+                       hdr[k] = append(hdr[k], v)
+               }
+
+               var body []byte
+       Body:
+               for n := 0; ; n++ {
+                       line := nextLine()
+                       if n == 0 && isHexDump([]byte(line)) {
+                               ungetLine = line
+                               b, err := parseHexDump(nextLine)
+                               if err != nil {
+                                       log.Fatalf("%s: malformed hex dump (after GET %s): %v", file, url, err)
+                               }
+                               body = b
+                               break
+                       }
+                       if line == "" { // EOF
+                               for i := 0; i < 2; i++ {
+                                       if len(body) > 0 && body[len(body)-1] == '\n' {
+                                               body = body[:len(body)-1]
+                                       }
+                               }
+                               break
+                       }
+                       body = append(body, line...)
+                       for line == "\n" {
+                               line = nextLine()
+                               if strings.HasPrefix(line, "GET ") {
+                                       ungetLine = line
+                                       body = body[:len(body)-1]
+                                       if len(body) > 0 {
+                                               body = body[:len(body)-1]
+                                       }
+                                       break Body
+                               }
+                               body = append(body, line...)
+                       }
+               }
+
+               Serve(url, status, hdr, body)
+       }
+}
+
+func isHexDump(data []byte) bool {
+       return bytes.HasPrefix(data, []byte("00000000  ")) || bytes.HasPrefix(data, []byte("0000000 "))
+}
+
+// parseHexDump parses the hex dump in text, which should be the
+// output of "hexdump -C" or Plan 9's "xd -b" or Go's hex.Dump
+// and returns the original data used to produce the dump.
+// It is meant to enable storing golden binary files as text, so that
+// changes to the golden files can be seen during code reviews.
+func parseHexDump(nextLine func() string) ([]byte, error) {
+       var out []byte
+       for {
+               line := nextLine()
+               if line == "" || line == "\n" {
+                       break
+               }
+               if i := strings.Index(line, "|"); i >= 0 { // remove text dump
+                       line = line[:i]
+               }
+               f := strings.Fields(line)
+               if len(f) > 1+16 {
+                       return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
+               }
+               if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
+                       continue
+               }
+               addr64, err := strconv.ParseUint(f[0], 16, 0)
+               if err != nil {
+                       return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
+               }
+               addr := int(addr64)
+               if len(out) < addr {
+                       out = append(out, make([]byte, addr-len(out))...)
+               }
+               for _, x := range f[1:] {
+                       val, err := strconv.ParseUint(x, 16, 8)
+                       if err != nil {
+                               return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
+                       }
+                       out = append(out, byte(val))
+               }
+       }
+       return out, nil
+}
index 5cb0c2431f2a832b2f939cae3c09bcacdfcfcbe2..35ff25027e11a2a0aea4157b55177469097f68c6 100644 (file)
@@ -18,6 +18,7 @@ import (
        "cmd/go/internal/base"
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
+       "cmd/go/internal/search"
 )
 
 var CmdBuild = &base.Command{
@@ -376,7 +377,7 @@ func libname(args []string, pkgs []*load.Package) (string, error) {
        }
        var haveNonMeta bool
        for _, arg := range args {
-               if load.IsMetaPackage(arg) {
+               if search.IsMetaPackage(arg) {
                        appendName(arg)
                } else {
                        haveNonMeta = true
index 1013e1a11f24fe9df366dcbffe799d7e4ad71929..5c4dc88821b52ba0629ba7425dbc0299f5c00615 100644 (file)
@@ -30,8 +30,15 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
        "cmd/go/internal/str"
+       "cmd/go/internal/vgo"
 )
 
+func init() {
+       vgo.InstallHook = func(args []string) {
+               CmdInstall.Run(CmdInstall, args)
+       }
+}
+
 // actionList returns the list of actions in the dag rooted at root
 // as visited in a depth-first post-order traversal.
 func actionList(root *Action) []*Action {
@@ -599,6 +606,13 @@ func (b *Builder) build(a *Action) (err error) {
                fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built)
        }
 
+       if p.Internal.BuildInfo != "" {
+               if err := b.writeFile(objdir+"_gomod_.go", vgo.ModInfoProg(p.Internal.BuildInfo)); err != nil {
+                       return err
+               }
+               gofiles = append(gofiles, objdir+"_gomod_.go")
+       }
+
        // Compile Go.
        objpkg := objdir + "_pkg_.a"
        ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), len(sfiles) > 0, gofiles)
diff --git a/src/cmd/go/testdata/badmod/go.mod b/src/cmd/go/testdata/badmod/go.mod
new file mode 100644 (file)
index 0000000..f7f6423
--- /dev/null
@@ -0,0 +1 @@
+module m
diff --git a/src/cmd/go/testdata/badmod/x.go b/src/cmd/go/testdata/badmod/x.go
new file mode 100644 (file)
index 0000000..579fb08
--- /dev/null
@@ -0,0 +1,4 @@
+package x
+
+import _ "appengine"
+import _ "nonexistent.rsc.io" // domain does not exist
diff --git a/src/cmd/go/testdata/vendormod/go.mod b/src/cmd/go/testdata/vendormod/go.mod
new file mode 100644 (file)
index 0000000..6f71634
--- /dev/null
@@ -0,0 +1,16 @@
+module m
+
+replace x v1.0.0 => ./x
+
+replace y v1.0.0 => ./y
+
+replace z v1.0.0 => ./z
+
+replace w v1.0.0 => ./w
+
+require (
+       w v1.0.0
+       x v1.0.0
+       y v1.0.0
+       z v1.0.0
+)
diff --git a/src/cmd/go/testdata/vendormod/v1.go b/src/cmd/go/testdata/vendormod/v1.go
new file mode 100644 (file)
index 0000000..6ca04a5
--- /dev/null
@@ -0,0 +1,3 @@
+package m
+
+import _ "x"
diff --git a/src/cmd/go/testdata/vendormod/v2.go b/src/cmd/go/testdata/vendormod/v2.go
new file mode 100644 (file)
index 0000000..8b089e4
--- /dev/null
@@ -0,0 +1,5 @@
+// +build abc
+
+package mMmMmMm
+
+import _ "y"
diff --git a/src/cmd/go/testdata/vendormod/v3.go b/src/cmd/go/testdata/vendormod/v3.go
new file mode 100644 (file)
index 0000000..318b5f0
--- /dev/null
@@ -0,0 +1,5 @@
+// +build !abc
+
+package m
+
+import _ "z"
diff --git a/src/cmd/go/testdata/vendormod/w/go.mod b/src/cmd/go/testdata/vendormod/w/go.mod
new file mode 100644 (file)
index 0000000..ce2a6c1
--- /dev/null
@@ -0,0 +1 @@
+module w
diff --git a/src/cmd/go/testdata/vendormod/w/w.go b/src/cmd/go/testdata/vendormod/w/w.go
new file mode 100644 (file)
index 0000000..a796c0b
--- /dev/null
@@ -0,0 +1 @@
+package w
diff --git a/src/cmd/go/testdata/vendormod/x/go.mod b/src/cmd/go/testdata/vendormod/x/go.mod
new file mode 100644 (file)
index 0000000..c191435
--- /dev/null
@@ -0,0 +1 @@
+module x
diff --git a/src/cmd/go/testdata/vendormod/x/x.go b/src/cmd/go/testdata/vendormod/x/x.go
new file mode 100644 (file)
index 0000000..823aafd
--- /dev/null
@@ -0,0 +1 @@
+package x
diff --git a/src/cmd/go/testdata/vendormod/y/go.mod b/src/cmd/go/testdata/vendormod/y/go.mod
new file mode 100644 (file)
index 0000000..ac82a48
--- /dev/null
@@ -0,0 +1 @@
+module y
diff --git a/src/cmd/go/testdata/vendormod/y/y.go b/src/cmd/go/testdata/vendormod/y/y.go
new file mode 100644 (file)
index 0000000..789ca71
--- /dev/null
@@ -0,0 +1 @@
+package y
diff --git a/src/cmd/go/testdata/vendormod/z/go.mod b/src/cmd/go/testdata/vendormod/z/go.mod
new file mode 100644 (file)
index 0000000..efc58fe
--- /dev/null
@@ -0,0 +1 @@
+module z
diff --git a/src/cmd/go/testdata/vendormod/z/z.go b/src/cmd/go/testdata/vendormod/z/z.go
new file mode 100644 (file)
index 0000000..46458cb
--- /dev/null
@@ -0,0 +1 @@
+package z