]> Cypherpunks repositories - gostls13.git/commitdiff
go/build: import packages in module mode when GO111MODULE is "on"
authorJay Conrod <jayconrod@google.com>
Tue, 8 Oct 2019 19:05:41 +0000 (15:05 -0400)
committerJay Conrod <jayconrod@google.com>
Wed, 9 Oct 2019 23:03:27 +0000 (23:03 +0000)
go/build.Import locates package dirctories using 'go list' when in
module mode (finding, downloading, and extracting modules is
complicated, so go/build does not handle it).

Previously, Import used 'go list' if GO111MODULE was not explicitly
off and a go.mod file was present (plus some other conditions). With
this change, if GO111MODULE is "on", a go.mod file does not need to be
present.

Fixes #34669

Change-Id: I9e56871054d4b07c3fc04b6f14a5c8c8e9f3c333
Reviewed-on: https://go-review.googlesource.com/c/go/+/199818
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
src/go/build/build.go
src/go/build/build_test.go

index 526d2fe27e883f16de96488039273596dc4e80e8..deeda35c2acf399e86d3efccd09d73e30b4beb08 100644 (file)
@@ -1008,8 +1008,12 @@ func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode,
                return errNoModules
        }
 
-       // If modules are not enabled, then the in-process code works fine and we should keep using it.
-       switch os.Getenv("GO111MODULE") {
+       // Predict whether module aware mode is enabled by checking the value of
+       // GO111MODULE and looking for a go.mod file in the source directory or
+       // one of its parents. Running 'go env GOMOD' in the source directory would
+       // give a canonical answer, but we'd prefer not to execute another command.
+       go111Module := os.Getenv("GO111MODULE")
+       switch go111Module {
        case "off":
                return errNoModules
        default: // "", "on", "auto", anything else
@@ -1031,19 +1035,21 @@ func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode,
                }
        }
 
-       // Look to see if there is a go.mod.
+       // Unless GO111MODULE=on, look to see if there is a go.mod.
        // Since go1.13, it doesn't matter if we're inside GOPATH.
-       parent := absSrcDir
-       for {
-               info, err := os.Stat(filepath.Join(parent, "go.mod"))
-               if err == nil && !info.IsDir() {
-                       break
-               }
-               d := filepath.Dir(parent)
-               if len(d) >= len(parent) {
-                       return errNoModules // reached top of file system, no go.mod
+       if go111Module != "on" {
+               parent := absSrcDir
+               for {
+                       info, err := os.Stat(filepath.Join(parent, "go.mod"))
+                       if err == nil && !info.IsDir() {
+                               break
+                       }
+                       d := filepath.Dir(parent)
+                       if len(d) >= len(parent) {
+                               return errNoModules // reached top of file system, no go.mod
+                       }
+                       parent = d
                }
-               parent = d
        }
 
        cmd := exec.Command("go", "list", "-e", "-compiler="+ctxt.Compiler, "-tags="+strings.Join(ctxt.BuildTags, ","), "-installsuffix="+ctxt.InstallSuffix, "-f={{.Dir}}\n{{.ImportPath}}\n{{.Root}}\n{{.Goroot}}\n{{if .Error}}{{.Error}}{{end}}\n", "--", path)
index 47a46f8c32dc4bd4cee78475695675a74622cf95..7040a1b85b78081a6e39a371d478a6fcebdd1b95 100644 (file)
@@ -420,6 +420,46 @@ func TestImportVendorParentFailure(t *testing.T) {
        }
 }
 
+// Check that a package is loaded in module mode if GO111MODULE=on, even when
+// no go.mod file is present. It should fail to resolve packages outside std.
+// Verifies golang.org/issue/34669.
+func TestImportPackageOutsideModule(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       // Disable module fetching for this test so that 'go list' fails quickly
+       // without trying to find the latest version of a module.
+       defer os.Setenv("GOPROXY", os.Getenv("GOPROXY"))
+       os.Setenv("GOPROXY", "off")
+
+       // Create a GOPATH in a temporary directory. We don't use testdata
+       // because it's in GOROOT, which interferes with the module heuristic.
+       gopath, err := ioutil.TempDir("", "gobuild-notmodule")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(gopath)
+       if err := os.MkdirAll(filepath.Join(gopath, "src/example.com/p"), 0777); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(filepath.Join(gopath, "src/example.com/p/p.go"), []byte("package p"), 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       defer os.Setenv("GO111MODULE", os.Getenv("GO111MODULE"))
+       os.Setenv("GO111MODULE", "on")
+       defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
+       os.Setenv("GOPATH", gopath)
+       ctxt := Default
+       ctxt.GOPATH = gopath
+
+       want := "cannot find module providing package"
+       if _, err := ctxt.Import("example.com/p", gopath, FindOnly); err == nil {
+               t.Fatal("importing package when no go.mod is present succeeded unexpectedly")
+       } else if errStr := err.Error(); !strings.Contains(errStr, want) {
+               t.Fatalf("error when importing package when no go.mod is present: got %q; want %q", errStr, want)
+       }
+}
+
 func TestImportDirTarget(t *testing.T) {
        testenv.MustHaveGoBuild(t) // really must just have source
        ctxt := Default