mirror of
https://github.com/golang/go
synced 2024-11-27 03:41:22 -07:00
go/build: use the main module's root when locating module sources
Previously, we were using srcDir, which would apply the wrong module dependencies (including the wrong 'replace' and 'exclude' directives) when locating an import path within a module. Fixes #34860 Change-Id: Ie59dcc2075a7b51ba40f7cd2f62dae27bf58c9b0 Reviewed-on: https://go-review.googlesource.com/c/go/+/203820 Reviewed-by: Jay Conrod <jayconrod@google.com>
This commit is contained in:
parent
ca70ada28d
commit
1d4369fa21
@ -35,6 +35,15 @@ type Context struct {
|
||||
GOOS string // target operating system
|
||||
GOROOT string // Go root
|
||||
GOPATH string // Go path
|
||||
|
||||
// WorkingDir is the caller's working directory, or the empty string to use
|
||||
// the current directory of the running process. In module mode, this is used
|
||||
// to locate the main module.
|
||||
//
|
||||
// If WorkingDir is non-empty, directories passed to Import and ImportDir must
|
||||
// be absolute.
|
||||
WorkingDir string
|
||||
|
||||
CgoEnabled bool // whether cgo files are included
|
||||
UseAllFiles bool // use files regardless of +build lines, file names
|
||||
Compiler string // compiler to assume when computing target paths
|
||||
@ -994,21 +1003,14 @@ var errNoModules = errors.New("not using modules")
|
||||
func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode) error {
|
||||
const debugImportGo = false
|
||||
|
||||
// To invoke the go command, we must know the source directory,
|
||||
// To invoke the go command,
|
||||
// we must not being doing special things like AllowBinary or IgnoreVendor,
|
||||
// and all the file system callbacks must be nil (we're meant to use the local file system).
|
||||
if srcDir == "" || mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
|
||||
if mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
|
||||
ctxt.JoinPath != nil || ctxt.SplitPathList != nil || ctxt.IsAbsPath != nil || ctxt.IsDir != nil || ctxt.HasSubdir != nil || ctxt.ReadDir != nil || ctxt.OpenFile != nil || !equal(ctxt.ReleaseTags, defaultReleaseTags) {
|
||||
return errNoModules
|
||||
}
|
||||
|
||||
// Find the absolute source directory. hasSubdir does not handle
|
||||
// relative paths (and can't because the callbacks don't support this).
|
||||
absSrcDir, err := filepath.Abs(srcDir)
|
||||
if err != nil {
|
||||
return errNoModules
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -1021,12 +1023,29 @@ func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode)
|
||||
// Maybe use modules.
|
||||
}
|
||||
|
||||
if srcDir != "" {
|
||||
var absSrcDir string
|
||||
if filepath.IsAbs(srcDir) {
|
||||
absSrcDir = srcDir
|
||||
} else if ctxt.WorkingDir != "" {
|
||||
return fmt.Errorf("go/build: WorkingDir is non-empty, so relative srcDir is not allowed: %v", srcDir)
|
||||
} else {
|
||||
// Find the absolute source directory. hasSubdir does not handle
|
||||
// relative paths (and can't because the callbacks don't support this).
|
||||
var err error
|
||||
absSrcDir, err = filepath.Abs(srcDir)
|
||||
if err != nil {
|
||||
return errNoModules
|
||||
}
|
||||
}
|
||||
|
||||
// If the source directory is in GOROOT, then the in-process code works fine
|
||||
// and we should keep using it. Moreover, the 'go list' approach below doesn't
|
||||
// take standard-library vendoring into account and will fail.
|
||||
if _, ok := ctxt.hasSubdir(filepath.Join(ctxt.GOROOT, "src"), absSrcDir); ok {
|
||||
return errNoModules
|
||||
}
|
||||
}
|
||||
|
||||
// For efficiency, if path is a standard library package, let the usual lookup code handle it.
|
||||
if ctxt.GOROOT != "" {
|
||||
@ -1039,7 +1058,24 @@ func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode)
|
||||
// Unless GO111MODULE=on, look to see if there is a go.mod.
|
||||
// Since go1.13, it doesn't matter if we're inside GOPATH.
|
||||
if go111Module != "on" {
|
||||
parent := absSrcDir
|
||||
var (
|
||||
parent string
|
||||
err error
|
||||
)
|
||||
if ctxt.WorkingDir == "" {
|
||||
parent, err = os.Getwd()
|
||||
if err != nil {
|
||||
// A nonexistent working directory can't be in a module.
|
||||
return errNoModules
|
||||
}
|
||||
} else {
|
||||
parent, err = filepath.Abs(ctxt.WorkingDir)
|
||||
if err != nil {
|
||||
// If the caller passed a bogus WorkingDir explicitly, that's materially
|
||||
// different from not having modules enabled.
|
||||
return err
|
||||
}
|
||||
}
|
||||
for {
|
||||
info, err := os.Stat(filepath.Join(parent, "go.mod"))
|
||||
if err == nil && !info.IsDir() {
|
||||
@ -1055,10 +1091,9 @@ func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode)
|
||||
|
||||
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)
|
||||
|
||||
// TODO(bcmills): This is wrong if srcDir is in a vendor directory, or if
|
||||
// srcDir is in some module dependency of the main module. The main module
|
||||
// chooses what the import paths mean: individual packages don't.
|
||||
cmd.Dir = srcDir
|
||||
if ctxt.WorkingDir != "" {
|
||||
cmd.Dir = ctxt.WorkingDir
|
||||
}
|
||||
|
||||
var stdout, stderr strings.Builder
|
||||
cmd.Stdout = &stdout
|
||||
@ -1077,7 +1112,7 @@ func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode)
|
||||
)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("go/build: importGo %s: %v\n%s\n", path, err, stderr.String())
|
||||
return fmt.Errorf("go/build: go list %s: %v\n%s\n", path, err, stderr.String())
|
||||
}
|
||||
|
||||
f := strings.SplitN(stdout.String(), "\n", 5)
|
||||
|
@ -320,7 +320,15 @@ func TestShellSafety(t *testing.T) {
|
||||
func TestImportDirNotExist(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t) // really must just have source
|
||||
ctxt := Default
|
||||
ctxt.GOPATH = ""
|
||||
|
||||
emptyDir, err := ioutil.TempDir("", t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(emptyDir)
|
||||
|
||||
ctxt.GOPATH = emptyDir
|
||||
ctxt.WorkingDir = emptyDir
|
||||
|
||||
tests := []struct {
|
||||
label string
|
||||
@ -451,6 +459,7 @@ func TestImportPackageOutsideModule(t *testing.T) {
|
||||
os.Setenv("GOPATH", gopath)
|
||||
ctxt := Default
|
||||
ctxt.GOPATH = gopath
|
||||
ctxt.WorkingDir = filepath.Join(gopath, "src/example.com/p")
|
||||
|
||||
want := "cannot find module providing package"
|
||||
if _, err := ctxt.Import("example.com/p", gopath, FindOnly); err == nil {
|
||||
@ -507,8 +516,11 @@ func TestMissingImportErrorRepetition(t *testing.T) {
|
||||
defer os.Setenv("GOPROXY", os.Getenv("GOPROXY"))
|
||||
os.Setenv("GOPROXY", "off")
|
||||
|
||||
ctxt := Default
|
||||
ctxt.WorkingDir = tmp
|
||||
|
||||
pkgPath := "example.com/hello"
|
||||
if _, err = Import(pkgPath, tmp, FindOnly); err == nil {
|
||||
if _, err = ctxt.Import(pkgPath, tmp, FindOnly); err == nil {
|
||||
t.Fatal("unexpected success")
|
||||
} else if n := strings.Count(err.Error(), pkgPath); n != 1 {
|
||||
t.Fatalf("package path %q appears in error %d times; should appear once\nerror: %v", pkgPath, n, err)
|
||||
|
Loading…
Reference in New Issue
Block a user