Skip to content

Commit 8368b3a

Browse files
committed
roachprod: add typegen code generator
Previously, when using the codec utility, all dynamic types needed to implement the `codec.DynamicType` interface and register itself against the codec package manually. This change provides a code generator `typegen` for generating type registration source files. Users of the codec utility can now simply add `typegen:reg` in a comment above the concrete implementations of dynamic types. Then it only requires adding the `genrule` in the bazel file of the package that wants to generate the type registrations, and the type registration source files will be generated automatically. Informs: #149451, #151461 Epic: None Release note: None
1 parent 95b3cab commit 8368b3a

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

pkg/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,8 @@ GO_TARGETS = [
16991699
"//pkg/roachprod/prometheus:prometheus_test",
17001700
"//pkg/roachprod/promhelperclient:promhelperclient",
17011701
"//pkg/roachprod/promhelperclient:promhelperclient_test",
1702+
"//pkg/roachprod/roachprodutil/codec/cmd/typegen:typegen",
1703+
"//pkg/roachprod/roachprodutil/codec/cmd/typegen:typegen_lib",
17021704
"//pkg/roachprod/roachprodutil/codec:codec",
17031705
"//pkg/roachprod/roachprodutil/codec:codec_test",
17041706
"//pkg/roachprod/roachprodutil:roachprodutil",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2+
3+
go_library(
4+
name = "typegen_lib",
5+
srcs = ["main.go"],
6+
importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/roachprodutil/codec/cmd/typegen",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//pkg/cli/exit",
10+
"@com_github_cockroachdb_errors//:errors",
11+
],
12+
)
13+
14+
go_binary(
15+
name = "typegen",
16+
embed = [":typegen_lib"],
17+
visibility = ["//visibility:public"],
18+
)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright 2025 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the CockroachDB Software License
4+
// included in the /LICENSE file.
5+
6+
package main
7+
8+
import (
9+
"flag"
10+
"fmt"
11+
"go/ast"
12+
"go/parser"
13+
"go/token"
14+
"os"
15+
"path/filepath"
16+
"sort"
17+
"strings"
18+
19+
"github.com/cockroachdb/cockroach/pkg/cli/exit"
20+
"github.com/cockroachdb/errors"
21+
)
22+
23+
var (
24+
outputFile = flag.String("output", "", "Output file; defaults to stdout.")
25+
)
26+
27+
func usage() {
28+
fmt.Println(`usage: typegen [flags] <source> [<type> ...]
29+
30+
This program generates an implementation of the [codec.DynamicType] interface
31+
for each specified type present in the source file. It will also create an
32+
'init function' that registers the types with the codec package.
33+
34+
Any type documented with the "// typegen:reg" comment above its definition will
35+
also be registered.
36+
37+
Flags:`)
38+
flag.PrintDefaults()
39+
}
40+
41+
func exitOnError(err error) {
42+
if err == nil {
43+
return
44+
}
45+
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err)
46+
exit.WithCode(exit.UnspecifiedError())
47+
}
48+
49+
func main() {
50+
// Flag handling section
51+
flag.Usage = usage
52+
flag.Parse()
53+
54+
if flag.NArg() < 1 {
55+
usage()
56+
exit.WithCode(exit.UnspecifiedError())
57+
}
58+
source := flag.Arg(0)
59+
verifyTypes := make(map[string]struct{})
60+
61+
for _, t := range flag.Args()[1:] {
62+
verifyTypes[t] = struct{}{}
63+
}
64+
65+
// Parse the source file.
66+
fs := token.NewFileSet()
67+
file, err := parser.ParseFile(fs, source, nil, parser.ParseComments)
68+
exitOnError(errors.Wrapf(err, "parsing %s", source))
69+
70+
// Find all types that need to be registered.
71+
types := make(map[string]struct{})
72+
for _, decl := range file.Decls {
73+
gen, ok := decl.(*ast.GenDecl)
74+
if !ok || gen.Tok != token.TYPE {
75+
continue
76+
}
77+
for _, spec := range gen.Specs {
78+
ts, ok := spec.(*ast.TypeSpec)
79+
if !ok {
80+
continue
81+
}
82+
83+
typeName := ts.Name.Name
84+
if gen.Doc != nil {
85+
text := gen.Doc.Text()
86+
if strings.Contains(text, "typegen:reg") {
87+
types[typeName] = struct{}{}
88+
}
89+
}
90+
91+
if _, ok := verifyTypes[typeName]; ok {
92+
delete(verifyTypes, typeName)
93+
types[typeName] = struct{}{}
94+
continue
95+
}
96+
}
97+
}
98+
99+
// Validate that types passed in via the command line were found.
100+
if len(verifyTypes) > 0 {
101+
var missing []string
102+
for t := range verifyTypes {
103+
missing = append(missing, t)
104+
}
105+
exitOnError(errors.Newf("the following types were not found in %s: %v", source, missing))
106+
}
107+
108+
// Fail if no types were found.
109+
if len(types) == 0 {
110+
exitOnError(errors.Newf("no types found in %s", source))
111+
}
112+
113+
var typeKeys []string
114+
for typeName := range types {
115+
typeKeys = append(typeKeys, typeName)
116+
}
117+
sort.Strings(typeKeys)
118+
119+
// Generate the type registration code.
120+
w := os.Stdout
121+
if *outputFile != "" {
122+
w, err = os.Create(*outputFile)
123+
exitOnError(errors.Wrapf(err, "creating %s", *outputFile))
124+
defer w.Close()
125+
}
126+
p := func(format string, args ...interface{}) {
127+
_, _ = fmt.Fprintf(w, format, args...)
128+
}
129+
130+
pkg := file.Name.Name
131+
132+
p("// Code generated by typegen; DO NOT EDIT.\n")
133+
p("// Generated from: %s\n\n", filepath.Base(source))
134+
p("package %s\n\n", pkg)
135+
136+
prefixCall := ""
137+
if pkg != "codec" {
138+
prefixCall = "codec."
139+
p("import \"github.com/cockroachdb/cockroach/pkg/roachprod/roachprodutil/codec\"\n\n")
140+
}
141+
142+
p("func init() {\n")
143+
for _, t := range typeKeys {
144+
p(" %sRegister(new(%s))\n", prefixCall, t)
145+
}
146+
p("}\n")
147+
148+
for _, t := range typeKeys {
149+
p("\nfunc (t %s) GetTypeName() %sTypeName {\n", t, prefixCall)
150+
p(" return %sResolveTypeName(t)\n", prefixCall)
151+
p("}\n")
152+
}
153+
}

0 commit comments

Comments
 (0)