diff --git a/staging/src/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go b/staging/src/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go index e9ef9acb073..a85117ae849 100644 --- a/staging/src/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go +++ b/staging/src/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go @@ -136,7 +136,7 @@ func getManualConversionFunctions(context *generator.Context, pkg *types.Package klog.Warning("Skipping nil package passed to getManualConversionFunctions") return } - klog.V(5).Infof("Scanning for conversion functions in %v", pkg.Name) + klog.V(5).Infof("Scanning for conversion functions in %v", pkg.Path) scopeName := types.Ref(conversionPackagePath, "Scope").Name errorName := types.Ref("", "error").Name @@ -219,58 +219,45 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat // have non-trivial conversion memoryEquivalentTypes := equalMemoryTypes{} - // We are generating conversions only for packages that are explicitly - // passed as InputDir. - processed := map[string]bool{} + // First load other "input" packages. We do this as a single call because + // it is MUCH faster. + filteredInputs := make([]string, 0, len(context.Inputs)) + otherPkgs := make([]string, 0, len(context.Inputs)) + pkgToPeers := map[string][]string{} + pkgToExternal := map[string]string{} for _, i := range context.Inputs { - // skip duplicates - if processed[i] { - continue - } - processed[i] = true + klog.V(5).Infof("pre-processing pkg %q", i) - klog.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 } - // Add conversion and defaulting functions. - getManualConversionFunctions(context, pkg, manualConversions) - // Only generate conversions for packages which explicitly request it // by specifying one or more "+k8s:conversion-gen=" // in their doc.go file. peerPkgs := extractTag(pkg.Comments) - if peerPkgs != nil { - klog.V(5).Infof(" tags: %q", peerPkgs) - if len(peerPkgs) == 1 && peerPkgs[0] == "false" { - // If a single +k8s:conversion-gen=false tag is defined, we still want - // the generator to fire for this package for explicit conversions, but - // we are clearing the peerPkgs to not generate any standard conversions. - peerPkgs = nil - } - } else { + if peerPkgs == nil { klog.V(5).Infof(" no tag") continue } - skipUnsafe := false - extraDirs := []string{} - if customArgs, ok := arguments.CustomArgs.(*conversionargs.CustomArgs); ok { - if len(peerPkgs) > 0 { - peerPkgs = append(peerPkgs, customArgs.BasePeerDirs...) - peerPkgs = append(peerPkgs, customArgs.ExtraPeerDirs...) - } - extraDirs = customArgs.ExtraDirs - skipUnsafe = customArgs.SkipUnsafe + klog.V(5).Infof(" tags: %q", peerPkgs) + if len(peerPkgs) == 1 && peerPkgs[0] == "false" { + // If a single +k8s:conversion-gen=false tag is defined, we still want + // the generator to fire for this package for explicit conversions, but + // we are clearing the peerPkgs to not generate any standard conversions. + peerPkgs = nil + } else { + // Save peers for each input + pkgToPeers[i] = peerPkgs } + otherPkgs = append(otherPkgs, peerPkgs...) + // Keep this one for further processing. + filteredInputs = append(filteredInputs, i) - // if the external types are not in the same package where the conversion functions to be generated + // 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 { @@ -278,39 +265,76 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat } externalTypes := externalTypesValues[0] klog.V(5).Infof(" external types tags: %q", externalTypes) - var err error - typesPkg, err = context.AddDirectory(externalTypes) - if err != nil { - klog.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) + otherPkgs = append(otherPkgs, externalTypes) + pkgToExternal[i] = externalTypes + } else { + pkgToExternal[i] = i } + } - // 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). - // Otherwise, the generator will create the file in the wrong location - // in the output directory. - // TODO: build a more fundamental concept in gengo for dealing with modifications - // to vendored packages. - for i := range peerPkgs { - peerPkgs[i] = genutil.Vendorless(peerPkgs[i]) + // Make sure explicit peer-packages are added. + var sanitizedPeerPkgs []string + if customArgs, ok := arguments.CustomArgs.(*conversionargs.CustomArgs); ok { + all := append(customArgs.BasePeerDirs, customArgs.ExtraPeerDirs...) + for _, pkg := range all { + // 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). + // Otherwise, the generator will create the file in the wrong location + // in the output directory. + pkg = genutil.Vendorless(pkg) + sanitizedPeerPkgs = append(sanitizedPeerPkgs, pkg) } - for i := range extraDirs { - extraDirs[i] = genutil.Vendorless(extraDirs[i]) - } - - // Make sure our peer-packages are added and fully parsed. - for _, pp := range append(peerPkgs, extraDirs...) { - context.AddDir(pp) - p := context.Universe[pp] - if nil == p { - klog.Fatalf("failed to find pkg: %s", pp) + if expanded, err := context.FindPackages(sanitizedPeerPkgs...); err != nil { + klog.Fatalf("cannot find peer packages: %v", err) + } else { + otherPkgs = append(otherPkgs, expanded...) + // for each pkg, add these extras, too + for k := range pkgToPeers { + pkgToPeers[k] = append(pkgToPeers[k], expanded...) } - getManualConversionFunctions(context, p, manualConversions) } + } + + if len(otherPkgs) > 0 { + if _, err := context.LoadPackages(otherPkgs...); err != nil { + klog.Fatalf("cannot load packages: %v", err) + } + } + // update context.Order to the latest context.Universe + orderer := namer.Orderer{Namer: namer.NewPublicNamer(1)} + context.Order = orderer.OrderUniverse(context.Universe) + + // Look for conversion functions in the peer-packages. + for _, pp := range otherPkgs { + p := context.Universe[pp] + if p == nil { + klog.Fatalf("failed to find pkg: %s", pp) + } + getManualConversionFunctions(context, p, manualConversions) + } + + skipUnsafe := false + if customArgs, ok := arguments.CustomArgs.(*conversionargs.CustomArgs); ok { + skipUnsafe = customArgs.SkipUnsafe + } + + // We are generating conversions only for packages that are explicitly + // passed as InputDir. + for _, i := range filteredInputs { + klog.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 + + // Add conversion and defaulting functions. + getManualConversionFunctions(context, pkg, manualConversions) + + // Find the right input pkg, which might not be this one. + externalTypes := pkgToExternal[i] + typesPkg = context.Universe[externalTypes] unsafeEquality := TypesEqual(memoryEquivalentTypes) if skipUnsafe { @@ -335,10 +359,11 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat &generator.DefaultPackage{ PackageName: filepath.Base(pkg.Path), PackagePath: path, + Source: pkg.SourcePath, // output pkg is the same as the input HeaderText: header, GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { return []generator.Generator{ - NewGenConversion(arguments.OutputFileBaseName, typesPkg.Path, pkg.Path, manualConversions, peerPkgs, unsafeEquality), + NewGenConversion(arguments.OutputFileBaseName, typesPkg.Path, pkg.Path, manualConversions, pkgToPeers[path], unsafeEquality), } }, FilterFunc: func(c *generator.Context, t *types.Type) bool {