| # Protocol Buffers - Google's data interchange format | |
| # Copyright 2023 Google LLC. All rights reserved. | |
| # | |
| # Use of this source code is governed by a BSD-style | |
| # license that can be found in the LICENSE file or at | |
| # https://developers.google.com/open-source/licenses/bsd | |
| """This file implements rust_proto_library (rule implementations).""" | |
| load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") | |
| load("@rules_rust//rust:defs.bzl", "rust_common") | |
| load("@rules_rust//rust:rust_common.bzl", "CrateInfo", "DepInfo") | |
| load("//bazel/common:proto_common.bzl", "proto_common") | |
| load("//bazel/common:proto_info.bzl", "ProtoInfo") | |
| load( | |
| "//rust/bazel:aspects.bzl", | |
| "RustProtoInfo", | |
| "label_to_crate_name", | |
| "proto_rust_toolchain_label", | |
| "rust_cc_proto_library_aspect", | |
| "rust_upb_proto_library_aspect", | |
| ) | |
| ProtoCrateNamesInfo = provider( | |
| doc = """A provider that contains both names a Protobuf crate has throughout the build.""", | |
| fields = { | |
| "crate_name": "The name of rust_proto_library.", | |
| "old_crate_name": "The name of the proto_library.", | |
| }, | |
| ) | |
| def _user_visible_label(ctx): | |
| label = str(ctx.label) | |
| # Legacy hand-written rules or unit test targets often invoke rust_upb_proto_library | |
| # or rust_cc_proto_library directly with names ending in `_upb_rust_proto` or `_cpp_rust_proto` | |
| # (e.g. `child_upb_rust_proto`). For these targets, replace the kernel infix to derive | |
| # the user-visible `_rust_proto` label (e.g. `child_rust_proto`). | |
| if label.endswith("_upb_rust_proto"): | |
| return label.removesuffix("_upb_rust_proto") + "_rust_proto" | |
| if label.endswith("_cpp_rust_proto"): | |
| return label.removesuffix("_cpp_rust_proto") + "_rust_proto" | |
| # Subtargets generated by the `rust_proto_library` symbolic macro (`name = "foo_rust_proto"`) | |
| # use the naming scheme `foo_rust_proto_upb` and `foo_rust_proto_cpp` per Bazel symbolic | |
| # macro naming rules. Strip `_upb` or `_cpp` to yield the original macro target label. | |
| label = label.removesuffix("_cpp") | |
| label = label.removesuffix("_upb") | |
| return label | |
| def _rust_proto_library_impl(ctx): | |
| if not _user_visible_label(ctx).endswith("_rust_proto"): | |
| fail( | |
| "{}: Name of rust_proto_library target should end with `_rust_proto`." | |
| .format(_user_visible_label(ctx)), | |
| ) | |
| deps = ctx.attr.deps | |
| if not deps: | |
| fail( | |
| "{}: Exactly 1 dependency in `deps` attribute expected, none were provided." | |
| .format(_user_visible_label(ctx)), | |
| ) | |
| if len(deps) > 1: | |
| fail( | |
| "{}: Exactly 1 dependency in `deps` attribute expected, too many were provided." | |
| .format(_user_visible_label(ctx)), | |
| ) | |
| dep = deps[0] | |
| rust_proto_info = dep[RustProtoInfo] | |
| proto_common.check_collocated( | |
| ctx.label, | |
| dep[ProtoInfo], | |
| ctx.attr._proto_lang_toolchain[proto_common.ProtoLangToolchainInfo], | |
| ) | |
| if len(rust_proto_info.dep_variant_infos) != 1: | |
| fail( | |
| "{}: rust_proto_library does not support src-less proto_library targets." | |
| .format(_user_visible_label(ctx)), | |
| ) | |
| dep_variant_info = rust_proto_info.dep_variant_infos[0] | |
| crate_info = dep_variant_info.crate_info | |
| # Change the crate name from the name of the proto_library to the name of the rust_proto_library. | |
| # | |
| # When the aspect visits proto_libraries, it doesn't know and cannot deduce the name of the | |
| # rust_proto_library (although the name of rust_proto_libraries is consistently ending with | |
| # _rust_proto, we can't rely on all proto_libraries to have a name consistently ending with | |
| # _proto), therefore we have to modify it after the fact here. | |
| # | |
| # Since Starlark providers are frozen once they leave the _impl function that defines them, | |
| # we have to create a shallow copy. | |
| toolchain = ctx.toolchains["@rules_rust//rust:toolchain_type"] | |
| fields = {field: getattr(crate_info, field) for field in dir(crate_info)} | |
| # Construct a label and compute the crate name. | |
| # The label's package and workspace root are only relevant when 1P crate renaming is enabled. | |
| # The current implementation of crate renaming supports only monorepos which | |
| # means that it will only rename when label.workspace_root is empty. | |
| label_str = _user_visible_label(ctx) | |
| label = Label(label_str) | |
| fields["name"] = label_to_crate_name(ctx, label, toolchain) | |
| # These two fields present on the dir(crate_info) but break on some versions of Bazel when | |
| # passed back in to crate_info. Strip them for now. | |
| fields.pop("to_json", None) | |
| fields.pop("to_proto", None) | |
| crate_info_with_rust_proto_name = rust_common.crate_info(**fields) | |
| return [ | |
| ProtoCrateNamesInfo( | |
| crate_name = crate_info_with_rust_proto_name.name, | |
| old_crate_name = crate_info.name, | |
| ), | |
| crate_info_with_rust_proto_name, | |
| dep_variant_info.dep_info, | |
| dep_variant_info.cc_info, | |
| DefaultInfo(files = dep_variant_info.crate_info.srcs), | |
| ] | |
| def _make_rust_proto_library(is_upb): | |
| return rule( | |
| implementation = _rust_proto_library_impl, | |
| attrs = { | |
| "deps": attr.label_list( | |
| mandatory = True, | |
| providers = [ProtoInfo], | |
| aspects = [rust_upb_proto_library_aspect if is_upb else rust_cc_proto_library_aspect], | |
| ), | |
| "_proto_lang_toolchain": attr.label( | |
| default = Label(proto_rust_toolchain_label(is_upb)), | |
| ), | |
| }, | |
| toolchains = [ | |
| "@rules_rust//rust:toolchain_type", | |
| ], | |
| provides = [ProtoCrateNamesInfo, CrateInfo, DepInfo, CcInfo], | |
| ) | |
| rust_upb_proto_library = _make_rust_proto_library(is_upb = True) | |
| rust_cc_proto_library = _make_rust_proto_library(is_upb = False) |