77
88"""This file implements rust_proto_library aspect."""
99
10- load ("@bazel_tools//tools/cpp:toolchain_utils.bzl" , "find_cpp_toolchain" )
1110load ("@rules_cc//cc/common:cc_common.bzl" , "cc_common" )
1211load ("@rules_cc//cc/common:cc_info.bzl" , "CcInfo" )
1312load ("@rules_rust//:version.bzl" , RUST_VERSION = "VERSION" )
@@ -17,9 +16,6 @@ load("@rules_rust//rust/private:providers.bzl", "CrateInfo", "DepInfo", "DepVari
1716
1817# buildifier: disable=bzl-visibility
1918load ("@rules_rust//rust/private:rustc.bzl" , "rustc_compile_action" )
20- load ("//bazel/common:proto_common.bzl" , "proto_common" )
21- load ("//bazel/common:proto_info.bzl" , "ProtoInfo" )
22- load ("//bazel/private:cc_proto_aspect.bzl" , "cc_proto_aspect" )
2319
2420visibility (["//rust/..." , "//third_party/crubit/rs_bindings_from_cc/..." ])
2521
@@ -34,8 +30,13 @@ CrateMappingInfo = provider(
3430RustProtoInfo = provider (
3531 doc = "Rust protobuf provider info" ,
3632 fields = {
37- "dep_variant_infos" : "List of DepVariantInfo for the compiled Rust " +
38- "gencode (also covers its transitive dependencies)" ,
33+ "dep_variant_infos" : "List(DepVariantInfo): infos for the compiled Rust gencode. \
34+ When the target is a regular proto library, this contains a single element -- the info for the top-level generated code. \
35+ When the target is a srcsless proto library, this contains the infos of its dependencies." ,
36+ "exports_dep_variant_infos" : "List(DepVariantInfo): Transitive infos from targets from the proto_library.exports attribute. \
37+ When the target is a srcsless proto library, this contains the exports infos from all of its dependencies. \
38+ This is a list instead of a depset, since we pass them as direct dependencies when compiling rust code. \
39+ We assume the proto exports feature is not widely used in a way where this will lead to unacceptable analysis-time overhead." ,
3940 "crate_mapping" : "depset(CrateMappingInfo) containing mappings of all transitive " +
4041 "dependencies of the current proto_library." ,
4142 },
@@ -50,6 +51,9 @@ def _rust_version_ge(version):
5051 return _version_parts (RUST_VERSION ) >= _version_parts (version )
5152
5253def label_to_crate_name (ctx , label , toolchain ):
54+ return compute_crate_name (ctx .workspace_name , label , toolchain )
55+
56+ def encode_label_as_crate_name (label ):
5357 return label .name .replace ("-" , "_" )
5458
5559def proto_rust_toolchain_label (is_upb ):
@@ -226,7 +230,7 @@ def _compile_cc(
226230 linking_context = linking_context ,
227231 )
228232
229- def _compile_rust (ctx , attr , src , extra_srcs , deps , runtime ):
233+ def _compile_rust (ctx , attr , src , extra_srcs , deps , aliases , runtime ):
230234 """Compiles a Rust source file.
231235
232236 Eventually this function could be upstreamed into rules_rust and be made present in rust_common.
@@ -237,6 +241,7 @@ def _compile_rust(ctx, attr, src, extra_srcs, deps, runtime):
237241 src (File): The crate root source file to be compiled.
238242 extra_srcs ([File]): Additional source files to include in the crate.
239243 deps (List[DepVariantInfo]): A list of dependencies needed.
244+ aliases (dict[Target, str]): A mapping from dependency target to its crate name.
240245 runtime: The protobuf runtime target.
241246
242247 Returns:
@@ -245,7 +250,7 @@ def _compile_rust(ctx, attr, src, extra_srcs, deps, runtime):
245250 toolchain = ctx .toolchains ["@rules_rust//rust:toolchain_type" ]
246251 output_hash = repr (hash (src .path ))
247252
248- crate_name = label_to_crate_name (ctx , ctx .label , toolchain )
253+ crate_name = compute_crate_name (ctx , ctx .label , toolchain )
249254
250255 lib_name = "{prefix}{name}-{lib_hash}{extension}" .format (
251256 prefix = "lib" ,
@@ -292,7 +297,7 @@ def _compile_rust(ctx, attr, src, extra_srcs, deps, runtime):
292297 # generated code to use a consistent name, even though the actual
293298 # name of the runtime crate varies depending on the protobuf kernel
294299 # and build system.
295- aliases = {runtime : "protobuf" },
300+ aliases = {runtime : "protobuf" } | aliases ,
296301 output = lib ,
297302 metadata = rmeta ,
298303 edition = "2024" ,
@@ -302,8 +307,6 @@ def _compile_rust(ctx, attr, src, extra_srcs, deps, runtime):
302307 compile_data_targets = depset ([]),
303308 owner = ctx .label ,
304309 ),
305- # Needed to make transitive public imports not violate layering.
306- force_all_deps_direct = True ,
307310 output_hash = output_hash ,
308311 )
309312
@@ -343,18 +346,36 @@ def _rust_proto_aspect_common(target, ctx, is_upb):
343346 transitive_crate_mappings .append (rust_proto_info .crate_mapping )
344347
345348 dep_variant_infos = []
346- for info in [d [RustProtoInfo ].dep_variant_infos for d in proto_deps ]:
347- dep_variant_infos += info
349+
350+ # Infos of exports of dependencies.
351+ dep_exports_dep_variant_infos = []
352+
353+ for dep in proto_deps :
354+ dep_variant_infos += dep [RustProtoInfo ].dep_variant_infos
355+ dep_exports_dep_variant_infos += dep [RustProtoInfo ].exports_dep_variant_infos
348356
349357 # If there are no srcs, then this is an alias library (which in Rust acts as a middle
350358 # library in a dependency chain). Don't generate any Rust code for it, but do propagate the
351359 # crate mappings.
352360 if not proto_srcs :
353361 return [RustProtoInfo (
354362 dep_variant_infos = dep_variant_infos ,
363+ exports_dep_variant_infos = dep_exports_dep_variant_infos ,
355364 crate_mapping = depset (transitive = transitive_crate_mappings ),
356365 )]
357366
367+ # Add the infos from dependencies' exports, as they are needed to compile the
368+ # generated code of this target.
369+ dep_variant_infos += dep_exports_dep_variant_infos
370+
371+ # Exports of this target are the directly and transitively exported
372+ # dependencies.
373+ exported_proto_deps = getattr (ctx .rule .attr , "exports" , [])
374+ exports_dep_variant_infos = []
375+ for d in exported_proto_deps :
376+ exports_dep_variant_infos .extend (d [RustProtoInfo ].dep_variant_infos )
377+ exports_dep_variant_infos .extend (d [RustProtoInfo ].exports_dep_variant_infos )
378+
358379 proto_lang_toolchain = ctx .attr ._proto_lang_toolchain [proto_common .ProtoLangToolchainInfo ]
359380 cc_toolchain = find_cpp_toolchain (ctx )
360381 toolchain = ctx .toolchains ["@rules_rust//rust:toolchain_type" ]
@@ -422,19 +443,35 @@ def _rust_proto_aspect_common(target, ctx, is_upb):
422443 for dep in ctx .attr ._extra_deps
423444 ]
424445
446+ aliases = {}
447+
448+ for d in dep_variant_infos :
449+ label = Label (d .crate_info .owner )
450+ target = struct (label = label )
451+ qualified_name = encode_label_as_crate_name (label )
452+ aliases [target ] = qualified_name
453+
454+ deps = ([dep_variant_info_for_runtime ] +
455+ dep_variant_info_for_native_gencode +
456+ dep_variant_infos +
457+ extra_dep_variant_infos +
458+ exports_dep_variant_infos )
459+
425460 dep_variant_info = _compile_rust (
426461 ctx = ctx ,
427462 attr = ctx .rule .attr ,
428463 src = entry_point_rs_output ,
429464 extra_srcs = rs_gencode ,
430- deps = [dep_variant_info_for_runtime ] + dep_variant_info_for_native_gencode + dep_variant_infos + extra_dep_variant_infos ,
465+ deps = deps ,
466+ aliases = aliases ,
431467 runtime = runtime ,
432468 )
433469 return [RustProtoInfo (
434470 dep_variant_infos = [dep_variant_info ],
471+ exports_dep_variant_infos = exports_dep_variant_infos ,
435472 crate_mapping = depset (
436473 direct = [CrateMappingInfo (
437- crate_name = label_to_crate_name (ctx , target .label , toolchain ),
474+ crate_name = encode_label_as_crate_name (ctx .label ),
438475 import_paths = tuple ([get_import_path (f ) for f in proto_srcs ]),
439476 )],
440477 transitive = transitive_crate_mappings ,
@@ -444,7 +481,7 @@ def _rust_proto_aspect_common(target, ctx, is_upb):
444481def _make_proto_library_aspect (is_upb ):
445482 return aspect (
446483 implementation = (_rust_upb_proto_aspect_impl if is_upb else _rust_cc_proto_aspect_impl ),
447- attr_aspects = ["deps" ],
484+ attr_aspects = ["deps" , "exports" ],
448485 requires = ([] if is_upb else [cc_proto_aspect ]),
449486 attrs = {
450487 "_collect_cc_coverage" : attr .label (
0 commit comments