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.
pull/6/head
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"
)
// 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.
// Remove it and use PublicNamer instead.
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!
`)...)
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 {
copyableType := false
for _, t := range p.Types {
if copyableWithinPackage(t) && inputs.Has(t.Name.Package) {
if copyableWithinPackage(t, boundingDirs) && inputs.Has(t.Name.Package) {
copyableType = true
}
}
@ -87,7 +103,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{}
generators = append(
generators, NewGenDeepCopy("deep_copy_generated", path, inputs.Has(path)))
generators, NewGenDeepCopy("deep_copy_generated", path, boundingDirs, inputs.Has(path)))
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
@ -108,6 +124,7 @@ const (
type genDeepCopy struct {
generator.DefaultGen
targetPackage string
boundingDirs []string
imports namer.ImportTracker
typesForInit []*types.Type
generateInitFunc bool
@ -115,12 +132,13 @@ type genDeepCopy struct {
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{
DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName,
},
targetPackage: targetPackage,
boundingDirs: boundingDirs,
imports: generator.NewImportTracker(),
typesForInit: make([]*types.Type, 0),
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 {
// Filter out all types not copyable within the package.
copyable := copyableWithinPackage(t)
copyable := g.copyableWithinPackage(t)
if copyable {
g.typesForInit = append(g.typesForInit, t)
}
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" {
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.
if t.Kind != types.Struct {
return false
@ -286,7 +324,7 @@ func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("}\n", nil)
default:
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)
funcName := g.funcNameTmpl(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)
if t.Elem.IsAssignable() {
sw.Do("(*out)[i] = in[i]\n", nil)
} else if copyableWithinPackage(t.Elem) {
} else if g.copyableWithinPackage(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("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("}\n", nil)
case types.Struct:
if copyableWithinPackage(t) {
if g.copyableWithinPackage(t) {
funcName := g.funcNameTmpl(t)
sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args)
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)
if t.Elem.Kind == types.Builtin {
sw.Do("**out = *in", nil)
} else if copyableWithinPackage(t.Elem) {
} else if g.copyableWithinPackage(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("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"
"github.com/golang/glog"
"github.com/spf13/pflag"
)
func main() {
@ -60,6 +61,15 @@ func main() {
"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(
generators.NameSystems(),
generators.DefaultNameSystem(),

View File

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