wire: use go/packages for analysis (google/go-cloud#623)
Unfortunately, this does come with a ~4x slowdown to Wire, as it is now pulling source for all transitively depended packages, but not trimming comments or function bodies. This is due to limitations with the ParseFile callback in go/packages. This comes with a single semantic change: when performing analysis, Wire will now evaluate everything with the wireinject build tag. I updated the build tags tests accordingly. Prior to this PR, only the packages directly named by the package patterns would be evaluated with the wireinject build tag. Dependencies would not have the wireinject build tag applied. There isn't a way to selectively apply build tags in go/packages, and there isn't a clear benefit to applying it selectively. Being consistent with other Go tooling provides greater benefit. I deleted the vendoring test, as non-top-level vendoring becomes obsolete with modules. go/packages now parses comments by default, so now the generated code includes comments for non-injector declarations. Fixes google/go-cloud#78
This commit is contained in:
@@ -44,11 +44,11 @@ type call struct {
|
|||||||
// out is the type this step produces.
|
// out is the type this step produces.
|
||||||
out types.Type
|
out types.Type
|
||||||
|
|
||||||
// importPath and name identify the provider to call for kind ==
|
// pkg and name identify the provider to call for kind ==
|
||||||
// funcProviderCall or the type to construct for kind ==
|
// funcProviderCall or the type to construct for kind ==
|
||||||
// structProvider.
|
// structProvider.
|
||||||
importPath string
|
pkg *types.Package
|
||||||
name string
|
name string
|
||||||
|
|
||||||
// args is a list of arguments to call the provider with. Each element is:
|
// args is a list of arguments to call the provider with. Each element is:
|
||||||
// a) one of the givens (args[i] < len(given)), or
|
// a) one of the givens (args[i] < len(given)), or
|
||||||
@@ -199,7 +199,7 @@ dfs:
|
|||||||
}
|
}
|
||||||
calls = append(calls, call{
|
calls = append(calls, call{
|
||||||
kind: kind,
|
kind: kind,
|
||||||
importPath: p.ImportPath,
|
pkg: p.Pkg,
|
||||||
name: p.Name,
|
name: p.Name,
|
||||||
args: args,
|
args: args,
|
||||||
fieldNames: p.Fields,
|
fieldNames: p.Fields,
|
||||||
@@ -419,7 +419,7 @@ func verifyAcyclic(providerMap *typeutil.Map, hasher typeutil.Hasher) []error {
|
|||||||
fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil))
|
fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil))
|
||||||
for j := i; j < len(curr); j++ {
|
for j := i; j < len(curr); j++ {
|
||||||
p := providerMap.At(curr[j]).(*ProvidedType).Provider()
|
p := providerMap.At(curr[j]).(*ProvidedType).Provider()
|
||||||
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.ImportPath, p.Name)
|
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Pkg.Path(), p.Name)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(sb, "%s\n", types.TypeString(a, nil))
|
fmt.Fprintf(sb, "%s\n", types.TypeString(a, nil))
|
||||||
ec.add(errors.New(sb.String()))
|
ec.add(errors.New(sb.String()))
|
||||||
|
|||||||
@@ -19,14 +19,13 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/packages"
|
||||||
"golang.org/x/tools/go/types/typeutil"
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -135,8 +134,8 @@ type IfaceBinding struct {
|
|||||||
// Provider records the signature of a provider. A provider is a
|
// Provider records the signature of a provider. A provider is a
|
||||||
// single Go object, either a function or a named struct type.
|
// single Go object, either a function or a named struct type.
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
// ImportPath is the package path that the Go object resides in.
|
// Pkg is the package that the Go object resides in.
|
||||||
ImportPath string
|
Pkg *types.Package
|
||||||
|
|
||||||
// Name is the name of the Go object.
|
// Name is the name of the Go object.
|
||||||
Name string
|
Name string
|
||||||
@@ -203,22 +202,26 @@ type Value struct {
|
|||||||
// In case of duplicate environment variables, the last one in the list
|
// In case of duplicate environment variables, the last one in the list
|
||||||
// takes precedence.
|
// takes precedence.
|
||||||
func Load(ctx context.Context, wd string, env []string, patterns []string) (*Info, []error) {
|
func Load(ctx context.Context, wd string, env []string, patterns []string) (*Info, []error) {
|
||||||
prog, errs := load(ctx, wd, env, patterns)
|
pkgs, errs := load(ctx, wd, env, patterns)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return nil, errs
|
return nil, errs
|
||||||
}
|
}
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
return new(Info), nil
|
||||||
|
}
|
||||||
|
fset := pkgs[0].Fset
|
||||||
info := &Info{
|
info := &Info{
|
||||||
Fset: prog.Fset,
|
Fset: fset,
|
||||||
Sets: make(map[ProviderSetID]*ProviderSet),
|
Sets: make(map[ProviderSetID]*ProviderSet),
|
||||||
}
|
}
|
||||||
oc := newObjectCache(prog)
|
oc := newObjectCache(pkgs)
|
||||||
ec := new(errorCollector)
|
ec := new(errorCollector)
|
||||||
for _, pkgInfo := range prog.InitialPackages() {
|
for _, pkg := range pkgs {
|
||||||
if isWireImport(pkgInfo.Pkg.Path()) {
|
if isWireImport(pkg.PkgPath) {
|
||||||
// The marker function package confuses analysis.
|
// The marker function package confuses analysis.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
scope := pkgInfo.Pkg.Scope()
|
scope := pkg.Types.Scope()
|
||||||
for _, name := range scope.Names() {
|
for _, name := range scope.Names() {
|
||||||
obj := scope.Lookup(name)
|
obj := scope.Lookup(name)
|
||||||
if !isProviderSetType(obj.Type()) {
|
if !isProviderSetType(obj.Type()) {
|
||||||
@@ -226,7 +229,7 @@ func Load(ctx context.Context, wd string, env []string, patterns []string) (*Inf
|
|||||||
}
|
}
|
||||||
item, errs := oc.get(obj)
|
item, errs := oc.get(obj)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ec.add(notePositionAll(prog.Fset.Position(obj.Pos()), errs)...)
|
ec.add(notePositionAll(fset.Position(obj.Pos()), errs)...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pset := item.(*ProviderSet)
|
pset := item.(*ProviderSet)
|
||||||
@@ -235,47 +238,47 @@ func Load(ctx context.Context, wd string, env []string, patterns []string) (*Inf
|
|||||||
id := ProviderSetID{ImportPath: pset.PkgPath, VarName: name}
|
id := ProviderSetID{ImportPath: pset.PkgPath, VarName: name}
|
||||||
info.Sets[id] = pset
|
info.Sets[id] = pset
|
||||||
}
|
}
|
||||||
for _, f := range pkgInfo.Files {
|
for _, f := range pkg.Syntax {
|
||||||
for _, decl := range f.Decls {
|
for _, decl := range f.Decls {
|
||||||
fn, ok := decl.(*ast.FuncDecl)
|
fn, ok := decl.(*ast.FuncDecl)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buildCall, err := findInjectorBuild(&pkgInfo.Info, fn)
|
buildCall, err := findInjectorBuild(pkg.TypesInfo, fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.add(notePosition(prog.Fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err)))
|
ec.add(notePosition(fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if buildCall == nil {
|
if buildCall == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
set, errs := oc.processNewSet(pkgInfo, buildCall, "")
|
set, errs := oc.processNewSet(pkg.TypesInfo, pkg.PkgPath, buildCall, "")
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ec.add(notePositionAll(prog.Fset.Position(fn.Pos()), errs)...)
|
ec.add(notePositionAll(fset.Position(fn.Pos()), errs)...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sig := pkgInfo.ObjectOf(fn.Name).Type().(*types.Signature)
|
sig := pkg.TypesInfo.ObjectOf(fn.Name).Type().(*types.Signature)
|
||||||
ins, out, err := injectorFuncSignature(sig)
|
ins, out, err := injectorFuncSignature(sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if w, ok := err.(*wireErr); ok {
|
if w, ok := err.(*wireErr); ok {
|
||||||
ec.add(notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error)))
|
ec.add(notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error)))
|
||||||
} else {
|
} else {
|
||||||
ec.add(notePosition(prog.Fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err)))
|
ec.add(notePosition(fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err)))
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, errs = solve(prog.Fset, out.out, ins, set)
|
_, errs = solve(fset, out.out, ins, set)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ec.add(mapErrors(errs, func(e error) error {
|
ec.add(mapErrors(errs, func(e error) error {
|
||||||
if w, ok := e.(*wireErr); ok {
|
if w, ok := e.(*wireErr); ok {
|
||||||
return notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error))
|
return notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error))
|
||||||
}
|
}
|
||||||
return notePosition(prog.Fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, e))
|
return notePosition(fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, e))
|
||||||
})...)
|
})...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
info.Injectors = append(info.Injectors, &Injector{
|
info.Injectors = append(info.Injectors, &Injector{
|
||||||
ImportPath: pkgInfo.Pkg.Path(),
|
ImportPath: pkg.PkgPath,
|
||||||
FuncName: fn.Name.Name,
|
FuncName: fn.Name.Name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -284,111 +287,43 @@ func Load(ctx context.Context, wd string, env []string, patterns []string) (*Inf
|
|||||||
return info, ec.errors
|
return info, ec.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// load typechecks the packages that match the given patterns, including
|
// load typechecks the packages that match the given patterns and
|
||||||
// function body type checking for the packages that directly match. The
|
// includes source for all transitive dependencies. The patterns are
|
||||||
// patterns are defined by the underlying build system. For the go tool,
|
// defined by the underlying build system. For the go tool, this is
|
||||||
// this is described at
|
// described at https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
|
||||||
// https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
|
|
||||||
//
|
//
|
||||||
// wd is the working directory and env is the set of environment
|
// wd is the working directory and env is the set of environment
|
||||||
// variables to use when loading the packages specified by patterns. If
|
// 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.
|
// 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
|
// In case of duplicate environment variables, the last one in the list
|
||||||
// takes precedence.
|
// takes precedence.
|
||||||
func load(ctx context.Context, wd string, env []string, patterns []string) (*loader.Program, []error) {
|
func load(ctx context.Context, wd string, env []string, patterns []string) ([]*packages.Package, []error) {
|
||||||
bctx := buildContextFromEnv(env)
|
cfg := &packages.Config{
|
||||||
var foundPkgs []*build.Package
|
Context: ctx,
|
||||||
ec := new(errorCollector)
|
Mode: packages.LoadAllSyntax,
|
||||||
for _, name := range patterns {
|
Dir: wd,
|
||||||
p, err := bctx.Import(name, wd, build.FindOnly)
|
Env: env,
|
||||||
if err != nil {
|
BuildFlags: []string{"-tags=wireinject"},
|
||||||
ec.add(err)
|
// TODO(light): Use ParseFile to skip function bodies and comments in indirect packages.
|
||||||
continue
|
|
||||||
}
|
|
||||||
foundPkgs = append(foundPkgs, p)
|
|
||||||
}
|
}
|
||||||
if len(ec.errors) > 0 {
|
escaped := make([]string, len(patterns))
|
||||||
return nil, ec.errors
|
for i := range patterns {
|
||||||
}
|
escaped[i] = "pattern=" + patterns[i]
|
||||||
conf := &loader.Config{
|
|
||||||
Build: bctx,
|
|
||||||
Cwd: wd,
|
|
||||||
TypeChecker: types.Config{
|
|
||||||
Error: func(err error) {
|
|
||||||
ec.add(err)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TypeCheckFuncBodies: func(path string) bool {
|
|
||||||
return importPathInPkgList(foundPkgs, path)
|
|
||||||
},
|
|
||||||
FindPackage: func(bctx *build.Context, importPath, fromDir string, mode build.ImportMode) (*build.Package, error) {
|
|
||||||
// Optimistically try to load in the package with normal build tags.
|
|
||||||
pkg, err := bctx.Import(importPath, fromDir, mode)
|
|
||||||
|
|
||||||
// If this is the generated package, then load it in with the
|
|
||||||
// wireinject build tag to pick up the injector template. Since
|
|
||||||
// the *build.Context is shared between calls to FindPackage, this
|
|
||||||
// uses a copy.
|
|
||||||
if pkg != nil && importPathInPkgList(foundPkgs, pkg.ImportPath) {
|
|
||||||
bctx2 := new(build.Context)
|
|
||||||
*bctx2 = *bctx
|
|
||||||
n := len(bctx2.BuildTags)
|
|
||||||
bctx2.BuildTags = append(bctx2.BuildTags[:n:n], "wireinject")
|
|
||||||
pkg, err = bctx2.Import(importPath, fromDir, mode)
|
|
||||||
}
|
|
||||||
return pkg, err
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, name := range patterns {
|
|
||||||
conf.Import(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
prog, err := conf.Load()
|
|
||||||
if len(ec.errors) > 0 {
|
|
||||||
return nil, ec.errors
|
|
||||||
}
|
}
|
||||||
|
pkgs, err := packages.Load(cfg, escaped...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{err}
|
return nil, []error{err}
|
||||||
}
|
}
|
||||||
return prog, nil
|
var errs []error
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
for _, p := range pkgs {
|
||||||
if path == p.ImportPath {
|
for _, e := range p.Errors {
|
||||||
return true
|
errs = append(errs, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
if len(errs) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
return pkgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info holds the result of Load.
|
// Info holds the result of Load.
|
||||||
@@ -427,9 +362,10 @@ func (in *Injector) String() string {
|
|||||||
|
|
||||||
// objectCache is a lazily evaluated mapping of objects to Wire structures.
|
// objectCache is a lazily evaluated mapping of objects to Wire structures.
|
||||||
type objectCache struct {
|
type objectCache struct {
|
||||||
prog *loader.Program
|
fset *token.FileSet
|
||||||
objects map[objRef]objCacheEntry
|
packages map[string]*packages.Package
|
||||||
hasher typeutil.Hasher
|
objects map[objRef]objCacheEntry
|
||||||
|
hasher typeutil.Hasher
|
||||||
}
|
}
|
||||||
|
|
||||||
type objRef struct {
|
type objRef struct {
|
||||||
@@ -442,12 +378,33 @@ type objCacheEntry struct {
|
|||||||
errs []error
|
errs []error
|
||||||
}
|
}
|
||||||
|
|
||||||
func newObjectCache(prog *loader.Program) *objectCache {
|
func newObjectCache(pkgs []*packages.Package) *objectCache {
|
||||||
return &objectCache{
|
if len(pkgs) == 0 {
|
||||||
prog: prog,
|
panic("object cache must have packages to draw from")
|
||||||
objects: make(map[objRef]objCacheEntry),
|
|
||||||
hasher: typeutil.MakeHasher(),
|
|
||||||
}
|
}
|
||||||
|
oc := &objectCache{
|
||||||
|
fset: pkgs[0].Fset,
|
||||||
|
packages: make(map[string]*packages.Package),
|
||||||
|
objects: make(map[objRef]objCacheEntry),
|
||||||
|
hasher: typeutil.MakeHasher(),
|
||||||
|
}
|
||||||
|
// Depth-first search of all dependencies to gather import path to
|
||||||
|
// packages.Package mapping. go/packages guarantees that for a single
|
||||||
|
// call to packages.Load and an import path X, there will exist only
|
||||||
|
// one *packages.Package value with PkgPath X.
|
||||||
|
stk := append([]*packages.Package(nil), pkgs...)
|
||||||
|
for len(stk) > 0 {
|
||||||
|
p := stk[len(stk)-1]
|
||||||
|
stk = stk[:len(stk)-1]
|
||||||
|
if oc.packages[p.PkgPath] != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oc.packages[p.PkgPath] = p
|
||||||
|
for _, imp := range p.Imports {
|
||||||
|
stk = append(stk, imp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return oc
|
||||||
}
|
}
|
||||||
|
|
||||||
// get converts a Go object into a Wire structure. It may return a
|
// get converts a Go object into a Wire structure. It may return a
|
||||||
@@ -478,9 +435,10 @@ func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return oc.processExpr(oc.prog.Package(obj.Pkg().Path()), spec.Values[i], obj.Name())
|
pkgPath := obj.Pkg().Path()
|
||||||
|
return oc.processExpr(oc.packages[pkgPath].TypesInfo, pkgPath, spec.Values[i], obj.Name())
|
||||||
case *types.Func:
|
case *types.Func:
|
||||||
return processFuncProvider(oc.prog.Fset, obj)
|
return processFuncProvider(oc.fset, obj)
|
||||||
default:
|
default:
|
||||||
return nil, []error{fmt.Errorf("%v is not a provider or a provider set", obj)}
|
return nil, []error{fmt.Errorf("%v is not a provider or a provider set", obj)}
|
||||||
}
|
}
|
||||||
@@ -490,10 +448,10 @@ func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) {
|
|||||||
func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
|
func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
|
||||||
// TODO(light): Walk files to build object -> declaration mapping, if more performant.
|
// TODO(light): Walk files to build object -> declaration mapping, if more performant.
|
||||||
// Recommended by https://golang.org/s/types-tutorial
|
// Recommended by https://golang.org/s/types-tutorial
|
||||||
pkg := oc.prog.Package(obj.Pkg().Path())
|
pkg := oc.packages[obj.Pkg().Path()]
|
||||||
pos := obj.Pos()
|
pos := obj.Pos()
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Syntax {
|
||||||
tokenFile := oc.prog.Fset.File(f.Pos())
|
tokenFile := oc.fset.File(f.Pos())
|
||||||
if base := tokenFile.Base(); base <= int(pos) && int(pos) < base+tokenFile.Size() {
|
if base := tokenFile.Base(); base <= int(pos) && int(pos) < base+tokenFile.Size() {
|
||||||
path, _ := astutil.PathEnclosingInterval(f, pos, pos)
|
path, _ := astutil.PathEnclosingInterval(f, pos, pos)
|
||||||
for _, node := range path {
|
for _, node := range path {
|
||||||
@@ -508,38 +466,38 @@ func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
|
|||||||
|
|
||||||
// processExpr converts an expression into a Wire structure. It may
|
// processExpr converts an expression into a Wire structure. It may
|
||||||
// return a *Provider, an *IfaceBinding, a *ProviderSet, or a *Value.
|
// return a *Provider, an *IfaceBinding, a *ProviderSet, or a *Value.
|
||||||
func (oc *objectCache) processExpr(pkg *loader.PackageInfo, expr ast.Expr, varName string) (interface{}, []error) {
|
func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Expr, varName string) (interface{}, []error) {
|
||||||
exprPos := oc.prog.Fset.Position(expr.Pos())
|
exprPos := oc.fset.Position(expr.Pos())
|
||||||
expr = astutil.Unparen(expr)
|
expr = astutil.Unparen(expr)
|
||||||
if obj := qualifiedIdentObject(&pkg.Info, expr); obj != nil {
|
if obj := qualifiedIdentObject(info, expr); obj != nil {
|
||||||
item, errs := oc.get(obj)
|
item, errs := oc.get(obj)
|
||||||
return item, mapErrors(errs, func(err error) error {
|
return item, mapErrors(errs, func(err error) error {
|
||||||
return notePosition(exprPos, err)
|
return notePosition(exprPos, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if call, ok := expr.(*ast.CallExpr); ok {
|
if call, ok := expr.(*ast.CallExpr); ok {
|
||||||
fnObj := qualifiedIdentObject(&pkg.Info, call.Fun)
|
fnObj := qualifiedIdentObject(info, call.Fun)
|
||||||
if fnObj == nil || !isWireImport(fnObj.Pkg().Path()) {
|
if fnObj == nil || !isWireImport(fnObj.Pkg().Path()) {
|
||||||
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
|
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
|
||||||
}
|
}
|
||||||
switch fnObj.Name() {
|
switch fnObj.Name() {
|
||||||
case "NewSet":
|
case "NewSet":
|
||||||
pset, errs := oc.processNewSet(pkg, call, varName)
|
pset, errs := oc.processNewSet(info, pkgPath, call, varName)
|
||||||
return pset, notePositionAll(exprPos, errs)
|
return pset, notePositionAll(exprPos, errs)
|
||||||
case "Bind":
|
case "Bind":
|
||||||
b, err := processBind(oc.prog.Fset, &pkg.Info, call)
|
b, err := processBind(oc.fset, info, call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{notePosition(exprPos, err)}
|
return nil, []error{notePosition(exprPos, err)}
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
case "Value":
|
case "Value":
|
||||||
v, err := processValue(oc.prog.Fset, &pkg.Info, call)
|
v, err := processValue(oc.fset, info, call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{notePosition(exprPos, err)}
|
return nil, []error{notePosition(exprPos, err)}
|
||||||
}
|
}
|
||||||
return v, nil
|
return v, nil
|
||||||
case "InterfaceValue":
|
case "InterfaceValue":
|
||||||
v, err := processInterfaceValue(oc.prog.Fset, &pkg.Info, call)
|
v, err := processInterfaceValue(oc.fset, info, call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{notePosition(exprPos, err)}
|
return nil, []error{notePosition(exprPos, err)}
|
||||||
}
|
}
|
||||||
@@ -548,8 +506,8 @@ func (oc *objectCache) processExpr(pkg *loader.PackageInfo, expr ast.Expr, varNa
|
|||||||
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
|
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tn := structArgType(&pkg.Info, expr); tn != nil {
|
if tn := structArgType(info, expr); tn != nil {
|
||||||
p, errs := processStructProvider(oc.prog.Fset, tn)
|
p, errs := processStructProvider(oc.fset, tn)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return nil, notePositionAll(exprPos, errs)
|
return nil, notePositionAll(exprPos, errs)
|
||||||
}
|
}
|
||||||
@@ -558,17 +516,17 @@ func (oc *objectCache) processExpr(pkg *loader.PackageInfo, expr ast.Expr, varNa
|
|||||||
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
|
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oc *objectCache) processNewSet(pkg *loader.PackageInfo, call *ast.CallExpr, varName string) (*ProviderSet, []error) {
|
func (oc *objectCache) processNewSet(info *types.Info, pkgPath string, call *ast.CallExpr, varName string) (*ProviderSet, []error) {
|
||||||
// Assumes that call.Fun is wire.NewSet or wire.Build.
|
// Assumes that call.Fun is wire.NewSet or wire.Build.
|
||||||
|
|
||||||
pset := &ProviderSet{
|
pset := &ProviderSet{
|
||||||
Pos: call.Pos(),
|
Pos: call.Pos(),
|
||||||
PkgPath: pkg.Pkg.Path(),
|
PkgPath: pkgPath,
|
||||||
VarName: varName,
|
VarName: varName,
|
||||||
}
|
}
|
||||||
ec := new(errorCollector)
|
ec := new(errorCollector)
|
||||||
for _, arg := range call.Args {
|
for _, arg := range call.Args {
|
||||||
item, errs := oc.processExpr(pkg, arg, "")
|
item, errs := oc.processExpr(info, pkgPath, arg, "")
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ec.add(errs...)
|
ec.add(errs...)
|
||||||
continue
|
continue
|
||||||
@@ -590,7 +548,7 @@ func (oc *objectCache) processNewSet(pkg *loader.PackageInfo, call *ast.CallExpr
|
|||||||
return nil, ec.errors
|
return nil, ec.errors
|
||||||
}
|
}
|
||||||
var errs []error
|
var errs []error
|
||||||
pset.providerMap, pset.srcMap, errs = buildProviderMap(oc.prog.Fset, oc.hasher, pset)
|
pset.providerMap, pset.srcMap, errs = buildProviderMap(oc.fset, oc.hasher, pset)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return nil, errs
|
return nil, errs
|
||||||
}
|
}
|
||||||
@@ -647,7 +605,7 @@ func processFuncProvider(fset *token.FileSet, fn *types.Func) (*Provider, []erro
|
|||||||
}
|
}
|
||||||
params := sig.Params()
|
params := sig.Params()
|
||||||
provider := &Provider{
|
provider := &Provider{
|
||||||
ImportPath: fn.Pkg().Path(),
|
Pkg: fn.Pkg(),
|
||||||
Name: fn.Name(),
|
Name: fn.Name(),
|
||||||
Pos: fn.Pos(),
|
Pos: fn.Pos(),
|
||||||
Args: make([]ProviderInput, params.Len()),
|
Args: make([]ProviderInput, params.Len()),
|
||||||
@@ -733,13 +691,13 @@ func processStructProvider(fset *token.FileSet, typeName *types.TypeName) (*Prov
|
|||||||
|
|
||||||
pos := typeName.Pos()
|
pos := typeName.Pos()
|
||||||
provider := &Provider{
|
provider := &Provider{
|
||||||
ImportPath: typeName.Pkg().Path(),
|
Pkg: typeName.Pkg(),
|
||||||
Name: typeName.Name(),
|
Name: typeName.Name(),
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
Args: make([]ProviderInput, st.NumFields()),
|
Args: make([]ProviderInput, st.NumFields()),
|
||||||
Fields: make([]string, st.NumFields()),
|
Fields: make([]string, st.NumFields()),
|
||||||
IsStruct: true,
|
IsStruct: true,
|
||||||
Out: []types.Type{out, types.NewPointer(out)},
|
Out: []types.Type{out, types.NewPointer(out)},
|
||||||
}
|
}
|
||||||
for i := 0; i < st.NumFields(); i++ {
|
for i := 0; i < st.NumFields(); i++ {
|
||||||
f := st.Field(i)
|
f := st.Field(i)
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//+build wireinject
|
//+build !wireinject
|
||||||
|
|
||||||
|
// Package bar includes both wireinject and non-wireinject variants.
|
||||||
package bar
|
package bar
|
||||||
|
|
||||||
import "github.com/google/go-cloud/wire"
|
import "github.com/google/go-cloud/wire"
|
||||||
@@ -12,9 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//+build !wireinject
|
//+build wireinject
|
||||||
|
|
||||||
// Package bar includes both wireinject and non-wireinject variants.
|
|
||||||
package bar
|
package bar
|
||||||
|
|
||||||
import "github.com/google/go-cloud/wire"
|
import "github.com/google/go-cloud/wire"
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// Copyright 2018 The Go Cloud Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//+build !wireinject
|
|
||||||
|
|
||||||
// Package bar includes both wireinject and non-wireinject variants.
|
|
||||||
package bar
|
|
||||||
|
|
||||||
import "github.com/google/go-cloud/wire"
|
|
||||||
|
|
||||||
// Set provides a friendly user greeting.
|
|
||||||
var Set = wire.NewSet(wire.Value("Hello, World!"))
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copyright 2018 The Go Cloud Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//+build wireinject
|
|
||||||
|
|
||||||
package bar
|
|
||||||
|
|
||||||
import "github.com/google/go-cloud/wire"
|
|
||||||
|
|
||||||
// Set provides an unfriendly user greeting.
|
|
||||||
var Set = wire.NewSet(wire.Value("Bah humbug! This is the wrong variant!"))
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// Code generated by Wire. DO NOT EDIT.
|
|
||||||
|
|
||||||
//go:generate wire
|
|
||||||
//+build !wireinject
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// Injectors from wire.go:
|
|
||||||
|
|
||||||
func injectedMessage() string {
|
|
||||||
string2 := _wireStringValue
|
|
||||||
return string2
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_wireStringValue = "Hello, World!"
|
|
||||||
)
|
|
||||||
@@ -22,6 +22,7 @@ func main() {
|
|||||||
fmt.Println(injectedMessage())
|
fmt.Println(injectedMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// provideMessage provides a friendly user greeting.
|
||||||
func provideMessage() string {
|
func provideMessage() string {
|
||||||
return "Hello, World!"
|
return "Hello, World!"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,8 @@ import "fmt"
|
|||||||
func main() {
|
func main() {
|
||||||
fmt.Println(injectedMessage())
|
fmt.Println(injectedMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// provideMessage provides a friendly user greeting.
|
||||||
|
func provideMessage() string {
|
||||||
|
return "Hello, World!"
|
||||||
|
}
|
||||||
@@ -17,11 +17,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"example.com/bar"
|
|
||||||
"github.com/google/go-cloud/wire"
|
"github.com/google/go-cloud/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
func injectedMessage() string {
|
func injectedMessage() string {
|
||||||
wire.Build(bar.Set)
|
wire.Build(provideMessage)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,9 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"example.com/bar"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Injectors from wire.go:
|
// Injectors from wire.go:
|
||||||
|
|
||||||
func injectedMessage() string {
|
func injectedMessage() string {
|
||||||
string2 := bar.ProvideMessage()
|
string2 := provideMessage()
|
||||||
return string2
|
return string2
|
||||||
}
|
}
|
||||||
@@ -15,4 +15,7 @@ func injectInterface() Interface {
|
|||||||
|
|
||||||
// wire.go:
|
// wire.go:
|
||||||
|
|
||||||
|
// Wire tries to disambiguate the variable "select" by prepending
|
||||||
|
// the package name; this package-scoped variable conflicts with that
|
||||||
|
// and forces a different name.
|
||||||
var mainSelect = 0
|
var mainSelect = 0
|
||||||
|
|||||||
21
internal/wire/testdata/Vendor/foo/foo.go
vendored
21
internal/wire/testdata/Vendor/foo/foo.go
vendored
@@ -1,21 +0,0 @@
|
|||||||
// Copyright 2018 The Go Cloud Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Println(injectedMessage())
|
|
||||||
}
|
|
||||||
27
internal/wire/testdata/Vendor/foo/wire.go
vendored
27
internal/wire/testdata/Vendor/foo/wire.go
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2018 The Go Cloud Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//+build wireinject
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"example.com/bar"
|
|
||||||
"github.com/google/go-cloud/wire"
|
|
||||||
)
|
|
||||||
|
|
||||||
func injectedMessage() string {
|
|
||||||
wire.Build(bar.ProvideMessage)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
1
internal/wire/testdata/Vendor/pkg
vendored
1
internal/wire/testdata/Vendor/pkg
vendored
@@ -1 +0,0 @@
|
|||||||
example.com/foo
|
|
||||||
21
internal/wire/testdata/Vendor/vendor/example.com/bar/bar.go
generated
vendored
21
internal/wire/testdata/Vendor/vendor/example.com/bar/bar.go
generated
vendored
@@ -1,21 +0,0 @@
|
|||||||
// Copyright 2018 The Go Cloud Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package bar is the vendored copy of bar which contains the real provider.
|
|
||||||
package bar
|
|
||||||
|
|
||||||
// ProvideMessage provides a friendly user greeting.
|
|
||||||
func ProvideMessage() string {
|
|
||||||
return "Hello, World!"
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Hello, World!
|
|
||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/packages"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GeneratedFile stores the content of a call to Generate and the
|
// GeneratedFile stores the content of a call to Generate and the
|
||||||
@@ -64,26 +64,25 @@ func (gen GeneratedFile) Commit() error {
|
|||||||
// In case of duplicate environment variables, the last one in the list
|
// In case of duplicate environment variables, the last one in the list
|
||||||
// takes precedence.
|
// takes precedence.
|
||||||
func Generate(ctx context.Context, wd string, env []string, pkgPattern string) (GeneratedFile, []error) {
|
func Generate(ctx context.Context, wd string, env []string, pkgPattern string) (GeneratedFile, []error) {
|
||||||
prog, errs := load(ctx, wd, env, []string{pkgPattern})
|
pkgs, errs := load(ctx, wd, env, []string{pkgPattern})
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return GeneratedFile{}, errs
|
return GeneratedFile{}, errs
|
||||||
}
|
}
|
||||||
if len(prog.InitialPackages()) != 1 {
|
if len(pkgs) != 1 {
|
||||||
// This is more of a violated precondition than anything else.
|
// This is more of a violated precondition than anything else.
|
||||||
return GeneratedFile{}, []error{fmt.Errorf("load: got %d packages", len(prog.InitialPackages()))}
|
return GeneratedFile{}, []error{fmt.Errorf("load: got %d packages", len(pkgs))}
|
||||||
}
|
}
|
||||||
pkgInfo := prog.InitialPackages()[0]
|
outDir, err := detectOutputDir(pkgs[0].GoFiles)
|
||||||
outDir, err := detectOutputDir(prog.Fset, pkgInfo.Files)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GeneratedFile{}, []error{fmt.Errorf("load: %v", err)}
|
return GeneratedFile{}, []error{fmt.Errorf("load: %v", err)}
|
||||||
}
|
}
|
||||||
outFname := filepath.Join(outDir, "wire_gen.go")
|
outFname := filepath.Join(outDir, "wire_gen.go")
|
||||||
g := newGen(prog, pkgInfo.Pkg.Path())
|
g := newGen(pkgs[0])
|
||||||
injectorFiles, errs := generateInjectors(g, pkgInfo)
|
injectorFiles, errs := generateInjectors(g, pkgs[0])
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return GeneratedFile{}, errs
|
return GeneratedFile{}, errs
|
||||||
}
|
}
|
||||||
copyNonInjectorDecls(g, injectorFiles, &pkgInfo.Info)
|
copyNonInjectorDecls(g, injectorFiles, pkgs[0].TypesInfo)
|
||||||
goSrc := g.frame()
|
goSrc := g.frame()
|
||||||
fmtSrc, err := format.Source(goSrc)
|
fmtSrc, err := format.Source(goSrc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -94,13 +93,13 @@ func Generate(ctx context.Context, wd string, env []string, pkgPattern string) (
|
|||||||
return GeneratedFile{Path: outFname, Content: fmtSrc}, nil
|
return GeneratedFile{Path: outFname, Content: fmtSrc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectOutputDir(fset *token.FileSet, files []*ast.File) (string, error) {
|
func detectOutputDir(paths []string) (string, error) {
|
||||||
if len(files) == 0 {
|
if len(paths) == 0 {
|
||||||
return "", errors.New("no files to derive output directory from")
|
return "", errors.New("no files to derive output directory from")
|
||||||
}
|
}
|
||||||
dir := filepath.Dir(fset.File(files[0].Package).Name())
|
dir := filepath.Dir(paths[0])
|
||||||
for _, f := range files[1:] {
|
for _, p := range paths[1:] {
|
||||||
if dir2 := filepath.Dir(fset.File(f.Package).Name()); dir2 != dir {
|
if dir2 := filepath.Dir(p); dir2 != dir {
|
||||||
return "", fmt.Errorf("found conflicting directories %q and %q", dir, dir2)
|
return "", fmt.Errorf("found conflicting directories %q and %q", dir, dir2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,17 +107,17 @@ func detectOutputDir(fset *token.FileSet, files []*ast.File) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateInjectors generates the injectors for a given package.
|
// generateInjectors generates the injectors for a given package.
|
||||||
func generateInjectors(g *gen, pkgInfo *loader.PackageInfo) (injectorFiles []*ast.File, _ []error) {
|
func generateInjectors(g *gen, pkg *packages.Package) (injectorFiles []*ast.File, _ []error) {
|
||||||
oc := newObjectCache(g.prog)
|
oc := newObjectCache([]*packages.Package{pkg})
|
||||||
injectorFiles = make([]*ast.File, 0, len(pkgInfo.Files))
|
injectorFiles = make([]*ast.File, 0, len(pkg.Syntax))
|
||||||
ec := new(errorCollector)
|
ec := new(errorCollector)
|
||||||
for _, f := range pkgInfo.Files {
|
for _, f := range pkg.Syntax {
|
||||||
for _, decl := range f.Decls {
|
for _, decl := range f.Decls {
|
||||||
fn, ok := decl.(*ast.FuncDecl)
|
fn, ok := decl.(*ast.FuncDecl)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buildCall, err := findInjectorBuild(&pkgInfo.Info, fn)
|
buildCall, err := findInjectorBuild(pkg.TypesInfo, fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.add(err)
|
ec.add(err)
|
||||||
continue
|
continue
|
||||||
@@ -129,16 +128,16 @@ func generateInjectors(g *gen, pkgInfo *loader.PackageInfo) (injectorFiles []*as
|
|||||||
if len(injectorFiles) == 0 || injectorFiles[len(injectorFiles)-1] != f {
|
if len(injectorFiles) == 0 || injectorFiles[len(injectorFiles)-1] != f {
|
||||||
// This is the first injector generated for this file.
|
// This is the first injector generated for this file.
|
||||||
// Write a file header.
|
// Write a file header.
|
||||||
name := filepath.Base(g.prog.Fset.File(f.Pos()).Name())
|
name := filepath.Base(g.pkg.Fset.File(f.Pos()).Name())
|
||||||
g.p("// Injectors from %s:\n\n", name)
|
g.p("// Injectors from %s:\n\n", name)
|
||||||
injectorFiles = append(injectorFiles, f)
|
injectorFiles = append(injectorFiles, f)
|
||||||
}
|
}
|
||||||
set, errs := oc.processNewSet(pkgInfo, buildCall, "")
|
set, errs := oc.processNewSet(pkg.TypesInfo, pkg.PkgPath, buildCall, "")
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ec.add(notePositionAll(g.prog.Fset.Position(fn.Pos()), errs)...)
|
ec.add(notePositionAll(g.pkg.Fset.Position(fn.Pos()), errs)...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sig := pkgInfo.ObjectOf(fn.Name).Type().(*types.Signature)
|
sig := pkg.TypesInfo.ObjectOf(fn.Name).Type().(*types.Signature)
|
||||||
if errs := g.inject(fn.Pos(), fn.Name.Name, sig, set); len(errs) > 0 {
|
if errs := g.inject(fn.Pos(), fn.Name.Name, sig, set); len(errs) > 0 {
|
||||||
ec.add(errs...)
|
ec.add(errs...)
|
||||||
continue
|
continue
|
||||||
@@ -155,7 +154,7 @@ func generateInjectors(g *gen, pkgInfo *loader.PackageInfo) (injectorFiles []*as
|
|||||||
// given files into the generated output.
|
// given files into the generated output.
|
||||||
func copyNonInjectorDecls(g *gen, files []*ast.File, info *types.Info) {
|
func copyNonInjectorDecls(g *gen, files []*ast.File, info *types.Info) {
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
name := filepath.Base(g.prog.Fset.File(f.Pos()).Name())
|
name := filepath.Base(g.pkg.Fset.File(f.Pos()).Name())
|
||||||
first := true
|
first := true
|
||||||
for _, decl := range f.Decls {
|
for _, decl := range f.Decls {
|
||||||
switch decl := decl.(type) {
|
switch decl := decl.(type) {
|
||||||
@@ -185,26 +184,26 @@ func copyNonInjectorDecls(g *gen, files []*ast.File, info *types.Info) {
|
|||||||
|
|
||||||
// importInfo holds info about an import.
|
// importInfo holds info about an import.
|
||||||
type importInfo struct {
|
type importInfo struct {
|
||||||
|
// name is the identifier that is used in the generated source.
|
||||||
name string
|
name string
|
||||||
// fullpath is the full, possibly vendored, path.
|
// differs is true if the import is given an identifier that does not
|
||||||
fullpath string
|
// match the package's identifier.
|
||||||
|
differs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// gen is the file-wide generator state.
|
// gen is the file-wide generator state.
|
||||||
type gen struct {
|
type gen struct {
|
||||||
currPackage string
|
pkg *packages.Package
|
||||||
buf bytes.Buffer
|
buf bytes.Buffer
|
||||||
imports map[string]*importInfo
|
imports map[string]importInfo
|
||||||
values map[ast.Expr]string
|
values map[ast.Expr]string
|
||||||
prog *loader.Program // for positions and determining package names
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGen(prog *loader.Program, pkg string) *gen {
|
func newGen(pkg *packages.Package) *gen {
|
||||||
return &gen{
|
return &gen{
|
||||||
currPackage: pkg,
|
pkg: pkg,
|
||||||
imports: make(map[string]*importInfo),
|
imports: make(map[string]importInfo),
|
||||||
values: make(map[ast.Expr]string),
|
values: make(map[ast.Expr]string),
|
||||||
prog: prog,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +217,7 @@ func (g *gen) frame() []byte {
|
|||||||
buf.WriteString("//go:generate wire\n")
|
buf.WriteString("//go:generate wire\n")
|
||||||
buf.WriteString("//+build !wireinject\n\n")
|
buf.WriteString("//+build !wireinject\n\n")
|
||||||
buf.WriteString("package ")
|
buf.WriteString("package ")
|
||||||
buf.WriteString(g.prog.Package(g.currPackage).Pkg.Name())
|
buf.WriteString(g.pkg.Name)
|
||||||
buf.WriteString("\n\n")
|
buf.WriteString("\n\n")
|
||||||
if len(g.imports) > 0 {
|
if len(g.imports) > 0 {
|
||||||
buf.WriteString("import (\n")
|
buf.WriteString("import (\n")
|
||||||
@@ -230,10 +229,10 @@ func (g *gen) frame() []byte {
|
|||||||
for _, path := range imps {
|
for _, path := range imps {
|
||||||
// Omit the local package identifier if it matches the package name.
|
// Omit the local package identifier if it matches the package name.
|
||||||
info := g.imports[path]
|
info := g.imports[path]
|
||||||
if g.prog.Package(info.fullpath).Pkg.Name() == info.name {
|
if info.differs {
|
||||||
fmt.Fprintf(&buf, "\t%q\n", path)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(&buf, "\t%s %q\n", info.name, path)
|
fmt.Fprintf(&buf, "\t%s %q\n", info.name, path)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&buf, "\t%q\n", path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.WriteString(")\n\n")
|
buf.WriteString(")\n\n")
|
||||||
@@ -246,7 +245,7 @@ func (g *gen) frame() []byte {
|
|||||||
func (g *gen) inject(pos token.Pos, name string, sig *types.Signature, set *ProviderSet) []error {
|
func (g *gen) inject(pos token.Pos, name string, sig *types.Signature, set *ProviderSet) []error {
|
||||||
injectSig, err := funcOutput(sig)
|
injectSig, err := funcOutput(sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{notePosition(g.prog.Fset.Position(pos),
|
return []error{notePosition(g.pkg.Fset.Position(pos),
|
||||||
fmt.Errorf("inject %s: %v", name, err))}
|
fmt.Errorf("inject %s: %v", name, err))}
|
||||||
}
|
}
|
||||||
params := sig.Params()
|
params := sig.Params()
|
||||||
@@ -254,13 +253,13 @@ func (g *gen) inject(pos token.Pos, name string, sig *types.Signature, set *Prov
|
|||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
given[i] = params.At(i).Type()
|
given[i] = params.At(i).Type()
|
||||||
}
|
}
|
||||||
calls, errs := solve(g.prog.Fset, injectSig.out, given, set)
|
calls, errs := solve(g.pkg.Fset, injectSig.out, given, set)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return mapErrors(errs, func(e error) error {
|
return mapErrors(errs, func(e error) error {
|
||||||
if w, ok := e.(*wireErr); ok {
|
if w, ok := e.(*wireErr); ok {
|
||||||
return notePosition(w.position, fmt.Errorf("inject %s: %v", name, w.error))
|
return notePosition(w.position, fmt.Errorf("inject %s: %v", name, w.error))
|
||||||
}
|
}
|
||||||
return notePosition(g.prog.Fset.Position(pos), fmt.Errorf("inject %s: %v", name, e))
|
return notePosition(g.pkg.Fset.Position(pos), fmt.Errorf("inject %s: %v", name, e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type pendingVar struct {
|
type pendingVar struct {
|
||||||
@@ -275,21 +274,21 @@ func (g *gen) inject(pos token.Pos, name string, sig *types.Signature, set *Prov
|
|||||||
if c.hasCleanup && !injectSig.cleanup {
|
if c.hasCleanup && !injectSig.cleanup {
|
||||||
ts := types.TypeString(c.out, nil)
|
ts := types.TypeString(c.out, nil)
|
||||||
ec.add(notePosition(
|
ec.add(notePosition(
|
||||||
g.prog.Fset.Position(pos),
|
g.pkg.Fset.Position(pos),
|
||||||
fmt.Errorf("inject %s: provider for %s returns cleanup but injection does not return cleanup function", name, ts)))
|
fmt.Errorf("inject %s: provider for %s returns cleanup but injection does not return cleanup function", name, ts)))
|
||||||
}
|
}
|
||||||
if c.hasErr && !injectSig.err {
|
if c.hasErr && !injectSig.err {
|
||||||
ts := types.TypeString(c.out, nil)
|
ts := types.TypeString(c.out, nil)
|
||||||
ec.add(notePosition(
|
ec.add(notePosition(
|
||||||
g.prog.Fset.Position(pos),
|
g.pkg.Fset.Position(pos),
|
||||||
fmt.Errorf("inject %s: provider for %s returns error but injection not allowed to fail", name, ts)))
|
fmt.Errorf("inject %s: provider for %s returns error but injection not allowed to fail", name, ts)))
|
||||||
}
|
}
|
||||||
if c.kind == valueExpr {
|
if c.kind == valueExpr {
|
||||||
if err := accessibleFrom(c.valueTypeInfo, c.valueExpr, g.currPackage); err != nil {
|
if err := accessibleFrom(c.valueTypeInfo, c.valueExpr, g.pkg.PkgPath); err != nil {
|
||||||
// TODO(light): Display line number of value expression.
|
// TODO(light): Display line number of value expression.
|
||||||
ts := types.TypeString(c.out, nil)
|
ts := types.TypeString(c.out, nil)
|
||||||
ec.add(notePosition(
|
ec.add(notePosition(
|
||||||
g.prog.Fset.Position(pos),
|
g.pkg.Fset.Position(pos),
|
||||||
fmt.Errorf("inject %s: value %s can't be used: %v", name, ts, err)))
|
fmt.Errorf("inject %s: value %s can't be used: %v", name, ts, err)))
|
||||||
}
|
}
|
||||||
if g.values[c.valueExpr] == "" {
|
if g.values[c.valueExpr] == "" {
|
||||||
@@ -347,9 +346,9 @@ func (g *gen) rewritePkgRefs(info *types.Info, node ast.Node) ast.Node {
|
|||||||
if obj == nil {
|
if obj == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if pkg := obj.Pkg(); pkg != nil && obj.Parent() == pkg.Scope() && pkg.Path() != g.currPackage {
|
if pkg := obj.Pkg(); pkg != nil && obj.Parent() == pkg.Scope() && pkg.Path() != g.pkg.PkgPath {
|
||||||
// An identifier from either a dot import or read from a different package.
|
// An identifier from either a dot import or read from a different package.
|
||||||
newPkgID := g.qualifyImport(pkg.Path())
|
newPkgID := g.qualifyImport(pkg.Name(), pkg.Path())
|
||||||
c.Replace(&ast.SelectorExpr{
|
c.Replace(&ast.SelectorExpr{
|
||||||
X: ast.NewIdent(newPkgID),
|
X: ast.NewIdent(newPkgID),
|
||||||
Sel: ast.NewIdent(node.Name),
|
Sel: ast.NewIdent(node.Name),
|
||||||
@@ -367,7 +366,8 @@ func (g *gen) rewritePkgRefs(info *types.Info, node ast.Node) ast.Node {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// This is a qualified identifier. Rewrite and avoid visiting subexpressions.
|
// This is a qualified identifier. Rewrite and avoid visiting subexpressions.
|
||||||
newPkgID := g.qualifyImport(pkgName.Imported().Path())
|
imported := pkgName.Imported()
|
||||||
|
newPkgID := g.qualifyImport(imported.Name(), imported.Path())
|
||||||
c.Replace(&ast.SelectorExpr{
|
c.Replace(&ast.SelectorExpr{
|
||||||
X: ast.NewIdent(newPkgID),
|
X: ast.NewIdent(newPkgID),
|
||||||
Sel: ast.NewIdent(node.Sel.Name),
|
Sel: ast.NewIdent(node.Sel.Name),
|
||||||
@@ -389,7 +389,7 @@ func (g *gen) rewritePkgRefs(info *types.Info, node ast.Node) ast.Node {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
var scopeStack []*types.Scope
|
var scopeStack []*types.Scope
|
||||||
pkgScope := g.prog.Package(g.currPackage).Pkg.Scope()
|
pkgScope := g.pkg.Types.Scope()
|
||||||
node = astutil.Apply(node, func(c *astutil.Cursor) bool {
|
node = astutil.Apply(node, func(c *astutil.Cursor) bool {
|
||||||
if scope := info.Scopes[c.Node()]; scope != nil {
|
if scope := info.Scopes[c.Node()]; scope != nil {
|
||||||
scopeStack = append(scopeStack, scope)
|
scopeStack = append(scopeStack, scope)
|
||||||
@@ -451,21 +451,21 @@ func (g *gen) rewritePkgRefs(info *types.Info, node ast.Node) ast.Node {
|
|||||||
// package references it encounters.
|
// package references it encounters.
|
||||||
func (g *gen) writeAST(info *types.Info, node ast.Node) {
|
func (g *gen) writeAST(info *types.Info, node ast.Node) {
|
||||||
node = g.rewritePkgRefs(info, node)
|
node = g.rewritePkgRefs(info, node)
|
||||||
if err := printer.Fprint(&g.buf, g.prog.Fset, node); err != nil {
|
if err := printer.Fprint(&g.buf, g.pkg.Fset, node); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gen) qualifiedID(path, sym string) string {
|
func (g *gen) qualifiedID(pkgName, pkgPath, sym string) string {
|
||||||
name := g.qualifyImport(path)
|
name := g.qualifyImport(pkgName, pkgPath)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return sym
|
return sym
|
||||||
}
|
}
|
||||||
return name + "." + sym
|
return name + "." + sym
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gen) qualifyImport(path string) string {
|
func (g *gen) qualifyImport(name, path string) string {
|
||||||
if path == g.currPackage {
|
if path == g.pkg.PkgPath {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
// TODO(light): This is depending on details of the current loader.
|
// TODO(light): This is depending on details of the current loader.
|
||||||
@@ -474,16 +474,19 @@ func (g *gen) qualifyImport(path string) string {
|
|||||||
if i := strings.LastIndex(path, vendorPart); i != -1 && (i == 0 || path[i-1] == '/') {
|
if i := strings.LastIndex(path, vendorPart); i != -1 && (i == 0 || path[i-1] == '/') {
|
||||||
unvendored = path[i+len(vendorPart):]
|
unvendored = path[i+len(vendorPart):]
|
||||||
}
|
}
|
||||||
if info := g.imports[unvendored]; info != nil {
|
if info, ok := g.imports[unvendored]; ok {
|
||||||
return info.name
|
return info.name
|
||||||
}
|
}
|
||||||
// TODO(light): Use parts of import path to disambiguate.
|
// TODO(light): Use parts of import path to disambiguate.
|
||||||
name := disambiguate(g.prog.Package(path).Pkg.Name(), func(n string) bool {
|
newName := disambiguate(name, func(n string) bool {
|
||||||
// Don't let an import take the "err" name. That's annoying.
|
// Don't let an import take the "err" name. That's annoying.
|
||||||
return n == "err" || g.nameInFileScope(n)
|
return n == "err" || g.nameInFileScope(n)
|
||||||
})
|
})
|
||||||
g.imports[unvendored] = &importInfo{name: name, fullpath: path}
|
g.imports[unvendored] = importInfo{
|
||||||
return name
|
name: newName,
|
||||||
|
differs: newName != name,
|
||||||
|
}
|
||||||
|
return newName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gen) nameInFileScope(name string) bool {
|
func (g *gen) nameInFileScope(name string) bool {
|
||||||
@@ -497,12 +500,12 @@ func (g *gen) nameInFileScope(name string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, obj := g.prog.Package(g.currPackage).Pkg.Scope().LookupParent(name, token.NoPos)
|
_, obj := g.pkg.Types.Scope().LookupParent(name, token.NoPos)
|
||||||
return obj != nil
|
return obj != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gen) qualifyPkg(pkg *types.Package) string {
|
func (g *gen) qualifyPkg(pkg *types.Package) string {
|
||||||
return g.qualifyImport(pkg.Path())
|
return g.qualifyImport(pkg.Name(), pkg.Path())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gen) p(format string, args ...interface{}) {
|
func (g *gen) p(format string, args ...interface{}) {
|
||||||
@@ -601,7 +604,7 @@ func (ig *injectorGen) funcProviderCall(lname string, c *call, injectSig outputS
|
|||||||
ig.p(", %s", ig.errVar)
|
ig.p(", %s", ig.errVar)
|
||||||
}
|
}
|
||||||
ig.p(" := ")
|
ig.p(" := ")
|
||||||
ig.p("%s(", ig.g.qualifiedID(c.importPath, c.name))
|
ig.p("%s(", ig.g.qualifiedID(c.pkg.Name(), c.pkg.Path(), c.name))
|
||||||
for i, a := range c.args {
|
for i, a := range c.args {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
ig.p(", ")
|
ig.p(", ")
|
||||||
@@ -634,7 +637,7 @@ func (ig *injectorGen) structProviderCall(lname string, c *call) {
|
|||||||
if _, ok := c.out.(*types.Pointer); ok {
|
if _, ok := c.out.(*types.Pointer); ok {
|
||||||
ig.p("&")
|
ig.p("&")
|
||||||
}
|
}
|
||||||
ig.p("%s{\n", ig.g.qualifiedID(c.importPath, c.name))
|
ig.p("%s{\n", ig.g.qualifiedID(c.pkg.Name(), c.pkg.Path(), c.name))
|
||||||
for i, a := range c.args {
|
for i, a := range c.args {
|
||||||
ig.p("\t\t%s: ", c.fieldNames[i])
|
ig.p("\t\t%s: ", c.fieldNames[i])
|
||||||
if a < len(ig.paramNames) {
|
if a < len(ig.paramNames) {
|
||||||
@@ -687,7 +690,7 @@ func (ig *injectorGen) writeAST(info *types.Info, node ast.Node) {
|
|||||||
if ig.discard {
|
if ig.discard {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := printer.Fprint(&ig.g.buf, ig.g.prog.Fset, node); err != nil {
|
if err := printer.Fprint(&ig.g.buf, ig.g.pkg.Fset, node); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ func TestWire(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(gopath)
|
defer os.RemoveAll(gopath)
|
||||||
|
gopath, err = filepath.EvalSymlinks(gopath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
if err := test.materialize(gopath); err != nil {
|
if err := test.materialize(gopath); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -150,18 +154,9 @@ func TestWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func goBuildCheck(goToolPath, gopath string, test *testCase) error {
|
func goBuildCheck(goToolPath, gopath string, test *testCase) error {
|
||||||
// Write go.mod files for example.com and the wire package.
|
|
||||||
// TODO(#78): Move this to happen in materialize() once modules work.
|
|
||||||
if err := writeGoMod(gopath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run `go build`.
|
// Run `go build`.
|
||||||
testExePath := filepath.Join(gopath, "bin", "testprog")
|
testExePath := filepath.Join(gopath, "bin", "testprog")
|
||||||
buildCmd := []string{"build", "-o", testExePath}
|
buildCmd := []string{"build", "-o", testExePath}
|
||||||
if test.name == "Vendor" && os.Getenv("GO111MODULE") == "on" {
|
|
||||||
buildCmd = append(buildCmd, "-mod=vendor")
|
|
||||||
}
|
|
||||||
buildCmd = append(buildCmd, test.pkg)
|
buildCmd = append(buildCmd, test.pkg)
|
||||||
cmd := exec.Command(goToolPath, buildCmd...)
|
cmd := exec.Command(goToolPath, buildCmd...)
|
||||||
cmd.Dir = filepath.Join(gopath, "src", "example.com")
|
cmd.Dir = filepath.Join(gopath, "src", "example.com")
|
||||||
@@ -532,28 +527,8 @@ func (test *testCase) materialize(gopath string) error {
|
|||||||
return fmt.Errorf("materialize GOPATH: %v", err)
|
return fmt.Errorf("materialize GOPATH: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeGoMod generates go.mod files for the test package and its dependency.
|
// Add go.mod files to example.com and github.com/google/go-cloud.
|
||||||
// The file structure looks like:
|
|
||||||
//
|
|
||||||
// gopath/src/
|
|
||||||
//
|
|
||||||
// example.com/
|
|
||||||
//
|
|
||||||
// go.mod
|
|
||||||
// replaces dependency with local copied one
|
|
||||||
//
|
|
||||||
// ... (Packages to be built and tested)
|
|
||||||
// any Go files copied recursively
|
|
||||||
//
|
|
||||||
// github.com/google/go-cloud/
|
|
||||||
//
|
|
||||||
// go.mod
|
|
||||||
//
|
|
||||||
// ... (Dependency files copied)
|
|
||||||
func writeGoMod(gopath string) error {
|
|
||||||
const importPath = "example.com"
|
const importPath = "example.com"
|
||||||
const depPath = "github.com/google/go-cloud"
|
const depPath = "github.com/google/go-cloud"
|
||||||
depLoc := filepath.Join(gopath, "src", filepath.FromSlash(depPath))
|
depLoc := filepath.Join(gopath, "src", filepath.FromSlash(depPath))
|
||||||
@@ -562,7 +537,7 @@ func writeGoMod(gopath string) error {
|
|||||||
if err := ioutil.WriteFile(gomod, []byte(example), 0666); err != nil {
|
if err := ioutil.WriteFile(gomod, []byte(example), 0666); err != nil {
|
||||||
return fmt.Errorf("generate go.mod for %s: %v", gomod, err)
|
return fmt.Errorf("generate go.mod for %s: %v", gomod, err)
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(filepath.Join(depLoc, "go.mod"), []byte("module "+depPath), 0666); err != nil {
|
if err := ioutil.WriteFile(filepath.Join(depLoc, "go.mod"), []byte("module "+depPath+"\n"), 0666); err != nil {
|
||||||
return fmt.Errorf("generate go.mod for %s: %v", depPath, err)
|
return fmt.Errorf("generate go.mod for %s: %v", depPath, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user