Commit d4e21288 authored by Russ Cox's avatar Russ Cox

cmd/go: add minimal module-awareness for legacy operation

We want authors to be able to publish code that works with both
the current standard go command and the planned new go command
support for modules. If authors have tagged their code v2 or later,
semantic import versioning means the import paths must include a
v2 path element after the path prefix naming the module.
One option for making this convention compatible with original go get
is to move code into a v2 subdirectory of the root.
That makes sense for some authors, but many authors would prefer
not to move all the code into a v2 subdirectory for a transition and
then move it back up once we everyone has a module-aware go command.

Instead, this CL teaches the old (non-module-aware) go command
a tiny amount about modules and their import paths, to expand
the options for authors who want to publish compatible packages.
If an author has a v2 of a package, say my/thing/v2/sub/pkg,
in the my/thing repo's sub/pkg subdirectory (no v2 in the file system path),
then old go get continues to import that package as my/thing/sub/pkg.
But when go get is processing code in any module (code in a tree with
a go.mod file) and encounters a path like my/thing/v2/sub/pkg,
it will check to see if my/thing/go.mod says "module my/thing/v2".
If so, the go command will read the import my/thing/v2/sub/pkg
as if it said my/thing/sub/pkg, which is the correct "old" import path
for the package in question.

This CL will be back-ported to Go 1.10 and Go 1.9 as well.

Once users have updated to the latest Go point releases containing
this new logic, authors will be able to update to using modules
within their own repos, including using semantic import paths
with vN path elements, and old go get will still be able to consume
those repositories.

This CL also makes "go get" ignore meta go-import lines using
the new "mod" VCS type. This allows a package to specify both
a "mod" type and a "git" type, to present more efficient module
access to module-aware go but still present a Git repo to the old
"go get".

Fixes #24751.
Fixes #25069.

Change-Id: I378955613a0d63834d4f50f121f4db7e4d87dc0a
Reviewed-on: https://go-review.googlesource.com/109340
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent b9ecac03
......@@ -55,6 +55,13 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
continue
}
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
// Ignore VCS type "mod", which is new Go modules.
// This code is for old go get and must ignore the new mod lines.
// Otherwise matchGoImport will complain about two
// different metaImport lines for the same Prefix.
if f[1] == "mod" {
continue
}
imports = append(imports, metaImport{
Prefix: f[0],
VCS: f[1],
......
......@@ -209,7 +209,7 @@ var downloadRootCache = map[string]bool{}
// download runs the download half of the get command
// for the package named by the argument.
func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) {
if mode&load.UseVendor != 0 {
if mode&load.ResolveImport != 0 {
// Caller is responsible for expanding vendor paths.
panic("internal error: download mode has useVendor set")
}
......
......@@ -47,6 +47,20 @@ var parseMetaGoImportsTests = []struct {
{"baz/quux", "git", "http://github.com/rsc/baz/quux"},
},
},
{
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
},
},
{
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
},
},
{
`<head>
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
......
......@@ -296,8 +296,8 @@ func runList(cmd *base.Command, args []string) {
for _, p := range pkgs {
// Show vendor-expanded paths in listing
p.TestImports = p.Vendored(p.TestImports)
p.XTestImports = p.Vendored(p.XTestImports)
p.TestImports = p.Resolve(p.TestImports)
p.XTestImports = p.Resolve(p.XTestImports)
}
if *listTest {
......
This diff is collapsed.
......@@ -58,7 +58,7 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
stk.Push(p.ImportPath + " (test)")
rawTestImports := str.StringList(p.TestImports)
for i, path := range p.TestImports {
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], UseVendor)
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport)
if p1.Error != nil {
return nil, nil, nil, p1.Error
}
......@@ -86,7 +86,7 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
pxtestNeedsPtest := false
rawXTestImports := str.StringList(p.XTestImports)
for i, path := range p.XTestImports {
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], UseVendor)
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport)
if p1.Error != nil {
return nil, nil, nil, p1.Error
}
......
......@@ -600,10 +600,10 @@ func runTest(cmd *base.Command, args []string) {
for _, path := range p.Imports {
deps[path] = true
}
for _, path := range p.Vendored(p.TestImports) {
for _, path := range p.Resolve(p.TestImports) {
deps[path] = true
}
for _, path := range p.Vendored(p.XTestImports) {
for _, path := range p.Resolve(p.XTestImports) {
deps[path] = true
}
}
......
package new
import _ "new/v2/p2"
package p1
import _ "old/p2"
import _ "new/v2"
import _ "new/v2/p2"
import _ "new/sub/v2/x/v1/y" // v2 is module, v1 is directory in module
import _ "new/sub/inner/x" // new/sub/inner/go.mod overrides new/sub/go.mod
package p1
import _ "old/p2"
import _ "new/p1"
import _ "new"
......@@ -328,3 +328,34 @@ func TestVendor12156(t *testing.T) {
tg.grepStderrNot("panic", "panicked")
tg.grepStderr(`cannot find package "x"`, "wrong error")
}
// Module legacy support does path rewriting very similar to vendoring.
func TestModLegacy(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/modlegacy"))
tg.run("list", "-f", "{{.Imports}}", "old/p1")
tg.grepStdout("new/p1", "old/p1 should import new/p1")
tg.run("list", "-f", "{{.Imports}}", "new/p1")
tg.grepStdout("new/p2", "new/p1 should import new/p2 (not new/v2/p2)")
tg.grepStdoutNot("new/v2", "new/p1 should NOT import new/v2*")
tg.grepStdout("new/sub/x/v1/y", "new/p1 should import new/sub/x/v1/y (not new/sub/v2/x/v1/y)")
tg.grepStdoutNot("new/sub/v2", "new/p1 should NOT import new/sub/v2*")
tg.grepStdout("new/sub/inner/x", "new/p1 should import new/sub/inner/x (no rewrites)")
tg.run("build", "old/p1", "new/p1")
}
func TestModLegacyGet(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("."))
tg.run("get", "vcs-test.golang.org/git/modlegacy1-old.git/p1")
tg.run("list", "-f", "{{.Deps}}", "vcs-test.golang.org/git/modlegacy1-old.git/p1")
tg.grepStdout("new.git/p2", "old/p1 should depend on new/p2")
tg.grepStdoutNot("new.git/v2/p2", "old/p1 should NOT depend on new/v2/p2")
tg.run("build", "vcs-test.golang.org/git/modlegacy1-old.git/p1", "vcs-test.golang.org/git/modlegacy1-new.git/p1")
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment