Generate DeepCopies per directory.

This commit is contained in:
Wojciech Tyczynski 2016-02-04 11:46:03 +01:00
parent e604efcf0d
commit 74d005f1cf
2 changed files with 80 additions and 73 deletions

View File

@ -93,6 +93,9 @@ func (g *GeneratorArgs) LoadGoBoilerplate() ([]byte, error) {
// directories. // directories.
func (g *GeneratorArgs) NewBuilder() (*parser.Builder, error) { func (g *GeneratorArgs) NewBuilder() (*parser.Builder, error) {
b := parser.New() b := parser.New()
// Ignore all auto-generated files.
b.AddBuildTags("ignore_autogenerated")
for _, d := range g.InputDirs { for _, d := range g.InputDirs {
if g.Recursive { if g.Recursive {
if err := b.AddDirRecursive(d); err != nil { if err := b.AddDirRecursive(d); err != nil {

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/cmd/libs/go2idl/generator" "k8s.io/kubernetes/cmd/libs/go2idl/generator"
"k8s.io/kubernetes/cmd/libs/go2idl/namer" "k8s.io/kubernetes/cmd/libs/go2idl/namer"
"k8s.io/kubernetes/cmd/libs/go2idl/types" "k8s.io/kubernetes/cmd/libs/go2idl/types"
"k8s.io/kubernetes/pkg/util/sets"
"github.com/golang/glog" "github.com/golang/glog"
) )
@ -55,48 +56,52 @@ func DefaultNameSystem() string {
return "public" return "public"
} }
func Packages(_ *generator.Context, arguments *args.GeneratorArgs) generator.Packages { func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate() boilerplate, err := arguments.LoadGoBoilerplate()
if err != nil { if err != nil {
glog.Fatalf("Failed loading boilerplate: %v", err) glog.Fatalf("Failed loading boilerplate: %v", err)
} }
inputs := sets.NewString(arguments.InputDirs...)
packages := generator.Packages{} packages := generator.Packages{}
for _, inputDir := range arguments.InputDirs { header := append([]byte(
packages = append(packages, `
&generator.DefaultPackage{ // +build !ignore_autogenerated
PackageName: filepath.Base(inputDir),
PackagePath: inputDir, `), boilerplate...)
HeaderText: append(boilerplate, []byte( header = append(header, []byte(
` `
// This file was autogenerated by deepcopy-gen. Do not edit it manually! // This file was autogenerated by deepcopy-gen. Do not edit it manually!
`)...), `)...)
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { for _, p := range context.Universe {
generators = []generator.Generator{} copyableType := false
// TODO: Check whether anything will be generated. for _, t := range p.Types {
generators = append(generators, NewGenDeepCopy("deep_copy_generated", inputDir)) if copyableWithinPackage(t) {
return generators copyableType = true
}, }
FilterFunc: func(c *generator.Context, t *types.Type) bool { }
switch t.Kind { if copyableType {
case types.Func, types.Chan: path := p.Path
// These types can't be copied. packages = append(packages,
return false &generator.DefaultPackage{
case types.Unknown, types.Unsupported: PackageName: filepath.Base(path),
// These types are explicitly ignored. PackagePath: path,
return false HeaderText: header,
case types.Array: GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// We don't support arrays. generators = []generator.Generator{}
return false generators = append(
} generators, NewGenDeepCopy("deep_copy_generated", path, inputs.Has(path)))
// Also, filter out private types. return generators
if namer.IsPrivateGoName(t.Name.Name) { },
return false FilterFunc: func(c *generator.Context, t *types.Type) bool {
} if t.Name.Package != path {
return true return false
}, }
}) return copyableWithinPackage(t)
},
})
}
} }
return packages return packages
} }
@ -109,19 +114,21 @@ const (
// genDeepCopy produces a file with a set for a single type. // genDeepCopy produces a file with a set for a single type.
type genDeepCopy struct { type genDeepCopy struct {
generator.DefaultGen generator.DefaultGen
targetPackage string targetPackage string
imports namer.ImportTracker imports namer.ImportTracker
typesForInit []*types.Type typesForInit []*types.Type
generateInitFunc bool
} }
func NewGenDeepCopy(sanitizedName, targetPackage string) generator.Generator { func NewGenDeepCopy(sanitizedName, targetPackage string, generateInitFunc bool) generator.Generator {
return &genDeepCopy{ return &genDeepCopy{
DefaultGen: generator.DefaultGen{ DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName, OptionalName: sanitizedName,
}, },
targetPackage: targetPackage, targetPackage: targetPackage,
imports: generator.NewImportTracker(), imports: generator.NewImportTracker(),
typesForInit: make([]*types.Type, 0), typesForInit: make([]*types.Type, 0),
generateInitFunc: generateInitFunc,
} }
} }
@ -133,21 +140,14 @@ func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems {
// Filter ignores all but one type because we're making a single file per type. // Filter ignores all but one type because we're making a single file per type.
func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool { func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool {
// Filter out all types not copyable within the package. // Filter out all types not copyable within the package.
copyable := g.copyableWithinPackage(t) copyable := copyableWithinPackage(t)
if copyable { if copyable {
g.typesForInit = append(g.typesForInit, t) g.typesForInit = append(g.typesForInit, t)
} }
return copyable return copyable
} }
func (g *genDeepCopy) copyableWithinPackage(t *types.Type) bool { func copyableWithinPackage(t *types.Type) bool {
// TODO: We should generate public DeepCopy functions per directory, instead
// of generating everything everywhere.
// This is done that way only to minimize number of changes per PR.
// Once this is done, we should replace HasPrefix with:
//if t.Name.Package != g.targetPackage {
// return false
//}
if !strings.HasPrefix(t.Name.Package, "k8s.io/kubernetes/") { if !strings.HasPrefix(t.Name.Package, "k8s.io/kubernetes/") {
return false return false
} }
@ -158,21 +158,9 @@ func (g *genDeepCopy) copyableWithinPackage(t *types.Type) bool {
if t.Kind != types.Struct { if t.Kind != types.Struct {
return false return false
} }
// TODO: This should be removed once we start generating public DeepCopy // Also, filter out private types.
// functions per directory. if namer.IsPrivateGoName(t.Name.Name) {
if t.Name.Package != g.targetPackage { return false
// We won't be able to access private fields.
// Thus, this type cannot have private fields.
for _, member := range t.Members {
if namer.IsPrivateGoName(member.Name) {
return false
}
// TODO: This is a temporary hack, to make avoid generating function
// for conversion.Equalities. We should get rid of it.
if member.Embedded {
return false
}
}
} }
return true return true
} }
@ -189,7 +177,7 @@ func (g *genDeepCopy) isOtherPackage(pkg string) bool {
func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) { func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) {
importLines := []string{} importLines := []string{}
if g.isOtherPackage(apiPackagePath) { if g.isOtherPackage(apiPackagePath) && g.generateInitFunc {
importLines = append(importLines, "api \""+apiPackagePath+"\"") importLines = append(importLines, "api \""+apiPackagePath+"\"")
} }
if g.isOtherPackage(conversionPackagePath) { if g.isOtherPackage(conversionPackagePath) {
@ -210,10 +198,21 @@ func argsFromType(t *types.Type) interface{} {
} }
func (g *genDeepCopy) funcNameTmpl(t *types.Type) string { func (g *genDeepCopy) funcNameTmpl(t *types.Type) string {
return "DeepCopy_$.type|public$" tmpl := "DeepCopy_$.type|public$"
g.imports.AddType(t)
if t.Name.Package != g.targetPackage {
tmpl = g.imports.LocalNameOf(t.Name.Package) + "." + tmpl
}
return tmpl
} }
func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error { func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error {
if !g.generateInitFunc {
// TODO: We should come up with a solution to register all generated
// deep-copy functions. However, for now, to avoid import cycles
// we register only those explicitly requested.
return nil
}
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do("func init() {\n", nil) sw.Do("func init() {\n", nil)
if g.targetPackage == apiPackagePath { if g.targetPackage == apiPackagePath {
@ -236,7 +235,11 @@ func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error {
func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
funcName := g.funcNameTmpl(t) funcName := g.funcNameTmpl(t)
sw.Do(fmt.Sprintf("func %s(in $.type|raw$, out *$.type|raw$, c *conversion.Cloner) error {\n", funcName), argsFromType(t)) if g.targetPackage == conversionPackagePath {
sw.Do(fmt.Sprintf("func %s(in $.type|raw$, out *$.type|raw$, c *Cloner) error {\n", funcName), argsFromType(t))
} else {
sw.Do(fmt.Sprintf("func %s(in $.type|raw$, out *$.type|raw$, c *conversion.Cloner) error {\n", funcName), argsFromType(t))
}
g.generateFor(t, sw) g.generateFor(t, sw)
sw.Do("return nil\n", nil) sw.Do("return nil\n", nil)
sw.Do("}\n\n", nil) sw.Do("}\n\n", nil)
@ -275,12 +278,12 @@ func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) {
func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) { func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = make($.|raw$)\n", t) sw.Do("*out = make($.|raw$)\n", t)
sw.Do("for key, val := range in {\n", nil)
if t.Key.IsAssignable() { if t.Key.IsAssignable() {
sw.Do("for key, val := range in {\n", nil)
if t.Elem.IsAssignable() { if t.Elem.IsAssignable() {
sw.Do("(*out)[key] = val\n", nil) sw.Do("(*out)[key] = val\n", nil)
} else { } else {
if g.copyableWithinPackage(t.Elem) { if copyableWithinPackage(t.Elem) {
sw.Do("newVal := new($.|raw$)\n", t.Elem) sw.Do("newVal := new($.|raw$)\n", t.Elem)
funcName := g.funcNameTmpl(t.Elem) funcName := g.funcNameTmpl(t.Elem)
sw.Do(fmt.Sprintf("if err := %s(val, newVal, c); err != nil {\n", funcName), argsFromType(t.Elem)) sw.Do(fmt.Sprintf("if err := %s(val, newVal, c); err != nil {\n", funcName), argsFromType(t.Elem))
@ -297,6 +300,7 @@ func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
} }
} else { } else {
// TODO: Implement it when necessary. // TODO: Implement it when necessary.
sw.Do("for range in {\n", nil)
sw.Do("// FIXME: Copying unassignable keys unsupported $.|raw$\n", t.Key) sw.Do("// FIXME: Copying unassignable keys unsupported $.|raw$\n", t.Key)
} }
sw.Do("}\n", nil) sw.Do("}\n", nil)
@ -310,7 +314,7 @@ func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("for i := range in {\n", nil) sw.Do("for i := range in {\n", nil)
if t.Elem.IsAssignable() { if t.Elem.IsAssignable() {
sw.Do("(*out)[i] = in[i]\n", nil) sw.Do("(*out)[i] = in[i]\n", nil)
} else if g.copyableWithinPackage(t.Elem) { } else if copyableWithinPackage(t.Elem) {
funcName := g.funcNameTmpl(t.Elem) funcName := g.funcNameTmpl(t.Elem)
sw.Do(fmt.Sprintf("if err := %s(in[i], &(*out)[i], c); err != nil {\n", funcName), argsFromType(t.Elem)) sw.Do(fmt.Sprintf("if err := %s(in[i], &(*out)[i], c); err != nil {\n", funcName), argsFromType(t.Elem))
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
@ -343,7 +347,7 @@ func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("out.$.name$ = nil\n", args) sw.Do("out.$.name$ = nil\n", args)
sw.Do("}\n", nil) sw.Do("}\n", nil)
case types.Struct: case types.Struct:
if g.copyableWithinPackage(m.Type) { if copyableWithinPackage(m.Type) {
funcName := g.funcNameTmpl(m.Type) funcName := g.funcNameTmpl(m.Type)
sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args) sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
@ -378,7 +382,7 @@ func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = new($.Elem|raw$)\n", t) sw.Do("*out = new($.Elem|raw$)\n", t)
if t.Elem.Kind == types.Builtin { if t.Elem.Kind == types.Builtin {
sw.Do("**out = *in", nil) sw.Do("**out = *in", nil)
} else if g.copyableWithinPackage(t.Elem) { } else if copyableWithinPackage(t.Elem) {
funcName := g.funcNameTmpl(t.Elem) funcName := g.funcNameTmpl(t.Elem)
sw.Do(fmt.Sprintf("if err := %s(*in, *out, c); err != nil {\n", funcName), argsFromType(t.Elem)) sw.Do(fmt.Sprintf("if err := %s(*in, *out, c); err != nil {\n", funcName), argsFromType(t.Elem))
sw.Do("return err\n", nil) sw.Do("return err\n", nil)