Skip to content

Commit

Permalink
Merge pull request #988 from 99designs/package-cache
Browse files Browse the repository at this point in the history
Cache all packages.Load calls in a central object
  • Loading branch information
vektah committed Feb 4, 2020
2 parents 565619a + 9ccd7ed commit 8ed2ec5
Show file tree
Hide file tree
Showing 31 changed files with 507 additions and 372 deletions.
45 changes: 24 additions & 21 deletions api/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@ import (
"github.com/99designs/gqlgen/plugin/federation"
"github.com/99designs/gqlgen/plugin/modelgen"
"github.com/99designs/gqlgen/plugin/resolvergen"
"github.com/99designs/gqlgen/plugin/schemaconfig"
"github.com/pkg/errors"
"golang.org/x/tools/go/packages"
)

func Generate(cfg *config.Config, option ...Option) error {
_ = syscall.Unlink(cfg.Exec.Filename)
if cfg.Model.IsDefined() {
_ = syscall.Unlink(cfg.Model.Filename)
}
if err := cfg.Check(); err != nil {
return errors.Wrap(err, "generating core failed")
}

plugins := []plugin.Plugin{schemaconfig.New()}
plugins := []plugin.Plugin{}
if cfg.Model.IsDefined() {
plugins = append(plugins, modelgen.New())
}
Expand All @@ -36,16 +31,30 @@ func Generate(cfg *config.Config, option ...Option) error {
o(cfg, &plugins)
}

schemaMutators := []codegen.SchemaMutator{}
for _, p := range plugins {
if inj, ok := p.(plugin.SourcesInjector); ok {
inj.InjectSources(cfg)
}
if mut, ok := p.(codegen.SchemaMutator); ok {
schemaMutators = append(schemaMutators, mut)
}

err := cfg.LoadSchema()
if err != nil {
return errors.Wrap(err, "failed to load schema")
}

for _, p := range plugins {
if mut, ok := p.(plugin.SchemaMutator); ok {
err := mut.MutateSchema(cfg.Schema)
if err != nil {
return errors.Wrap(err, p.Name())
}
}
}

if err := cfg.Init(); err != nil {
return errors.Wrap(err, "generating core failed")
}

for _, p := range plugins {
if mut, ok := p.(plugin.ConfigMutator); ok {
err := mut.MutateConfig(cfg)
Expand All @@ -55,7 +64,7 @@ func Generate(cfg *config.Config, option ...Option) error {
}
}
// Merge again now that the generated models have been injected into the typemap
data, err := codegen.BuildData(cfg, schemaMutators)
data, err := codegen.BuildData(cfg)
if err != nil {
return errors.Wrap(err, "merging type systems failed")
}
Expand Down Expand Up @@ -95,17 +104,11 @@ func validate(cfg *config.Config) error {
if cfg.Resolver.IsDefined() {
roots = append(roots, cfg.Resolver.ImportPath())
}
_, err := packages.Load(&packages.Config{
Mode: packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedImports |
packages.NeedTypes |
packages.NeedTypesSizes |
packages.NeedSyntax |
packages.NeedTypesInfo}, roots...)
if err != nil {
return errors.Wrap(err, "validation failed")

cfg.Packages.LoadAll(roots...)
errs := cfg.Packages.Errors()
if len(errs) > 0 {
return errs
}
return nil
}
74 changes: 7 additions & 67 deletions codegen/config/binder.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package config

import (
"bytes"
"fmt"
"go/token"
"go/types"
Expand All @@ -10,73 +9,22 @@ import (
"github.com/99designs/gqlgen/internal/code"
"github.com/pkg/errors"
"github.com/vektah/gqlparser/ast"
"golang.org/x/tools/go/packages"
)

// Binder connects graphql types to golang types using static analysis
type Binder struct {
pkgs map[string]*packages.Package
pkgs *code.Packages
schema *ast.Schema
cfg *Config
References []*TypeReference
PkgErrors PkgErrors
SawInvalid bool
}

func (c *Config) NewBinder(s *ast.Schema) (*Binder, error) {
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedImports |
packages.NeedTypes |
packages.NeedTypesSizes |
packages.NeedSyntax |
packages.NeedTypesInfo,
}, c.Models.ReferencedPackages()...)
if err != nil {
return nil, err
}

mp := map[string]*packages.Package{}
var pkgErrs PkgErrors
for _, p := range pkgs {
populatePkg(mp, p)
for _, e := range p.Errors {
if e.Kind == packages.ListError {
return nil, e
}
}
pkgErrs = append(pkgErrs, p.Errors...)
}

func (c *Config) NewBinder() *Binder {
return &Binder{
pkgs: mp,
schema: s,
cfg: c,
PkgErrors: pkgErrs,
}, nil
}

type PkgErrors []packages.Error

func (p PkgErrors) Error() string {
var b bytes.Buffer
b.WriteString("packages.Load: ")
for _, e := range p {
b.WriteString(e.Error() + "\n")
}
return b.String()
}

func populatePkg(mp map[string]*packages.Package, p *packages.Package) {
imp := code.NormalizeVendor(p.PkgPath)
if _, ok := mp[imp]; ok {
return
}
mp[imp] = p
for _, p := range p.Imports {
populatePkg(mp, p)
pkgs: c.Packages,
schema: c.Schema,
cfg: c,
}
}

Expand All @@ -97,7 +45,7 @@ func (b *Binder) ObjectPosition(typ types.Object) token.Position {
Filename: "unknown",
}
}
pkg := b.getPkg(typ.Pkg().Path())
pkg := b.pkgs.Load(typ.Pkg().Path())
return pkg.Fset.Position(typ.Pos())
}

Expand Down Expand Up @@ -128,14 +76,6 @@ func (b *Binder) FindType(pkgName string, typeName string) (types.Type, error) {
return obj.Type(), nil
}

func (b *Binder) getPkg(find string) *packages.Package {
imp := code.NormalizeVendor(find)
if p, ok := b.pkgs[imp]; ok {
return p
}
return nil
}

var MapType = types.NewMap(types.Typ[types.String], types.NewInterfaceType(nil, nil).Complete())
var InterfaceType = types.NewInterfaceType(nil, nil)

Expand Down Expand Up @@ -175,7 +115,7 @@ func (b *Binder) FindObject(pkgName string, typeName string) (types.Object, erro
fullName = pkgName + "." + typeName
}

pkg := b.getPkg(pkgName)
pkg := b.pkgs.LoadWithTypes(pkgName)
if pkg == nil {
return nil, errors.Errorf("required package was not loaded: %s", fullName)
}
Expand Down
12 changes: 6 additions & 6 deletions codegen/config/binder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"go/types"
"testing"

"github.com/99designs/gqlgen/internal/code"

"github.com/stretchr/testify/require"
"github.com/vektah/gqlparser"
"github.com/vektah/gqlparser/ast"
Expand Down Expand Up @@ -49,19 +51,17 @@ func createBinder(cfg Config) (*Binder, *ast.Schema) {
Model: []string{"github.com/99designs/gqlgen/example/chat.Message"},
},
}
cfg.Packages = &code.Packages{}

s := gqlparser.MustLoadSchema(&ast.Source{Name: "TestAutobinding.schema", Input: `
cfg.Schema = gqlparser.MustLoadSchema(&ast.Source{Name: "TestAutobinding.schema", Input: `
type Message { id: ID }
type Query {
messages: [Message!]!
}
`})

b, err := cfg.NewBinder(s)
if err != nil {
panic(err)
}
b := cfg.NewBinder()

return b, s
return b, cfg.Schema
}
Loading

0 comments on commit 8ed2ec5

Please sign in to comment.