interp: support yet another vendoring case

* interp: support another vendoring case

Namely, when the vendor dir is a sibling (or an uncle) relative to the
current pkg

Fixes #758

* make linter happier

* address review comments

* fix, cleanup, add unit tests

* add dummy files to force dirs into git
This commit is contained in:
mpl
2020-07-15 15:35:04 +02:00
committed by GitHub
parent 5eecbe515b
commit 563270ca02
8 changed files with 144 additions and 16 deletions

View File

@@ -0,0 +1,11 @@
package main
import (
"fmt"
"guthib.com/foo/pkg"
)
func main() {
fmt.Printf("%s", pkg.NewSample()())
}

View File

@@ -0,0 +1,17 @@
package pkg
import (
"fmt"
"guthib.com/bar"
)
func Here() string {
return "hello"
}
func NewSample() func() string {
return func() string {
return fmt.Sprintf("%s %s", bar.Bar(), Here())
}
}

View File

@@ -0,0 +1,6 @@
package bar
// Bar is bar
func Bar() string {
return "Yo"
}

View File

@@ -83,6 +83,18 @@ func TestPackages(t *testing.T) {
expected: "Fromage",
evalFile: "./_pkg11/src/foo/foo.go",
},
{
desc: "vendor dir is a sibling or an uncle",
goPath: "./_pkg12/",
expected: "Yo hello",
topImport: "guthib.com/foo/pkg",
},
{
desc: "eval main with vendor as a sibling",
goPath: "./_pkg12/",
expected: "Yo hello",
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
},
}
for _, test := range testCases {
@@ -116,7 +128,7 @@ func TestPackages(t *testing.T) {
os.Stdout = pw
if _, err := i.Eval(string(data)); err != nil {
t.Fatal(err)
fatalStderrf(t, "%v", err)
}
var buf bytes.Buffer
@@ -127,10 +139,10 @@ func TestPackages(t *testing.T) {
}()
if err := pw.Close(); err != nil {
t.Fatal(err)
fatalStderrf(t, "%v", err)
}
if err := <-errC; err != nil {
t.Fatal(err)
fatalStderrf(t, "%v", err)
}
msg = buf.String()
} else {
@@ -153,12 +165,17 @@ func TestPackages(t *testing.T) {
}
if msg != test.expected {
t.Errorf("Got %q, want %q", msg, test.expected)
fatalStderrf(t, "Got %q, want %q", msg, test.expected)
}
})
}
}
func fatalStderrf(t *testing.T, format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
t.FailNow()
}
func TestPackagesError(t *testing.T) {
testCases := []struct {
desc string

View File

@@ -22,7 +22,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
// In all other cases, absolute import paths are resolved from the GOPATH
// and the nested "vendor" directories.
if isPathRelative(path) {
if rPath == "main" {
if rPath == mainID {
rPath = "."
}
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
@@ -153,7 +153,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
func (interp *Interpreter) rootFromSourceLocation(rPath string) (string, error) {
sourceFile := interp.Name
if rPath != "main" || !strings.HasSuffix(sourceFile, ".go") {
if rPath != mainID || !strings.HasSuffix(sourceFile, ".go") {
return rPath, nil
}
wd, err := os.Getwd()
@@ -188,13 +188,62 @@ func pkgDir(goPath string, root, path string) (string, string, error) {
return "", "", fmt.Errorf("unable to find source related to: %q", path)
}
return pkgDir(goPath, previousRoot(root), path)
rootPath := filepath.Join(goPath, "src", root)
prevRoot, err := previousRoot(rootPath, root)
if err != nil {
return "", "", err
}
// Find the previous source root (vendor > vendor > ... > GOPATH).
func previousRoot(root string) string {
splitRoot := strings.Split(root, string(filepath.Separator))
return pkgDir(goPath, prevRoot, path)
}
const vendor = "vendor"
// Find the previous source root (vendor > vendor > ... > GOPATH).
func previousRoot(rootPath, root string) (string, error) {
rootPath = filepath.Clean(rootPath)
parent, final := filepath.Split(rootPath)
parent = filepath.Clean(parent)
// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
if root != mainID && final != vendor {
root = strings.TrimSuffix(root, string(filepath.Separator))
prefix := strings.TrimSuffix(rootPath, root)
// look for the closest vendor in one of our direct ancestors, as it takes priority.
var vendored string
for {
fi, err := os.Lstat(filepath.Join(parent, vendor))
if err == nil && fi.IsDir() {
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
break
}
if !os.IsNotExist(err) {
return "", err
}
// stop when we reach GOPATH/src/blah
parent = filepath.Dir(parent)
if parent == prefix {
break
}
// just an additional failsafe, stop if we reach the filesystem root.
// TODO(mpl): It should probably be a critical error actually,
// as we shouldn't have gone that high up in the tree.
if parent == string(filepath.Separator) {
break
}
}
if vendored != "" {
return vendored, nil
}
}
// TODO(mpl): the algorithm below might be redundant with the one above,
// but keeping it for now. Investigate/simplify/remove later.
splitRoot := strings.Split(root, string(filepath.Separator))
var index int
for i := len(splitRoot) - 1; i >= 0; i-- {
if splitRoot[i] == "vendor" {
@@ -204,10 +253,10 @@ func previousRoot(root string) string {
}
if index == 0 {
return ""
return "", nil
}
return filepath.Join(splitRoot[:index]...)
return filepath.Join(splitRoot[:index]...), nil
}
func effectivePkg(root, path string) string {

View File

@@ -198,6 +198,7 @@ func Test_previousRoot(t *testing.T) {
testCases := []struct {
desc string
root string
rootPathSuffix string
expected string
}{
{
@@ -215,6 +216,18 @@ func Test_previousRoot(t *testing.T) {
root: "github.com/foo/pkg/vendor/guthib.com/containous/fromage/vendor/guthib.com/containous/fuu",
expected: "github.com/foo/pkg/vendor/guthib.com/containous/fromage",
},
{
desc: "vendor is sibling",
root: "github.com/foo/bar",
rootPathSuffix: "testdata/src/github.com/foo/bar",
expected: "github.com/foo",
},
{
desc: "vendor is uncle",
root: "github.com/foo/bar/baz",
rootPathSuffix: "testdata/src/github.com/foo/bar/baz",
expected: "github.com/foo",
},
}
for _, test := range testCases {
@@ -222,7 +235,20 @@ func Test_previousRoot(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
p := previousRoot(test.root)
var rootPath string
if test.rootPathSuffix != "" {
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
rootPath = filepath.Join(wd, test.rootPathSuffix)
} else {
rootPath = vendor
}
p, err := previousRoot(rootPath, test.root)
if err != nil {
t.Error(err)
}
if p != test.expected {
t.Errorf("got: %s, want: %s", p, test.expected)

View File

@@ -0,0 +1 @@
package baz

View File

@@ -0,0 +1 @@
package whatever