go/loader: support relative imports like the go tool does
Config.Cwd sets the base directory; os.Getwd is its default. +Test. Change-Id: I213abfb30085cd1306719ed6f94aeae6a3170bc0 Reviewed-on: https://go-review.googlesource.com/7502 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
bf80246757
commit
8cc1c75580
|
@ -268,6 +268,11 @@ type Config struct {
|
||||||
// to startup, or by setting Build.CgoEnabled=false.
|
// to startup, or by setting Build.CgoEnabled=false.
|
||||||
Build *build.Context
|
Build *build.Context
|
||||||
|
|
||||||
|
// The current directory, used for resolving relative package
|
||||||
|
// references such as "./go/loader". If empty, os.Getwd will be
|
||||||
|
// used instead.
|
||||||
|
Cwd string
|
||||||
|
|
||||||
// If DisplayPath is non-nil, it is used to transform each
|
// If DisplayPath is non-nil, it is used to transform each
|
||||||
// file name obtained from Build.Import(). This can be used
|
// file name obtained from Build.Import(). This can be used
|
||||||
// to prevent a virtualized build.Config's file names from
|
// to prevent a virtualized build.Config's file names from
|
||||||
|
@ -640,8 +645,24 @@ func (conf *Config) Load() (*Program, error) {
|
||||||
conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
|
conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set default working directory for relative package references.
|
||||||
|
if conf.Cwd == "" {
|
||||||
|
var err error
|
||||||
|
conf.Cwd, err = os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install default FindPackage hook using go/build logic.
|
||||||
if conf.FindPackage == nil {
|
if conf.FindPackage == nil {
|
||||||
conf.FindPackage = defaultFindPackage
|
conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) {
|
||||||
|
bp, err := ctxt.Import(path, conf.Cwd, 0)
|
||||||
|
if _, ok := err.(*build.NoGoError); ok {
|
||||||
|
return bp, nil // empty directory is not an error
|
||||||
|
}
|
||||||
|
return bp, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prog := &Program{
|
prog := &Program{
|
||||||
|
@ -843,17 +864,6 @@ func (conf *Config) build() *build.Context {
|
||||||
return &build.Default
|
return &build.Default
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultFindPackage locates the specified (possibly empty) package
|
|
||||||
// using go/build logic. It returns an error if not found.
|
|
||||||
func defaultFindPackage(ctxt *build.Context, path string) (*build.Package, error) {
|
|
||||||
// Import(srcDir="") disables local imports, e.g. import "./foo".
|
|
||||||
bp, err := ctxt.Import(path, "", 0)
|
|
||||||
if _, ok := err.(*build.NoGoError); ok {
|
|
||||||
return bp, nil // empty directory is not an error
|
|
||||||
}
|
|
||||||
return bp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsePackageFiles enumerates the files belonging to package path,
|
// parsePackageFiles enumerates the files belonging to package path,
|
||||||
// then loads, parses and returns them, plus a list of I/O or parse
|
// then loads, parses and returns them, plus a list of I/O or parse
|
||||||
// errors that were encountered.
|
// errors that were encountered.
|
||||||
|
@ -1084,7 +1094,7 @@ func (imp *importer) loadFromSource(path string) (*PackageInfo, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err // package not found
|
return nil, err // package not found
|
||||||
}
|
}
|
||||||
info := imp.newPackageInfo(path)
|
info := imp.newPackageInfo(bp.ImportPath)
|
||||||
info.Importable = true
|
info.Importable = true
|
||||||
files, errs := imp.conf.parsePackageFiles(bp, 'g')
|
files, errs := imp.conf.parsePackageFiles(bp, 'g')
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
|
|
@ -356,6 +356,38 @@ func TestLoad_BadDependency_AllowErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCwd(t *testing.T) {
|
||||||
|
ctxt := fakeContext(map[string]string{"one/two/three": `package three`})
|
||||||
|
for _, test := range []struct {
|
||||||
|
cwd, arg, want string
|
||||||
|
}{
|
||||||
|
{cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one", arg: "two/three", want: ""},
|
||||||
|
} {
|
||||||
|
conf := loader.Config{
|
||||||
|
Cwd: test.cwd,
|
||||||
|
Build: ctxt,
|
||||||
|
}
|
||||||
|
conf.Import(test.arg)
|
||||||
|
|
||||||
|
var got string
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if prog != nil {
|
||||||
|
got = imported(prog)
|
||||||
|
}
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Load(%s) from %s: Imported = %s, want %s",
|
||||||
|
test.arg, test.cwd, got, test.want)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(adonovan): more Load tests:
|
// TODO(adonovan): more Load tests:
|
||||||
//
|
//
|
||||||
// failures:
|
// failures:
|
||||||
|
|
Loading…
Reference in New Issue