go/loader: define a FindPackage hook for build systems not compatible with "go build".

Google's proprietary build system, for example, does not use the
_test.go suffix to distinguish test from non-test files; this
information is stated explicitly in another form.

Change-Id: I3a8e919dbc556b6d5cfea1d2123da2616bd934d4
Reviewed-on: https://go-review.googlesource.com/5450
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/go/loader/loader.go b/go/loader/loader.go
index 0fdda05..209b49a 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -290,6 +290,15 @@
 	// to Program.Created.
 	ImportPkgs map[string]bool
 
+	// FindPackage is called during Load to create the build.Package
+	// for a given import path.  If nil, a default implementation
+	// based on ctxt.Import is used.  A client may use this hook to
+	// adapt to a proprietary build system that does not follow the
+	// "go build" layout conventions, for example.
+	//
+	// It must be safe to call concurrently from multiple goroutines.
+	FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error)
+
 	// PackageCreated is a hook called when a types.Package
 	// is created but before it has been populated.
 	//
@@ -625,6 +634,10 @@
 		conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
 	}
 
+	if conf.FindPackage == nil {
+		conf.FindPackage = defaultFindPackage
+	}
+
 	prog := &Program{
 		Fset:        conf.fset(),
 		Imported:    make(map[string]*PackageInfo),
@@ -663,7 +676,7 @@
 			continue
 		}
 
-		bp, err := conf.findSourcePackage(path)
+		bp, err := conf.FindPackage(conf.build(), path)
 		if err != nil {
 			// Package not found, or can't even parse package declaration.
 			// Already reported by previous loop; ignore it.
@@ -820,12 +833,11 @@
 	return &build.Default
 }
 
-// findSourcePackage locates the specified (possibly empty) package
+// defaultFindPackage locates the specified (possibly empty) package
 // using go/build logic.  It returns an error if not found.
-//
-func (conf *Config) findSourcePackage(path string) (*build.Package, error) {
+func defaultFindPackage(ctxt *build.Context, path string) (*build.Package, error) {
 	// Import(srcDir="") disables local imports, e.g. import "./foo".
-	bp, err := conf.build().Import(path, "", 0)
+	bp, err := ctxt.Import(path, "", 0)
 	if _, ok := err.(*build.NoGoError); ok {
 		return bp, nil // empty directory is not an error
 	}
@@ -1058,7 +1070,7 @@
 // The returned PackageInfo's typeCheck function must be called.
 //
 func (imp *importer) loadFromSource(path string) (*PackageInfo, error) {
-	bp, err := imp.conf.findSourcePackage(path)
+	bp, err := imp.conf.FindPackage(imp.conf.build(), path)
 	if err != nil {
 		return nil, err // package not found
 	}