From 6d8adfe4025a5fbc7cff2cb6e2bceca52714f177 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Wed, 1 Oct 2025 13:56:47 -0400 Subject: [PATCH] Isolate generated ProtoMessage() methods in build-tagged files --- hack/update-codegen.sh | 1 + .../runtime/serializer/protobuf/protobuf.go | 2 - .../cmd/go-to-protobuf/protobuf/cmd.go | 12 +++-- .../cmd/go-to-protobuf/protobuf/package.go | 6 ++- .../cmd/go-to-protobuf/protobuf/parser.go | 48 +++++++++++++++++-- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index fbf09ea4205..ebe260a33ad 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -145,6 +145,7 @@ function codegen::protobuf() { git_find -z \ ':(glob)**/generated.proto' \ ':(glob)**/generated.pb.go' \ + ':(glob)**/generated.protomessage.pb.go' \ | xargs -0 rm -f if kube::protoc::check_protoc >/dev/null; then diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go index a359324332f..67a2d124745 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go @@ -320,8 +320,6 @@ type unbufferedMarshaller interface { // unmarshaler is the subset of gogo Message and Unmarshaler used by unmarshal type unmarshaler interface { - // Marker method, currently defined on all protoc-generated messages - ProtoMessage() // Reset() is called on the top-level message before unmarshaling, // and clears all existing data from the message instance. Reset() diff --git a/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/cmd.go b/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/cmd.go index 51b75fc2d61..773482d6e3b 100644 --- a/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/cmd.go +++ b/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/cmd.go @@ -279,6 +279,7 @@ func Run(g *Generator) { path := filepath.Join(g.OutputDir, p.ImportPath()) outputPath := filepath.Join(g.OutputDir, p.OutputPath()) + protomessageOutputPath := filepath.Join(g.OutputDir, p.ProtomessageOutputPath()) // generate the gogoprotobuf protoc cmd := exec.Command("protoc", append(args, path)...) @@ -295,12 +296,17 @@ func Run(g *Generator) { // alter the generated protobuf file to remove the generated types (but leave the serializers) and rewrite the // package statement to match the desired package name - if err := RewriteGeneratedGogoProtobufFile(outputPath, p.ExtractGeneratedType, p.OptionalTypeName, buf.Bytes(), g.DropGogoGo); err != nil { + if err := RewriteGeneratedGogoProtobufFile(outputPath, protomessageOutputPath, p.ExtractGeneratedType, p.OptionalTypeName, buf.Bytes(), g.DropGogoGo); err != nil { log.Fatalf("Unable to rewrite generated %s: %v", outputPath, err) } + outputPaths := []string{outputPath} + if g.DropGogoGo { + outputPaths = append(outputPaths, protomessageOutputPath) + } + // sort imports - cmd = exec.Command("goimports", "-w", outputPath) + cmd = exec.Command("goimports", append([]string{"-w"}, outputPaths...)...) out, err = cmd.CombinedOutput() if len(out) > 0 { log.Print(string(out)) @@ -311,7 +317,7 @@ func Run(g *Generator) { } // format and simplify the generated file - cmd = exec.Command("gofmt", "-s", "-w", outputPath) + cmd = exec.Command("gofmt", append([]string{"-s", "-w"}, outputPaths...)...) out, err = cmd.CombinedOutput() if len(out) > 0 { log.Print(string(out)) diff --git a/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/package.go b/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/package.go index b31a7c4dd79..c35b73242af 100644 --- a/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/package.go +++ b/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/package.go @@ -80,7 +80,7 @@ type protobufPackage struct { } func (p *protobufPackage) Clean() error { - for _, s := range []string{p.ImportPath(), p.OutputPath()} { + for _, s := range []string{p.ImportPath(), p.OutputPath(), p.ProtomessageOutputPath()} { if err := os.Remove(filepath.Join(p.Dir(), filepath.Base(s))); err != nil && !os.IsNotExist(err) { return err } @@ -200,6 +200,10 @@ func (p *protobufPackage) OutputPath() string { return filepath.Join(p.Path(), "generated.pb.go") } +func (p *protobufPackage) ProtomessageOutputPath() string { + return filepath.Join(p.Path(), "generated.protomessage.pb.go") +} + var ( _ = generator.Target(&protobufPackage{}) ) diff --git a/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/parser.go b/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/parser.go index d0c0d69a4f2..ba0d112f6ea 100644 --- a/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/parser.go +++ b/staging/src/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/parser.go @@ -27,6 +27,7 @@ import ( "go/token" "os" "reflect" + "regexp" "strings" customreflect "k8s.io/code-generator/third_party/forked/golang/reflect" @@ -77,8 +78,46 @@ type ExtractFunc func(*ast.TypeSpec) bool // and should have its marshal functions adjusted to remove the 'Items' accessor. type OptionalFunc func(name string) bool -func RewriteGeneratedGogoProtobufFile(name string, extractFn ExtractFunc, optionalFn OptionalFunc, header []byte, dropGogo bool) error { - return rewriteFile(name, header, func(fset *token.FileSet, file *ast.File) error { +func RewriteGeneratedGogoProtobufFile(file, protomessageFile string, extractFn ExtractFunc, optionalFn OptionalFunc, header []byte, dropGogo bool) error { + + // Optionally extract ProtoMessage() marker methods to a separate build-tagged file + if dropGogo { + data, err := os.ReadFile(file) + if err != nil { + return err + } + + packageRE := regexp.MustCompile(`^package .*`) + wrotePackage := false + + protomessageFuncRE := regexp.MustCompile(`^func \(.*\) ProtoMessage\(\) \{\}$`) + + b := bytes.NewBuffer(nil) + // add build tag + b.WriteString("//go:build kubernetes_protomessage_one_more_release\n") + b.WriteString("// +build kubernetes_protomessage_one_more_release\n\n") + // add boilerplate + b.Write(header) + b.WriteString("\n// Code generated by go-to-protobuf. DO NOT EDIT.\n\n") + for _, line := range bytes.Split(data, []byte("\n")) { + // copy package + if packageRE.Match(line) && !wrotePackage { + b.Write(line) + b.WriteString("\n\n") + wrotePackage = true + } + // copy empty ProtoMessage impls + if protomessageFuncRE.Match(line) { + b.Write(line) + b.WriteString("\n\n") + } + } + if err := os.WriteFile(protomessageFile, b.Bytes(), os.FileMode(0644)); err != nil { + return err + } + } + + return rewriteFile(file, header, func(fset *token.FileSet, file *ast.File) error { cmap := ast.NewCommentMap(fset, file, file.Comments) // transform methods that point to optional maps or slices @@ -172,9 +211,8 @@ var keepFuncs = map[string]bool{ "valueToStringGenerated": true, // unmarshal - "Reset": true, - "ProtoMessage": true, - "Unmarshal": true, + "Reset": true, + "Unmarshal": true, // marshal "Size": true,