diff --git a/cmd/libs/go2idl/conversion-gen/generators/conversion.go b/cmd/libs/go2idl/conversion-gen/generators/conversion.go index 026a9a33cda..83ea0e7dd46 100644 --- a/cmd/libs/go2idl/conversion-gen/generators/conversion.go +++ b/cmd/libs/go2idl/conversion-gen/generators/conversion.go @@ -46,13 +46,26 @@ type CustomArgs struct { SkipUnsafe bool } -// This is the comment tag that carries parameters for conversion generation. -const tagName = "k8s:conversion-gen" +// These are the comment tags that carry parameters for conversion generation. +const ( + // e.g., "+k8s:conversion-gen=" in doc.go, where is the + // import path of the package the peer types are defined in. + // e.g., "+k8s:conversion-gen=false" in a type's comment will let + // conversion-gen skip that type. + tagName = "k8s:conversion-gen" + // e.g., "+k8s:conversion-gen-external-types=" in doc.go, where + // is the relative path to the package the types are defined in. + externalTypesTagName = "k8s:conversion-gen-external-types" +) func extractTag(comments []string) []string { return types.ExtractCommentTags("+", comments)[tagName] } +func extractExternalTypesTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[externalTypesTagName] +} + func isCopyOnly(comments []string) bool { values := types.ExtractCommentTags("+", comments)["k8s:conversion-fn"] return len(values) == 1 && values[0] == "copy-only" @@ -198,6 +211,10 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat for i := range inputs { glog.V(5).Infof("considering pkg %q", i) pkg := context.Universe[i] + // typesPkg is where the versioned types are defined. Sometimes it is + // different from pkg. For example, kubernetes core/v1 types are defined + // in vendor/k8s.io/api/core/v1, while pkg is at pkg/api/v1. + typesPkg := pkg if pkg == nil { // If the input had no Go files, for example. continue @@ -224,6 +241,24 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat skipUnsafe = customArgs.SkipUnsafe } + // if the external types are not in the same package where the conversion functions to be generated + externalTypesValues := extractExternalTypesTag(pkg.Comments) + if externalTypesValues != nil { + if len(externalTypesValues) != 1 { + glog.Fatalf(" expect only one value for %q tag, got: %q", externalTypesTagName, externalTypesValues) + } + externalTypes := externalTypesValues[0] + glog.V(5).Infof(" external types tags: %q", externalTypes) + var err error + typesPkg, err = context.AddDirectory(filepath.Join(pkg.Path, externalTypes)) + if err != nil { + glog.Fatalf("cannot import package %s", externalTypes) + } + // update context.Order to the latest context.Universe + orderer := namer.Orderer{Namer: namer.NewPublicNamer(1)} + context.Order = orderer.OrderUniverse(context.Universe) + } + // if the source path is within a /vendor/ directory (for example, // k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow // generation to output to the proper relative path (under vendor). @@ -263,11 +298,11 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat HeaderText: header, GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { return []generator.Generator{ - NewGenConversion(arguments.OutputFileBaseName, pkg.Path, manualConversions, peerPkgs, unsafeEquality), + NewGenConversion(arguments.OutputFileBaseName, typesPkg.Path, pkg.Path, manualConversions, peerPkgs, unsafeEquality), } }, FilterFunc: func(c *generator.Context, t *types.Type) bool { - return t.Name.Package == pkg.Path + return t.Name.Package == typesPkg.Path }, }) } @@ -380,7 +415,12 @@ type TypesEqual interface { // genConversion produces a file with a autogenerated conversions. type genConversion struct { generator.DefaultGen - targetPackage string + // the package that contains the types that conversion func are going to be + // generated for + typesPackage string + // the package that the conversion funcs are going to be output to + outputPackage string + // packages that contain the peer of types in typesPacakge peerPackages []string manualConversions conversionFuncMap imports namer.ImportTracker @@ -389,12 +429,13 @@ type genConversion struct { useUnsafe TypesEqual } -func NewGenConversion(sanitizedName, targetPackage string, manualConversions conversionFuncMap, peerPkgs []string, useUnsafe TypesEqual) generator.Generator { +func NewGenConversion(sanitizedName, typesPackage, outputPackage string, manualConversions conversionFuncMap, peerPkgs []string, useUnsafe TypesEqual) generator.Generator { return &genConversion{ DefaultGen: generator.DefaultGen{ OptionalName: sanitizedName, }, - targetPackage: targetPackage, + typesPackage: typesPackage, + outputPackage: outputPackage, peerPackages: peerPkgs, manualConversions: manualConversions, imports: generator.NewImportTracker(), @@ -407,7 +448,7 @@ func NewGenConversion(sanitizedName, targetPackage string, manualConversions con func (g *genConversion) 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), + "raw": namer.NewRawNamer(g.outputPackage, g.imports), "publicIT": &namerPlusImportTracking{ delegate: conversionNamer(), tracker: g.imports, @@ -428,13 +469,13 @@ func (n *namerPlusImportTracking) Name(t *types.Type) string { func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type) bool { var t *types.Type var other *types.Type - if inType.Name.Package == g.targetPackage { + if inType.Name.Package == g.typesPackage { t, other = inType, outType } else { t, other = outType, inType } - if t.Name.Package != g.targetPackage { + if t.Name.Package != g.typesPackage { return false } // If the type has opted out, skip it. @@ -471,10 +512,10 @@ func (g *genConversion) Filter(c *generator.Context, t *types.Type) bool { } func (g *genConversion) isOtherPackage(pkg string) bool { - if pkg == g.targetPackage { + if pkg == g.outputPackage { return false } - if strings.HasSuffix(pkg, `"`+g.targetPackage+`"`) { + if strings.HasSuffix(pkg, `"`+g.outputPackage+`"`) { return false } return true