wire/internal/wire: use on-disk GOPATH in generate tests (google/go-cloud#616)

The primary motivation is to permit a move to using go/packages instead
of go/loader. go/packages runs exclusively by shelling out to the go
tool, which precludes use of the in-memory "magic" GOPATH being used
up to this point.

This has a secondary effect of removing a lot of code to support "magic"
GOPATH from the test infrastructure. This is on the whole good, but
necessitated a change in the error scrubbing: since the filenames are
no longer fixed, error scrubbing also must remove the leading
$GOPATH/src lines.

Another related change: since all callers of Generate needed to know the
package path in order to write out wire_gen.go (necessitating a
find-only import search) and Generate already has this information,
Generate now returns this information to the caller. This should further
reduce callers' coupling to Wire's load internals. It also eliminates
code duplication.

This should hopefully shake out any difference in path separators for
running on Windows, but I have not tested that yet.

Updates google/go-cloud#78
Updates google/go-cloud#323
This commit is contained in:
Ross Light
2018-11-06 08:44:51 -08:00
parent ab113bf8d1
commit 64470a2452
24 changed files with 301 additions and 367 deletions

View File

@@ -15,6 +15,7 @@
package wire
import (
"context"
"errors"
"fmt"
"go/ast"
@@ -190,11 +191,19 @@ type Value struct {
info *types.Info
}
// Load finds all the provider sets in the given packages, as well as
// the provider sets' transitive dependencies. It may return both errors
// and Info.
func Load(bctx *build.Context, wd string, pkgs []string) (*Info, []error) {
prog, errs := load(bctx, wd, pkgs)
// Load finds all the provider sets in the packages that match the given
// patterns, as well as the provider sets' transitive dependencies. It
// may return both errors and Info. The patterns are defined by the
// underlying build system. For the go tool, this is described at
// https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
//
// wd is the working directory and env is the set of environment
// variables to use when loading the packages specified by patterns. If
// env is nil or empty, it is interpreted as an empty set of variables.
// In case of duplicate environment variables, the last one in the list
// takes precedence.
func Load(ctx context.Context, wd string, env []string, patterns []string) (*Info, []error) {
prog, errs := load(ctx, wd, env, patterns)
if len(errs) > 0 {
return nil, errs
}
@@ -275,12 +284,22 @@ func Load(bctx *build.Context, wd string, pkgs []string) (*Info, []error) {
return info, ec.errors
}
// load typechecks the packages, including function body type checking
// for the packages directly named.
func load(bctx *build.Context, wd string, pkgs []string) (*loader.Program, []error) {
// load typechecks the packages that match the given patterns, including
// function body type checking for the packages that directly match. The
// patterns are defined by the underlying build system. For the go tool,
// this is described at
// https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
//
// wd is the working directory and env is the set of environment
// variables to use when loading the packages specified by patterns. If
// env is nil or empty, it is interpreted as an empty set of variables.
// In case of duplicate environment variables, the last one in the list
// takes precedence.
func load(ctx context.Context, wd string, env []string, patterns []string) (*loader.Program, []error) {
bctx := buildContextFromEnv(env)
var foundPkgs []*build.Package
ec := new(errorCollector)
for _, name := range pkgs {
for _, name := range patterns {
p, err := bctx.Import(name, wd, build.FindOnly)
if err != nil {
ec.add(err)
@@ -320,7 +339,7 @@ func load(bctx *build.Context, wd string, pkgs []string) (*loader.Program, []err
return pkg, err
},
}
for _, name := range pkgs {
for _, name := range patterns {
conf.Import(name)
}
@@ -334,6 +353,35 @@ func load(bctx *build.Context, wd string, pkgs []string) (*loader.Program, []err
return prog, nil
}
func buildContextFromEnv(env []string) *build.Context {
// TODO(#78): Remove this function in favor of using go/packages,
// which does not need a *build.Context.
getenv := func(name string) string {
for i := len(env) - 1; i >= 0; i-- {
if strings.HasPrefix(env[i], name+"=") {
return env[i][len(name)+1:]
}
}
return ""
}
bctx := new(build.Context)
*bctx = build.Default
if v := getenv("GOARCH"); v != "" {
bctx.GOARCH = v
}
if v := getenv("GOOS"); v != "" {
bctx.GOOS = v
}
if v := getenv("GOROOT"); v != "" {
bctx.GOROOT = v
}
if v := getenv("GOPATH"); v != "" {
bctx.GOPATH = v
}
return bctx
}
func importPathInPkgList(pkgs []*build.Package, path string) bool {
for _, p := range pkgs {
if path == p.ImportPath {