diff --git a/go/buildutil/util.go b/go/buildutil/util.go index 6dc0cfb8..c2d28431 100644 --- a/go/buildutil/util.go +++ b/go/buildutil/util.go @@ -66,11 +66,21 @@ func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Packag // paths will not use `\` unless the PathSeparator // is also `\`, thus we can rely on filepath.ToSlash for some sanity. - dirSlash := path.Dir(filepath.ToSlash(filename)) + "/" + resolvedFilename, err := filepath.EvalSymlinks(filepath.Dir(filename)) + if err != nil { + return nil, fmt.Errorf("can't evaluate symlinks of %s: %v", path.Dir(filename), err) + } + + resolvedDir := filepath.ToSlash(resolvedFilename) + dirSlash := resolvedDir + "/" // We assume that no source root (GOPATH[i] or GOROOT) contains any other. for _, srcdir := range ctxt.SrcDirs() { - srcdirSlash := filepath.ToSlash(srcdir) + "/" + resolvedSrcdir, err := filepath.EvalSymlinks(srcdir) + if err != nil { + continue // e.g. non-existent dir on $GOPATH + } + srcdirSlash := filepath.ToSlash(resolvedSrcdir) + "/" if dirHasPrefix(dirSlash, srcdirSlash) { importPath := dirSlash[len(srcdirSlash) : len(dirSlash)-len("/")] return ctxt.Import(importPath, dir, build.FindOnly) diff --git a/go/buildutil/util_test.go b/go/buildutil/util_test.go index dd55533c..148214dc 100644 --- a/go/buildutil/util_test.go +++ b/go/buildutil/util_test.go @@ -10,6 +10,7 @@ package buildutil_test import ( "go/build" + "io/ioutil" "os" "path/filepath" "runtime" @@ -23,22 +24,46 @@ func TestContainingPackage(t *testing.T) { goroot := runtime.GOROOT() gopath := filepath.SplitList(os.Getenv("GOPATH"))[0] - tests := [][2]string{ - {goroot + "/src/fmt/print.go", "fmt"}, - {goroot + "/src/encoding/json/foo.go", "encoding/json"}, - {goroot + "/src/encoding/missing/foo.go", "(not found)"}, - {gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go", - "golang.org/x/tools/go/buildutil"}, + // Make a symlink to gopath for test + tmp, err := ioutil.TempDir(os.TempDir(), "go") + if err != nil { + t.Errorf("Unable to create a temporary directory in %s", os.TempDir()) } - for _, test := range tests { - file, want := test[0], test[1] - bp, err := buildutil.ContainingPackage(&build.Default, ".", file) - got := bp.ImportPath + + // symlink between $GOPATH/src and /tmp/go/src + // in order to test all possible symlink cases + if err := os.Symlink(gopath+"/src", tmp+"/src"); err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(tmp) + + for _, test := range []struct { + gopath, filename, wantPkg string + }{ + {gopath, goroot + "/src/fmt/print.go", "fmt"}, + {gopath, goroot + "/src/encoding/json/foo.go", "encoding/json"}, + {gopath, goroot + "/src/encoding/missing/foo.go", "(not found)"}, + {gopath, gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go", + "golang.org/x/tools/go/buildutil"}, + {gopath, tmp + "/src/golang.org/x/tools/go/buildutil/util_test.go", + "golang.org/x/tools/go/buildutil"}, + {tmp, gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go", + "golang.org/x/tools/go/buildutil"}, + {tmp, tmp + "/src/golang.org/x/tools/go/buildutil/util_test.go", + "golang.org/x/tools/go/buildutil"}, + } { + var got string + var buildContext = build.Default + buildContext.GOPATH = test.gopath + bp, err := buildutil.ContainingPackage(&buildContext, ".", test.filename) if err != nil { got = "(not found)" + } else { + got = bp.ImportPath } - if got != want { - t.Errorf("ContainingPackage(%q) = %s, want %s", file, got, want) + if got != test.wantPkg { + t.Errorf("ContainingPackage(%q) = %s, want %s", test.filename, got, test.wantPkg) } }