• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""This file implements rust_proto_library."""
2
3load("@rules_rust//rust:defs.bzl", "rust_common")
4load("//bazel/common:proto_common.bzl", "proto_common")
5load("//bazel/common:proto_info.bzl", "ProtoInfo")
6load(
7    "//rust:aspects.bzl",
8    "RustProtoInfo",
9    "label_to_crate_name",
10    "proto_rust_toolchain_label",
11    "rust_cc_proto_library_aspect",
12    "rust_upb_proto_library_aspect",
13)
14
15def rust_proto_library(name, deps, **args):
16    """Declares all the boilerplate needed to use Rust protobufs conveniently.
17
18    Hopefully no user will ever need to read this code.
19
20    Args:
21        name: name of the Rust protobuf target.
22        deps: proto_library target for which to generate Rust gencode.
23        **args: other args passed to the rust_<kernel>_proto_library targets.
24    """
25    if not name.endswith("_rust_proto"):
26        fail(
27            "Name rust_proto_library target should end with `_rust_proto`, but was '{}'"
28                .format(name),
29        )
30    name = name.removesuffix("_rust_proto")
31    alias_args = {}
32    if "visibility" in args:
33        alias_args["visibility"] = args.pop("visibility")
34    native.alias(
35        name = name + "_rust_proto",
36        actual = select({
37            "//rust:use_upb_kernel": name + "_upb_rust_proto",
38            "//conditions:default": name + "_cpp_rust_proto",
39        }),
40        **alias_args
41    )
42
43    rust_upb_proto_library(
44        name = name + "_upb_rust_proto",
45        deps = deps,
46        visibility = ["//visibility:private"],
47        **args
48    )
49
50    rust_cc_proto_library(
51        name = name + "_cpp_rust_proto",
52        deps = deps,
53        visibility = ["//visibility:private"],
54        **args
55    )
56
57def _user_visible_label(ctx):
58    label = str(ctx.label)
59    label = label.removesuffix("_cpp_rust_proto")
60    label = label.removesuffix("_upb_rust_proto")
61    return label + "_rust_proto"
62
63def _rust_proto_library_impl(ctx):
64    if not ctx.label.name.endswith("_rust_proto"):
65        fail(
66            "{}: Name of rust_proto_library target should end with `_rust_proto`."
67                .format(_user_visible_label(ctx)),
68        )
69    deps = ctx.attr.deps
70    if not deps:
71        fail(
72            "{}: Exactly 1 dependency in `deps` attribute expected, none were provided."
73                .format(_user_visible_label(ctx)),
74        )
75    if len(deps) > 1:
76        fail(
77            "{}: Exactly 1 dependency in `deps` attribute expected, too many were provided."
78                .format(_user_visible_label(ctx)),
79        )
80
81    dep = deps[0]
82    rust_proto_info = dep[RustProtoInfo]
83
84    dep_variant_info = rust_proto_info.dep_variant_info
85    crate_info = dep_variant_info.crate_info
86
87    # Change the crate name from the hame of the proto_library to the name of the rust_proto_library.
88    #
89    # When the aspect visits proto_libraries, it doesn't know and cannot deduce the name of the
90    # rust_proto_library (although the name of rust_proto_libraries is consistently ending with
91    # _rust_proto, we can't rely on all proto_libraries to have a name consistently ending with
92    # _proto), therefore we have to modify it after the fact here.
93    #
94    # Since Starlark providers are frozen once they leave the _impl function that defines them,
95    # we have to create a shallow copy.
96    toolchain = ctx.toolchains["@rules_rust//rust:toolchain_type"]
97    fields = {field: getattr(crate_info, field) for field in dir(crate_info)}
98    pkg, name = _user_visible_label(ctx).rsplit(":")
99    label = struct(**{"name": name, "pkg": pkg})
100    fields["name"] = label_to_crate_name(ctx, label, toolchain)
101
102    # These two fields present on the dir(crate_info) but break on some versions of Bazel when
103    # passed back in to crate_info. Strip them for now.
104    fields.pop("to_json")
105    fields.pop("to_proto")
106
107    crate_info_with_rust_proto_name = rust_common.crate_info(**fields)
108
109    return [
110        crate_info_with_rust_proto_name,
111        dep_variant_info.dep_info,
112        dep_variant_info.cc_info,
113        DefaultInfo(files = dep_variant_info.crate_info.srcs),
114    ]
115
116def _make_rust_proto_library(is_upb):
117    return rule(
118        implementation = _rust_proto_library_impl,
119        attrs = {
120            "deps": attr.label_list(
121                mandatory = True,
122                providers = [ProtoInfo],
123                aspects = [rust_upb_proto_library_aspect if is_upb else rust_cc_proto_library_aspect],
124            ),
125            "_proto_lang_toolchain": attr.label(
126                default = Label(proto_rust_toolchain_label(is_upb)),
127            ),
128        },
129        toolchains = [
130            "@rules_rust//rust:toolchain_type",
131        ],
132    )
133
134rust_upb_proto_library = _make_rust_proto_library(is_upb = True)
135rust_cc_proto_library = _make_rust_proto_library(is_upb = False)
136