From c64fedf703f4dbf614e670548c9d0cd69cae6d90 Mon Sep 17 00:00:00 2001 From: John Howard Date: Tue, 12 Dec 2023 09:42:57 -0800 Subject: [PATCH] Add ability to include a build tag This enables conditional compiling. This is particularly useful for shared protobufs for common APIs, where not all consumers have the same preferences around build size vs benefits of vtprotobuf. This allows the consumer of the library to chose whether vtprotobuf is used, rather than only the producer of the library. Additionally, it provides guidance to use `vtprotobuf` as the tag. The hope is that if multiple libraries do this, a user can just set that tag instead of `-tags=envoy_vt,enable_vtprotobuf_prometheus,vtproto_k8s` or something like that. See https://github.com/envoyproxy/envoy/pull/31172, where this will be utilized (I think). --- Makefile | 10 ++++++++++ README.md | 14 ++++++++++++-- cmd/protoc-gen-go-vtproto/main.go | 1 + generator/generator.go | 12 +++++++++--- testproto/buildtag/empty/empty_vtproto.pb.go | 19 +++++++++++++++++++ 5 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 testproto/buildtag/empty/empty_vtproto.pb.go diff --git a/Makefile b/Makefile index c524dbb..41c524d 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,16 @@ gen-testproto: get-grpc-testproto gen-wkt-testproto install testproto/proto2/scalars.proto \ testproto/unsafe/unsafe.proto \ || exit 1; + $(PROTOBUF_ROOT)/src/protoc \ + --proto_path=testproto \ + --proto_path=include \ + --go_out=. --plugin protoc-gen-go="${GOBIN}/protoc-gen-go" \ + --go-vtproto_opt=paths=source_relative \ + --go-vtproto_opt=buildTag=vtprotobuf \ + --go-vtproto_out=allow-empty=true:./testproto/buildtag --plugin protoc-gen-go-vtproto="${GOBIN}/protoc-gen-go-vtproto" \ + -I$(PROTOBUF_ROOT)/src \ + testproto/empty/empty.proto \ + || exit 1; get-grpc-testproto: install $(PROTOBUF_ROOT)/src/protoc \ diff --git a/README.md b/README.md index 4a3784f..5222f04 100644 --- a/README.md +++ b/README.md @@ -108,10 +108,20 @@ The following features can be generated: --go-vtproto_opt=pool=vitess.io/vitess/go/vt/proto/query.Row \ --go-vtproto_opt=pool=vitess.io/vitess/go/vt/proto/binlogdata.VStreamRowsResponse \ ``` +6. (Optional) if you want to selectively compile the generate `vtprotobuf` files, the `--vtproto_opt=buildTag=` can be used. -6. Compile the `.proto` files in your project. You should see `_vtproto.pb.go` files next to the `.pb.go` and `_grpc.pb.go` files that were already being generated. + When using this option, the generated code will only be compiled in if a build tag is provided. -7. (Optional) Switch your RPC framework to use the optimized helpers (see following sections) + It is recommended, but not required, to use `vtprotobuf` as the build tag if this is desired, especially if your project is imported by others. + This will reduce the number of build tags a user will need to configure if they are importing multiple libraries following this pattern. + + When using this option, it is strongly recommended to make your code compile with and without the build tag. + This can be done with type assertions before using `vtprotobuf` generated methods. + The `grpc.Codec{}` object (discussed below) shows an example. + +7. Compile the `.proto` files in your project. You should see `_vtproto.pb.go` files next to the `.pb.go` and `_grpc.pb.go` files that were already being generated. + +8. (Optional) Switch your RPC framework to use the optimized helpers (see following sections) ## `vtprotobuf` package and well-known types diff --git a/cmd/protoc-gen-go-vtproto/main.go b/cmd/protoc-gen-go-vtproto/main.go index e0ebb60..3a317d3 100644 --- a/cmd/protoc-gen-go-vtproto/main.go +++ b/cmd/protoc-gen-go-vtproto/main.go @@ -26,6 +26,7 @@ func main() { f.Var(&cfg.Poolable, "pool", "use memory pooling for this object") f.BoolVar(&cfg.Wrap, "wrap", false, "generate wrapper types") f.StringVar(&features, "features", "all", "list of features to generate (separated by '+')") + f.StringVar(&cfg.BuildTag, "buildTag", "", "the go:build tag to set on generated files") protogen.Options{ParamFunc: f.Set}.Run(func(plugin *protogen.Plugin) error { gen, err := generator.NewGenerator(plugin, strings.Split(features, "+"), &cfg) diff --git a/generator/generator.go b/generator/generator.go index 2263880..416232e 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -37,9 +37,10 @@ func (o ObjectSet) Set(s string) error { } type Config struct { - Poolable ObjectSet - Wrap bool - AllowEmpty bool + Poolable ObjectSet + Wrap bool + AllowEmpty bool + BuildTag string } type Generator struct { @@ -97,6 +98,11 @@ func (gen *Generator) generateFile(gf *protogen.GeneratedFile, file *protogen.Fi LocalPackages: gen.local, } + if p.Config.BuildTag != "" { + // Support both forms of tags for maximum compatibility + p.P("//go:build ", p.Config.BuildTag) + p.P("// +build ", p.Config.BuildTag) + } p.P("// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.") if bi, ok := debug.ReadBuildInfo(); ok { p.P("// protoc-gen-go-vtproto version: ", bi.Main.Version) diff --git a/testproto/buildtag/empty/empty_vtproto.pb.go b/testproto/buildtag/empty/empty_vtproto.pb.go new file mode 100644 index 0000000..7a3e0d5 --- /dev/null +++ b/testproto/buildtag/empty/empty_vtproto.pb.go @@ -0,0 +1,19 @@ +//go:build vtprotobuf +// +build vtprotobuf + +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: (devel) +// source: empty/empty.proto + +package empty + +import ( + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +)