Consolidate rust_prost_library and fix extension-only proto generation. (#2047)
This PR consolidates rust_prost_library and rust_tonic_library into one rule; `rust_prost_library`. `rust_prost_library` will generate tonic services if you provide a `tonic_plugin` in the toolchain definition. If you do not provide that plugin and you try to build a proto with services, it will print a warning that you should add a `tonic_plugin`.
This PR also handles extension-only proto files. Prost does not generate a file if there are no messages, enums, or services and it appears that Prost doesn't even support proto2 extensions. So to work around this issue, protoc_wrapper will generate an empty `.rs` file in the case that there are only extensions defined in a file.
Closes: #2046
diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel
index fe4399d..c412e2f 100644
--- a/WORKSPACE.bazel
+++ b/WORKSPACE.bazel
@@ -52,6 +52,10 @@
rules_rust_test_deps()
+load("//test:deps_transitive.bzl", "rules_rust_test_deps_transitive")
+
+rules_rust_test_deps_transitive()
+
# --- end stardoc
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index f97a034..5408d9a 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -122,7 +122,6 @@
"rust_proto_transitive_repositories",
"rust_proto_toolchain",
"rust_prost_library",
- "rust_tonic_library",
],
),
page(
diff --git a/docs/flatten.md b/docs/flatten.md
index 9997b80..08ed04a 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -44,7 +44,6 @@
* [rust_stdlib_filegroup](#rust_stdlib_filegroup)
* [rust_test](#rust_test)
* [rust_test_suite](#rust_test_suite)
-* [rust_tonic_library](#rust_tonic_library)
* [rust_toolchain](#rust_toolchain)
* [rust_toolchain_repository](#rust_toolchain_repository)
* [rust_toolchain_repository_proxy](#rust_toolchain_repository_proxy)
@@ -2017,25 +2016,6 @@
| <a id="rust_test_suite-kwargs"></a>kwargs | Additional keyword arguments for the underyling [rust_test](#rust_test) targets. The <code>tags</code> argument is also passed to the generated <code>test_suite</code> target. | none |
-<a id="rust_tonic_library"></a>
-
-## rust_tonic_library
-
-<pre>
-rust_tonic_library(<a href="#rust_tonic_library-name">name</a>, <a href="#rust_tonic_library-kwargs">kwargs</a>)
-</pre>
-
-A rule for generating a Rust library using Prost and Tonic.
-
-**PARAMETERS**
-
-
-| Name | Description | Default Value |
-| :------------- | :------------- | :------------- |
-| <a id="rust_tonic_library-name"></a>name | The name of the target. | none |
-| <a id="rust_tonic_library-kwargs"></a>kwargs | Additional keyword arguments for the underlying <code>rust_tonic_library</code> rule. | none |
-
-
<a id="rust_toolchain_repository"></a>
## rust_toolchain_repository
diff --git a/docs/rust_proto.md b/docs/rust_proto.md
index 1e0b041..5453e46 100644
--- a/docs/rust_proto.md
+++ b/docs/rust_proto.md
@@ -7,7 +7,6 @@
* [rust_proto_transitive_repositories](#rust_proto_transitive_repositories)
* [rust_proto_toolchain](#rust_proto_toolchain)
* [rust_prost_library](#rust_prost_library)
-* [rust_tonic_library](#rust_tonic_library)
## Overview
@@ -15,8 +14,7 @@
There are two rule sets. The first ruleset defines the `rust_proto_library` and `rust_grpc_library`
rules which generate Rust code using the [`rust-protobuf`] dependencies. The second ruleset defines
-the `rust_prost_library` and `rust_tonic_library` rules which generate Rust code using the [`prost`]
-and [`tonic`] dependencies respectively.
+the `rust_prost_library` which generates Rust code using the [`prost`] and [`tonic`] dependencies.
[rust]: http://www.rust-lang.org/
[protobuf]: https://developers.google.com/protocol-buffers/
@@ -427,22 +425,3 @@
-<a id="rust_tonic_library"></a>
-
-## rust_tonic_library
-
-<pre>
-rust_tonic_library(<a href="#rust_tonic_library-name">name</a>, <a href="#rust_tonic_library-kwargs">kwargs</a>)
-</pre>
-
-A rule for generating a Rust library using Prost and Tonic.
-
-**PARAMETERS**
-
-
-| Name | Description | Default Value |
-| :------------- | :------------- | :------------- |
-| <a id="rust_tonic_library-name"></a>name | The name of the target. | none |
-| <a id="rust_tonic_library-kwargs"></a>kwargs | Additional keyword arguments for the underlying <code>rust_tonic_library</code> rule. | none |
-
-
diff --git a/docs/rust_proto.vm b/docs/rust_proto.vm
index b2dce93..967b2ae 100644
--- a/docs/rust_proto.vm
+++ b/docs/rust_proto.vm
@@ -4,8 +4,7 @@
There are two rule sets. The first ruleset defines the `rust_proto_library` and `rust_grpc_library`
rules which generate Rust code using the [`rust-protobuf`] dependencies. The second ruleset defines
-the `rust_prost_library` and `rust_tonic_library` rules which generate Rust code using the [`prost`]
-and [`tonic`] dependencies respectively.
+the `rust_prost_library` which generates Rust code using the [`prost`] and [`tonic`] dependencies.
[rust]: http://www.rust-lang.org/
[protobuf]: https://developers.google.com/protocol-buffers/
diff --git a/docs/symbols.bzl b/docs/symbols.bzl
index d1a8d65..141dc60 100644
--- a/docs/symbols.bzl
+++ b/docs/symbols.bzl
@@ -32,7 +32,6 @@
_rust_grpc_library = "rust_grpc_library",
_rust_prost_library = "rust_prost_library",
_rust_proto_library = "rust_proto_library",
- _rust_tonic_library = "rust_tonic_library",
)
load(
"@rules_rust//proto:repositories.bzl",
@@ -127,7 +126,6 @@
rust_proto_library = _rust_proto_library
rust_grpc_library = _rust_grpc_library
rust_prost_library = _rust_prost_library
-rust_tonic_library = _rust_tonic_library
rust_bindgen = _rust_bindgen
rust_bindgen_dependencies = _rust_bindgen_dependencies
diff --git a/proto/defs.bzl b/proto/defs.bzl
index cc38b48..12d4bd3 100644
--- a/proto/defs.bzl
+++ b/proto/defs.bzl
@@ -3,7 +3,6 @@
load(
"//proto/prost:defs.bzl",
_rust_prost_library = "rust_prost_library",
- _rust_tonic_library = "rust_tonic_library",
)
load(
":proto.bzl",
@@ -15,4 +14,3 @@
rust_grpc_library = _rust_grpc_library
rust_prost_library = _rust_prost_library
-rust_tonic_library = _rust_tonic_library
diff --git a/proto/prost/defs.bzl b/proto/prost/defs.bzl
index 56a45c5..401f106 100644
--- a/proto/prost/defs.bzl
+++ b/proto/prost/defs.bzl
@@ -4,7 +4,6 @@
"//proto/prost/private:prost.bzl",
_rust_prost_library = "rust_prost_library",
_rust_prost_toolchain = "rust_prost_toolchain",
- _rust_tonic_library = "rust_tonic_library",
)
def rust_prost_library(name, **kwargs):
@@ -30,27 +29,4 @@
**kwargs
)
-def rust_tonic_library(name, **kwargs):
- """A rule for generating a Rust library using Prost and Tonic.
-
- Args:
- name (str): The name of the target.
- **kwargs (dict): Additional keyword arguments for the underlying
- `rust_tonic_library` rule.
- """
-
- # Clippy and Rustfmt will attempt to run on these targets.
- # This is not correct and likely a bug in target detection.
- tags = kwargs.pop("tags", [])
- if "no-clippy" not in tags:
- tags.append("no-clippy")
- if "no-rustfmt" not in tags:
- tags.append("no-rustfmt")
-
- _rust_tonic_library(
- name = name,
- tags = tags,
- **kwargs
- )
-
rust_prost_toolchain = _rust_prost_toolchain
diff --git a/proto/prost/private/prost.bzl b/proto/prost/private/prost.bzl
index f41e4fe..e2b6c39 100644
--- a/proto/prost/private/prost.bzl
+++ b/proto/prost/private/prost.bzl
@@ -8,16 +8,22 @@
# buildifier: disable=bzl-visibility
load("//rust/private:utils.bzl", "can_build_metadata")
-load(":providers.bzl", "ProstProtoInfo", "TonicProtoInfo")
RUST_EDITION = "2021"
TOOLCHAIN_TYPE = "@rules_rust//proto/prost:toolchain_type"
-def _create_proto_lang_toolchain(ctx, prost_toolchain):
- is_tonic = prost_toolchain.tonic_runtime != None
+ProstProtoInfo = provider(
+ doc = "Rust Prost provider info",
+ fields = {
+ "dep_variant_info": "DepVariantInfo: For the compiled Rust gencode (also covers its " +
+ "transitive dependencies)",
+ "package_info": "File: A newline delimited file of `--extern_path` values for protoc.",
+ "transitive_dep_infos": "depset[DepVariantInfo]: Transitive dependencies of the compiled crate.",
+ },
+)
- mnemonic = "TonicGenProto" if is_tonic else "ProstGenProto"
+def _create_proto_lang_toolchain(ctx, prost_toolchain):
proto_lang_toolchain = proto_common.ProtoLangToolchainInfo(
out_replacement_format_flag = "--prost_out=%s",
plugin_format_flag = prost_toolchain.prost_plugin_flag,
@@ -26,26 +32,22 @@
provided_proto_sources = depset(),
proto_compiler = ctx.attr._prost_process_wrapper[DefaultInfo].files_to_run,
protoc_opts = prost_toolchain.protoc_opts,
- progress_message = mnemonic + " %{label}",
- mnemonic = mnemonic,
+ progress_message = "ProstGenProto %{label}",
+ mnemonic = "ProstGenProto",
)
return proto_lang_toolchain
-def _compile_proto(ctx, crate_name, proto_info, deps, prost_toolchain, is_tonic, rustfmt_toolchain = None):
- kind = "tonic" if is_tonic else "prost"
- extension = ".tonic.rs" if is_tonic else ".rs"
- provider = TonicProtoInfo if is_tonic else ProstProtoInfo
-
- deps_info_file = ctx.actions.declare_file(ctx.label.name + ".{}_deps_info".format(kind))
- dep_package_infos = [dep[provider].package_info for dep in deps]
+def _compile_proto(ctx, crate_name, proto_info, deps, prost_toolchain, rustfmt_toolchain = None):
+ deps_info_file = ctx.actions.declare_file(ctx.label.name + ".prost_deps_info")
+ dep_package_infos = [dep[ProstProtoInfo].package_info for dep in deps]
ctx.actions.write(
output = deps_info_file,
content = "\n".join([file.path for file in dep_package_infos]),
)
- package_info_file = ctx.actions.declare_file(ctx.label.name + ".{}_package_info".format(kind))
- lib_rs = ctx.actions.declare_file("{}.lib{}".format(ctx.label.name, extension))
+ package_info_file = ctx.actions.declare_file(ctx.label.name + ".prost_package_info")
+ lib_rs = ctx.actions.declare_file("{}.lib.rs".format(ctx.label.name))
proto_compiler = prost_toolchain.proto_compiler[DefaultInfo].files_to_run
tools = depset([proto_compiler.executable])
@@ -62,10 +64,7 @@
additional_args.add("--descriptor_set={}".format(proto_info.direct_descriptor_set.path))
additional_args.add_all(prost_toolchain.prost_opts, format_each = "--prost_opt=%s")
- if is_tonic:
- if not prost_toolchain.tonic_plugin:
- fail("Tonic plugin not configured for this toolchain")
-
+ if prost_toolchain.tonic_plugin:
tonic_plugin = prost_toolchain.tonic_plugin[DefaultInfo].files_to_run
additional_args.add(prost_toolchain.tonic_plugin_flag % tonic_plugin.executable.path)
additional_args.add("--tonic_opt=no_include")
@@ -78,7 +77,7 @@
additional_args.add("--rustfmt={}".format(rustfmt_toolchain.rustfmt.path))
tools = depset(transitive = [tools, rustfmt_toolchain.all_files])
- additional_inputs = depset([deps_info_file, proto_info.direct_descriptor_set] + [dep[provider].package_info for dep in deps])
+ additional_inputs = depset([deps_info_file, proto_info.direct_descriptor_set] + [dep[ProstProtoInfo].package_info for dep in deps])
proto_common.compile(
actions = ctx.actions,
@@ -114,7 +113,7 @@
return provider
fail("Couldn't find a CcInfo in the list of providers")
-def _compile_rust(ctx, attr, crate_name, src, deps, edition, is_tonic):
+def _compile_rust(ctx, attr, crate_name, src, deps, edition):
"""Compiles a Rust source file.
Args:
@@ -124,13 +123,12 @@
src (File): The crate root source file to be compiled.
deps (List of DepVariantInfo): A list of dependencies needed.
edition (str): The Rust edition to use.
- is_tonic (bool): Whether or not the crate is a tonic library.
Returns:
A DepVariantInfo provider.
"""
toolchain = ctx.toolchains["@rules_rust//rust:toolchain_type"]
- output_hash = repr(hash(src.path + (".tonic" if is_tonic else ".prost")))
+ output_hash = repr(hash(src.path + ".prost"))
lib_name = "{prefix}{name}-{lib_hash}{extension}".format(
prefix = "lib",
@@ -194,8 +192,7 @@
)
def _rust_prost_aspect_impl(target, ctx):
- proto_info_provider = TonicProtoInfo if ctx.attr._is_tonic else ProstProtoInfo
- if proto_info_provider in target:
+ if ProstProtoInfo in target:
return []
runtime_deps = []
@@ -221,7 +218,7 @@
direct_deps = []
transitive_deps = []
for proto_dep in proto_deps:
- proto_info = proto_dep[proto_info_provider]
+ proto_info = proto_dep[ProstProtoInfo]
direct_deps.append(proto_info.dep_variant_info)
transitive_deps.append(depset(
@@ -241,7 +238,6 @@
proto_info = proto_info,
deps = proto_deps,
prost_toolchain = prost_toolchain,
- is_tonic = ctx.attr._is_tonic,
rustfmt_toolchain = rustfmt_toolchain,
)
@@ -252,100 +248,82 @@
src = lib_rs,
deps = deps,
edition = RUST_EDITION,
- is_tonic = ctx.attr._is_tonic,
)
return [
- proto_info_provider(
+ ProstProtoInfo(
dep_variant_info = dep_variant_info,
transitive_dep_infos = depset(transitive = transitive_deps),
package_info = package_info_file,
),
]
-def _make_rust_prost_aspect(doc, is_tonic):
- return aspect(
- doc = doc,
- implementation = _rust_prost_aspect_impl,
- attr_aspects = ["deps"],
- attrs = {
- "_cc_toolchain": attr.label(
- doc = (
- "In order to use find_cc_toolchain, your rule has to depend " +
- "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " +
- "docs for details."
- ),
- default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
- ),
- "_collect_cc_coverage": attr.label(
- default = Label("//util:collect_coverage"),
- executable = True,
- cfg = "exec",
- ),
- "_error_format": attr.label(
- default = Label("//:error_format"),
- ),
- "_extra_exec_rustc_flag": attr.label(
- default = Label("//:extra_exec_rustc_flag"),
- ),
- "_extra_exec_rustc_flags": attr.label(
- default = Label("//:extra_exec_rustc_flags"),
- ),
- "_extra_rustc_flag": attr.label(
- default = Label("//:extra_rustc_flag"),
- ),
- "_extra_rustc_flags": attr.label(
- default = Label("//:extra_rustc_flags"),
- ),
- "_grep_includes": attr.label(
- allow_single_file = True,
- default = Label("@bazel_tools//tools/cpp:grep-includes"),
- cfg = "exec",
- ),
- "_is_tonic": attr.bool(
- doc = "Indicates whether or not Tonic behavior should be enabled.",
- default = is_tonic,
- ),
- "_process_wrapper": attr.label(
- doc = "A process wrapper for running rustc on all platforms.",
- default = Label("//util/process_wrapper"),
- executable = True,
- allow_single_file = True,
- cfg = "exec",
- ),
- "_prost_process_wrapper": attr.label(
- doc = "The wrapper script for the Prost protoc plugin.",
- cfg = "exec",
- executable = True,
- default = Label("//proto/prost/private:protoc_wrapper"),
- ),
- },
- fragments = ["cpp"],
- host_fragments = ["cpp"],
- toolchains = [
- TOOLCHAIN_TYPE,
- "@bazel_tools//tools/cpp:toolchain_type",
- "@rules_rust//rust:toolchain_type",
- "@rules_rust//rust/rustfmt:toolchain_type",
- ],
- incompatible_use_toolchain_transition = True,
- )
-
-_rust_prost_aspect = _make_rust_prost_aspect(
+rust_prost_aspect = aspect(
doc = "An aspect used to generate and compile proto files with Prost.",
- is_tonic = False,
-)
-
-_rust_tonic_aspect = _make_rust_prost_aspect(
- doc = "An aspect used to generate and compile proto files with Prost and Tonic.",
- is_tonic = True,
+ implementation = _rust_prost_aspect_impl,
+ attr_aspects = ["deps"],
+ attrs = {
+ "_cc_toolchain": attr.label(
+ doc = (
+ "In order to use find_cc_toolchain, your rule has to depend " +
+ "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " +
+ "docs for details."
+ ),
+ default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+ ),
+ "_collect_cc_coverage": attr.label(
+ default = Label("//util:collect_coverage"),
+ executable = True,
+ cfg = "exec",
+ ),
+ "_error_format": attr.label(
+ default = Label("//:error_format"),
+ ),
+ "_extra_exec_rustc_flag": attr.label(
+ default = Label("//:extra_exec_rustc_flag"),
+ ),
+ "_extra_exec_rustc_flags": attr.label(
+ default = Label("//:extra_exec_rustc_flags"),
+ ),
+ "_extra_rustc_flag": attr.label(
+ default = Label("//:extra_rustc_flag"),
+ ),
+ "_extra_rustc_flags": attr.label(
+ default = Label("//:extra_rustc_flags"),
+ ),
+ "_grep_includes": attr.label(
+ allow_single_file = True,
+ default = Label("@bazel_tools//tools/cpp:grep-includes"),
+ cfg = "exec",
+ ),
+ "_process_wrapper": attr.label(
+ doc = "A process wrapper for running rustc on all platforms.",
+ default = Label("//util/process_wrapper"),
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ ),
+ "_prost_process_wrapper": attr.label(
+ doc = "The wrapper script for the Prost protoc plugin.",
+ cfg = "exec",
+ executable = True,
+ default = Label("//proto/prost/private:protoc_wrapper"),
+ ),
+ },
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ TOOLCHAIN_TYPE,
+ "@bazel_tools//tools/cpp:toolchain_type",
+ "@rules_rust//rust:toolchain_type",
+ "@rules_rust//rust/rustfmt:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
)
def _rust_prost_library_impl(ctx):
- proto_info_provider = TonicProtoInfo if ctx.attr._is_tonic else ProstProtoInfo
-
proto_dep = ctx.attr.proto
- rust_proto_info = proto_dep[proto_info_provider]
+ rust_proto_info = proto_dep[ProstProtoInfo]
dep_variant_info = rust_proto_info.dep_variant_info
return [
@@ -358,37 +336,22 @@
),
]
-def _make_rust_prost_library_rule(doc, is_tonic):
- return rule(
- doc = doc,
- implementation = _rust_prost_library_impl,
- attrs = {
- "proto": attr.label(
- doc = "A `proto_library` target for which to generate Rust gencode.",
- providers = [ProtoInfo],
- aspects = [_rust_tonic_aspect if is_tonic else _rust_prost_aspect],
- mandatory = True,
- ),
- "_collect_cc_coverage": attr.label(
- default = Label("@rules_rust//util:collect_coverage"),
- executable = True,
- cfg = "exec",
- ),
- "_is_tonic": attr.bool(
- doc = "Indicates whether or not Tonic behavior should be enabled.",
- default = is_tonic,
- ),
- },
- )
-
-rust_tonic_library = _make_rust_prost_library_rule(
- doc = "A rule for generating a Rust library using Prost and Tonic.",
- is_tonic = True,
-)
-
-rust_prost_library = _make_rust_prost_library_rule(
+rust_prost_library = rule(
doc = "A rule for generating a Rust library using Prost.",
- is_tonic = False,
+ implementation = _rust_prost_library_impl,
+ attrs = {
+ "proto": attr.label(
+ doc = "A `proto_library` target for which to generate Rust gencode.",
+ providers = [ProtoInfo],
+ aspects = [rust_prost_aspect],
+ mandatory = True,
+ ),
+ "_collect_cc_coverage": attr.label(
+ default = Label("@rules_rust//util:collect_coverage"),
+ executable = True,
+ cfg = "exec",
+ ),
+ },
)
def _rust_prost_toolchain_impl(ctx):
diff --git a/proto/prost/private/protoc_wrapper.rs b/proto/prost/private/protoc_wrapper.rs
index 817faac..6c3593e 100644
--- a/proto/prost/private/protoc_wrapper.rs
+++ b/proto/prost/private/protoc_wrapper.rs
@@ -287,17 +287,12 @@
/// expected to convert proto files into a BTreeMap of
/// `example.prost.helloworld`: `crate_name::example::prost::helloworld`.
fn get_extern_paths(
- descriptor_set_path: &PathBuf,
+ descriptor_set: &FileDescriptorSet,
crate_name: &str,
) -> Result<BTreeMap<ProtoPath, RustModulePath>, String> {
let mut extern_paths = BTreeMap::new();
let rust_path = RustModulePath(crate_name.to_string());
- let descriptor_set_bytes =
- fs::read(descriptor_set_path).expect("Failed to read descriptor set");
- let descriptor_set = FileDescriptorSet::decode(descriptor_set_bytes.as_slice())
- .expect("Failed to decode descriptor set");
-
for file in descriptor_set.file.iter() {
descriptor_set_file_to_extern_paths(&mut extern_paths, &rust_path, file);
}
@@ -488,9 +483,6 @@
("--prost_out", value) => {
out_dir = Some(PathBuf::from(value));
}
- ("--crate_name", value) => {
- crate_name = Some(value.to_string());
- }
("--package_info_output", value) => {
let (key, value) = value
.split_once('=')
@@ -509,6 +501,11 @@
.expect("Failed to read file")
.lines()
{
+ if crate_name.as_ref().unwrap() == "annotations_proto"
+ && flag.contains("MethodOptions")
+ {
+ continue;
+ }
tonic_or_prost_opts.push(format!("extern_path={}", flag.trim()));
}
}
@@ -620,6 +617,63 @@
out_dir
}
+/// Parse the descriptor set file into a `FileDescriptorSet`.
+fn parse_descriptor_set_file(descriptor_set_path: &PathBuf) -> FileDescriptorSet {
+ let descriptor_set_bytes =
+ fs::read(descriptor_set_path).expect("Failed to read descriptor set");
+ let descriptor_set = FileDescriptorSet::decode(descriptor_set_bytes.as_slice())
+ .expect("Failed to decode descriptor set");
+
+ descriptor_set
+}
+
+/// Get the package name from the descriptor set.
+fn get_package_name(descriptor_set: &FileDescriptorSet) -> Option<String> {
+ let mut package_name = None;
+
+ for file in &descriptor_set.file {
+ if let Some(package) = &file.package {
+ package_name = Some(package.clone());
+ break;
+ }
+ }
+
+ package_name
+}
+
+/// Whether the proto file should expect to generate a .rs file.
+///
+/// If the proto file contains any messages, enums, or services, then it should generate a rust file.
+/// If the proto file only contains extensions, then it will not generate any rust files.
+fn expect_fs_file_to_be_generated(descriptor_set: &FileDescriptorSet) -> bool {
+ let mut expect_rs = false;
+
+ for file in descriptor_set.file.iter() {
+ let has_messages = !file.message_type.is_empty();
+ let has_enums = !file.enum_type.is_empty();
+ let has_services = !file.service.is_empty();
+ let has_extensions = !file.extension.is_empty();
+
+ let has_definition = has_messages || has_enums || has_services;
+
+ if has_definition {
+ return true;
+ } else if !has_definition && !has_extensions {
+ expect_rs = true;
+ }
+ }
+
+ expect_rs
+}
+
+/// Whether the proto file should expect to generate service definitions.
+fn has_services(descriptor_set: &FileDescriptorSet) -> bool {
+ descriptor_set
+ .file
+ .iter()
+ .any(|file| !file.service.is_empty())
+}
+
fn main() {
// Always enable backtraces for the protoc wrapper.
env::set_var("RUST_BACKTRACE", "1");
@@ -642,6 +696,15 @@
let out_dir = get_and_create_output_dir(&out_dir, &label);
+ let descriptor_set = parse_descriptor_set_file(&descriptor_set);
+ let package_name = get_package_name(&descriptor_set).unwrap_or_default();
+ let expect_rs = expect_fs_file_to_be_generated(&descriptor_set);
+ let has_services = has_services(&descriptor_set);
+
+ if has_services && !is_tonic {
+ println!("Warning: Service definitions will not be generated because the prost toolchain did not define a tonic plugin.");
+ }
+
let mut cmd = process::Command::new(&protoc);
cmd.arg(format!("--prost_out={}", out_dir.display()));
if is_tonic {
@@ -719,9 +782,21 @@
}
// Locate all prost-generated outputs.
- let rust_files = find_generated_rust_files(&out_dir);
+ let mut rust_files = find_generated_rust_files(&out_dir);
if rust_files.is_empty() {
- panic!("No .rs files were generated by prost.");
+ if expect_rs {
+ panic!("No .rs files were generated by prost.");
+ } else {
+ let file_stem = if package_name.is_empty() {
+ "_"
+ } else {
+ &package_name
+ };
+ let file_stem = format!("{}{}", file_stem, if is_tonic { ".tonic" } else { "" });
+ let empty_rs_file = out_dir.join(format!("{}.rs", file_stem));
+ fs::write(&empty_rs_file, "").expect("Failed to write file.");
+ rust_files.insert(empty_rs_file);
+ }
}
let extern_paths = get_extern_paths(&descriptor_set, &crate_name)
@@ -784,6 +859,7 @@
use super::*;
+ use prost_types::{FieldDescriptorProto, FileDescriptorProto, ServiceDescriptorProto};
use std::collections::BTreeMap;
#[test]
@@ -1005,6 +1081,131 @@
}
#[test]
+ fn expect_fs_file_to_be_generated_test() {
+ {
+ // Empty descriptor set should create a file.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(expect_fs_file_to_be_generated(&descriptor_set));
+ }
+ {
+ // Descriptor set with only message should create a file.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ message_type: vec![DescriptorProto {
+ name: Some("Foo".to_string()),
+ ..DescriptorProto::default()
+ }],
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(expect_fs_file_to_be_generated(&descriptor_set));
+ }
+ {
+ // Descriptor set with only enum should create a file.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ enum_type: vec![EnumDescriptorProto {
+ name: Some("Foo".to_string()),
+ ..EnumDescriptorProto::default()
+ }],
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(expect_fs_file_to_be_generated(&descriptor_set));
+ }
+ {
+ // Descriptor set with only service should create a file.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ service: vec![ServiceDescriptorProto {
+ name: Some("Foo".to_string()),
+ ..ServiceDescriptorProto::default()
+ }],
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(expect_fs_file_to_be_generated(&descriptor_set));
+ }
+ {
+ // Descriptor set with only extensions should not create a file.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ extension: vec![FieldDescriptorProto {
+ name: Some("Foo".to_string()),
+ ..FieldDescriptorProto::default()
+ }],
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(!expect_fs_file_to_be_generated(&descriptor_set));
+ }
+ }
+
+ #[test]
+ fn has_services_test() {
+ {
+ // Empty file should not have services.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(!has_services(&descriptor_set));
+ }
+ {
+ // File with only message should not have services.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ message_type: vec![DescriptorProto {
+ name: Some("Foo".to_string()),
+ ..DescriptorProto::default()
+ }],
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(!has_services(&descriptor_set));
+ }
+ {
+ // File with services should have services.
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ service: vec![ServiceDescriptorProto {
+ name: Some("Foo".to_string()),
+ ..ServiceDescriptorProto::default()
+ }],
+ ..FileDescriptorProto::default()
+ }],
+ };
+ assert!(has_services(&descriptor_set));
+ }
+ }
+
+ #[test]
+ fn get_package_name_test() {
+ let descriptor_set = FileDescriptorSet {
+ file: vec![FileDescriptorProto {
+ name: Some("foo.proto".to_string()),
+ package: Some("foo".to_string()),
+ ..FileDescriptorProto::default()
+ }],
+ };
+
+ assert_eq!(get_package_name(&descriptor_set), Some("foo".to_string()));
+ }
+
+ #[test]
fn is_keyword_test() {
let non_keywords = [
"foo", "bar", "baz", "qux", "quux", "corge", "grault", "garply", "waldo", "fred",
diff --git a/proto/prost/private/providers.bzl b/proto/prost/private/providers.bzl
deleted file mode 100644
index 3405060..0000000
--- a/proto/prost/private/providers.bzl
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Prost and Tonic providers."""
-
-ProstProtoInfo = provider(
- doc = "Rust Prost provider info",
- fields = {
- "dep_variant_info": "DepVariantInfo: For the compiled Rust gencode (also covers its " +
- "transitive dependencies)",
- "package_info": "File: A newline delimited file of `--extern_path` values for protoc.",
- "transitive_dep_infos": "depset[DepVariantInfo]: Transitive dependencies of the compiled crate.",
- },
-)
-
-TonicProtoInfo = provider(
- doc = "Rust Tonic provider info",
- fields = {
- "dep_variant_info": "DepVariantInfo for the compiled Rust gencode (also covers its " +
- "transitive dependencies)",
- "package_info": "File: A newline delimited file of `--extern_path` values for protoc.",
- "transitive_dep_infos": "depset[DepVariantInfo]: Transitive dependencies of the compiled crate.",
- },
-)
diff --git a/proto/prost/private/tests/package_names/BUILD.bazel b/proto/prost/private/tests/package_names/BUILD.bazel
index 526d207..7c9cedc 100644
--- a/proto/prost/private/tests/package_names/BUILD.bazel
+++ b/proto/prost/private/tests/package_names/BUILD.bazel
@@ -1,6 +1,6 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_rust//rust:defs.bzl", "rust_test")
-load("//proto/prost:defs.bzl", "rust_tonic_library")
+load("//proto/prost:defs.bzl", "rust_prost_library")
package(default_visibility = ["//proto/prost/private/tests:__subpackages__"])
@@ -11,7 +11,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "pkg_empty_rs_proto",
proto = ":pkg_empty_proto",
)
@@ -23,7 +23,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "pkg_rs_proto",
proto = ":pkg_proto",
)
@@ -35,7 +35,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "pkg_a_rs_proto",
proto = ":pkg_a_proto",
)
@@ -47,7 +47,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "pkg_a_b_rs_proto",
proto = ":pkg_a_b_proto",
)
diff --git a/proto/prost/private/tests/remote/BUILD.bazel b/proto/prost/private/tests/remote/BUILD.bazel
new file mode 100644
index 0000000..3619c16
--- /dev/null
+++ b/proto/prost/private/tests/remote/BUILD.bazel
@@ -0,0 +1,6 @@
+load("//proto:defs.bzl", "rust_prost_library")
+
+rust_prost_library(
+ name = "annotations_rs_proto",
+ proto = "@com_google_googleapis//google/api:annotations_proto",
+)
diff --git a/proto/prost/private/tests/services/echo/BUILD.bazel b/proto/prost/private/tests/services/echo/BUILD.bazel
index 9a1d3d1..50ae7ab 100644
--- a/proto/prost/private/tests/services/echo/BUILD.bazel
+++ b/proto/prost/private/tests/services/echo/BUILD.bazel
@@ -1,5 +1,5 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
-load("//proto/prost:defs.bzl", "rust_tonic_library")
+load("//proto/prost:defs.bzl", "rust_prost_library")
load("//rust:defs.bzl", "rust_binary")
package(default_visibility = ["//proto/prost/private/tests:__subpackages__"])
@@ -11,7 +11,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "echo_rs_proto",
proto = ":echo_proto",
)
diff --git a/proto/prost/private/tests/services/helloworld/BUILD.bazel b/proto/prost/private/tests/services/helloworld/BUILD.bazel
index 56b4d77..796492f 100644
--- a/proto/prost/private/tests/services/helloworld/BUILD.bazel
+++ b/proto/prost/private/tests/services/helloworld/BUILD.bazel
@@ -1,5 +1,5 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
-load("//proto/prost:defs.bzl", "rust_tonic_library")
+load("//proto/prost:defs.bzl", "rust_prost_library")
load("//rust:defs.bzl", "rust_binary")
package(default_visibility = ["//proto/prost/private/tests:__subpackages__"])
@@ -11,7 +11,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "helloworld_rs_proto",
proto = ":helloworld_proto",
)
diff --git a/proto/prost/private/tests/transitive_dependencies/BUILD.bazel b/proto/prost/private/tests/transitive_dependencies/BUILD.bazel
index 6605b1c..5bc3b6a 100644
--- a/proto/prost/private/tests/transitive_dependencies/BUILD.bazel
+++ b/proto/prost/private/tests/transitive_dependencies/BUILD.bazel
@@ -1,5 +1,5 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
-load("//proto/prost:defs.bzl", "rust_tonic_library")
+load("//proto/prost:defs.bzl", "rust_prost_library")
load("//rust:defs.bzl", "rust_test")
package(default_visibility = ["//proto/prost/private/tests:__subpackages__"])
@@ -18,7 +18,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "a_rs_proto",
proto = ":a_proto",
)
diff --git a/proto/prost/private/tests/transitive_dependencies/b/BUILD.bazel b/proto/prost/private/tests/transitive_dependencies/b/BUILD.bazel
index 51efd12..209a149 100644
--- a/proto/prost/private/tests/transitive_dependencies/b/BUILD.bazel
+++ b/proto/prost/private/tests/transitive_dependencies/b/BUILD.bazel
@@ -1,5 +1,5 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
-load("//proto/prost:defs.bzl", "rust_tonic_library")
+load("//proto/prost:defs.bzl", "rust_prost_library")
load("//rust:defs.bzl", "rust_test")
package(default_visibility = ["//proto/prost/private/tests:__subpackages__"])
@@ -16,7 +16,7 @@
],
)
-rust_tonic_library(
+rust_prost_library(
name = "b_rs_proto",
proto = ":b_proto",
)
diff --git a/test/deps.bzl b/test/deps.bzl
index 3582161..d2f33f0 100644
--- a/test/deps.bzl
+++ b/test/deps.bzl
@@ -45,3 +45,13 @@
name = "rules_rust_toolchain_test_target_json",
target_json = Label("//test/unit/toolchain:toolchain-test-triple.json"),
)
+
+ maybe(
+ http_archive,
+ name = "com_google_googleapis",
+ urls = [
+ "https://github.com/googleapis/googleapis/archive/18becb1d1426feb7399db144d7beeb3284f1ccb0.zip",
+ ],
+ strip_prefix = "googleapis-18becb1d1426feb7399db144d7beeb3284f1ccb0",
+ sha256 = "b8c487191eb942361af905e40172644eab490190e717c3d09bf83e87f3994fff",
+ )
diff --git a/test/deps_transitive.bzl b/test/deps_transitive.bzl
new file mode 100644
index 0000000..396dc14
--- /dev/null
+++ b/test/deps_transitive.bzl
@@ -0,0 +1,18 @@
+"""Rules rust test dependencies transitive dependencies."""
+
+load("@com_google_googleapis//:repository_rules.bzl", "switched_rules_by_language")
+
+def rules_rust_test_deps_transitive():
+ switched_rules_by_language(
+ name = "com_google_googleapis_imports",
+ cc = False,
+ csharp = False,
+ gapic = False,
+ go = False,
+ grpc = False,
+ java = False,
+ nodejs = False,
+ php = False,
+ python = False,
+ ruby = False,
+ )