func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type) bool { var t *types.Type var other *types.Type if inType.Name.Package == g.targetPackage { t, other = inType, outType } else { t, other = outType, inType } if t.Name.Package != g.targetPackage { return false } // If the type has opted out, skip it. tagvals := extractTag(t.CommentLines) if tagvals != nil { if tagvals[0] != "false" { glog.Fatalf("Type %v: unsupported %s value: %q", t, tagName, tagvals[0]) } glog.V(5).Infof("type %v requests no conversion generation, skipping", t) 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(other.Name.Name) { return false } return true }
func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type) bool { var t *types.Type var other *types.Type if inType.Name.Package == g.targetPackage { t, other = inType, outType } else { t, other = outType, inType } if t.Name.Package != g.targetPackage { return false } if types.ExtractCommentTags("+", t.CommentLines)["genconversion"] == "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(other.Name.Name) { return false } return true }
// assignGoTypeToProtoPackage looks for Go and Protobuf types that are referenced by a type in // a package. It will not recurse into protobuf types. func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global typeNameSet, optional map[types.Name]struct{}) { newT, isProto := isFundamentalProtoType(t) if isProto { t = newT } if otherP, ok := global[t.Name]; ok { if _, ok := local[t.Name]; !ok { p.Imports.AddType(&types.Type{ Kind: types.Protobuf, Name: otherP.ProtoTypeName(), }) } return } global[t.Name] = p if _, ok := local[t.Name]; ok { return } // don't recurse into existing proto types if isProto { p.Imports.AddType(t) return } local[t.Name] = p for _, m := range t.Members { if namer.IsPrivateGoName(m.Name) { continue } field := &protoField{} tag := reflect.StructTag(m.Tags).Get("protobuf") if tag == "-" { continue } if err := protobufTagToField(tag, field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil { assignGoTypeToProtoPackage(p, field.Type, local, global, optional) continue } assignGoTypeToProtoPackage(p, m.Type, local, global, optional) } // TODO: should methods be walked? if t.Elem != nil { assignGoTypeToProtoPackage(p, t.Elem, local, global, optional) } if t.Key != nil { assignGoTypeToProtoPackage(p, t.Key, local, global, optional) } if t.Underlying != nil { if t.Kind == types.Alias && isOptionalAlias(t) { optional[t.Name] = struct{}{} } assignGoTypeToProtoPackage(p, t.Underlying, local, global, optional) } }
func copyableWithinPackage(t *types.Type) bool { if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "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 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 Packages(_ *generator.Context, arguments *args.GeneratorArgs) generator.Packages { boilerplate, err := arguments.LoadGoBoilerplate() if err != nil { glog.Fatalf("Failed loading boilerplate: %v", err) } packages := generator.Packages{} for _, inputDir := range arguments.InputDirs { packages = append(packages, &generator.DefaultPackage{ PackageName: filepath.Base(inputDir), PackagePath: inputDir, HeaderText: append(boilerplate, []byte( ` // This file was autogenerated by deepcopy-gen. Do not edit it manually! `)...), GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { generators = []generator.Generator{} // TODO: Check whether anything will be generated. generators = append(generators, NewGenDeepCopy("deep_copy_generated", inputDir)) return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { switch t.Kind { case types.Func, types.Chan: // These types can't be copied. return false case types.Unknown, types.Unsupported: // These types are explicitly ignored. return false case types.Array: // We don't support arrays. return false } // Also, filter out private types. if namer.IsPrivateGoName(t.Name.Name) { return false } return true }, }) } return packages }
func copyableWithinPackage(t *types.Type, explicitCopyRequired bool) bool { tag := types.ExtractCommentTags("+", t.CommentLines)["gencopy"] if tag == "false" { return false } if explicitCopyRequired && tag != "true" { 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) 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/") { return false } if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" { return false } // TODO: Consider generating functions for other kinds too. if t.Kind != types.Struct { return false } // TODO: This should be removed once we start generating public DeepCopy // functions per directory. if t.Name.Package != g.targetPackage { // 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 }
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 conversion-gen. Do not edit it manually! `)...) // Accumulate pre-existing conversion and default functions. // TODO: This is too ad-hoc. We need a better way. manualConversions := conversionFuncMap{} manualDefaults := defaulterFuncMap{} // We are generating conversions only for packages that are explicitly // passed as InputDir. 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 } // Add conversion and defaulting functions. getManualConversionFunctions(context, pkg, manualConversions) getManualDefaultingFunctions(context, pkg, manualDefaults) // Only generate conversions for packages which explicitly request it // by specifying one or more "+k8s:conversion-gen=<peer-pkg>" // in their doc.go file. peerPkgs := extractTag(pkg.Comments) if peerPkgs != nil { glog.V(5).Infof(" tags: %q", peerPkgs) } else { glog.V(5).Infof(" no tag") continue } if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok { if len(customArgs.ExtraPeerDirs) > 0 { peerPkgs = append(peerPkgs, customArgs.ExtraPeerDirs...) } } // Make sure our peer-packages are added and fully parsed. for _, pp := range peerPkgs { context.AddDir(pp) getManualConversionFunctions(context, context.Universe[pp], manualConversions) getManualDefaultingFunctions(context, context.Universe[pp], manualDefaults) } pkgNeedsGeneration := false for _, t := range pkg.Types { // Check whether this type can be auto-converted to the peer // package type. peerType := getPeerTypeFor(context, t, peerPkgs) if peerType == nil { // We did not find a corresponding type. continue } if namer.IsPrivateGoName(peerType.Name.Name) { // We won't be able to convert to a private type. glog.V(5).Infof(" found a peer type %v, but it is a private name", t) continue } // If we can generate conversion in any direction, we should // generate this package. if isConvertible(t, peerType, manualConversions) || isConvertible(peerType, t, manualConversions) { pkgNeedsGeneration = true break } } if !pkgNeedsGeneration { glog.V(5).Infof(" no viable conversions, not generating for this package") continue } packages = append(packages, &generator.DefaultPackage{ PackageName: filepath.Base(pkg.Path), PackagePath: pkg.Path, HeaderText: header, GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { generators = []generator.Generator{} generators = append( generators, NewGenConversion(arguments.OutputFileBaseName, pkg.Path, manualConversions, manualDefaults, peerPkgs)) return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return t.Name.Package == pkg.Path }, }) } return packages }
func membersToFields(locator ProtobufLocator, t *types.Type, localPackage types.Name, omitFieldTypes map[types.Name]struct{}) ([]protoField, error) { fields := []protoField{} for _, m := range t.Members { if namer.IsPrivateGoName(m.Name) { // skip private fields continue } if _, ok := omitFieldTypes[types.Name{Name: m.Type.Name.Name, Package: m.Type.Name.Package}]; ok { continue } tags := reflect.StructTag(m.Tags) field := protoField{ LocalPackage: localPackage, Tag: -1, Extras: make(map[string]string), } if err := protobufTagToField(tags.Get("protobuf"), &field, m, t, localPackage); err != nil { return nil, err } // extract information from JSON field tag if tag := tags.Get("json"); len(tag) > 0 { parts := strings.Split(tag, ",") if len(field.Name) == 0 && len(parts[0]) != 0 { field.Name = parts[0] } if field.Tag == -1 && field.Name == "-" { continue } } if field.Type == nil { if err := memberTypeToProtobufField(locator, &field, m.Type); err != nil { return nil, fmt.Errorf("unable to embed type %q as field %q in %q: %v", m.Type, field.Name, t.Name, err) } } if len(field.Name) == 0 { field.Name = namer.IL(m.Name) } if field.Map && field.Repeated { // maps cannot be repeated field.Repeated = false field.Nullable = true } if !field.Nullable { field.Extras["(gogoproto.nullable)"] = "false" } if (field.Type.Name.Name == "bytes" && field.Type.Name.Package == "") || (field.Repeated && field.Type.Name.Package == "" && namer.IsPrivateGoName(field.Type.Name.Name)) { delete(field.Extras, "(gogoproto.nullable)") } if field.Name != m.Name { field.Extras["(gogoproto.customname)"] = strconv.Quote(m.Name) } field.CommentLines = m.CommentLines fields = append(fields, field) } // assign tags highest := 0 byTag := make(map[int]*protoField) // fields are in Go struct order, which we preserve for i := range fields { field := &fields[i] tag := field.Tag if tag != -1 { if existing, ok := byTag[tag]; ok { return nil, fmt.Errorf("field %q and %q both have tag %d", field.Name, existing.Name, tag) } byTag[tag] = field } if tag > highest { highest = tag } } // starting from the highest observed tag, assign new field tags for i := range fields { field := &fields[i] if field.Tag != -1 { continue } highest++ field.Tag = highest byTag[field.Tag] = field } return fields, nil }
func (b bodyGen) doStruct(sw *generator.SnippetWriter) error { if len(b.t.Name.Name) == 0 { return nil } if namer.IsPrivateGoName(b.t.Name.Name) { return nil } var alias *types.Type var fields []protoField options := []string{} allOptions := types.ExtractCommentTags("+", b.t.CommentLines) for k, v := range allOptions { switch { case strings.HasPrefix(k, "protobuf.options."): key := strings.TrimPrefix(k, "protobuf.options.") switch key { case "marshal": if v == "false" { if !b.omitGogo { options = append(options, "(gogoproto.marshaler) = false", "(gogoproto.unmarshaler) = false", "(gogoproto.sizer) = false", ) } } default: if !b.omitGogo || !strings.HasPrefix(key, "(gogoproto.") { options = append(options, fmt.Sprintf("%s = %s", key, v)) } } // protobuf.as allows a type to have the same message contents as another Go type case k == "protobuf.as": fields = nil if alias = b.locator.GoTypeForName(types.Name{Name: v}); alias == nil { return fmt.Errorf("type %v references alias %q which does not exist", b.t, v) } // protobuf.embed instructs the generator to use the named type in this package // as an embedded message. case k == "protobuf.embed": fields = []protoField{ { Tag: 1, Name: v, Type: &types.Type{ Name: types.Name{ Name: v, Package: b.localPackage.Package, Path: b.localPackage.Path, }, }, }, } } } if alias == nil { alias = b.t } // If we don't explicitly embed anything, generate fields by traversing fields. if fields == nil { memberFields, err := membersToFields(b.locator, alias, b.localPackage, b.omitFieldTypes) if err != nil { return fmt.Errorf("type %v cannot be converted to protobuf: %v", b.t, err) } fields = memberFields } out := sw.Out() genComment(out, b.t.CommentLines, "") sw.Do(`message $.Name.Name$ { `, b.t) if len(options) > 0 { sort.Sort(sort.StringSlice(options)) for _, s := range options { fmt.Fprintf(out, " option %s;\n", s) } fmt.Fprintln(out) } for i, field := range fields { genComment(out, field.CommentLines, " ") fmt.Fprintf(out, " ") switch { case field.Map: case field.Repeated: fmt.Fprintf(out, "repeated ") case field.Required: fmt.Fprintf(out, "required ") default: fmt.Fprintf(out, "optional ") } sw.Do(`$.Type|local$ $.Name$ = $.Tag$`, field) if len(field.Extras) > 0 { extras := []string{} for k, v := range field.Extras { if b.omitGogo && strings.HasPrefix(k, "(gogoproto.") { continue } extras = append(extras, fmt.Sprintf("%s = %s", k, v)) } sort.Sort(sort.StringSlice(extras)) if len(extras) > 0 { fmt.Fprintf(out, " [") fmt.Fprint(out, strings.Join(extras, ", ")) fmt.Fprintf(out, "]") } } fmt.Fprintf(out, ";\n") if i != len(fields)-1 { fmt.Fprintf(out, "\n") } } fmt.Fprintf(out, "}\n\n") return nil }
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(arguments.InputDirs...) packages := generator.Packages{} header := append([]byte( `// +build !ignore_autogenerated `), boilerplate...) header = append(header, []byte( ` // This file was autogenerated by conversion-gen. Do not edit it manually! `)...) // Compute all pre-existing conversion functions. preexisting := existingConversionFunctions(context) preexistingDefaults := existingDefaultingFunctions(context) // We are generating conversions only for packages that are explicitly // passed as InputDir, and only for those that have a corresponding type // (in the directory one above) and can be automatically converted to. for _, p := range context.Universe { path := p.Path if !inputs.Has(path) { continue } // Only generate conversions for package which explicitly requested it // byt setting "+genversion=true" in their doc.go file. filtered := false for _, comment := range p.DocComments { comment := strings.Trim(comment, "//") if types.ExtractCommentTags("+", comment)["genconversion"] == "true" { filtered = true } } if !filtered { continue } convertibleType := false for _, t := range p.Types { // Check whether this type can be auto-converted to the internal // version. internalType, exists := getInternalTypeFor(context, t) if !exists { // There is no corresponding type in the internal package. continue } // We won't be able to convert to private type. if namer.IsPrivateGoName(internalType.Name.Name) { continue } // If we can generate conversion in any direction, we should // generate this package. if isConvertible(t, internalType, preexisting) || isConvertible(internalType, t, preexisting) { convertibleType = true } } if convertibleType { packages = append(packages, &generator.DefaultPackage{ PackageName: filepath.Base(path), PackagePath: path, HeaderText: header, GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { generators = []generator.Generator{} generators = append( generators, NewGenConversion("conversion_generated", path, preexisting, preexistingDefaults)) return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return t.Name.Package == path }, }) } } return packages }