Re-add constraints to deep-copy

This re-institutes some of the rolled-back logic from previous commits. It
bounds the scope of what the deepcopy generator is willing to do with regards
to generating and calling generated functions.
This commit is contained in:
Tim Hockin 2016-06-29 02:00:55 -07:00
parent e18b2f3a2e
commit be481060ea
4 changed files with 160 additions and 9 deletions

View File

@ -31,6 +31,12 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
// CustomArgs is used tby the go2idl framework to pass args specific to this
// generator.
type CustomArgs struct {
BoundingDirs []string // Only deal with types rooted under these dirs.
}
// TODO: This is created only to reduce number of changes in a single PR. // TODO: This is created only to reduce number of changes in a single PR.
// Remove it and use PublicNamer instead. // Remove it and use PublicNamer instead.
func deepCopyNamer() *namer.NameStrategy { func deepCopyNamer() *namer.NameStrategy {
@ -70,10 +76,20 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
// This file was autogenerated by deepcopy-gen. Do not edit it manually! // This file was autogenerated by deepcopy-gen. Do not edit it manually!
`)...) `)...)
boundingDirs := []string{}
if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok {
for i := range customArgs.BoundingDirs {
// Strip any trailing slashes - they are not exactly "correct" but
// this is friendlier.
boundingDirs = append(boundingDirs, strings.TrimRight(customArgs.BoundingDirs[i], "/"))
}
}
for _, p := range context.Universe { for _, p := range context.Universe {
copyableType := false copyableType := false
for _, t := range p.Types { for _, t := range p.Types {
if copyableWithinPackage(t) && inputs.Has(t.Name.Package) { if copyableWithinPackage(t, boundingDirs) && inputs.Has(t.Name.Package) {
copyableType = true copyableType = true
} }
} }
@ -87,7 +103,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{} generators = []generator.Generator{}
generators = append( generators = append(
generators, NewGenDeepCopy("deep_copy_generated", path, inputs.Has(path))) generators, NewGenDeepCopy("deep_copy_generated", path, boundingDirs, inputs.Has(path)))
return generators return generators
}, },
FilterFunc: func(c *generator.Context, t *types.Type) bool { FilterFunc: func(c *generator.Context, t *types.Type) bool {
@ -108,6 +124,7 @@ const (
type genDeepCopy struct { type genDeepCopy struct {
generator.DefaultGen generator.DefaultGen
targetPackage string targetPackage string
boundingDirs []string
imports namer.ImportTracker imports namer.ImportTracker
typesForInit []*types.Type typesForInit []*types.Type
generateInitFunc bool generateInitFunc bool
@ -115,12 +132,13 @@ type genDeepCopy struct {
globalVariables map[string]interface{} globalVariables map[string]interface{}
} }
func NewGenDeepCopy(sanitizedName, targetPackage string, generateInitFunc bool) generator.Generator { func NewGenDeepCopy(sanitizedName, targetPackage string, boundingDirs []string, generateInitFunc bool) generator.Generator {
return &genDeepCopy{ return &genDeepCopy{
DefaultGen: generator.DefaultGen{ DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName, OptionalName: sanitizedName,
}, },
targetPackage: targetPackage, targetPackage: targetPackage,
boundingDirs: boundingDirs,
imports: generator.NewImportTracker(), imports: generator.NewImportTracker(),
typesForInit: make([]*types.Type, 0), typesForInit: make([]*types.Type, 0),
generateInitFunc: generateInitFunc, generateInitFunc: generateInitFunc,
@ -134,17 +152,37 @@ func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems {
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 := copyableWithinPackage(t) copyable := g.copyableWithinPackage(t)
if copyable { if copyable {
g.typesForInit = append(g.typesForInit, t) g.typesForInit = append(g.typesForInit, t)
} }
return copyable return copyable
} }
func copyableWithinPackage(t *types.Type) bool { func (g *genDeepCopy) copyableWithinPackage(t *types.Type) bool {
return copyableWithinPackage(t, g.boundingDirs)
}
func isRootedUnder(pkg string, roots []string) bool {
// Add trailing / to avoid false matches, e.g. foo/bar vs foo/barn. This
// assumes that bounding dirs do not have trailing slashes.
pkg = pkg + "/"
for _, root := range roots {
if strings.HasPrefix(pkg, root+"/") {
return true
}
}
return false
}
func copyableWithinPackage(t *types.Type, boundingDirs []string) bool {
if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" { if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" {
return false return false
} }
// Only packages within the restricted range can be processed.
if !isRootedUnder(t.Name.Package, boundingDirs) {
return false
}
// TODO: Consider generating functions for other kinds too. // TODO: Consider generating functions for other kinds too.
if t.Kind != types.Struct { if t.Kind != types.Struct {
return false return false
@ -286,7 +324,7 @@ func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("}\n", nil) sw.Do("}\n", nil)
default: default:
sw.Do("for key, val := range in {\n", nil) sw.Do("for key, val := range in {\n", nil)
if copyableWithinPackage(t.Elem) { if g.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))
@ -318,7 +356,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 copyableWithinPackage(t.Elem) { } else if g.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)
@ -357,7 +395,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 copyableWithinPackage(t) { if g.copyableWithinPackage(t) {
funcName := g.funcNameTmpl(t) funcName := g.funcNameTmpl(t)
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)
@ -390,7 +428,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 copyableWithinPackage(t.Elem) { } else if g.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)

View File

@ -0,0 +1,102 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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
http://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 generators
import "testing"
func Test_isRootedUnder(t *testing.T) {
testCases := []struct {
path string
roots []string
expect bool
}{
{
path: "/foo/bar",
roots: nil,
expect: false,
},
{
path: "/foo/bar",
roots: []string{},
expect: false,
},
{
path: "/foo/bar",
roots: []string{
"/bad",
},
expect: false,
},
{
path: "/foo/bar",
roots: []string{
"/foo",
},
expect: true,
},
{
path: "/foo/bar",
roots: []string{
"/bad",
"/foo",
},
expect: true,
},
{
path: "/foo/bar/qux/zorb",
roots: []string{
"/foo/bar/qux",
},
expect: true,
},
{
path: "/foo/bar",
roots: []string{
"/foo/bar",
},
expect: true,
},
{
path: "/foo/barn",
roots: []string{
"/foo/bar",
},
expect: false,
},
{
path: "/foo/bar",
roots: []string{
"/foo/barn",
},
expect: false,
},
{
path: "/foo/bar",
roots: []string{
"",
},
expect: true,
},
}
for i, tc := range testCases {
r := isRootedUnder(tc.path, tc.roots)
if r != tc.expect {
t.Errorf("case[%d]: expected %t, got %t for %q in %q", i, tc.expect, r, tc.path, tc.roots)
}
}
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen/generators" "k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen/generators"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/pflag"
) )
func main() { func main() {
@ -60,6 +61,15 @@ func main() {
"k8s.io/kubernetes/federation/apis/federation/v1beta1", "k8s.io/kubernetes/federation/apis/federation/v1beta1",
} }
// Custom args.
customArgs := &generators.CustomArgs{
BoundingDirs: []string{"k8s.io/kubernetes"},
}
pflag.CommandLine.StringSliceVar(&customArgs.BoundingDirs, "bounding-dirs", customArgs.BoundingDirs,
"Comma-separated list of import paths which bound the types for which deep-copies will be generated.")
arguments.CustomArgs = customArgs
// Run it.
if err := arguments.Execute( if err := arguments.Execute(
generators.NameSystems(), generators.NameSystems(),
generators.DefaultNameSystem(), generators.DefaultNameSystem(),

View File

@ -35,6 +35,7 @@ bench-workers
bind-address bind-address
bind-pods-burst bind-pods-burst
bind-pods-qps bind-pods-qps
bounding-dirs
build-only build-only
build-services build-services
build-tag build-tag