Skip to content

Commit 563270c

Browse files
authored
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
1 parent 5eecbe5 commit 563270c

File tree

8 files changed

+143
-15
lines changed

8 files changed

+143
-15
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"guthib.com/foo/pkg"
7+
)
8+
9+
func main() {
10+
fmt.Printf("%s", pkg.NewSample()())
11+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package pkg
2+
3+
import (
4+
"fmt"
5+
6+
"guthib.com/bar"
7+
)
8+
9+
func Here() string {
10+
return "hello"
11+
}
12+
13+
func NewSample() func() string {
14+
return func() string {
15+
return fmt.Sprintf("%s %s", bar.Bar(), Here())
16+
}
17+
}

example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/pkg/pkg_test.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ func TestPackages(t *testing.T) {
8383
expected: "Fromage",
8484
evalFile: "./_pkg11/src/foo/foo.go",
8585
},
86+
{
87+
desc: "vendor dir is a sibling or an uncle",
88+
goPath: "./_pkg12/",
89+
expected: "Yo hello",
90+
topImport: "guthib.com/foo/pkg",
91+
},
92+
{
93+
desc: "eval main with vendor as a sibling",
94+
goPath: "./_pkg12/",
95+
expected: "Yo hello",
96+
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
97+
},
8698
}
8799

88100
for _, test := range testCases {
@@ -116,7 +128,7 @@ func TestPackages(t *testing.T) {
116128
os.Stdout = pw
117129

118130
if _, err := i.Eval(string(data)); err != nil {
119-
t.Fatal(err)
131+
fatalStderrf(t, "%v", err)
120132
}
121133

122134
var buf bytes.Buffer
@@ -127,10 +139,10 @@ func TestPackages(t *testing.T) {
127139
}()
128140

129141
if err := pw.Close(); err != nil {
130-
t.Fatal(err)
142+
fatalStderrf(t, "%v", err)
131143
}
132144
if err := <-errC; err != nil {
133-
t.Fatal(err)
145+
fatalStderrf(t, "%v", err)
134146
}
135147
msg = buf.String()
136148
} else {
@@ -153,12 +165,17 @@ func TestPackages(t *testing.T) {
153165
}
154166

155167
if msg != test.expected {
156-
t.Errorf("Got %q, want %q", msg, test.expected)
168+
fatalStderrf(t, "Got %q, want %q", msg, test.expected)
157169
}
158170
})
159171
}
160172
}
161173

174+
func fatalStderrf(t *testing.T, format string, args ...interface{}) {
175+
fmt.Fprintf(os.Stderr, format+"\n", args...)
176+
t.FailNow()
177+
}
178+
162179
func TestPackagesError(t *testing.T) {
163180
testCases := []struct {
164181
desc string

interp/src.go

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
2222
// In all other cases, absolute import paths are resolved from the GOPATH
2323
// and the nested "vendor" directories.
2424
if isPathRelative(path) {
25-
if rPath == "main" {
25+
if rPath == mainID {
2626
rPath = "."
2727
}
2828
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
@@ -153,7 +153,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
153153

154154
func (interp *Interpreter) rootFromSourceLocation(rPath string) (string, error) {
155155
sourceFile := interp.Name
156-
if rPath != "main" || !strings.HasSuffix(sourceFile, ".go") {
156+
if rPath != mainID || !strings.HasSuffix(sourceFile, ".go") {
157157
return rPath, nil
158158
}
159159
wd, err := os.Getwd()
@@ -188,13 +188,62 @@ func pkgDir(goPath string, root, path string) (string, string, error) {
188188
return "", "", fmt.Errorf("unable to find source related to: %q", path)
189189
}
190190

191-
return pkgDir(goPath, previousRoot(root), path)
191+
rootPath := filepath.Join(goPath, "src", root)
192+
prevRoot, err := previousRoot(rootPath, root)
193+
if err != nil {
194+
return "", "", err
195+
}
196+
197+
return pkgDir(goPath, prevRoot, path)
192198
}
193199

200+
const vendor = "vendor"
201+
194202
// Find the previous source root (vendor > vendor > ... > GOPATH).
195-
func previousRoot(root string) string {
196-
splitRoot := strings.Split(root, string(filepath.Separator))
203+
func previousRoot(rootPath, root string) (string, error) {
204+
rootPath = filepath.Clean(rootPath)
205+
parent, final := filepath.Split(rootPath)
206+
parent = filepath.Clean(parent)
207+
208+
// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
209+
if root != mainID && final != vendor {
210+
root = strings.TrimSuffix(root, string(filepath.Separator))
211+
prefix := strings.TrimSuffix(rootPath, root)
212+
213+
// look for the closest vendor in one of our direct ancestors, as it takes priority.
214+
var vendored string
215+
for {
216+
fi, err := os.Lstat(filepath.Join(parent, vendor))
217+
if err == nil && fi.IsDir() {
218+
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
219+
break
220+
}
221+
if !os.IsNotExist(err) {
222+
return "", err
223+
}
224+
225+
// stop when we reach GOPATH/src/blah
226+
parent = filepath.Dir(parent)
227+
if parent == prefix {
228+
break
229+
}
230+
231+
// just an additional failsafe, stop if we reach the filesystem root.
232+
// TODO(mpl): It should probably be a critical error actually,
233+
// as we shouldn't have gone that high up in the tree.
234+
if parent == string(filepath.Separator) {
235+
break
236+
}
237+
}
197238

239+
if vendored != "" {
240+
return vendored, nil
241+
}
242+
}
243+
244+
// TODO(mpl): the algorithm below might be redundant with the one above,
245+
// but keeping it for now. Investigate/simplify/remove later.
246+
splitRoot := strings.Split(root, string(filepath.Separator))
198247
var index int
199248
for i := len(splitRoot) - 1; i >= 0; i-- {
200249
if splitRoot[i] == "vendor" {
@@ -204,10 +253,10 @@ func previousRoot(root string) string {
204253
}
205254

206255
if index == 0 {
207-
return ""
256+
return "", nil
208257
}
209258

210-
return filepath.Join(splitRoot[:index]...)
259+
return filepath.Join(splitRoot[:index]...), nil
211260
}
212261

213262
func effectivePkg(root, path string) string {

interp/src_test.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,10 @@ func Test_pkgDir(t *testing.T) {
196196

197197
func Test_previousRoot(t *testing.T) {
198198
testCases := []struct {
199-
desc string
200-
root string
201-
expected string
199+
desc string
200+
root string
201+
rootPathSuffix string
202+
expected string
202203
}{
203204
{
204205
desc: "GOPATH",
@@ -215,14 +216,39 @@ func Test_previousRoot(t *testing.T) {
215216
root: "github.com/foo/pkg/vendor/guthib.com/containous/fromage/vendor/guthib.com/containous/fuu",
216217
expected: "github.com/foo/pkg/vendor/guthib.com/containous/fromage",
217218
},
219+
{
220+
desc: "vendor is sibling",
221+
root: "github.com/foo/bar",
222+
rootPathSuffix: "testdata/src/github.com/foo/bar",
223+
expected: "github.com/foo",
224+
},
225+
{
226+
desc: "vendor is uncle",
227+
root: "github.com/foo/bar/baz",
228+
rootPathSuffix: "testdata/src/github.com/foo/bar/baz",
229+
expected: "github.com/foo",
230+
},
218231
}
219232

220233
for _, test := range testCases {
221234
test := test
222235
t.Run(test.desc, func(t *testing.T) {
223236
t.Parallel()
224237

225-
p := previousRoot(test.root)
238+
var rootPath string
239+
if test.rootPathSuffix != "" {
240+
wd, err := os.Getwd()
241+
if err != nil {
242+
t.Fatal(err)
243+
}
244+
rootPath = filepath.Join(wd, test.rootPathSuffix)
245+
} else {
246+
rootPath = vendor
247+
}
248+
p, err := previousRoot(rootPath, test.root)
249+
if err != nil {
250+
t.Error(err)
251+
}
226252

227253
if p != test.expected {
228254
t.Errorf("got: %s, want: %s", p, test.expected)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package baz
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package whatever

0 commit comments

Comments
 (0)