remove more redundant files

This commit is contained in:
Daniel Smith 2016-08-30 10:27:33 -07:00
parent 2709f07c71
commit ac9e5496d9
9 changed files with 4 additions and 1677 deletions

View File

@ -1,624 +0,0 @@
/*
Copyright 2015 The Kubernetes 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
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 (
"fmt"
"io"
"path/filepath"
"strings"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/kubernetes/pkg/util/sets"
"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.
}
// This is the comment tag that carries parameters for deep-copy generation.
const tagName = "k8s:deepcopy-gen"
// Known values for the comment tag.
const tagValuePackage = "package"
// tagValue holds parameters from a tagName tag.
type tagValue struct {
value string
register bool
}
func extractTag(comments []string) *tagValue {
tagVals := types.ExtractCommentTags("+", comments)[tagName]
if tagVals == nil {
// No match for the tag.
return nil
}
// If there are multiple values, abort.
if len(tagVals) > 1 {
glog.Fatalf("Found %d %s tags: %q", len(tagVals), tagName, tagVals)
}
// If we got here we are returning something.
tag := &tagValue{}
// Get the primary value.
parts := strings.Split(tagVals[0], ",")
if len(parts) >= 1 {
tag.value = parts[0]
}
// Parse extra arguments.
parts = parts[1:]
for i := range parts {
kv := strings.SplitN(parts[i], "=", 2)
k := kv[0]
v := ""
if len(kv) == 2 {
v = kv[1]
}
switch k {
case "register":
if v != "false" {
tag.register = true
}
default:
glog.Fatalf("Unsupported %s param: %q", tagName, parts[i])
}
}
return tag
}
// TODO: This is created only to reduce number of changes in a single PR.
// Remove it and use PublicNamer instead.
func deepCopyNamer() *namer.NameStrategy {
return &namer.NameStrategy{
Join: func(pre string, in []string, post string) string {
return strings.Join(in, "_")
},
PrependPackageNames: 1,
}
}
// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
return namer.NameSystems{
"public": deepCopyNamer(),
"raw": namer.NewRawNamer("", nil),
}
}
// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
return "public"
}
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
if err != nil {
glog.Fatalf("Failed loading boilerplate: %v", err)
}
inputs := sets.NewString(context.Inputs...)
packages := generator.Packages{}
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
header = append(header, []byte(
`
// 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 i := range inputs {
glog.V(5).Infof("considering pkg %q", i)
pkg := context.Universe[i]
if pkg == nil {
// If the input had no Go files, for example.
continue
}
ptag := extractTag(pkg.Comments)
ptagValue := ""
ptagRegister := false
if ptag != nil {
ptagValue = ptag.value
if ptagValue != tagValuePackage {
glog.Fatalf("Package %v: unsupported %s value: %q", i, tagName, ptagValue)
}
ptagRegister = ptag.register
glog.V(5).Infof(" tag.value: %q, tag.register: %t", ptagValue, ptagRegister)
} else {
glog.V(5).Infof(" no tag")
}
// If the pkg-scoped tag says to generate, we can skip scanning types.
pkgNeedsGeneration := (ptagValue == tagValuePackage)
if !pkgNeedsGeneration {
// If the pkg-scoped tag did not exist, scan all types for one that
// explicitly wants generation.
for _, t := range pkg.Types {
glog.V(5).Infof(" considering type %q", t.Name.String())
ttag := extractTag(t.CommentLines)
if ttag != nil && ttag.value == "true" {
glog.V(5).Infof(" tag=true")
if !copyableType(t) {
glog.Fatalf("Type %v requests deepcopy generation but is not copyable", t)
}
pkgNeedsGeneration = true
break
}
}
}
if pkgNeedsGeneration {
packages = append(packages,
&generator.DefaultPackage{
PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0],
PackagePath: pkg.Path,
HeaderText: header,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{}
generators = append(
generators, NewGenDeepCopy(arguments.OutputFileBaseName, pkg.Path, boundingDirs, (ptagValue == tagValuePackage), ptagRegister))
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
return t.Name.Package == pkg.Path
},
})
}
}
return packages
}
const (
apiPackagePath = "k8s.io/kubernetes/pkg/api"
conversionPackagePath = "k8s.io/kubernetes/pkg/conversion"
runtimePackagePath = "k8s.io/kubernetes/pkg/runtime"
)
// genDeepCopy produces a file with autogenerated deep-copy functions.
type genDeepCopy struct {
generator.DefaultGen
targetPackage string
boundingDirs []string
allTypes bool
registerTypes bool
imports namer.ImportTracker
typesForInit []*types.Type
}
func NewGenDeepCopy(sanitizedName, targetPackage string, boundingDirs []string, allTypes, registerTypes bool) generator.Generator {
return &genDeepCopy{
DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName,
},
targetPackage: targetPackage,
boundingDirs: boundingDirs,
allTypes: allTypes,
registerTypes: registerTypes,
imports: generator.NewImportTracker(),
typesForInit: make([]*types.Type, 0),
}
}
func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems {
// Have the raw namer for this file track what it imports.
return namer.NameSystems{
"raw": namer.NewRawNamer(g.targetPackage, g.imports),
"dcFnName": &dcFnNamer{
public: deepCopyNamer(),
tracker: g.imports,
myPackage: g.targetPackage,
},
}
}
func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool {
// Filter out types not being processed or not copyable within the package.
enabled := g.allTypes
if !enabled {
ttag := extractTag(t.CommentLines)
if ttag != nil && ttag.value == "true" {
enabled = true
}
}
copyable := enabled && copyableType(t)
if copyable {
g.typesForInit = append(g.typesForInit, t)
}
return copyable
}
func (g *genDeepCopy) copyableAndInBounds(t *types.Type) bool {
if !copyableType(t) {
return false
}
// Only packages within the restricted range can be processed.
if !isRootedUnder(t.Name.Package, g.boundingDirs) {
return false
}
return true
}
// hasDeepCopyMethod returns true if an appropriate DeepCopy() method is
// defined for the given type. This allows more efficient deep copy
// implementations to be defined by the type's author. The correct signature
// for a type T is:
// func (t T) DeepCopy() T
// or:
// func (t *T) DeepCopyt() T
func hasDeepCopyMethod(t *types.Type) bool {
for mn, mt := range t.Methods {
if mn != "DeepCopy" {
continue
}
if len(mt.Signature.Parameters) != 0 {
return false
}
if len(mt.Signature.Results) != 1 || mt.Signature.Results[0].Name != t.Name {
return false
}
return true
}
return false
}
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 copyableType(t *types.Type) bool {
// If the type opts out of copy-generation, stop.
ttag := extractTag(t.CommentLines)
if ttag != nil && ttag.value == "false" {
return false
}
// TODO: Consider generating functions for other kinds too.
if t.Kind != types.Struct {
return false
}
// Also, filter out private types.
if namer.IsPrivateGoName(t.Name.Name) {
return false
}
return true
}
func (g *genDeepCopy) isOtherPackage(pkg string) bool {
if pkg == g.targetPackage {
return false
}
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
return false
}
return true
}
func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) {
importLines := []string{}
for _, singleImport := range g.imports.ImportLines() {
if g.isOtherPackage(singleImport) {
importLines = append(importLines, singleImport)
}
}
return importLines
}
func argsFromType(t *types.Type) generator.Args {
return generator.Args{
"type": t,
}
}
type dcFnNamer struct {
public namer.Namer
tracker namer.ImportTracker
myPackage string
}
func (n *dcFnNamer) Name(t *types.Type) string {
pubName := n.public.Name(t)
n.tracker.AddType(t)
if t.Name.Package == n.myPackage {
return "DeepCopy_" + pubName
}
return fmt.Sprintf("%s.DeepCopy_%s", n.tracker.LocalNameOf(t.Name.Package), pubName)
}
func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error {
cloner := c.Universe.Type(types.Name{Package: conversionPackagePath, Name: "Cloner"})
g.imports.AddType(cloner)
if !g.registerTypes {
// 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
}
glog.V(5).Infof("registering types in pkg %q", g.targetPackage)
sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do("func init() {\n", nil)
sw.Do("SchemeBuilder.Register(RegisterDeepCopies)\n", nil)
sw.Do("}\n\n", nil)
scheme := c.Universe.Type(types.Name{Package: runtimePackagePath, Name: "Scheme"})
schemePtr := &types.Type{
Kind: types.Pointer,
Elem: scheme,
}
sw.Do("// RegisterDeepCopies adds deep-copy functions to the given scheme. Public\n", nil)
sw.Do("// to allow building arbitrary schemes.\n", nil)
sw.Do("func RegisterDeepCopies(scheme $.|raw$) error {\n", schemePtr)
sw.Do("return scheme.AddGeneratedDeepCopyFuncs(\n", nil)
for _, t := range g.typesForInit {
args := argsFromType(t).
With("typeof", c.Universe.Package("reflect").Function("TypeOf"))
sw.Do("conversion.GeneratedDeepCopyFunc{Fn: $.type|dcFnName$, InType: $.typeof|raw$(&$.type|raw${})},\n", args)
}
sw.Do(")\n", nil)
sw.Do("}\n\n", nil)
return sw.Error()
}
func (g *genDeepCopy) needsGeneration(t *types.Type) bool {
tag := extractTag(t.CommentLines)
tv := ""
if tag != nil {
tv = tag.value
if tv != "true" && tv != "false" {
glog.Fatalf("Type %v: unsupported %s value: %q", t, tagName, tag.value)
}
}
if g.allTypes && tv == "false" {
// The whole package is being generated, but this type has opted out.
glog.V(5).Infof("not generating for type %v because type opted out", t)
return false
}
if !g.allTypes && tv != "true" {
// The whole package is NOT being generated, and this type has NOT opted in.
glog.V(5).Infof("not generating for type %v because type did not opt in", t)
return false
}
return true
}
func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
if !g.needsGeneration(t) {
return nil
}
glog.V(5).Infof("generating for type %v", t)
sw := generator.NewSnippetWriter(w, c, "$", "$")
args := argsFromType(t).
With("clonerType", types.Ref(conversionPackagePath, "Cloner"))
sw.Do("func $.type|dcFnName$(in interface{}, out interface{}, c *$.clonerType|raw$) error {{\n", args)
sw.Do("in := in.(*$.type|raw$)\nout := out.(*$.type|raw$)\n", argsFromType(t))
g.generateFor(t, sw)
sw.Do("return nil\n", nil)
sw.Do("}}\n\n", nil)
return sw.Error()
}
// we use the system of shadowing 'in' and 'out' so that the same code is valid
// at any nesting level. This makes the autogenerator easy to understand, and
// the compiler shouldn't care.
func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) {
var f func(*types.Type, *generator.SnippetWriter)
switch t.Kind {
case types.Builtin:
f = g.doBuiltin
case types.Map:
f = g.doMap
case types.Slice:
f = g.doSlice
case types.Struct:
f = g.doStruct
case types.Interface:
f = g.doInterface
case types.Pointer:
f = g.doPointer
case types.Alias:
f = g.doAlias
default:
f = g.doUnknown
}
f(t, sw)
}
func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = *in\n", nil)
}
func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = make($.|raw$)\n", t)
if t.Key.IsAssignable() {
switch {
case hasDeepCopyMethod(t.Elem):
sw.Do("for key, val := range *in {\n", nil)
sw.Do("(*out)[key] = val.DeepCopy()\n", nil)
sw.Do("}\n", nil)
case t.Elem.IsAnonymousStruct():
sw.Do("for key := range *in {\n", nil)
sw.Do("(*out)[key] = struct{}{}\n", nil)
sw.Do("}\n", nil)
case t.Elem.IsAssignable():
sw.Do("for key, val := range *in {\n", nil)
sw.Do("(*out)[key] = val\n", nil)
sw.Do("}\n", nil)
default:
sw.Do("for key, val := range *in {\n", nil)
if g.copyableAndInBounds(t.Elem) {
sw.Do("newVal := new($.|raw$)\n", t.Elem)
sw.Do("if err := $.type|dcFnName$(&val, newVal, c); err != nil {\n", argsFromType(t.Elem))
sw.Do("return err\n", nil)
sw.Do("}\n", nil)
sw.Do("(*out)[key] = *newVal\n", nil)
} else {
sw.Do("if newVal, err := c.DeepCopy(&val); err != nil {\n", nil)
sw.Do("return err\n", nil)
sw.Do("} else {\n", nil)
sw.Do("(*out)[key] = *newVal.(*$.|raw$)\n", t.Elem)
sw.Do("}\n", nil)
}
sw.Do("}\n", nil)
}
} else {
// 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("}\n", nil)
}
}
func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = make($.|raw$, len(*in))\n", t)
if t.Elem.Kind == types.Builtin {
sw.Do("copy(*out, *in)\n", nil)
} else {
sw.Do("for i := range *in {\n", nil)
if hasDeepCopyMethod(t.Elem) {
sw.Do("(*out)[i] = (*in)[i].DeepCopy()\n", nil)
} else if t.Elem.IsAssignable() {
sw.Do("(*out)[i] = (*in)[i]\n", nil)
} else if g.copyableAndInBounds(t.Elem) {
sw.Do("if err := $.type|dcFnName$(&(*in)[i], &(*out)[i], c); err != nil {\n", argsFromType(t.Elem))
sw.Do("return err\n", nil)
sw.Do("}\n", nil)
} else {
sw.Do("if newVal, err := c.DeepCopy(&(*in)[i]); err != nil {\n", nil)
sw.Do("return err\n", nil)
sw.Do("} else {\n", nil)
sw.Do("(*out)[i] = *newVal.(*$.|raw$)\n", t.Elem)
sw.Do("}\n", nil)
}
sw.Do("}\n", nil)
}
}
func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
if len(t.Members) == 0 {
// at least do something with in/out to avoid "declared and not used" errors
sw.Do("_ = in\n_ = out\n", nil)
}
for _, m := range t.Members {
t := m.Type
if t.Kind == types.Alias {
copied := *t.Underlying
copied.Name = t.Name
t = &copied
}
args := generator.Args{
"type": t,
"name": m.Name,
}
switch t.Kind {
case types.Builtin:
sw.Do("out.$.name$ = in.$.name$\n", args)
case types.Map, types.Slice, types.Pointer:
sw.Do("if in.$.name$ != nil {\n", args)
sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
g.generateFor(t, sw)
sw.Do("} else {\n", nil)
sw.Do("out.$.name$ = nil\n", args)
sw.Do("}\n", nil)
case types.Struct:
if hasDeepCopyMethod(t) {
sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args)
} else if t.IsAssignable() {
sw.Do("out.$.name$ = in.$.name$\n", args)
} else if g.copyableAndInBounds(t) {
sw.Do("if err := $.type|dcFnName$(&in.$.name$, &out.$.name$, c); err != nil {\n", args)
sw.Do("return err\n", nil)
sw.Do("}\n", nil)
} else {
sw.Do("if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args)
sw.Do("return err\n", nil)
sw.Do("} else {\n", nil)
sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args)
sw.Do("}\n", nil)
}
default:
sw.Do("if in.$.name$ == nil {\n", args)
sw.Do("out.$.name$ = nil\n", args)
sw.Do("} else if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args)
sw.Do("return err\n", nil)
sw.Do("} else {\n", nil)
sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args)
sw.Do("}\n", nil)
}
}
}
func (g *genDeepCopy) doInterface(t *types.Type, sw *generator.SnippetWriter) {
// TODO: Add support for interfaces.
g.doUnknown(t, sw)
}
func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
if hasDeepCopyMethod(t.Elem) {
sw.Do("*out = new($.Elem|raw$)\n", t)
sw.Do("**out = (*in).DeepCopy()\n", nil)
} else if t.Elem.IsAssignable() {
sw.Do("*out = new($.Elem|raw$)\n", t)
sw.Do("**out = **in", nil)
} else if g.copyableAndInBounds(t.Elem) {
sw.Do("*out = new($.Elem|raw$)\n", t)
sw.Do("if err := $.type|dcFnName$(*in, *out, c); err != nil {\n", argsFromType(t.Elem))
sw.Do("return err\n", nil)
sw.Do("}\n", nil)
} else {
sw.Do("if newVal, err := c.DeepCopy(*in); err != nil {\n", nil)
sw.Do("return err\n", nil)
sw.Do("} else {\n", nil)
sw.Do("*out = newVal.(*$.|raw$)\n", t.Elem)
sw.Do("}\n", nil)
}
}
func (g *genDeepCopy) doAlias(t *types.Type, sw *generator.SnippetWriter) {
// TODO: Add support for aliases.
g.doUnknown(t, sw)
}
func (g *genDeepCopy) doUnknown(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("// FIXME: Type $.|raw$ is unsupported.\n", t)
}

View File

@ -1,344 +0,0 @@
/*
Copyright 2016 The Kubernetes 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
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"
"k8s.io/gengo/types"
)
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)
}
}
}
func Test_hasDeepCopyMethod(t *testing.T) {
testCases := []struct {
typ types.Type
expect bool
}{
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
// No DeepCopy method.
Methods: map[string]*types.Type{},
},
expect: false,
},
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
Methods: map[string]*types.Type{
// No DeepCopy method.
"method": {
Name: types.Name{Package: "pkgname", Name: "func()"},
Kind: types.Func,
Signature: &types.Signature{
Parameters: []*types.Type{},
Results: []*types.Type{},
},
},
},
},
expect: false,
},
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
Methods: map[string]*types.Type{
// Wrong signature (no result).
"DeepCopy": {
Name: types.Name{Package: "pkgname", Name: "func()"},
Kind: types.Func,
Signature: &types.Signature{
Parameters: []*types.Type{},
Results: []*types.Type{},
},
},
},
},
expect: false,
},
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
Methods: map[string]*types.Type{
// Wrong signature (wrong result).
"DeepCopy": {
Name: types.Name{Package: "pkgname", Name: "func() int"},
Kind: types.Func,
Signature: &types.Signature{
Parameters: []*types.Type{},
Results: []*types.Type{
{
Name: types.Name{Name: "int"},
Kind: types.Builtin,
},
},
},
},
},
},
expect: false,
},
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
Methods: map[string]*types.Type{
// Correct signature.
"DeepCopy": {
Name: types.Name{Package: "pkgname", Name: "func() pkgname.typename"},
Kind: types.Func,
Signature: &types.Signature{
Parameters: []*types.Type{},
Results: []*types.Type{
{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
},
},
},
},
},
},
expect: true,
},
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
Methods: map[string]*types.Type{
// Wrong signature (has params).
"DeepCopy": {
Name: types.Name{Package: "pkgname", Name: "func(int) pkgname.typename"},
Kind: types.Func,
Signature: &types.Signature{
Parameters: []*types.Type{
{
Name: types.Name{Name: "int"},
Kind: types.Builtin,
},
},
Results: []*types.Type{
{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
},
},
},
},
},
},
expect: false,
},
{
typ: types.Type{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
Methods: map[string]*types.Type{
// Wrong signature (extra results).
"DeepCopy": {
Name: types.Name{Package: "pkgname", Name: "func() (pkgname.typename, int)"},
Kind: types.Func,
Signature: &types.Signature{
Parameters: []*types.Type{},
Results: []*types.Type{
{
Name: types.Name{Package: "pkgname", Name: "typename"},
Kind: types.Builtin,
},
{
Name: types.Name{Name: "int"},
Kind: types.Builtin,
},
},
},
},
},
},
expect: false,
},
}
for i, tc := range testCases {
r := hasDeepCopyMethod(&tc.typ)
if r != tc.expect {
t.Errorf("case[%d]: expected %t, got %t", i, tc.expect, r)
}
}
}
func Test_extractTagParams(t *testing.T) {
testCases := []struct {
comments []string
expect *tagValue
}{
{
comments: []string{
"Human comment",
},
expect: nil,
},
{
comments: []string{
"Human comment",
"+k8s:deepcopy-gen",
},
expect: &tagValue{
value: "",
register: false,
},
},
{
comments: []string{
"Human comment",
"+k8s:deepcopy-gen=package",
},
expect: &tagValue{
value: "package",
register: false,
},
},
{
comments: []string{
"Human comment",
"+k8s:deepcopy-gen=package,register",
},
expect: &tagValue{
value: "package",
register: true,
},
},
{
comments: []string{
"Human comment",
"+k8s:deepcopy-gen=package,register=true",
},
expect: &tagValue{
value: "package",
register: true,
},
},
{
comments: []string{
"Human comment",
"+k8s:deepcopy-gen=package,register=false",
},
expect: &tagValue{
value: "package",
register: false,
},
},
}
for i, tc := range testCases {
r := extractTag(tc.comments)
if r == nil && tc.expect != nil {
t.Errorf("case[%d]: expected non-nil", i)
}
if r != nil && tc.expect == nil {
t.Errorf("case[%d]: expected nil, got %v", i, *r)
}
if r != nil && *r != *tc.expect {
t.Errorf("case[%d]: expected %v, got %v", i, *tc.expect, *r)
}
}
}

View File

@ -49,7 +49,7 @@ package main
import (
"k8s.io/gengo/args"
"k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen/generators"
"k8s.io/gengo/examples/deepcopy-gen/generators"
"github.com/golang/glog"
"github.com/spf13/pflag"

View File

@ -1,272 +0,0 @@
/*
Copyright 2016 The Kubernetes 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
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 has the generators for the import-boss utility.
package generators
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"github.com/golang/glog"
)
const (
importBossFileType = "import-boss"
)
// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer("", nil),
}
}
// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
return "raw"
}
// Packages makes the sets package definition.
func Packages(c *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
pkgs := generator.Packages{}
c.FileTypes = map[string]generator.FileType{
importBossFileType: importRuleFile{},
}
for _, p := range c.Universe {
if !arguments.InputIncludes(p) {
// Don't run on e.g. third party dependencies.
continue
}
savedPackage := p
pkgs = append(pkgs, &generator.DefaultPackage{
PackageName: p.Name,
PackagePath: p.Path,
// GeneratorFunc returns a list of generators. Each generator makes a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
return []generator.Generator{&importRules{
myPackage: savedPackage,
}}
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
return false
},
})
}
return pkgs
}
// A single import restriction rule.
type Rule struct {
// All import paths that match this regexp...
SelectorRegexp string
// ... must have one of these prefixes ...
AllowedPrefixes []string
// ... and must not have one of these prefixes.
ForbiddenPrefixes []string
}
type fileFormat struct {
CurrentImports []string
Rules []Rule
}
func readFile(path string) (*fileFormat, error) {
currentBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("couldn't read %v: %v", path, err)
}
var current fileFormat
err = json.Unmarshal(currentBytes, &current)
if err != nil {
return nil, fmt.Errorf("couldn't unmarshal %v: %v", path, err)
}
return &current, nil
}
func writeFile(path string, ff *fileFormat) error {
raw, err := json.MarshalIndent(ff, "", "\t")
if err != nil {
return fmt.Errorf("couldn't format data for file %v.\n%#v", path, ff)
}
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("couldn't open %v for writing: %v", path, err)
}
defer f.Close()
_, err = f.Write(raw)
return err
}
// This does the actual checking, since it knows the literal destination file.
type importRuleFile struct{}
func (importRuleFile) AssembleFile(f *generator.File, path string) error {
return nil
}
// TODO: make a flag to enable this, or expose this information in some other way.
func (importRuleFile) listEntireImportTree(f *generator.File, path string) error {
// If the file exists, populate its current imports. This is mostly to help
// humans figure out what they need to fix.
if _, err := os.Stat(path); err != nil {
// Ignore packages which haven't opted in by adding an .import-restrictions file.
return nil
}
current, err := readFile(path)
if err != nil {
return err
}
current.CurrentImports = []string{}
for v := range f.Imports {
current.CurrentImports = append(current.CurrentImports, v)
}
sort.Strings(current.CurrentImports)
return writeFile(path, current)
}
// removeLastDir removes the last directory, but leaves the file name
// unchanged. It returns the new path and the removed directory. So:
// "a/b/c/file" -> ("a/b/file", "c")
func removeLastDir(path string) (newPath, removedDir string) {
dir, file := filepath.Split(path)
dir = strings.TrimSuffix(dir, string(filepath.Separator))
return filepath.Join(filepath.Dir(dir), file), filepath.Base(dir)
}
// Keep going up a directory until we find an .import-restrictions file.
func recursiveRead(path string) (*fileFormat, string, error) {
for {
if _, err := os.Stat(path); err == nil {
ff, err := readFile(path)
return ff, path, err
}
nextPath, removedDir := removeLastDir(path)
if nextPath == path || removedDir == "src" {
break
}
path = nextPath
}
return nil, "", nil
}
func (importRuleFile) VerifyFile(f *generator.File, path string) error {
rules, actualPath, err := recursiveRead(path)
if err != nil {
return fmt.Errorf("error finding rules file: %v", err)
}
if rules == nil {
// No restrictions on this directory.
return nil
}
for _, r := range rules.Rules {
re, err := regexp.Compile(r.SelectorRegexp)
if err != nil {
return fmt.Errorf("regexp `%s` in file %q doesn't compile: %v", r.SelectorRegexp, actualPath, err)
}
for v := range f.Imports {
glog.V(4).Infof("Checking %v matches %v: %v\n", r.SelectorRegexp, v, re.MatchString(v))
if !re.MatchString(v) {
continue
}
for _, forbidden := range r.ForbiddenPrefixes {
glog.V(4).Infof("Checking %v against %v\n", v, forbidden)
if strings.HasPrefix(v, forbidden) {
return fmt.Errorf("import %v has forbidden prefix %v", v, forbidden)
}
}
found := false
for _, allowed := range r.AllowedPrefixes {
glog.V(4).Infof("Checking %v against %v\n", v, allowed)
if strings.HasPrefix(v, allowed) {
found = true
break
}
}
if !found {
return fmt.Errorf("import %v did not match any allowed prefix", v)
}
}
}
if len(rules.Rules) > 0 {
glog.V(2).Infof("%v passes rules found in %v\n", path, actualPath)
}
return nil
}
// importRules produces a file with a set for a single type.
type importRules struct {
myPackage *types.Package
imports namer.ImportTracker
}
var (
_ = generator.Generator(&importRules{})
_ = generator.FileType(importRuleFile{})
)
func (r *importRules) Name() string { return "import rules" }
func (r *importRules) Filter(*generator.Context, *types.Type) bool { return false }
func (r *importRules) Namers(*generator.Context) namer.NameSystems { return nil }
func (r *importRules) PackageVars(*generator.Context) []string { return []string{} }
func (r *importRules) PackageConsts(*generator.Context) []string { return []string{} }
func (r *importRules) GenerateType(*generator.Context, *types.Type, io.Writer) error { return nil }
func (r *importRules) Filename() string { return ".import-restrictions" }
func (r *importRules) FileType() string { return importBossFileType }
func (r *importRules) Init(c *generator.Context, w io.Writer) error { return nil }
func (r *importRules) Finalize(c *generator.Context, w io.Writer) error { return nil }
func dfsImports(dest *[]string, seen map[string]bool, p *types.Package) {
for _, p2 := range p.Imports {
if seen[p2.Path] {
continue
}
seen[p2.Path] = true
dfsImports(dest, seen, p2)
*dest = append(*dest, p2.Path)
}
}
func (r *importRules) Imports(*generator.Context) []string {
all := []string{}
dfsImports(&all, map[string]bool{}, r.myPackage)
return all
}

View File

@ -1,36 +0,0 @@
/*
Copyright 2016 The Kubernetes 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
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 TestRemoveLastDir(t *testing.T) {
table := map[string]struct{ newPath, removedDir string }{
"a/b/c": {"a/c", "b"},
}
for input, expect := range table {
gotPath, gotRemoved := removeLastDir(input)
if e, a := expect.newPath, gotPath; e != a {
t.Errorf("%v: wanted %v, got %v", input, e, a)
}
if e, a := expect.removedDir, gotRemoved; e != a {
t.Errorf("%v: wanted %v, got %v", input, e, a)
}
}
}

View File

@ -33,7 +33,7 @@ limitations under the License.
// {
// "SelectorRegexp": "k8s[.]io",
// "AllowedPrefixes": [
// "k8s.io/kubernetes/cmd/libs/go2idl",
// "k8s.io/gengo/examples",
// "k8s.io/kubernetes/third_party"
// ],
// "ForbiddenPrefixes": [
@ -59,7 +59,7 @@ import (
"os"
"k8s.io/gengo/args"
"k8s.io/kubernetes/cmd/libs/go2idl/import-boss/generators"
"k8s.io/gengo/examples/import-boss/generators"
"github.com/golang/glog"
)

View File

@ -1,364 +0,0 @@
/*
Copyright 2015 The Kubernetes 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
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 has the generators for the set-gen utility.
package generators
import (
"io"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"github.com/golang/glog"
)
// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
return namer.NameSystems{
"public": namer.NewPublicNamer(0),
"private": namer.NewPrivateNamer(0),
"raw": namer.NewRawNamer("", nil),
}
}
// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
return "public"
}
// Packages makes the sets package definition.
func Packages(_ *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
if err != nil {
glog.Fatalf("Failed loading boilerplate: %v", err)
}
return generator.Packages{&generator.DefaultPackage{
PackageName: "sets",
PackagePath: arguments.OutputPackagePath,
HeaderText: append(boilerplate, []byte(
`
// This file was autogenerated by set-gen. Do not edit it manually!
`)...),
PackageDocumentation: []byte(
`// Package sets has auto-generated set types.
`),
// GeneratorFunc returns a list of generators. Each generator makes a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
// Always generate a "doc.go" file.
generator.DefaultGen{OptionalName: "doc"},
// Make a separate file for the Empty type, since it's shared by every type.
generator.DefaultGen{
OptionalName: "empty",
OptionalBody: []byte(emptyTypeDecl),
},
}
// Since we want a file per type that we generate a set for, we
// have to provide a function for this.
for _, t := range c.Order {
generators = append(generators, &genSet{
DefaultGen: generator.DefaultGen{
// Use the privatized version of the
// type name as the file name.
//
// TODO: make a namer that converts
// camelCase to '-' separation for file
// names?
OptionalName: c.Namers["private"].Name(t),
},
outputPackage: arguments.OutputPackagePath,
typeToMatch: t,
imports: generator.NewImportTracker(),
})
}
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
// It would be reasonable to filter by the type's package here.
// It might be necessary if your input directory has a big
// import graph.
switch t.Kind {
case types.Map, types.Slice, types.Pointer:
// These types can't be keys in a map.
return false
case types.Builtin:
return true
case types.Struct:
// Only some structs can be keys in a map. This is triggered by the line
// // +genset
// or
// // +genset=true
return extractBoolTagOrDie("genset", t.CommentLines) == true
}
return false
},
}}
}
// genSet produces a file with a set for a single type.
type genSet struct {
generator.DefaultGen
outputPackage string
typeToMatch *types.Type
imports namer.ImportTracker
}
// Filter ignores all but one type because we're making a single file per type.
func (g *genSet) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch }
func (g *genSet) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *genSet) Imports(c *generator.Context) (imports []string) {
return append(g.imports.ImportLines(), "reflect", "sort")
}
// args constructs arguments for templates. Usage:
// g.args(t, "key1", value1, "key2", value2, ...)
//
// 't' is loaded with the key 'type'.
//
// We could use t directly as the argument, but doing it this way makes it easy
// to mix in additional parameters. This feature is not used in this set
// generator, but is present as an example.
func (g *genSet) args(t *types.Type, kv ...interface{}) interface{} {
m := map[interface{}]interface{}{"type": t}
for i := 0; i < len(kv)/2; i++ {
m[kv[i*2]] = kv[i*2+1]
}
return m
}
// GenerateType makes the body of a file implementing a set for type t.
func (g *genSet) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do(setCode, g.args(t))
sw.Do("func less$.type|public$(lhs, rhs $.type|raw$) bool {\n", g.args(t))
g.lessBody(sw, t)
sw.Do("}\n", g.args(t))
return sw.Error()
}
func (g *genSet) lessBody(sw *generator.SnippetWriter, t *types.Type) {
// TODO: make this recursive, handle pointers and multiple nested structs...
switch t.Kind {
case types.Struct:
for _, m := range types.FlattenMembers(t.Members) {
sw.Do("if lhs.$.Name$ < rhs.$.Name$ { return true }\n", m)
sw.Do("if lhs.$.Name$ > rhs.$.Name$ { return false }\n", m)
}
sw.Do("return false\n", nil)
default:
sw.Do("return lhs < rhs\n", nil)
}
}
// written to the "empty.go" file.
var emptyTypeDecl = `
// Empty is public since it is used by some internal API objects for conversions between external
// string arrays and internal sets, and conversion logic requires public types today.
type Empty struct{}
`
// Written for every type. If you've never used text/template before:
// $.type$ refers to the source type; |public means to
// call the function giving the public name, |raw the raw type name.
var setCode = `// sets.$.type|public$ is a set of $.type|raw$s, implemented via map[$.type|raw$]struct{} for minimal memory consumption.
type $.type|public$ map[$.type|raw$]Empty
// New creates a $.type|public$ from a list of values.
func New$.type|public$(items ...$.type|raw$) $.type|public$ {
ss := $.type|public${}
ss.Insert(items...)
return ss
}
// $.type|public$KeySet creates a $.type|public$ from a keys of a map[$.type|raw$](? extends interface{}).
// If the value passed in is not actually a map, this will panic.
func $.type|public$KeySet(theMap interface{}) $.type|public$ {
v := reflect.ValueOf(theMap)
ret := $.type|public${}
for _, keyValue := range v.MapKeys() {
ret.Insert(keyValue.Interface().($.type|raw$))
}
return ret
}
// Insert adds items to the set.
func (s $.type|public$) Insert(items ...$.type|raw$) {
for _, item := range items {
s[item] = Empty{}
}
}
// Delete removes all items from the set.
func (s $.type|public$) Delete(items ...$.type|raw$) {
for _, item := range items {
delete(s, item)
}
}
// Has returns true if and only if item is contained in the set.
func (s $.type|public$) Has(item $.type|raw$) bool {
_, contained := s[item]
return contained
}
// HasAll returns true if and only if all items are contained in the set.
func (s $.type|public$) HasAll(items ...$.type|raw$) bool {
for _, item := range items {
if !s.Has(item) {
return false
}
}
return true
}
// HasAny returns true if any items are contained in the set.
func (s $.type|public$) HasAny(items ...$.type|raw$) bool {
for _, item := range items {
if s.Has(item) {
return true
}
}
return false
}
// Difference returns a set of objects that are not in s2
// For example:
// s1 = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s1.Difference(s2) = {a3}
// s2.Difference(s1) = {a4, a5}
func (s $.type|public$) Difference(s2 $.type|public$) $.type|public$ {
result := New$.type|public$()
for key := range s {
if !s2.Has(key) {
result.Insert(key)
}
}
return result
}
// Union returns a new set which includes items in either s1 or s2.
// For example:
// s1 = {a1, a2}
// s2 = {a3, a4}
// s1.Union(s2) = {a1, a2, a3, a4}
// s2.Union(s1) = {a1, a2, a3, a4}
func (s1 $.type|public$) Union(s2 $.type|public$) $.type|public$ {
result := New$.type|public$()
for key := range s1 {
result.Insert(key)
}
for key := range s2 {
result.Insert(key)
}
return result
}
// Intersection returns a new set which includes the item in BOTH s1 and s2
// For example:
// s1 = {a1, a2}
// s2 = {a2, a3}
// s1.Intersection(s2) = {a2}
func (s1 $.type|public$) Intersection(s2 $.type|public$) $.type|public$ {
var walk, other $.type|public$
result := New$.type|public$()
if s1.Len() < s2.Len() {
walk = s1
other = s2
} else {
walk = s2
other = s1
}
for key := range walk {
if other.Has(key) {
result.Insert(key)
}
}
return result
}
// IsSuperset returns true if and only if s1 is a superset of s2.
func (s1 $.type|public$) IsSuperset(s2 $.type|public$) bool {
for item := range s2 {
if !s1.Has(item) {
return false
}
}
return true
}
// Equal returns true if and only if s1 is equal (as a set) to s2.
// Two sets are equal if their membership is identical.
// (In practice, this means same elements, order doesn't matter)
func (s1 $.type|public$) Equal(s2 $.type|public$) bool {
return len(s1) == len(s2) && s1.IsSuperset(s2)
}
type sortableSliceOf$.type|public$ []$.type|raw$
func (s sortableSliceOf$.type|public$) Len() int { return len(s) }
func (s sortableSliceOf$.type|public$) Less(i, j int) bool { return less$.type|public$(s[i], s[j]) }
func (s sortableSliceOf$.type|public$) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// List returns the contents as a sorted $.type|raw$ slice.
func (s $.type|public$) List() []$.type|raw$ {
res := make(sortableSliceOf$.type|public$, 0, len(s))
for key := range s {
res = append(res, key)
}
sort.Sort(res)
return []$.type|raw$(res)
}
// UnsortedList returns the slice with contents in random order.
func (s $.type|public$) UnsortedList() []$.type|raw$ {
res :=make([]$.type|raw$, 0, len(s))
for key := range s {
res = append(res, key)
}
return res
}
// Returns a single element from the set.
func (s $.type|public$) PopAny() ($.type|raw$, bool) {
for key := range s {
s.Delete(key)
return key, true
}
var zeroValue $.type|raw$
return zeroValue, false
}
// Len returns the size of the set.
func (s $.type|public$) Len() int {
return len(s)
}
`

View File

@ -1,33 +0,0 @@
/*
Copyright 2016 The Kubernetes 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
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 (
"github.com/golang/glog"
"k8s.io/gengo/types"
)
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
// it exists, the value is boolean. If the tag did not exist, it returns
// false.
func extractBoolTagOrDie(key string, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}

View File

@ -28,7 +28,7 @@ import (
"os"
"k8s.io/gengo/args"
"k8s.io/kubernetes/cmd/libs/go2idl/set-gen/generators"
"k8s.io/gengo/examples/set-gen/generators"
"github.com/golang/glog"
)