Skip to content

Commit 6ebdf85

Browse files
HoneyryderChuckcopybara-github
authored andcommitted
ruby codegen: support generation of rbs files (#15633)
this introduces support for a new protoc option, `--rbs_out`, which points to a directory where ruby type definition files, defined in the RBS format, are stored. [rbs](https://github.com/ruby/rbs) is the type signature syntax blessed by the ruby core team, used by static analysis tools such as [steep](https://github.com/soutaro/steep), which integrates with VS Code, the `irb` console (for features such as autocompletion). and [typeprof](https://github.com/ruby/typeprof). It relies on type definitions written into `.rbs` files. The `protobuf` library already exposes type definitions in [gem_rbs_collection](https://github.com/ruby/gem_rbs_collection/tree/main/gems/google-protobuf/3.22), which is used to source type definitions for libraries which do not want, or can't maintain type definitions themselves. (`protobuf` could arguably import these into this repository, lmk if you're interested). This should fix gaps such as better IDE integration, such as the ones described [here](#9495). The plan is to do roughly the same type of integration as was done for `.pyi` annotations, which also write to separate files: add the cli option and the rbs generator. protobuf classes in ruby rely on dynamic attribution of the base class, which makes what I'm trying to achieve a bit difficult. Ideally the type hierarchy could be specified statically in the ruby source code. ```ruby class Bar < AbstractMessage class Bar < Google::Protobuf::DescriptorPool.generated_pool.lookup("Bar").msgclass ``` ``` Closes #15633 COPYBARA_INTEGRATE_REVIEW=#15633 from HoneyryderChuck:ruby-rbs-integration 2167aa9 PiperOrigin-RevId: 859200823
1 parent a090210 commit 6ebdf85

13 files changed

+2122
-5
lines changed

protobuf.bzl

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ def _PyOuts(srcs, use_grpc_plugin = False):
6767
def _RubyOuts(srcs):
6868
return [s[:-len(".proto")] + "_pb.rb" for s in srcs]
6969

70+
def _RBSOuts(srcs):
71+
return [s[:-len(".proto")] + "_pb.rbs" for s in srcs]
72+
7073
def _CsharpOuts(srcs):
7174
return [
7275
"".join([token.capitalize() for token in src[:-len(".proto")].split("_")]) + ".cs"
@@ -180,6 +183,8 @@ def _proto_gen_impl(ctx):
180183
outs.extend(_PyOuts([src.basename], use_grpc_plugin = use_grpc_plugin))
181184
elif lang == "ruby":
182185
outs.extend(_RubyOuts([src.basename]))
186+
elif lang == "rbs":
187+
outs.extend(_RBSOuts([src.basename]))
183188

184189
# Otherwise, rely on user-supplied outs.
185190
args.append(("--%s_out=" + path_tpl) % (lang, gen_dir))
@@ -542,8 +547,6 @@ def internal_ruby_proto_library(
542547
543548
"""
544549

545-
# Note: we need to run the protoc build twice to get separate targets for
546-
# the generated header and the source files.
547550
_proto_gen(
548551
name = name + "_genproto",
549552
srcs = srcs,
@@ -568,6 +571,58 @@ def internal_ruby_proto_library(
568571
**kwargs
569572
)
570573

574+
def internal_rbs_proto_library(
575+
name,
576+
ruby_library,
577+
srcs = [],
578+
deps = [],
579+
includes = ["."],
580+
protoc = "@com_google_protobuf//:protoc",
581+
testonly = None,
582+
visibility = ["//visibility:public"],
583+
**kwargs):
584+
"""Create RBS type definitions for the Ruby protobuf library from proto source files
585+
586+
NOTE: the rule is only an internal workaround to generate protos. The
587+
interface may change and the rule may be removed when bazel has introduced
588+
the native rule.
589+
590+
Args:
591+
name: the name of the ruby_proto_library.
592+
ruby_library: the ruby library rule to document.
593+
srcs: the .proto files to compile.
594+
deps: a list of dependency labels; must be a internal_rbs_proto_library.
595+
includes: a string indicating the include path of the .proto files.
596+
protoc: the label of the protocol compiler to generate the sources.
597+
testonly: common rule attribute (see:
598+
https://bazel.build/reference/be/common-definitions#common-attributes)
599+
visibility: the visibility of the generated files.
600+
**kwargs: other keyword arguments that are passed to ruby_library.
601+
602+
"""
603+
604+
_proto_gen(
605+
name = name + "_genproto_rbs",
606+
srcs = srcs,
607+
deps = [s + "_genproto_rbs" for s in deps],
608+
langs = ["rbs"],
609+
includes = includes,
610+
protoc = protoc,
611+
testonly = testonly,
612+
visibility = visibility,
613+
tags = ["manual"],
614+
)
615+
616+
ruby_library(
617+
name = name,
618+
srcs = [name + "_genproto_rbs"],
619+
deps = [],
620+
testonly = testonly,
621+
visibility = visibility,
622+
includes = includes,
623+
**kwargs
624+
)
625+
571626
# When canonical labels are in use, use additional "@" prefix
572627
_canonical_label_prefix = "@" if str(Label("//:protoc")).startswith("@@") else ""
573628

src/google/protobuf/compiler/main.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "google/protobuf/compiler/php/php_generator.h"
1616
#include "google/protobuf/compiler/python/generator.h"
1717
#include "google/protobuf/compiler/python/pyi_generator.h"
18+
#include "google/protobuf/compiler/ruby/rbs_generator.h"
1819
#include "google/protobuf/compiler/ruby/ruby_generator.h"
1920
#include "google/protobuf/compiler/rust/generator.h"
2021
#ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
@@ -95,6 +96,10 @@ int ProtobufMain(int argc, char* argv[]) {
9596
cli.RegisterGenerator("--ruby_out", "--ruby_opt", &rb_generator,
9697
"Generate Ruby source file.");
9798

99+
ruby::RBSGenerator rbs_generator;
100+
cli.RegisterGenerator("--rbs_out", "--rbs_opt", &rbs_generator,
101+
"Generate RBS type definition.");
102+
98103
// CSharp
99104
csharp::Generator csharp_generator;
100105
cli.RegisterGenerator("--csharp_out", "--csharp_opt", &csharp_generator,

src/google/protobuf/compiler/ruby/BUILD.bazel

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@ load("//build_defs:cpp_opts.bzl", "COPTS")
88

99
cc_library(
1010
name = "ruby",
11-
srcs = ["ruby_generator.cc"],
12-
hdrs = ["ruby_generator.h"],
11+
srcs = [
12+
"rbs_generator.cc",
13+
"ruby_generator.cc",
14+
],
15+
hdrs = [
16+
"rbs_generator.h",
17+
"ruby_generator.h",
18+
],
1319
copts = COPTS,
1420
strip_include_prefix = "/src",
1521
visibility = [
@@ -23,6 +29,7 @@ cc_library(
2329
"//src/google/protobuf/compiler:retention",
2430
"//src/google/protobuf/io",
2531
"//src/google/protobuf/io:printer",
32+
"@abseil-cpp//absl/container:flat_hash_map",
2633
"@abseil-cpp//absl/container:flat_hash_set",
2734
"@abseil-cpp//absl/log:absl_log",
2835
"@abseil-cpp//absl/strings",
@@ -31,19 +38,27 @@ cc_library(
3138

3239
cc_test(
3340
name = "generator_unittest",
34-
srcs = ["ruby_generator_unittest.cc"],
41+
srcs = [
42+
"rbs_generator_unittest.cc",
43+
"ruby_generator_unittest.cc",
44+
],
3545
data = [
3646
"ruby_generated_code.proto",
3747
"ruby_generated_code_pb.rb",
48+
"ruby_generated_code_pb.rbs",
3849
"ruby_generated_code_proto2.proto",
3950
"ruby_generated_code_proto2_import.proto",
4051
"ruby_generated_code_proto2_pb.rb",
52+
"ruby_generated_code_proto2_pb.rbs",
4153
"ruby_generated_pkg_explicit.proto",
4254
"ruby_generated_pkg_explicit_legacy.proto",
4355
"ruby_generated_pkg_explicit_legacy_pb.rb",
56+
"ruby_generated_pkg_explicit_legacy_pb.rbs",
4457
"ruby_generated_pkg_explicit_pb.rb",
58+
"ruby_generated_pkg_explicit_pb.rbs",
4559
"ruby_generated_pkg_implicit.proto",
4660
"ruby_generated_pkg_implicit_pb.rb",
61+
"ruby_generated_pkg_implicit_pb.rbs",
4762
"//src/google/protobuf:testdata",
4863
],
4964
deps = [

0 commit comments

Comments
 (0)