-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Advice on writing bazel rule for protoc-gen-dart #59
Comments
Hi Joackim, There's an old The real difficulty in implementing this wasn't writing the rules or anything; it was that back when I put it together in 2016 or so, we didn't have a mechanism to create standalone Dart binaries; everything ran from source. The proto rules depend on (a) protoc, which is a standalone binary and (b) the dart protoc plugin, which is itself written in Dart, and has a bunch of dependencies. As a result, this required pulling in a pile of third-party dependencies into the repo, which would have been a pain to maintain. The other difficulty at the time was that no formal public protoc rules existed publicly at the time. Internally at Google we had a cross-platform I'm not sure what the status is of a standard |
@mark-dropbear Sadly nothing yet. Before @cbracken 's answer I had tried to pull and build the protoc plugin in my repo, which posed the issues he described with third party dependencies. I don't know if the idea of maintaining another repo to build the plugin can really make things easier when you have multiple operating systems as build targets. If I manage the time to give it a shot I'll let you know. |
Thanks for the update @jdebou I've also reached out to the maintainer of the main proto ruleset at the moment to see what options look like from that perspective because it seems like it was actually on their road map and they have already added a bunch of other languages already as far as I can tell. Perhaps there is an easier path there via a community effort? I'm literally running my first Bazel commands as we speak so I am super unqualified to say if there is any potential there or not but it initially seems promising. |
@cbracken would you be open to taking contributions making incremental progress toward supporting protobuf in this rules repository?
E.g. https://github.com/bshi/rules_dart/blob/master/.github/workflows/protoc_plugin.yml (sample output) |
I created a simple rule I attach the prebuilt binaries (would be great when Examples: dart_proto_library(
name = "role_v1_dart_proto",
proto = ":role_v1_proto",
substitutions = {
"../../../../google": "package:global_proto/google",
"../../../global": "package:global_proto/mindful/global",
},
visibility = ["//visibility:public"],
)
dart_proto_library(
name = "timestamp_dart_proto",
grpc = False,
proto = "@com_github_protocolbuffers_protobuf//:timestamp_proto",
visibility = ["//visibility:public"],
) Rule: load("@bazel_skylib//lib:paths.bzl", "paths")
def _dart_proto_library_impl(ctx):
descriptor_set_in = []
for file in ctx.attr.proto[ProtoInfo].transitive_descriptor_sets.to_list():
descriptor_set_in.append(file.path)
name = ctx.attr.proto[ProtoInfo].direct_sources[0].basename.split(".")[0]
outputs = [
ctx.actions.declare_file("%s.pb.dart" % name),
ctx.actions.declare_file("%s.pbenum.dart" % name),
ctx.actions.declare_file("%s.pbjson.dart" % name),
]
dart_options = ""
if ctx.attr.grpc:
dart_options = "grpc:"
outputs.append(ctx.actions.declare_file("%s.pbgrpc.dart" % name))
proto_file = paths.dirname(ctx.build_file_path) + "/" + ctx.attr.proto[ProtoInfo].direct_sources[0].path.split("/")[-1] # google/protobuf/timestamp.proto or mindful/global/order/v1/order.proto
ctx.actions.run(
executable = ctx.executable.protoc,
progress_message = "Generating Dart proto files",
inputs = [ctx.executable.protoc_gen_dart] + [ctx.attr.proto[ProtoInfo].direct_sources[0]] + ctx.attr.proto[ProtoInfo].transitive_descriptor_sets.to_list(),
tools = [ctx.executable.protoc, ctx.executable.protoc_gen_dart],
outputs = outputs,
mnemonic = "DartProtoGen",
arguments = [
"--plugin=protoc-gen-dart=%s" % ctx.file.protoc_gen_dart.path,
"--dart_out=%s" % dart_options + ctx.configuration.genfiles_dir.path,
"--descriptor_set_in=%s" % ":".join(descriptor_set_in),
"%s" % proto_file, # ctx.attr.proto[ProtoInfo].direct_sources[0].path,
],
)
if len(ctx.attr.substitutions) == 0:
return [
DefaultInfo(
files = depset(outputs),
),
]
substitution_outputs = [
ctx.actions.declare_file("%s.pb.dart.substitution" % name),
ctx.actions.declare_file("%s.pbenum.dart.substitution" % name),
ctx.actions.declare_file("%s.pbjson.dart.substitution" % name),
]
if ctx.attr.grpc:
substitution_outputs.append(ctx.actions.declare_file("%s.pbgrpc.dart.substitution" % name))
for i in range(len(outputs)):
ctx.actions.expand_template(
template = outputs[i],
output = substitution_outputs[i],
substitutions = ctx.attr.substitutions,
)
return [
DefaultInfo(
files = depset(outputs + substitution_outputs),
),
]
dart_proto_library = rule(
implementation = _dart_proto_library_impl,
attrs = {
"proto": attr.label(
allow_single_file = True,
mandatory = True,
),
"grpc": attr.bool(
default = True,
doc = "Generate gRPC headers",
),
"protoc": attr.label(
allow_single_file = True,
executable = True,
default = Label("@com_google_protobuf//:protoc"),
cfg = "host",
),
"protoc_gen_dart": attr.label(
allow_single_file = True,
executable = True,
default = Label("@com_github_mindful_hq_rules//:protoc_gen_dart"),
cfg = "host",
),
"substitutions": attr.string_dict(
default = {},
doc = "Substitutions to apply to the proto. The files will be generated with the .substitution suffix",
),
},
doc = "Builds dart proto files",
) |
Here is an updated version which supports multiple .proto files in your load("@bazel_skylib//lib:paths.bzl", "paths")
def _dart_proto_library_impl(ctx):
descriptor_set_in = []
for file in ctx.attr.proto[ProtoInfo].transitive_descriptor_sets.to_list():
descriptor_set_in.append(file.path)
outputs = []
proto_files = []
grpc_files = 0
for source in ctx.attr.proto[ProtoInfo].direct_sources:
name = source.basename.split(".")[0]
outputs.append(ctx.actions.declare_file("%s.pb.dart" % name))
outputs.append(ctx.actions.declare_file("%s.pbenum.dart" % name))
outputs.append(ctx.actions.declare_file("%s.pbjson.dart" % name))
for i in range(len(ctx.attr.grpc)):
if ctx.attr.grpc[i] == source.basename:
grpc_files += 1
outputs.append(ctx.actions.declare_file("%s.pbgrpc.dart" % name))
proto_files.append(paths.dirname(ctx.build_file_path) + "/" + source.path.split("/")[-1])
dart_options = ""
if grpc_files != 0:
dart_options = "grpc:"
ctx.actions.run(
executable = ctx.executable.protoc,
progress_message = "Generating Dart proto files",
inputs = [ctx.executable.protoc_gen_dart] + ctx.attr.proto[ProtoInfo].direct_sources + ctx.attr.proto[ProtoInfo].transitive_descriptor_sets.to_list(),
tools = [ctx.executable.protoc, ctx.executable.protoc_gen_dart],
outputs = outputs,
mnemonic = "DartProtoGen",
arguments = [
"--plugin=protoc-gen-dart=%s" % ctx.file.protoc_gen_dart.path,
"--dart_out=%s" % dart_options + ctx.configuration.genfiles_dir.path,
"--descriptor_set_in=%s" % ":".join(descriptor_set_in),
] + proto_files, # ctx.attr.proto[ProtoInfo].direct_sources[i].path
)
if len(ctx.attr.substitutions) == 0:
return [
DefaultInfo(
files = depset(outputs),
),
]
substitution_outputs = []
for i in range(len(outputs)):
substitution_outputs.append(ctx.actions.declare_file("%s.substitution" % outputs[i].basename))
ctx.actions.expand_template(
template = outputs[i],
output = substitution_outputs[i],
substitutions = ctx.attr.substitutions,
)
return [
DefaultInfo(
files = depset(outputs + substitution_outputs),
),
]
dart_proto_library = rule(
implementation = _dart_proto_library_impl,
attrs = {
"proto": attr.label(
allow_single_file = True,
mandatory = True,
),
"grpc": attr.string_list(
default = [],
doc = "Proto files that should generate gRPC code",
),
"protoc": attr.label(
allow_single_file = True,
executable = True,
default = Label("@com_google_protobuf//:protoc"),
cfg = "host",
),
"protoc_gen_dart": attr.label(
allow_single_file = True,
executable = True,
default = Label("@com_github_mindful_hq_rules//:protoc_gen_dart"),
cfg = "host",
),
"substitutions": attr.string_dict(
default = {},
doc = "Substitutions to apply to the proto. The files will be generated with the .substitution suffix",
),
},
doc = "Builds dart proto files",
) example: dart_proto_library(
name = "global_v1_dart_proto",
grpc = [
"version.proto",
],
proto = ":global_v1_proto",
visibility = ["//visibility:public"],
) output bazel build //mindful/global/v1:global_v1_dart_proto
INFO: Analyzed target //mindful/global/v1:global_v1_dart_proto (1 packages loaded, 4 targets configured).
INFO: Found 1 target...
Target //mindful/global/v1:global_v1_dart_proto up-to-date:
bazel-bin/mindful/global/v1/order.pb.dart
bazel-bin/mindful/global/v1/order.pbenum.dart
bazel-bin/mindful/global/v1/order.pbjson.dart
bazel-bin/mindful/global/v1/version.pb.dart
bazel-bin/mindful/global/v1/version.pbenum.dart
bazel-bin/mindful/global/v1/version.pbjson.dart
bazel-bin/mindful/global/v1/version.pbgrpc.dart
INFO: Elapsed time: 0.122s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action |
Hello there,
I am looking for some advice on how the rules in this repo could be used to create a bazel rule to generate pb.dart files with protoc.
I am using protobuf and grpc in a flutter app to interact with a c++ engine. Apart from the flutter app, the whole project is managed with bazel.
So far I have been painfully using the method described by the protoc-gen-dart documentation to generate the pb.dart files, but am now looking to fully integrate this step with bazel.
While looking around this repository and the protobuf.dart repository, it appears that this has already been done, but I cannot find any documentation on it.
Any pointer on how to achieve this task would be greatly appreciated.
Cheers.
Joackim
The text was updated successfully, but these errors were encountered: