From 4f362534125f38bd4478b7beb5fcf247e306b435 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 16:52:14 -0400 Subject: [PATCH 01/11] Split build-script feature resolution --- rs/BUILD.bazel | 1 + rs/extensions.bzl | 41 ++++- rs/private/cargo_workspace_graph.bzl | 157 +++++++++++++++-- rs/private/cargo_workspace_graph_test.bzl | 5 + rs/private/git_crate_metadata_repository.bzl | 2 +- rs/private/repository_utils.bzl | 103 ++++++++++- rs/private/resolver.bzl | 143 +++++++++++---- rs/private/rustc_src_repository.bzl | 7 +- rs/rules_rust.bzl | 6 +- rs/rust_crate.bzl | 163 ++++++++++++++++-- test/MODULE.bazel | 1 + test/MODULE.bazel.lock | 2 + test/build_exec_feature_split/Cargo.lock | 21 +++ test/build_exec_feature_split/Cargo.toml | 14 ++ test/build_exec_feature_split/src/lib.rs | 3 + .../vendor/consumer/Cargo.toml | 12 ++ .../vendor/consumer/build.rs | 3 + .../vendor/consumer/src/lib.rs | 3 + .../vendor/shared/Cargo.toml | 8 + .../vendor/shared/src/lib.rs | 8 + 20 files changed, 619 insertions(+), 84 deletions(-) create mode 100644 test/build_exec_feature_split/Cargo.lock create mode 100644 test/build_exec_feature_split/Cargo.toml create mode 100644 test/build_exec_feature_split/src/lib.rs create mode 100644 test/build_exec_feature_split/vendor/consumer/Cargo.toml create mode 100644 test/build_exec_feature_split/vendor/consumer/build.rs create mode 100644 test/build_exec_feature_split/vendor/consumer/src/lib.rs create mode 100644 test/build_exec_feature_split/vendor/shared/Cargo.toml create mode 100644 test/build_exec_feature_split/vendor/shared/src/lib.rs diff --git a/rs/BUILD.bazel b/rs/BUILD.bazel index f563df71..9e376b75 100644 --- a/rs/BUILD.bazel +++ b/rs/BUILD.bazel @@ -44,6 +44,7 @@ bzl_library( srcs = ["extensions.bzl"], visibility = ["//visibility:public"], deps = [ + "//rs/platforms:triples", "//rs/private:annotations", "//rs/private:cargo_credentials", "//rs/private:cargo_workspace_graph", diff --git a/rs/extensions.bzl b/rs/extensions.bzl index 21ea4e73..50942404 100644 --- a/rs/extensions.bzl +++ b/rs/extensions.bzl @@ -2,6 +2,7 @@ load("@aspect_tools_telemetry_report//:defs.bzl", "TELEMETRY") # buildifier: di load("@bazel_lib//lib:repo_utils.bzl", "repo_utils") load("@bazel_skylib//lib:paths.bzl", "paths") load("@rs_rust_host_tools//:defs.bzl", "RS_HOST_CARGO_LABEL") +load("//rs/platforms:triples.bzl", "SUPPORTED_EXEC_TRIPLES") load("//rs/private:annotations.bzl", "annotation_for", "build_annotation_map", "well_known_annotation_snippet_paths") load("//rs/private:cargo_credentials.bzl", "load_cargo_credentials") load( @@ -250,8 +251,14 @@ def _generate_hub_and_spokes( facts_by_fq_crate[_fq_crate(name, version)] = fact - resolved_facts = resolve_package_facts(packages, facts_by_fq_crate, platform_triples) + resolved_facts = resolve_package_facts( + packages, + facts_by_fq_crate, + platform_triples, + exec_platform_triples = SUPPORTED_EXEC_TRIPLES, + ) feature_resolutions_by_fq_crate = resolved_facts.feature_resolutions_by_fq_crate + exec_feature_resolutions_by_fq_crate = resolved_facts.exec_feature_resolutions_by_fq_crate versions_by_name = resolved_facts.versions_by_name # Only files in the current Bazel workspace can/should be watched, so check where our manifests are located. @@ -267,6 +274,7 @@ def _generate_hub_and_spokes( annotations = annotations, platform_triples = platform_triples, materialize_workspace_members = False, + exec_platform_triples = SUPPORTED_EXEC_TRIPLES, validate_lockfile = validate_lockfile, debug = debug, dep_label_prefix = "@%s//:" % hub_name, @@ -277,6 +285,7 @@ def _generate_hub_and_spokes( platform_cfg_attrs = workspace_resolution.platform_cfg_attrs workspace_dep_labels_by_triple = workspace_resolution.workspace_dep_labels_by_triple workspace_dep_versions_by_name = workspace_resolution.workspace_dep_versions_by_name + exec_feature_resolutions_by_fq_crate = workspace_resolution.exec_feature_resolutions_by_fq_crate _date(mctx, "set up initial deps!") @@ -290,6 +299,7 @@ def _generate_hub_and_spokes( source = package["source"] feature_resolutions = feature_resolutions_by_fq_crate[_fq_crate(crate_name, version)] + exec_feature_resolutions = exec_feature_resolutions_by_fq_crate[_fq_crate(crate_name, version)] annotation = annotation_for(annotations, crate_name, version, hub_name) suggested_annotation = None @@ -323,6 +333,7 @@ crate.annotation( gen_build_script = annotation.gen_build_script, build_script_deps = [], build_script_deps_select = _select(feature_resolutions.build_deps), + exec_build_script_deps_select = _select(exec_feature_resolutions.build_deps), build_script_data = annotation.build_script_data, build_script_data_select = annotation.build_script_data_select, build_script_env = annotation.build_script_env, @@ -338,9 +349,14 @@ crate.annotation( deps = annotation.deps, crate_tags = annotation.tags, deps_select = _select(feature_resolutions.deps), + exec_deps_select = _select(exec_feature_resolutions.deps), aliases = feature_resolutions.aliases, + exec_aliases = exec_feature_resolutions.aliases, crate_features = annotation.crate_features, crate_features_select = _select(feature_resolutions.features_enabled), + exec_crate_features_select = _select(exec_feature_resolutions.features_enabled), + target_active = any(feature_resolutions.active.values()), + exec_active = any(exec_feature_resolutions.active.values()), use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, ) @@ -556,6 +572,17 @@ cargo_lints( if platform not in resolved_platforms: resolved_platforms.append(platform) + resolved_exec_platforms = [] + for triple in SUPPORTED_EXEC_TRIPLES: + platform = platform_label(triple, use_legacy_rules_rust_platforms) + if platform not in resolved_exec_platforms: + resolved_exec_platforms.append(platform) + + resolved_target_and_exec_platforms = resolved_platforms[:] + for platform in resolved_exec_platforms: + if platform not in resolved_target_and_exec_platforms: + resolved_target_and_exec_platforms.append(platform) + defs_bzl_contents = \ """load(":data.bzl", "DEP_DATA") load("@rules_rs//rs/private:all_crate_deps.bzl", _all_crate_deps = "all_crate_deps") @@ -595,9 +622,21 @@ RESOLVED_PLATFORMS = select({{ {target_compatible_with}, "//conditions:default": ["@platforms//:incompatible"], }}) + +RESOLVED_EXEC_PLATFORMS = select({{ + {exec_target_compatible_with}, + "//conditions:default": ["@platforms//:incompatible"], +}}) + +RESOLVED_TARGET_AND_EXEC_PLATFORMS = select({{ + {target_and_exec_compatible_with}, + "//conditions:default": ["@platforms//:incompatible"], +}}) """.format( platforms = render_string_list(resolved_platforms), target_compatible_with = ",\n ".join(['"%s": []' % platform for platform in resolved_platforms]), + exec_target_compatible_with = ",\n ".join(['"%s": []' % platform for platform in resolved_exec_platforms]), + target_and_exec_compatible_with = ",\n ".join(['"%s": []' % platform for platform in resolved_target_and_exec_platforms]), this_repo = repr("@" + hub_name + "//:"), ) diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 9baee0e8..c16302b3 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -1,6 +1,6 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("//rs/private:cfg_parser.bzl", "cfg_matches_expr_for_cfg_attrs", "triple_to_cfg_attrs") -load("//rs/private:resolver.bzl", "resolve") +load("//rs/private:resolver.bzl", "resolve", "seed_exec_build_dependencies") load("//rs/private:select_utils.bzl", "compute_select") load("//rs/private:semver.bzl", "select_matching_version") @@ -62,17 +62,40 @@ def cfg_match_info_for_target(target, platform_cfg_attrs, cfg_match_cache): cfg_match_cache[target] = match_info return match_info -def new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples): +def _new_feature_resolution(package_index, possible_deps, possible_features, platform_triples, build_dep_triples = None): + if build_dep_triples == None: + build_dep_triples = platform_triples return struct( + active = {triple: False for triple in platform_triples}, features_enabled = {triple: set() for triple in platform_triples}, - build_deps = {triple: set() for triple in platform_triples}, + build_deps = {triple: set() for triple in build_dep_triples}, deps = {triple: set() for triple in platform_triples}, aliases = {}, + deferred_build_dep_features = {triple: {} for triple in platform_triples}, package_index = package_index, possible_deps = possible_deps, possible_features = possible_features, ) +def _new_package_feature_resolution( + package_index, + package_info, + platform_triples, + dep_converter, + skip_internal_rustc_placeholder_crates, + build_dep_triples = None): + return _new_feature_resolution( + package_index, + prepare_possible_deps( + package_info.get("dependencies", []), + converter = dep_converter, + skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, + ), + package_info.get("features", {}), + platform_triples, + build_dep_triples = build_dep_triples, + ) + _INTERNAL_RUSTC_PLACEHOLDER_CRATES = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -367,8 +390,15 @@ def split_lockfile_packages(hub_name, cargo_metadata, workspace_cargo_toml, all_ workspace_members = workspace_members, ) -def _resolve_packages(packages, package_info_by_fq_crate, platform_triples, dep_converter = None, skip_internal_rustc_placeholder_crates = True): +def _resolve_packages( + packages, + package_info_by_fq_crate, + platform_triples, + dep_converter = None, + exec_platform_triples = [], + skip_internal_rustc_placeholder_crates = True): feature_resolutions_by_fq_crate = {} + exec_feature_resolutions_by_fq_crate = {} versions_by_name = {} for package_index in range(len(packages)): @@ -380,25 +410,40 @@ def _resolve_packages(packages, package_info_by_fq_crate, platform_triples, dep_ add_to_dict(versions_by_name, name, version) package_info = package_info_by_fq_crate[fq] - possible_deps = prepare_possible_deps( - package_info.get("dependencies", []), - converter = dep_converter, - skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, + feature_resolutions = _new_package_feature_resolution( + package_index, + package_info, + platform_triples, + dep_converter, + skip_internal_rustc_placeholder_crates, + build_dep_triples = exec_platform_triples or platform_triples, ) - feature_resolutions = new_feature_resolutions(package_index, possible_deps, package_info.get("features", {}), platform_triples) package["feature_resolutions"] = feature_resolutions feature_resolutions_by_fq_crate[fq] = feature_resolutions + if exec_platform_triples: + exec_feature_resolutions = _new_package_feature_resolution( + package_index, + package_info, + exec_platform_triples, + dep_converter, + skip_internal_rustc_placeholder_crates, + ) + package["exec_feature_resolutions"] = exec_feature_resolutions + exec_feature_resolutions_by_fq_crate[fq] = exec_feature_resolutions + return struct( + exec_feature_resolutions_by_fq_crate = exec_feature_resolutions_by_fq_crate, feature_resolutions_by_fq_crate = feature_resolutions_by_fq_crate, versions_by_name = versions_by_name, ) -def resolve_package_facts(packages, facts_by_fq_crate, platform_triples, skip_internal_rustc_placeholder_crates = True): +def resolve_package_facts(packages, facts_by_fq_crate, platform_triples, exec_platform_triples = [], skip_internal_rustc_placeholder_crates = True): return _resolve_packages( packages, facts_by_fq_crate, platform_triples, + exec_platform_triples = exec_platform_triples, skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, ) @@ -483,12 +528,14 @@ def resolve_cargo_workspace_members( annotations, platform_triples, materialize_workspace_members, + exec_platform_triples = [], validate_lockfile = True, debug = False, dep_label_prefix = "//:", skip_internal_rustc_placeholder_crates = True, watch_manifests = False, use_legacy_rules_rust_platforms = False): + split_exec_resolution = bool(exec_platform_triples) platform_cfg_attrs = [triple_to_cfg_attrs(triple) for triple in platform_triples] platform_cfg_attrs_by_triple = {} for cfg_attr in platform_cfg_attrs: @@ -496,6 +543,12 @@ def resolve_cargo_workspace_members( cfg_match_cache = {None: struct(matches = platform_triples, uses_feature_cfg = False)} + exec_platform_cfg_attrs = [triple_to_cfg_attrs(triple) for triple in exec_platform_triples] + exec_platform_cfg_attrs_by_triple = {} + for cfg_attr in exec_platform_cfg_attrs: + exec_platform_cfg_attrs_by_triple[cfg_attr["_triple"]] = cfg_attr + exec_cfg_match_cache = {None: struct(matches = exec_platform_triples, uses_feature_cfg = False)} + workspace_member_keys = {} for package in cargo_metadata["packages"]: workspace_member_keys[(package["name"], package["version"])] = True @@ -503,6 +556,11 @@ def resolve_cargo_workspace_members( resolver_versions_by_name = {name: versions[:] for name, versions in versions_by_name.items()} workspace_members_by_key = {(package["name"], package["version"]): package for package in workspace_members} resolver_packages = packages[:] + exec_feature_resolutions_by_fq_crate = { + fq_crate(package["name"], package["version"]): package["exec_feature_resolutions"] + for package in packages + if split_exec_resolution + } for package in cargo_metadata["packages"]: name = package["name"] version = package["version"] @@ -514,13 +572,6 @@ def resolve_cargo_workspace_members( else: resolver_versions_by_name[name] = [version] - possible_features = package.get("features", {}) - possible_deps = prepare_possible_deps( - package.get("dependencies", []), - converter = cargo_metadata_dep_to_dep_dict, - skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, - ) - package_index = len(resolver_packages) lockfile_pkg = workspace_members_by_key.get((name, version), {}) resolver_package = { @@ -529,12 +580,35 @@ def resolve_cargo_workspace_members( "dependencies": lockfile_pkg.get("dependencies", []), } - feature_resolutions = new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples) + feature_resolutions = _new_package_feature_resolution( + package_index, + package, + platform_triples, + cargo_metadata_dep_to_dep_dict, + skip_internal_rustc_placeholder_crates, + build_dep_triples = exec_platform_triples or platform_triples, + ) resolver_package["feature_resolutions"] = feature_resolutions feature_resolutions_by_fq_crate[fq_crate(name, version)] = feature_resolutions + if split_exec_resolution: + exec_feature_resolutions = _new_package_feature_resolution( + package_index, + package, + exec_platform_triples, + cargo_metadata_dep_to_dep_dict, + skip_internal_rustc_placeholder_crates, + ) + resolver_package["exec_feature_resolutions"] = exec_feature_resolutions + exec_feature_resolutions_by_fq_crate[fq_crate(name, version)] = exec_feature_resolutions + resolver_packages.append(resolver_package) + exec_resolver_packages = [ + dict(package, feature_resolutions = package["exec_feature_resolutions"]) + for package in resolver_packages + ] if split_exec_resolution else [] + _resolve_possible_deps( resolver_packages, resolver_versions_by_name, @@ -545,6 +619,17 @@ def resolve_cargo_workspace_members( dep_label_prefix, ) + if split_exec_resolution: + _resolve_possible_deps( + exec_resolver_packages, + resolver_versions_by_name, + exec_feature_resolutions_by_fq_crate, + exec_platform_triples, + exec_platform_cfg_attrs, + exec_cfg_match_cache, + dep_label_prefix, + ) + workspace_fq_deps = compute_workspace_fq_deps(workspace_members, resolver_versions_by_name) workspace_dep_versions_by_name = {} workspace_dep_labels_by_triple = {triple: set() for triple in platform_triples} @@ -554,6 +639,8 @@ def resolve_cargo_workspace_members( ctx.watch(package["manifest_path"]) package_feature_resolutions = feature_resolutions_by_fq_crate[fq_crate(package["name"], package["version"])] + for triple in platform_triples: + package_feature_resolutions.active[triple] = True if "default" in package.get("features", {}): for triple in platform_triples: package_feature_resolutions.features_enabled[triple].add("default") @@ -611,8 +698,11 @@ def resolve_cargo_workspace_members( match_info = cfg_match_info_for_target(target, platform_cfg_attrs, cfg_match_cache) for triple in match_info.matches: + if dep.get("kind", "normal") == "build": + continue if not is_first_party_dep or materialize_workspace_members: workspace_dep_labels_by_triple[triple].add(":" + dep_name) + feature_resolutions.active[triple] = True feature_resolutions.features_enabled[triple].update(features) for crate, annotation_versions in annotations.items(): @@ -633,7 +723,33 @@ def resolve_cargo_workspace_members( if triple in features_enabled: features_enabled[triple].update(features) - resolve(ctx, resolver_packages, feature_resolutions_by_fq_crate, platform_cfg_attrs_by_triple, debug) + if split_exec_resolution: + exec_features_enabled = exec_feature_resolutions_by_fq_crate[fq_crate(crate, version)].features_enabled + if annotation.crate_features: + for triple in exec_platform_triples: + exec_features_enabled[triple].update(annotation.crate_features) + for triple, features in annotation.crate_features_select.items(): + if triple in exec_features_enabled: + exec_features_enabled[triple].update(features) + + resolve( + ctx, + resolver_packages, + feature_resolutions_by_fq_crate, + platform_cfg_attrs_by_triple, + debug, + include_build_dependencies = not split_exec_resolution, + ) + + if split_exec_resolution: + seed_exec_build_dependencies(resolver_packages, exec_platform_cfg_attrs_by_triple) + resolve( + ctx, + exec_resolver_packages, + exec_feature_resolutions_by_fq_crate, + exec_platform_cfg_attrs_by_triple, + debug, + ) for package in packages: feature_resolutions = package["feature_resolutions"] @@ -651,6 +767,9 @@ def resolve_cargo_workspace_members( return struct( cfg_match_cache = cfg_match_cache, + exec_feature_resolutions_by_fq_crate = exec_feature_resolutions_by_fq_crate, + exec_platform_cfg_attrs = exec_platform_cfg_attrs, + exec_platform_cfg_attrs_by_triple = exec_platform_cfg_attrs_by_triple, feature_resolutions_by_fq_crate = feature_resolutions_by_fq_crate, platform_cfg_attrs = platform_cfg_attrs, platform_cfg_attrs_by_triple = platform_cfg_attrs_by_triple, diff --git a/rs/private/cargo_workspace_graph_test.bzl b/rs/private/cargo_workspace_graph_test.bzl index 18bfd84a..d6fc2248 100644 --- a/rs/private/cargo_workspace_graph_test.bzl +++ b/rs/private/cargo_workspace_graph_test.bzl @@ -256,11 +256,16 @@ def _resolve_package_facts_attaches_feature_resolutions_impl(ctx): }, }, ["x86_64-unknown-linux-gnu"], + exec_platform_triples = ["aarch64-apple-darwin"], ) asserts.equals(env, {"serde": ["1.0.0"]}, got.versions_by_name) asserts.true(env, "feature_resolutions" in packages[0]) + asserts.true(env, "exec_feature_resolutions" in packages[0]) asserts.equals(env, ["serde-1.0.0"], got.feature_resolutions_by_fq_crate.keys()) + asserts.equals(env, ["serde-1.0.0"], got.exec_feature_resolutions_by_fq_crate.keys()) + asserts.equals(env, ["x86_64-unknown-linux-gnu"], got.feature_resolutions_by_fq_crate["serde-1.0.0"].active.keys()) + asserts.equals(env, ["aarch64-apple-darwin"], got.exec_feature_resolutions_by_fq_crate["serde-1.0.0"].active.keys()) return unittest.end(env) resolve_package_facts_attaches_feature_resolutions_test = unittest.make(_resolve_package_facts_attaches_feature_resolutions_impl) diff --git a/rs/private/git_crate_metadata_repository.bzl b/rs/private/git_crate_metadata_repository.bzl index 4acf39cd..e76a8b96 100644 --- a/rs/private/git_crate_metadata_repository.bzl +++ b/rs/private/git_crate_metadata_repository.bzl @@ -3,7 +3,7 @@ load(":repository_utils.bzl", "render_rust_crate_call", "rust_crate_attrs") def _git_crate_metadata_repository_implementation(rctx): rctx.file("crate.bzl", """\ load("@rules_rs//rs:rust_crate.bzl", "rust_crate") -load("@{hub_name}//:defs.bzl", "RESOLVED_PLATFORMS") +load("@{hub_name}//:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") def crate( crate_name, diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index ae205629..e3ff055f 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -172,26 +172,40 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( {indent} aliases = {{ {indent} {aliases} {indent} }}, +{indent} exec_aliases = {{ +{indent} {exec_aliases} +{indent} }}, {indent} deps = [ {indent} {deps} {indent} ]{extra_deps}{conditional_deps}, +{indent} exec_deps = [ +{indent} {exec_deps} +{indent} ]{extra_deps}{exec_conditional_deps}, {indent} data = [ {indent} {data} {indent} ], {extra_compile_data_attr}{indent} crate_features = {crate_features}, {indent} triples = {triples}, {indent} conditional_crate_features = {conditional_crate_features}, +{indent} exec_crate_features = {exec_crate_features}, +{indent} exec_triples = {exec_triples}, +{indent} exec_conditional_crate_features = {exec_conditional_crate_features}, {indent} crate_root = {crate_root}, {indent} edition = {edition}, {rustc_env_attr}{indent} rustc_flags = {rustc_flags}{conditional_rustc_flags}, {indent} tags = {tags}, {indent} target_compatible_with = RESOLVED_PLATFORMS, +{indent} exec_target_compatible_with = RESOLVED_EXEC_PLATFORMS, +{indent} target_and_exec_compatible_with = RESOLVED_TARGET_AND_EXEC_PLATFORMS, {indent} links = {links}, {indent} build_script = {build_script}, {indent} build_script_data = {build_script_data}{conditional_build_script_data}, {indent} build_deps = [ {indent} {build_deps} {indent} ]{conditional_build_deps}, +{indent} exec_build_deps = [ +{indent} {exec_build_deps} +{indent} ]{exec_conditional_build_deps}, {indent} build_script_env = {build_script_env}{conditional_build_script_env}, {indent} allow_build_script_to_detect_nonhermetic_paths = {allow_build_script_to_detect_nonhermetic_paths}, {indent} build_script_toolchains = {build_script_toolchains}, @@ -201,25 +215,85 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( {indent} has_lib = {has_lib}, {indent} binaries = {binaries}, {indent} use_legacy_rules_rust_platforms = {use_legacy_rules_rust_platforms}, +{indent} resolution_kind = {resolution_kind}, {skip_deps_verification_attr}{indent}) """ +def _merge_resolution_selects(target_select, exec_select): + merged = dict(target_select) + differs = False + for triple, exec_items in exec_select.items(): + target_items = target_select.get(triple) + if target_items == None: + merged[triple] = exec_items + elif sorted(target_items) != sorted(exec_items): + differs = True + return merged, differs + +def _merge_aliases(target_aliases, exec_aliases): + merged = dict(target_aliases) + differs = False + for dependency, exec_alias in exec_aliases.items(): + target_alias = target_aliases.get(dependency) + if target_alias != None and target_alias != exec_alias: + differs = True + else: + merged[dependency] = exec_alias + return merged, differs + def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): # We keep conditional_crate_features unrendered here because it must be treated specially for build scripts. # See `rust_crate.bzl` for details. + if not attr.target_active and attr.exec_active: + resolution_kind = "exec" + crate_features_select = attr.exec_crate_features_select + deps_select = attr.exec_deps_select + build_deps_select = attr.exec_build_script_deps_select + aliases = attr.exec_aliases + elif not attr.exec_active: + resolution_kind = "target" + crate_features_select = attr.crate_features_select + deps_select = attr.deps_select + build_deps_select = attr.build_script_deps_select + aliases = attr.aliases + else: + crate_features_select, crate_features_differ = _merge_resolution_selects( + attr.crate_features_select, + attr.exec_crate_features_select, + ) + deps_select, deps_differ = _merge_resolution_selects(attr.deps_select, attr.exec_deps_select) + build_deps_select, build_deps_differ = _merge_resolution_selects( + attr.build_script_deps_select, + attr.exec_build_script_deps_select, + ) + aliases, aliases_differ = _merge_aliases(attr.aliases, attr.exec_aliases) + differs = crate_features_differ or deps_differ or build_deps_differ or aliases_differ + resolution_kind = "split" if differs else "target_and_exec" + + if differs: + crate_features_select = attr.crate_features_select + deps_select = attr.deps_select + build_deps_select = attr.build_script_deps_select + aliases = attr.aliases + crate_features, conditional_crate_features = compute_select( _exclude_deps_from_features(attr.crate_features), - {platform: _exclude_deps_from_features(features) for platform, features in attr.crate_features_select.items()}, + {platform: _exclude_deps_from_features(features) for platform, features in crate_features_select.items()}, + ) + exec_crate_features, exec_conditional_crate_features = compute_select( + _exclude_deps_from_features(attr.crate_features), + {platform: _exclude_deps_from_features(features) for platform, features in attr.exec_crate_features_select.items()}, ) use_legacy_rules_rust_platforms = attr.use_legacy_rules_rust_platforms - build_deps, conditional_build_deps = render_select(attr.build_script_deps, attr.build_script_deps_select, use_legacy_rules_rust_platforms) + build_deps, conditional_build_deps = render_select(attr.build_script_deps, build_deps_select, use_legacy_rules_rust_platforms) + exec_build_deps, exec_conditional_build_deps = render_select(attr.build_script_deps, attr.exec_build_script_deps_select, use_legacy_rules_rust_platforms) build_script_data, conditional_build_script_data = render_select(attr.build_script_data, attr.build_script_data_select, use_legacy_rules_rust_platforms) build_script_tools, conditional_build_script_tools = render_select(attr.build_script_tools, attr.build_script_tools_select, use_legacy_rules_rust_platforms) rustc_flags, conditional_rustc_flags = render_select(attr.rustc_flags, attr.rustc_flags_select, use_legacy_rules_rust_platforms) - deps, conditional_deps = render_select(attr.deps + bazel_metadata.get("deps", []), attr.deps_select, use_legacy_rules_rust_platforms) + deps, conditional_deps = render_select(attr.deps + bazel_metadata.get("deps", []), deps_select, use_legacy_rules_rust_platforms) + exec_deps, exec_conditional_deps = render_select(attr.deps + bazel_metadata.get("deps", []), attr.exec_deps_select, use_legacy_rules_rust_platforms) conditional_build_script_env = render_select_build_script_env(attr.build_script_env_select, use_legacy_rules_rust_platforms) - list_indent = ",\n%s " % indent extra_deps = " + " + extra_deps if extra_deps else "" extra_compile_data = getattr(attr, "extra_compile_data", []) @@ -242,15 +316,21 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_name = values["crate_name"], purl = values["purl"], version = values["version"], - aliases = list_indent.join(['"%s": "%s"' % kv for kv in attr.aliases.items()]), + aliases = list_indent.join(['"%s": "%s"' % kv for kv in aliases.items()]), + exec_aliases = list_indent.join(['"%s": "%s"' % kv for kv in attr.exec_aliases.items()]), deps = list_indent.join(['"%s"' % d for d in sorted(deps)]), + exec_deps = list_indent.join(['"%s"' % d for d in sorted(exec_deps)]), extra_deps = extra_deps, conditional_deps = " + " + conditional_deps if conditional_deps else "", + exec_conditional_deps = " + " + exec_conditional_deps if exec_conditional_deps else "", data = list_indent.join(['"%s"' % str(d) for d in attr.data]), extra_compile_data_attr = extra_compile_data_attr, crate_features = repr(sorted(crate_features)), - triples = repr(attr.crate_features_select.keys()), + triples = repr(crate_features_select.keys()), conditional_crate_features = repr(conditional_crate_features), + exec_crate_features = repr(sorted(exec_crate_features)), + exec_triples = repr(attr.exec_crate_features_select.keys()), + exec_conditional_crate_features = repr(exec_conditional_crate_features), crate_root = values["crate_root"], edition = values["edition"], rustc_env_attr = rustc_env_attr, @@ -263,6 +343,8 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i conditional_build_script_data = " + " + conditional_build_script_data if conditional_build_script_data else "", build_deps = list_indent.join(['"%s"' % d for d in sorted(build_deps)]), conditional_build_deps = " + " + conditional_build_deps if conditional_build_deps else "", + exec_build_deps = list_indent.join(['"%s"' % d for d in sorted(exec_build_deps)]), + exec_conditional_build_deps = " + " + exec_conditional_build_deps if exec_conditional_build_deps else "", build_script_env = repr(attr.build_script_env), conditional_build_script_env = " | " + conditional_build_script_env if conditional_build_script_env else "", allow_build_script_to_detect_nonhermetic_paths = repr(attr.allow_build_script_to_detect_nonhermetic_paths), @@ -274,6 +356,7 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i has_lib = values["has_lib"], binaries = values["binaries"], use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, + resolution_kind = repr(resolution_kind), skip_deps_verification_attr = skip_deps_verification_attr, ) @@ -287,7 +370,7 @@ def render_build_file_content(rctx, attr, values, bazel_metadata = {}): return """\ load("@rules_rs//rs:rust_crate.bzl", "rust_crate") load("@rules_rs//rs:rust_binary.bzl", "rust_binary") -load("@{hub_name}//:defs.bzl", "RESOLVED_PLATFORMS") +load("@{hub_name}//:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") {rust_crate_call}""".format( hub_name = attr.hub_name, @@ -299,6 +382,7 @@ rust_crate_attrs = { "gen_build_script": attr.string(), "build_script_deps": attr.label_list(), "build_script_deps_select": _label_list_dict(), + "exec_build_script_deps_select": _label_list_dict(), "build_script_data": attr.label_list(), "build_script_data_select": _label_list_dict(), "build_script_env": attr.string_dict(), @@ -314,9 +398,14 @@ rust_crate_attrs = { "data": attr.label_list(), "deps": attr.label_list(), "deps_select": _label_list_dict(), + "exec_deps_select": _label_list_dict(), "aliases": attr.string_dict(), + "exec_aliases": attr.string_dict(), "crate_features": attr.string_list(), "crate_features_select": attr.string_list_dict(), + "exec_crate_features_select": attr.string_list_dict(), + "target_active": attr.bool(), + "exec_active": attr.bool(), "use_legacy_rules_rust_platforms": attr.bool(), } diff --git a/rs/private/resolver.bzl b/rs/private/resolver.bzl index dc122de0..5813d210 100644 --- a/rs/private/resolver.bzl +++ b/rs/private/resolver.bzl @@ -16,8 +16,7 @@ def _count(feature_resolutions_by_fq_crate): return n def _dep_target_matches_triple(dep, triple, package_feature_set, cfg_attrs_by_triple): - remaining = dep["target"] - if triple not in remaining: + if triple not in dep["target"]: return False if not dep.get("feature_sensitive", False): @@ -30,7 +29,7 @@ def _dep_target_matches_triple(dep, triple, package_feature_set, cfg_attrs_by_tr features = package_feature_set, ).matches) -def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, debug): +def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, debug, include_build_dependencies): new_dirty_package_indices = set() for index in dirty_package_indices: @@ -43,13 +42,13 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb deps = feature_resolutions.deps if _propagate_feature_enablement( - package_changed, new_dirty_package_indices, package, features_enabled, feature_resolutions, cfg_attrs_by_triple, debug, + include_build_dependencies, ): package_changed = True @@ -60,6 +59,8 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb continue kind = dep.get("kind", "normal") + if kind == "build" and not include_build_dependencies: + continue dep_feature_resolutions = dep["feature_resolutions"] @@ -77,15 +78,17 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb else: match = dep["target"] - to_remove = None for triple in match: + if not feature_resolutions.active[triple]: + continue + if optional: features_for_triple = features_enabled[triple] if dep_name not in features_for_triple and prefixed_dep_alias not in features_for_triple: continue triple_deps = deps[triple] if kind == "normal" else feature_resolutions.build_deps[triple] - if package_changed or bazel_target not in triple_deps: + if bazel_target not in triple_deps: package_changed = True triple_deps.add(bazel_target) @@ -94,21 +97,16 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb triple_features = dep_feature_resolutions.features_enabled[triple] + if not dep_feature_resolutions.active[triple]: + dep_feature_resolutions.active[triple] = True + new_dirty_package_indices.add(dep_feature_resolutions.package_index) + dep_features = dep.get("features") if dep_features: prev_length = len(triple_features) triple_features.update(dep_features) if prev_length != len(triple_features): new_dirty_package_indices.add(dep_feature_resolutions.package_index) - if not to_remove: - to_remove = set() - to_remove.add(triple) - - if to_remove: - if len(to_remove) == len(match): - dep["bazel_target"] = None - else: - match.difference_update(to_remove) if package_changed: new_dirty_package_indices.add(index) @@ -116,17 +114,18 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb return new_dirty_package_indices def _propagate_feature_enablement( - package_changed, dirty_package_indices, package, features_enabled, feature_resolutions, cfg_attrs_by_triple, - debug): + debug, + include_build_dependencies): + package_changed = False possible_features = feature_resolutions.possible_features for triple, feature_set in features_enabled.items(): - if not feature_set: + if not feature_resolutions.active[triple] or not feature_set: continue # Enable any features that are implied by previously-enabled features. @@ -146,7 +145,7 @@ def _propagate_feature_enablement( dep_name = feature[:idx] dep_feature = feature[idx + 1:] - dep_optional = False + has_optional_dependency = False optional_marker = False if dep_name[-1] == "?": optional_marker = True @@ -154,19 +153,35 @@ def _propagate_feature_enablement( found = False for dep in feature_resolutions.possible_deps: - if dep_name == dep["name"] and _dep_target_matches_triple(dep, triple, feature_set, cfg_attrs_by_triple): - found = True - dep_optional = dep.get("optional", False) - if not optional_marker or not dep_optional or dep_name in feature_set or ("dep:" + dep_name) in feature_set: - dep_feature_resolutions = dep["feature_resolutions"] - triple_features = dep_feature_resolutions.features_enabled[triple] - if dep_feature not in triple_features: - triple_features.add(dep_feature) - dirty_package_indices.add(dep_feature_resolutions.package_index) - break + if dep_name != dep["name"]: + continue + + defer_build_dependency = dep.get("kind", "normal") == "build" and not include_build_dependencies + if not defer_build_dependency and not _dep_target_matches_triple(dep, triple, feature_set, cfg_attrs_by_triple): + continue + + found = True + dep_optional = dep.get("optional", False) + has_optional_dependency = has_optional_dependency or dep_optional + if optional_marker and dep_optional and dep_name not in feature_set and ("dep:" + dep_name) not in feature_set: + continue + + if defer_build_dependency: + deferred = feature_resolutions.deferred_build_dep_features[triple] + deferred_features = deferred.get(dep_name) + if deferred_features == None: + deferred_features = set() + deferred[dep_name] = deferred_features + deferred_features.add(dep_feature) + else: + dep_feature_resolutions = dep["feature_resolutions"] + triple_features = dep_feature_resolutions.features_enabled[triple] + if dep_feature not in triple_features: + triple_features.add(dep_feature) + dirty_package_indices.add(dep_feature_resolutions.package_index) # Only optional deps need to be explicitly enabled when a subfeature is toggled. - if dep_optional and (not optional_marker) and dep_name not in feature_set: + if has_optional_dependency and (not optional_marker) and dep_name not in feature_set: package_changed = True feature_set.add(dep_name) @@ -177,19 +192,77 @@ def _propagate_feature_enablement( _MAX_ROUNDS = 50 -def resolve(mctx, packages, feature_resolutions_by_fq_crate, cfg_attrs_by_triple, debug): +def resolve(mctx, packages, feature_resolutions_by_fq_crate, cfg_attrs_by_triple, debug, include_build_dependencies = True): # Do some rounds of mutual resolution; bail when no more changes dirty_package_indices = range(len(packages)) for i in range(_MAX_ROUNDS): mctx.report_progress("Running round %s of dependency/feature resolution" % i) - dirty_package_indices = _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, debug) + dirty_package_indices = _resolve_one_round( + packages, + dirty_package_indices, + cfg_attrs_by_triple, + debug, + include_build_dependencies, + ) if not dirty_package_indices: if debug: count = _count(feature_resolutions_by_fq_crate) print("Got count", count, "in", i + 1, "rounds") - break + return dirty_package_indices = sorted(dirty_package_indices) - if i == _MAX_ROUNDS: - fail("Resolution did not converge! This is likely a bug in rules_rs, please report it to github.com/hermeticbuild/rules_rs") + fail("Resolution did not converge! This is likely a bug in rules_rs, please report it to github.com/hermeticbuild/rules_rs") + +def seed_exec_build_dependencies(packages, exec_cfg_attrs_by_triple): + """Seeds exec resolution from build dependencies of target-active packages.""" + for package in packages: + target_resolution = package["feature_resolutions"] + exec_resolution = package["exec_feature_resolutions"] + + target_features = set() + deferred_build_dep_features = {} + target_active = False + for triple, active in target_resolution.active.items(): + if not active: + continue + target_active = True + target_features.update(target_resolution.features_enabled[triple]) + for dep_name, features in target_resolution.deferred_build_dep_features[triple].items(): + existing = deferred_build_dep_features.get(dep_name) + if existing == None: + existing = set() + deferred_build_dep_features[dep_name] = existing + existing.update(features) + + if not target_active: + continue + + for dep in exec_resolution.possible_deps: + if dep.get("kind", "normal") != "build": + continue + + bazel_target = dep.get("bazel_target") + if not bazel_target: + continue + + dep_name = dep["name"] + prefixed_dep_alias = "dep:" + dep_name + if dep.get("optional", False) and dep_name not in target_features and prefixed_dep_alias not in target_features: + continue + + dep_resolution = dep["feature_resolutions"] + for exec_triple in dep["target"]: + if not _dep_target_matches_triple(dep, exec_triple, target_features, exec_cfg_attrs_by_triple): + continue + + target_resolution.build_deps[exec_triple].add(bazel_target) + if "package" in dep: + target_resolution.aliases[bazel_target] = dep_name.replace("-", "_") + + if not dep_resolution.active[exec_triple]: + dep_resolution.active[exec_triple] = True + + dep_features = dep.get("features", []) + dep_resolution.features_enabled[exec_triple].update(dep_features) + dep_resolution.features_enabled[exec_triple].update(deferred_build_dep_features.get(dep_name, [])) diff --git a/rs/private/rustc_src_repository.bzl b/rs/private/rustc_src_repository.bzl index b67853fd..ceadc00e 100644 --- a/rs/private/rustc_src_repository.bzl +++ b/rs/private/rustc_src_repository.bzl @@ -183,7 +183,7 @@ def _cargo_build_values(rctx, bazel_package, workspace_cargo_toml, target_name): def _render_crate_build_file(source_root, crate_attr, values, bazel_metadata): return """\ load("@rules_rs//rs:rust_crate.bzl", "rust_crate") -load("//{source_root}:defs.bzl", "RESOLVED_PLATFORMS") +load("//{source_root}:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") {srcs_filegroup}{rust_crate_call}{package_metadata_bazel_additive_build_file_content}""".format( source_root = source_root, @@ -432,7 +432,10 @@ alias( rustc_srcs.add(_target_label(bazel_package, "srcs")) _prune_rustc_src(rctx, source_root) - rctx.file(paths.join(source_root, "defs.bzl"), "RESOLVED_PLATFORMS = []") + rctx.file( + paths.join(source_root, "defs.bzl"), + "RESOLVED_EXEC_PLATFORMS = []\nRESOLVED_PLATFORMS = []\nRESOLVED_TARGET_AND_EXEC_PLATFORMS = []", + ) return sorted(rustc_srcs) rustc_src_repository = repository_rule( diff --git a/rs/rules_rust.bzl b/rs/rules_rust.bzl index 7caba377..b4f4596a 100644 --- a/rs/rules_rust.bzl +++ b/rs/rules_rust.bzl @@ -31,9 +31,9 @@ def _rules_rust_impl(mctx): http_archive( name = "rules_rust", - integrity = "sha256-gf19wNwRaWAqlUwFMLUfQq51YKGtSc7zU5TpXxGYfhc=", - strip_prefix = "rules_rust-0a4149d8a1af5f5881e73f7e2d5ff490b633fea4", - url = "https://github.com/hermeticbuild/rules_rust/archive/0a4149d8a1af5f5881e73f7e2d5ff490b633fea4.tar.gz", + integrity = "sha256-MyZ49sySXriUUCZjcHpHHvMtHBVs35IfSfBIOzp5i1I=", + strip_prefix = "rules_rust-7837073014b566291368027aa116cd03fe108049", + url = "https://github.com/hermeticbuild/rules_rust/archive/7837073014b566291368027aa116cd03fe108049.tar.gz", patches = patches, patch_strip = strip, ) diff --git a/rs/rust_crate.bzl b/rs/rust_crate.bzl index 8af42ef0..a6ac51e4 100644 --- a/rs/rust_crate.bzl +++ b/rs/rust_crate.bzl @@ -14,10 +14,9 @@ def _platform(triple, use_legacy_rules_rust_platforms): return "@rules_rust//rust/platform:" + triple.replace("-musl", "-gnu").replace("-gnullvm", "-msvc") return "@rules_rs//rs/platforms/config:" + triple -def rust_crate( +def _rust_crate_impl( name, crate_name, - purl, version, aliases, deps, @@ -30,8 +29,10 @@ def rust_crate( rustc_flags, tags, target_compatible_with, + package_metadata_name, links, build_script, + build_script_name, build_script_data, build_deps, build_script_env, @@ -46,12 +47,6 @@ def rust_crate( extra_compile_data = [], rustc_env = {}, skip_deps_verification = False): - package_metadata( - name = name + "_package_metadata", - purl = purl, - visibility = ["//visibility:public"], - ) - compile_data = native.glob( include = ["**"], exclude = [ @@ -74,7 +69,7 @@ def rust_crate( ) default_tags = [ - "crate-name=" + name, + "crate-name=" + crate_name, "manual", "noclippy", "norustfmt", @@ -114,29 +109,29 @@ def rust_crate( # The build script is cfg-exec, but the features must be selected according to the target. # Only stamp out one target per triple when there are per-platform feature deltas. for triple in triples: - build_script_name = "_bs_" + triple - branches[_platform(triple, use_legacy_rules_rust_platforms)] = build_script_name + triple_build_script_name = build_script_name + "_" + triple + branches[_platform(triple, use_legacy_rules_rust_platforms)] = triple_build_script_name cargo_build_script( - name = build_script_name, + name = triple_build_script_name, crate_features = crate_features + conditional_crate_features.get(triple, []), **build_script_kwargs ) native.alias( - name = "_bs", + name = build_script_name, actual = select(branches), tags = build_script_target_tags, ) else: cargo_build_script( - name = "_bs", + name = build_script_name, crate_features = crate_features, **build_script_kwargs ) - maybe_build_script = ["_bs"] + maybe_build_script = [build_script_name] else: maybe_build_script = [] @@ -186,7 +181,7 @@ def rust_crate( rustc_flags = rustc_flags + ["--cap-lints=allow"], tags = crate_tags, target_compatible_with = target_compatible_with, - package_metadata = [name + "_package_metadata"], + package_metadata = [package_metadata_name], skip_deps_verification = skip_deps_verification, visibility = ["//visibility:public"], skip_per_crate_rustc_flags = True, @@ -217,3 +212,139 @@ def rust_crate( version = version, visibility = ["//visibility:public"], ) + +def rust_crate( + name, + crate_name, + purl, + version, + aliases, + exec_aliases, + deps, + exec_deps, + data, + crate_features, + triples, + conditional_crate_features, + exec_crate_features, + exec_triples, + exec_conditional_crate_features, + crate_root, + edition, + rustc_flags, + tags, + target_compatible_with, + exec_target_compatible_with, + target_and_exec_compatible_with, + links, + build_script, + build_script_data, + build_deps, + exec_build_deps, + build_script_env, + allow_build_script_to_detect_nonhermetic_paths, + build_script_toolchains, + build_script_tools, + build_script_tags, + is_proc_macro, + has_lib, + binaries, + use_legacy_rules_rust_platforms, + resolution_kind, + extra_compile_data = [], + rustc_env = {}, + skip_deps_verification = False): + package_metadata_name = name + "_package_metadata" + package_metadata( + name = package_metadata_name, + purl = purl, + visibility = ["//visibility:public"], + ) + + crate_name = crate_name or name.replace("-", "_") + common = dict( + crate_name = crate_name, + version = version, + data = data, + crate_root = crate_root, + edition = edition, + rustc_flags = rustc_flags, + tags = tags, + package_metadata_name = package_metadata_name, + links = links, + build_script = build_script, + build_script_data = build_script_data, + build_script_env = build_script_env, + allow_build_script_to_detect_nonhermetic_paths = allow_build_script_to_detect_nonhermetic_paths, + build_script_toolchains = build_script_toolchains, + build_script_tools = build_script_tools, + build_script_tags = build_script_tags, + is_proc_macro = is_proc_macro, + has_lib = has_lib, + use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, + extra_compile_data = extra_compile_data, + rustc_env = rustc_env, + skip_deps_verification = skip_deps_verification, + ) + + if resolution_kind == "split": + crate_variants = [ + dict( + name = name + "_target", + aliases = aliases, + deps = deps, + crate_features = crate_features, + triples = triples, + conditional_crate_features = conditional_crate_features, + target_compatible_with = target_compatible_with, + build_script_name = "_bs_target", + build_deps = build_deps, + binaries = binaries, + ), + dict( + name = name + "_exec", + aliases = exec_aliases, + deps = exec_deps, + crate_features = exec_crate_features, + triples = exec_triples, + conditional_crate_features = exec_conditional_crate_features, + target_compatible_with = exec_target_compatible_with, + build_script_name = "_bs_exec", + build_deps = exec_build_deps, + binaries = {}, + ), + ] + else: + compatibility = { + "target": target_compatible_with, + "exec": exec_target_compatible_with, + "target_and_exec": target_and_exec_compatible_with, + }[resolution_kind] + crate_variants = [dict( + name = name, + aliases = aliases, + deps = deps, + crate_features = crate_features, + triples = triples, + conditional_crate_features = conditional_crate_features, + target_compatible_with = compatibility, + build_script_name = "_bs", + build_deps = build_deps, + binaries = {} if resolution_kind == "exec" else binaries, + )] + + for crate_variant in crate_variants: + _rust_crate_impl(**(common | crate_variant)) + + if resolution_kind != "split": + return + + native.alias( + name = name, + actual = select({ + "@rules_rust//cargo/settings:use_exec_features_enabled": name + "_exec", + "//conditions:default": name + "_target", + }), + tags = ["crate-name=" + crate_name] + tags, + visibility = ["//visibility:public"], + ) diff --git a/test/MODULE.bazel b/test/MODULE.bazel index 37fcd9ec..5aea9601 100644 --- a/test/MODULE.bazel +++ b/test/MODULE.bazel @@ -152,6 +152,7 @@ TESTS = [ "build_script_env_select", "build_script_runtime_env", "build_dep_features", + "build_exec_feature_split", "cfg_feature_target_dep", "empty_workspace", "feature_name_overrides_implicit_dep", diff --git a/test/MODULE.bazel.lock b/test/MODULE.bazel.lock index 4c39a38a..fc093cfa 100644 --- a/test/MODULE.bazel.lock +++ b/test/MODULE.bazel.lock @@ -2435,6 +2435,8 @@ "paste_1.0.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"paste-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", "pastey_0.1.1": "{\"dependencies\":[],\"features\":{}}", "pastey_0.2.3": "{\"dependencies\":[],\"features\":{}}", + "path+build_exec_feature_split/vendor/consumer_feature-split-consumer": "{\"dependencies\":[{\"default_features\":true,\"features\":[\"target_feature\"],\"name\":\"feature-split-shared\",\"optional\":false},{\"default_features\":true,\"features\":[\"exec_feature\"],\"kind\":\"build\",\"name\":\"feature-split-shared\",\"optional\":false}],\"features\":{},\"strip_prefix\":\"\"}", + "path+build_exec_feature_split/vendor/shared_feature-split-shared": "{\"dependencies\":[],\"features\":{\"exec_feature\":[],\"target_feature\":[]},\"strip_prefix\":\"\"}", "path+cfg_feature_target_dep/inner_inner": "{\"dependencies\":[{\"name\":\"itoa\",\"req\":\"1.0.15\",\"target\":\"cfg(feature = \\\"with_itoa\\\")\"}],\"features\":{\"with_itoa\":[]},\"strip_prefix\":\"\"}", "path+first_party_feature_propagation/../first_party_feature_propagation.deps/dep_leaf_dep_leaf": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"itoa\",\"optional\":true,\"req\":\"1.0.15\"},{\"default_features\":true,\"features\":[],\"name\":\"ryu\",\"optional\":true,\"req\":\"1.0.20\"}],\"features\":{\"default\":[],\"default_extra\":[\"dep:itoa\"],\"with_extra\":[\"dep:ryu\"]},\"strip_prefix\":\"\"}", "path+vendored_crate_override/vendor/itoa_itoa": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"no-panic\",\"optional\":true,\"req\":\"0.1\"}],\"features\":{},\"strip_prefix\":\"\"}", diff --git a/test/build_exec_feature_split/Cargo.lock b/test/build_exec_feature_split/Cargo.lock new file mode 100644 index 00000000..048c9ae0 --- /dev/null +++ b/test/build_exec_feature_split/Cargo.lock @@ -0,0 +1,21 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "build-exec-feature-split" +version = "0.1.0" +dependencies = [ + "feature-split-consumer", +] + +[[package]] +name = "feature-split-consumer" +version = "0.1.0" +dependencies = [ + "feature-split-shared", +] + +[[package]] +name = "feature-split-shared" +version = "0.1.0" diff --git a/test/build_exec_feature_split/Cargo.toml b/test/build_exec_feature_split/Cargo.toml new file mode 100644 index 00000000..943f9606 --- /dev/null +++ b/test/build_exec_feature_split/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "build-exec-feature-split" +version = "0.1.0" +edition = "2024" + +[workspace] +resolver = "2" +exclude = ["vendor/consumer", "vendor/shared"] + +[dependencies] +feature-split-consumer = { path = "vendor/consumer" } + +[patch.crates-io] +feature-split-shared = { path = "vendor/shared" } diff --git a/test/build_exec_feature_split/src/lib.rs b/test/build_exec_feature_split/src/lib.rs new file mode 100644 index 00000000..cc90e757 --- /dev/null +++ b/test/build_exec_feature_split/src/lib.rs @@ -0,0 +1,3 @@ +pub fn run() { + feature_split_consumer::run(); +} diff --git a/test/build_exec_feature_split/vendor/consumer/Cargo.toml b/test/build_exec_feature_split/vendor/consumer/Cargo.toml new file mode 100644 index 00000000..fb3bc49a --- /dev/null +++ b/test/build_exec_feature_split/vendor/consumer/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "feature-split-consumer" +version = "0.1.0" +edition = "2024" + +[dependencies] +# The target crate must not receive exec_feature from build-dependencies. +feature-split-shared = { path = "../shared", features = ["target_feature"] } + +[build-dependencies] +# The build script must not receive target_feature from dependencies. +feature-split-shared = { path = "../shared", features = ["exec_feature"] } diff --git a/test/build_exec_feature_split/vendor/consumer/build.rs b/test/build_exec_feature_split/vendor/consumer/build.rs new file mode 100644 index 00000000..6a8e211f --- /dev/null +++ b/test/build_exec_feature_split/vendor/consumer/build.rs @@ -0,0 +1,3 @@ +fn main() { + feature_split_shared::exec_only(); +} diff --git a/test/build_exec_feature_split/vendor/consumer/src/lib.rs b/test/build_exec_feature_split/vendor/consumer/src/lib.rs new file mode 100644 index 00000000..c7972580 --- /dev/null +++ b/test/build_exec_feature_split/vendor/consumer/src/lib.rs @@ -0,0 +1,3 @@ +pub fn run() { + feature_split_shared::target_only(); +} diff --git a/test/build_exec_feature_split/vendor/shared/Cargo.toml b/test/build_exec_feature_split/vendor/shared/Cargo.toml new file mode 100644 index 00000000..6cd26e08 --- /dev/null +++ b/test/build_exec_feature_split/vendor/shared/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "feature-split-shared" +version = "0.1.0" +edition = "2024" + +[features] +exec_feature = [] +target_feature = [] diff --git a/test/build_exec_feature_split/vendor/shared/src/lib.rs b/test/build_exec_feature_split/vendor/shared/src/lib.rs new file mode 100644 index 00000000..799e33ec --- /dev/null +++ b/test/build_exec_feature_split/vendor/shared/src/lib.rs @@ -0,0 +1,8 @@ +#[cfg(all(feature = "exec_feature", feature = "target_feature"))] +compile_error!("target and exec features were unified"); + +#[cfg(feature = "exec_feature")] +pub fn exec_only() {} + +#[cfg(feature = "target_feature")] +pub fn target_only() {} From e7bed041cae8de1351169b5d8d043cff2430b0e6 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 17:09:29 -0400 Subject: [PATCH 02/11] Simplify target and exec crate generation --- rs/extensions.bzl | 3 +- rs/private/cargo_workspace_graph.bzl | 48 ++-- rs/private/cargo_workspace_graph_test.bzl | 6 +- rs/private/git_crate_metadata_repository.bzl | 2 +- rs/private/repository_utils.bzl | 226 +++++++++++-------- rs/private/rustc_src_repository.bzl | 2 +- rs/rust_crate.bzl | 165 ++------------ 7 files changed, 181 insertions(+), 271 deletions(-) diff --git a/rs/extensions.bzl b/rs/extensions.bzl index 50942404..85da01b9 100644 --- a/rs/extensions.bzl +++ b/rs/extensions.bzl @@ -255,10 +255,9 @@ def _generate_hub_and_spokes( packages, facts_by_fq_crate, platform_triples, - exec_platform_triples = SUPPORTED_EXEC_TRIPLES, + build_dep_triples = SUPPORTED_EXEC_TRIPLES, ) feature_resolutions_by_fq_crate = resolved_facts.feature_resolutions_by_fq_crate - exec_feature_resolutions_by_fq_crate = resolved_facts.exec_feature_resolutions_by_fq_crate versions_by_name = resolved_facts.versions_by_name # Only files in the current Bazel workspace can/should be watched, so check where our manifests are located. diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index c16302b3..0a28f974 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -395,10 +395,9 @@ def _resolve_packages( package_info_by_fq_crate, platform_triples, dep_converter = None, - exec_platform_triples = [], + build_dep_triples = None, skip_internal_rustc_placeholder_crates = True): feature_resolutions_by_fq_crate = {} - exec_feature_resolutions_by_fq_crate = {} versions_by_name = {} for package_index in range(len(packages)): @@ -416,34 +415,22 @@ def _resolve_packages( platform_triples, dep_converter, skip_internal_rustc_placeholder_crates, - build_dep_triples = exec_platform_triples or platform_triples, + build_dep_triples = build_dep_triples, ) package["feature_resolutions"] = feature_resolutions feature_resolutions_by_fq_crate[fq] = feature_resolutions - if exec_platform_triples: - exec_feature_resolutions = _new_package_feature_resolution( - package_index, - package_info, - exec_platform_triples, - dep_converter, - skip_internal_rustc_placeholder_crates, - ) - package["exec_feature_resolutions"] = exec_feature_resolutions - exec_feature_resolutions_by_fq_crate[fq] = exec_feature_resolutions - return struct( - exec_feature_resolutions_by_fq_crate = exec_feature_resolutions_by_fq_crate, feature_resolutions_by_fq_crate = feature_resolutions_by_fq_crate, versions_by_name = versions_by_name, ) -def resolve_package_facts(packages, facts_by_fq_crate, platform_triples, exec_platform_triples = [], skip_internal_rustc_placeholder_crates = True): +def resolve_package_facts(packages, facts_by_fq_crate, platform_triples, build_dep_triples = None, skip_internal_rustc_placeholder_crates = True): return _resolve_packages( packages, facts_by_fq_crate, platform_triples, - exec_platform_triples = exec_platform_triples, + build_dep_triples = build_dep_triples, skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, ) @@ -556,11 +543,6 @@ def resolve_cargo_workspace_members( resolver_versions_by_name = {name: versions[:] for name, versions in versions_by_name.items()} workspace_members_by_key = {(package["name"], package["version"]): package for package in workspace_members} resolver_packages = packages[:] - exec_feature_resolutions_by_fq_crate = { - fq_crate(package["name"], package["version"]): package["exec_feature_resolutions"] - for package in packages - if split_exec_resolution - } for package in cargo_metadata["packages"]: name = package["name"] version = package["version"] @@ -591,18 +573,20 @@ def resolve_cargo_workspace_members( resolver_package["feature_resolutions"] = feature_resolutions feature_resolutions_by_fq_crate[fq_crate(name, version)] = feature_resolutions - if split_exec_resolution: - exec_feature_resolutions = _new_package_feature_resolution( - package_index, - package, + resolver_packages.append(resolver_package) + + exec_feature_resolutions_by_fq_crate = {} + if split_exec_resolution: + for package in resolver_packages: + target_resolution = package["feature_resolutions"] + exec_resolution = _new_feature_resolution( + target_resolution.package_index, + [dict(dep) for dep in target_resolution.possible_deps], + target_resolution.possible_features, exec_platform_triples, - cargo_metadata_dep_to_dep_dict, - skip_internal_rustc_placeholder_crates, ) - resolver_package["exec_feature_resolutions"] = exec_feature_resolutions - exec_feature_resolutions_by_fq_crate[fq_crate(name, version)] = exec_feature_resolutions - - resolver_packages.append(resolver_package) + package["exec_feature_resolutions"] = exec_resolution + exec_feature_resolutions_by_fq_crate[fq_crate(package["name"], package["version"])] = exec_resolution exec_resolver_packages = [ dict(package, feature_resolutions = package["exec_feature_resolutions"]) diff --git a/rs/private/cargo_workspace_graph_test.bzl b/rs/private/cargo_workspace_graph_test.bzl index d6fc2248..591997b0 100644 --- a/rs/private/cargo_workspace_graph_test.bzl +++ b/rs/private/cargo_workspace_graph_test.bzl @@ -256,16 +256,14 @@ def _resolve_package_facts_attaches_feature_resolutions_impl(ctx): }, }, ["x86_64-unknown-linux-gnu"], - exec_platform_triples = ["aarch64-apple-darwin"], + build_dep_triples = ["aarch64-apple-darwin"], ) asserts.equals(env, {"serde": ["1.0.0"]}, got.versions_by_name) asserts.true(env, "feature_resolutions" in packages[0]) - asserts.true(env, "exec_feature_resolutions" in packages[0]) asserts.equals(env, ["serde-1.0.0"], got.feature_resolutions_by_fq_crate.keys()) - asserts.equals(env, ["serde-1.0.0"], got.exec_feature_resolutions_by_fq_crate.keys()) asserts.equals(env, ["x86_64-unknown-linux-gnu"], got.feature_resolutions_by_fq_crate["serde-1.0.0"].active.keys()) - asserts.equals(env, ["aarch64-apple-darwin"], got.exec_feature_resolutions_by_fq_crate["serde-1.0.0"].active.keys()) + asserts.equals(env, ["aarch64-apple-darwin"], got.feature_resolutions_by_fq_crate["serde-1.0.0"].build_deps.keys()) return unittest.end(env) resolve_package_facts_attaches_feature_resolutions_test = unittest.make(_resolve_package_facts_attaches_feature_resolutions_impl) diff --git a/rs/private/git_crate_metadata_repository.bzl b/rs/private/git_crate_metadata_repository.bzl index e76a8b96..7fa2d67a 100644 --- a/rs/private/git_crate_metadata_repository.bzl +++ b/rs/private/git_crate_metadata_repository.bzl @@ -2,7 +2,7 @@ load(":repository_utils.bzl", "render_rust_crate_call", "rust_crate_attrs") def _git_crate_metadata_repository_implementation(rctx): rctx.file("crate.bzl", """\ -load("@rules_rs//rs:rust_crate.bzl", "rust_crate") +load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") load("@{hub_name}//:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") def crate( diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index e3ff055f..3014b673 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -165,47 +165,33 @@ def cargo_build_file_values(rctx, cargo_toml, gen_binaries, package_path = "", g ) _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( -{indent} name = {name}, -{indent} crate_name = {crate_name}, +{indent} name = {name}{name_suffix}, +{indent} crate_name = {crate_name} or {name}.replace("-", "_"), {indent} purl = {purl}, {indent} version = {version}, {indent} aliases = {{ {indent} {aliases} {indent} }}, -{indent} exec_aliases = {{ -{indent} {exec_aliases} -{indent} }}, {indent} deps = [ {indent} {deps} {indent} ]{extra_deps}{conditional_deps}, -{indent} exec_deps = [ -{indent} {exec_deps} -{indent} ]{extra_deps}{exec_conditional_deps}, {indent} data = [ {indent} {data} {indent} ], {extra_compile_data_attr}{indent} crate_features = {crate_features}, {indent} triples = {triples}, {indent} conditional_crate_features = {conditional_crate_features}, -{indent} exec_crate_features = {exec_crate_features}, -{indent} exec_triples = {exec_triples}, -{indent} exec_conditional_crate_features = {exec_conditional_crate_features}, {indent} crate_root = {crate_root}, {indent} edition = {edition}, {rustc_env_attr}{indent} rustc_flags = {rustc_flags}{conditional_rustc_flags}, {indent} tags = {tags}, -{indent} target_compatible_with = RESOLVED_PLATFORMS, -{indent} exec_target_compatible_with = RESOLVED_EXEC_PLATFORMS, -{indent} target_and_exec_compatible_with = RESOLVED_TARGET_AND_EXEC_PLATFORMS, +{indent} target_compatible_with = {target_compatible_with}, {indent} links = {links}, {indent} build_script = {build_script}, {indent} build_script_data = {build_script_data}{conditional_build_script_data}, {indent} build_deps = [ {indent} {build_deps} {indent} ]{conditional_build_deps}, -{indent} exec_build_deps = [ -{indent} {exec_build_deps} -{indent} ]{exec_conditional_build_deps}, {indent} build_script_env = {build_script_env}{conditional_build_script_env}, {indent} allow_build_script_to_detect_nonhermetic_paths = {allow_build_script_to_detect_nonhermetic_paths}, {indent} build_script_toolchains = {build_script_toolchains}, @@ -215,85 +201,47 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( {indent} has_lib = {has_lib}, {indent} binaries = {binaries}, {indent} use_legacy_rules_rust_platforms = {use_legacy_rules_rust_platforms}, -{indent} resolution_kind = {resolution_kind}, +{indent} build_script_name = {build_script_name}, {skip_deps_verification_attr}{indent}) """ -def _merge_resolution_selects(target_select, exec_select): - merged = dict(target_select) - differs = False - for triple, exec_items in exec_select.items(): - target_items = target_select.get(triple) - if target_items == None: - merged[triple] = exec_items - elif sorted(target_items) != sorted(exec_items): - differs = True - return merged, differs - -def _merge_aliases(target_aliases, exec_aliases): - merged = dict(target_aliases) - differs = False - for dependency, exec_alias in exec_aliases.items(): - target_alias = target_aliases.get(dependency) - if target_alias != None and target_alias != exec_alias: - differs = True - else: - merged[dependency] = exec_alias - return merged, differs +_RUST_CRATE_TARGET_EXEC_ALIAS = """{indent}rust_crate_target_exec_alias( +{indent} name = {name}, +{indent} crate_name = {crate_name}, +{indent} tags = {tags}, +{indent}) +""" -def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): +def _render_rust_crate_call( + attr, + values, + aliases, + crate_features_select, + deps_select, + build_deps_select, + target_compatible_with, + name_suffix, + build_script_name, + binaries, + bazel_metadata, + extra_deps, + indent, + skip_deps_verification): # We keep conditional_crate_features unrendered here because it must be treated specially for build scripts. # See `rust_crate.bzl` for details. - if not attr.target_active and attr.exec_active: - resolution_kind = "exec" - crate_features_select = attr.exec_crate_features_select - deps_select = attr.exec_deps_select - build_deps_select = attr.exec_build_script_deps_select - aliases = attr.exec_aliases - elif not attr.exec_active: - resolution_kind = "target" - crate_features_select = attr.crate_features_select - deps_select = attr.deps_select - build_deps_select = attr.build_script_deps_select - aliases = attr.aliases - else: - crate_features_select, crate_features_differ = _merge_resolution_selects( - attr.crate_features_select, - attr.exec_crate_features_select, - ) - deps_select, deps_differ = _merge_resolution_selects(attr.deps_select, attr.exec_deps_select) - build_deps_select, build_deps_differ = _merge_resolution_selects( - attr.build_script_deps_select, - attr.exec_build_script_deps_select, - ) - aliases, aliases_differ = _merge_aliases(attr.aliases, attr.exec_aliases) - differs = crate_features_differ or deps_differ or build_deps_differ or aliases_differ - resolution_kind = "split" if differs else "target_and_exec" - - if differs: - crate_features_select = attr.crate_features_select - deps_select = attr.deps_select - build_deps_select = attr.build_script_deps_select - aliases = attr.aliases - crate_features, conditional_crate_features = compute_select( _exclude_deps_from_features(attr.crate_features), {platform: _exclude_deps_from_features(features) for platform, features in crate_features_select.items()}, ) - exec_crate_features, exec_conditional_crate_features = compute_select( - _exclude_deps_from_features(attr.crate_features), - {platform: _exclude_deps_from_features(features) for platform, features in attr.exec_crate_features_select.items()}, - ) use_legacy_rules_rust_platforms = attr.use_legacy_rules_rust_platforms build_deps, conditional_build_deps = render_select(attr.build_script_deps, build_deps_select, use_legacy_rules_rust_platforms) - exec_build_deps, exec_conditional_build_deps = render_select(attr.build_script_deps, attr.exec_build_script_deps_select, use_legacy_rules_rust_platforms) build_script_data, conditional_build_script_data = render_select(attr.build_script_data, attr.build_script_data_select, use_legacy_rules_rust_platforms) build_script_tools, conditional_build_script_tools = render_select(attr.build_script_tools, attr.build_script_tools_select, use_legacy_rules_rust_platforms) rustc_flags, conditional_rustc_flags = render_select(attr.rustc_flags, attr.rustc_flags_select, use_legacy_rules_rust_platforms) deps, conditional_deps = render_select(attr.deps + bazel_metadata.get("deps", []), deps_select, use_legacy_rules_rust_platforms) - exec_deps, exec_conditional_deps = render_select(attr.deps + bazel_metadata.get("deps", []), attr.exec_deps_select, use_legacy_rules_rust_platforms) conditional_build_script_env = render_select_build_script_env(attr.build_script_env_select, use_legacy_rules_rust_platforms) + list_indent = ",\n%s " % indent extra_deps = " + " + extra_deps if extra_deps else "" extra_compile_data = getattr(attr, "extra_compile_data", []) @@ -313,38 +261,32 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i return _RUST_CRATE_MACRO_CALL.format( indent = indent, name = values["name"], + name_suffix = " + " + repr(name_suffix) if name_suffix else "", crate_name = values["crate_name"], purl = values["purl"], version = values["version"], aliases = list_indent.join(['"%s": "%s"' % kv for kv in aliases.items()]), - exec_aliases = list_indent.join(['"%s": "%s"' % kv for kv in attr.exec_aliases.items()]), deps = list_indent.join(['"%s"' % d for d in sorted(deps)]), - exec_deps = list_indent.join(['"%s"' % d for d in sorted(exec_deps)]), extra_deps = extra_deps, conditional_deps = " + " + conditional_deps if conditional_deps else "", - exec_conditional_deps = " + " + exec_conditional_deps if exec_conditional_deps else "", data = list_indent.join(['"%s"' % str(d) for d in attr.data]), extra_compile_data_attr = extra_compile_data_attr, crate_features = repr(sorted(crate_features)), triples = repr(crate_features_select.keys()), conditional_crate_features = repr(conditional_crate_features), - exec_crate_features = repr(sorted(exec_crate_features)), - exec_triples = repr(attr.exec_crate_features_select.keys()), - exec_conditional_crate_features = repr(exec_conditional_crate_features), crate_root = values["crate_root"], edition = values["edition"], rustc_env_attr = rustc_env_attr, rustc_flags = repr(rustc_flags), conditional_rustc_flags = " + " + conditional_rustc_flags if conditional_rustc_flags else "", tags = repr(attr.crate_tags), + target_compatible_with = target_compatible_with, links = values["links"], build_script = values["build_script"], build_script_data = repr(build_script_data), conditional_build_script_data = " + " + conditional_build_script_data if conditional_build_script_data else "", build_deps = list_indent.join(['"%s"' % d for d in sorted(build_deps)]), conditional_build_deps = " + " + conditional_build_deps if conditional_build_deps else "", - exec_build_deps = list_indent.join(['"%s"' % d for d in sorted(exec_build_deps)]), - exec_conditional_build_deps = " + " + exec_conditional_build_deps if exec_conditional_build_deps else "", build_script_env = repr(attr.build_script_env), conditional_build_script_env = " | " + conditional_build_script_env if conditional_build_script_env else "", allow_build_script_to_detect_nonhermetic_paths = repr(attr.allow_build_script_to_detect_nonhermetic_paths), @@ -354,12 +296,118 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i build_script_tags = repr(attr.build_script_tags), is_proc_macro = values["is_proc_macro"], has_lib = values["has_lib"], - binaries = values["binaries"], + binaries = binaries, use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, - resolution_kind = repr(resolution_kind), + build_script_name = repr(build_script_name), skip_deps_verification_attr = skip_deps_verification_attr, ) +def _merge_resolution_selects(target_select, exec_select): + merged = dict(target_select) + differs = False + for triple, exec_items in exec_select.items(): + target_items = target_select.get(triple) + if target_items == None: + merged[triple] = exec_items + elif sorted(target_items) != sorted(exec_items): + differs = True + return merged, differs + +def _merge_aliases(target_aliases, exec_aliases): + merged = dict(target_aliases) + differs = False + for dependency, exec_alias in exec_aliases.items(): + target_alias = target_aliases.get(dependency) + if target_alias != None and target_alias != exec_alias: + differs = True + else: + merged[dependency] = exec_alias + return merged, differs + +def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): + aliases = attr.aliases + crate_features_select = attr.crate_features_select + deps_select = attr.deps_select + build_deps_select = attr.build_script_deps_select + target_compatible_with = "RESOLVED_PLATFORMS" + binaries = values["binaries"] + + if not attr.target_active and attr.exec_active: + aliases = attr.exec_aliases + crate_features_select = attr.exec_crate_features_select + deps_select = attr.exec_deps_select + build_deps_select = attr.exec_build_script_deps_select + target_compatible_with = "RESOLVED_EXEC_PLATFORMS" + binaries = "{}" + elif attr.exec_active: + merged_crate_features_select, crate_features_differ = _merge_resolution_selects(crate_features_select, attr.exec_crate_features_select) + merged_deps_select, deps_differ = _merge_resolution_selects(deps_select, attr.exec_deps_select) + merged_build_deps_select, build_deps_differ = _merge_resolution_selects(build_deps_select, attr.exec_build_script_deps_select) + merged_aliases, aliases_differ = _merge_aliases(aliases, attr.exec_aliases) + if crate_features_differ or deps_differ or build_deps_differ or aliases_differ: + target_call = _render_rust_crate_call( + attr, + values, + aliases, + crate_features_select, + deps_select, + build_deps_select, + "RESOLVED_PLATFORMS", + "_target", + "_bs_target", + binaries, + bazel_metadata, + extra_deps, + indent, + skip_deps_verification, + ) + exec_call = _render_rust_crate_call( + attr, + values, + attr.exec_aliases, + attr.exec_crate_features_select, + attr.exec_deps_select, + attr.exec_build_script_deps_select, + "RESOLVED_EXEC_PLATFORMS", + "_exec", + "_bs_exec", + "{}", + bazel_metadata, + extra_deps, + indent, + skip_deps_verification, + ) + alias_call = _RUST_CRATE_TARGET_EXEC_ALIAS.format( + crate_name = values["crate_name"], + indent = indent, + name = values["name"], + tags = repr(attr.crate_tags), + ) + return target_call + "\n" + exec_call + "\n" + alias_call + + aliases = merged_aliases + crate_features_select = merged_crate_features_select + deps_select = merged_deps_select + build_deps_select = merged_build_deps_select + target_compatible_with = "RESOLVED_TARGET_AND_EXEC_PLATFORMS" + + return _render_rust_crate_call( + attr, + values, + aliases, + crate_features_select, + deps_select, + build_deps_select, + target_compatible_with, + "", + "_bs", + binaries, + bazel_metadata, + extra_deps, + indent, + skip_deps_verification, + ) + def render_build_file_content(rctx, attr, values, bazel_metadata = {}): additive_build_file_content = "" if attr.additive_build_file: @@ -368,7 +416,7 @@ def render_build_file_content(rctx, attr, values, bazel_metadata = {}): additive_build_file_content += bazel_metadata.get("additive_build_file_content", "") return """\ -load("@rules_rs//rs:rust_crate.bzl", "rust_crate") +load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") load("@rules_rs//rs:rust_binary.bzl", "rust_binary") load("@{hub_name}//:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") diff --git a/rs/private/rustc_src_repository.bzl b/rs/private/rustc_src_repository.bzl index ceadc00e..e8178517 100644 --- a/rs/private/rustc_src_repository.bzl +++ b/rs/private/rustc_src_repository.bzl @@ -182,7 +182,7 @@ def _cargo_build_values(rctx, bazel_package, workspace_cargo_toml, target_name): def _render_crate_build_file(source_root, crate_attr, values, bazel_metadata): return """\ -load("@rules_rs//rs:rust_crate.bzl", "rust_crate") +load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") load("//{source_root}:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") {srcs_filegroup}{rust_crate_call}{package_metadata_bazel_additive_build_file_content}""".format( diff --git a/rs/rust_crate.bzl b/rs/rust_crate.bzl index a6ac51e4..4dae60f0 100644 --- a/rs/rust_crate.bzl +++ b/rs/rust_crate.bzl @@ -14,9 +14,21 @@ def _platform(triple, use_legacy_rules_rust_platforms): return "@rules_rust//rust/platform:" + triple.replace("-musl", "-gnu").replace("-gnullvm", "-msvc") return "@rules_rs//rs/platforms/config:" + triple -def _rust_crate_impl( +def rust_crate_target_exec_alias(name, crate_name, tags): + native.alias( + name = name, + actual = select({ + "@rules_rust//cargo/settings:use_exec_features_enabled": name + "_exec", + "//conditions:default": name + "_target", + }), + tags = ["crate-name=" + (crate_name or name)] + tags, + visibility = ["//visibility:public"], + ) + +def rust_crate( name, crate_name, + purl, version, aliases, deps, @@ -29,10 +41,8 @@ def _rust_crate_impl( rustc_flags, tags, target_compatible_with, - package_metadata_name, links, build_script, - build_script_name, build_script_data, build_deps, build_script_env, @@ -46,7 +56,14 @@ def _rust_crate_impl( use_legacy_rules_rust_platforms, extra_compile_data = [], rustc_env = {}, - skip_deps_verification = False): + skip_deps_verification = False, + build_script_name = "_bs"): + package_metadata( + name = name + "_package_metadata", + purl = purl, + visibility = ["//visibility:public"], + ) + compile_data = native.glob( include = ["**"], exclude = [ @@ -69,7 +86,7 @@ def _rust_crate_impl( ) default_tags = [ - "crate-name=" + crate_name, + "crate-name=" + (crate_name or name), "manual", "noclippy", "norustfmt", @@ -181,7 +198,7 @@ def _rust_crate_impl( rustc_flags = rustc_flags + ["--cap-lints=allow"], tags = crate_tags, target_compatible_with = target_compatible_with, - package_metadata = [package_metadata_name], + package_metadata = [name + "_package_metadata"], skip_deps_verification = skip_deps_verification, visibility = ["//visibility:public"], skip_per_crate_rustc_flags = True, @@ -212,139 +229,3 @@ def _rust_crate_impl( version = version, visibility = ["//visibility:public"], ) - -def rust_crate( - name, - crate_name, - purl, - version, - aliases, - exec_aliases, - deps, - exec_deps, - data, - crate_features, - triples, - conditional_crate_features, - exec_crate_features, - exec_triples, - exec_conditional_crate_features, - crate_root, - edition, - rustc_flags, - tags, - target_compatible_with, - exec_target_compatible_with, - target_and_exec_compatible_with, - links, - build_script, - build_script_data, - build_deps, - exec_build_deps, - build_script_env, - allow_build_script_to_detect_nonhermetic_paths, - build_script_toolchains, - build_script_tools, - build_script_tags, - is_proc_macro, - has_lib, - binaries, - use_legacy_rules_rust_platforms, - resolution_kind, - extra_compile_data = [], - rustc_env = {}, - skip_deps_verification = False): - package_metadata_name = name + "_package_metadata" - package_metadata( - name = package_metadata_name, - purl = purl, - visibility = ["//visibility:public"], - ) - - crate_name = crate_name or name.replace("-", "_") - common = dict( - crate_name = crate_name, - version = version, - data = data, - crate_root = crate_root, - edition = edition, - rustc_flags = rustc_flags, - tags = tags, - package_metadata_name = package_metadata_name, - links = links, - build_script = build_script, - build_script_data = build_script_data, - build_script_env = build_script_env, - allow_build_script_to_detect_nonhermetic_paths = allow_build_script_to_detect_nonhermetic_paths, - build_script_toolchains = build_script_toolchains, - build_script_tools = build_script_tools, - build_script_tags = build_script_tags, - is_proc_macro = is_proc_macro, - has_lib = has_lib, - use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, - extra_compile_data = extra_compile_data, - rustc_env = rustc_env, - skip_deps_verification = skip_deps_verification, - ) - - if resolution_kind == "split": - crate_variants = [ - dict( - name = name + "_target", - aliases = aliases, - deps = deps, - crate_features = crate_features, - triples = triples, - conditional_crate_features = conditional_crate_features, - target_compatible_with = target_compatible_with, - build_script_name = "_bs_target", - build_deps = build_deps, - binaries = binaries, - ), - dict( - name = name + "_exec", - aliases = exec_aliases, - deps = exec_deps, - crate_features = exec_crate_features, - triples = exec_triples, - conditional_crate_features = exec_conditional_crate_features, - target_compatible_with = exec_target_compatible_with, - build_script_name = "_bs_exec", - build_deps = exec_build_deps, - binaries = {}, - ), - ] - else: - compatibility = { - "target": target_compatible_with, - "exec": exec_target_compatible_with, - "target_and_exec": target_and_exec_compatible_with, - }[resolution_kind] - crate_variants = [dict( - name = name, - aliases = aliases, - deps = deps, - crate_features = crate_features, - triples = triples, - conditional_crate_features = conditional_crate_features, - target_compatible_with = compatibility, - build_script_name = "_bs", - build_deps = build_deps, - binaries = {} if resolution_kind == "exec" else binaries, - )] - - for crate_variant in crate_variants: - _rust_crate_impl(**(common | crate_variant)) - - if resolution_kind != "split": - return - - native.alias( - name = name, - actual = select({ - "@rules_rust//cargo/settings:use_exec_features_enabled": name + "_exec", - "//conditions:default": name + "_target", - }), - tags = ["crate-name=" + crate_name] + tags, - visibility = ["//visibility:public"], - ) From 4d6245613e4410f68313f898fc9eb8ce5f9c5095 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 17:16:03 -0400 Subject: [PATCH 03/11] Simplify exec resolution state --- rs/private/cargo_workspace_graph.bzl | 9 ++----- rs/private/resolver.bzl | 37 ++++++++-------------------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 0a28f974..1995f2f8 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -576,6 +576,7 @@ def resolve_cargo_workspace_members( resolver_packages.append(resolver_package) exec_feature_resolutions_by_fq_crate = {} + exec_resolver_packages = [] if split_exec_resolution: for package in resolver_packages: target_resolution = package["feature_resolutions"] @@ -587,11 +588,7 @@ def resolve_cargo_workspace_members( ) package["exec_feature_resolutions"] = exec_resolution exec_feature_resolutions_by_fq_crate[fq_crate(package["name"], package["version"])] = exec_resolution - - exec_resolver_packages = [ - dict(package, feature_resolutions = package["exec_feature_resolutions"]) - for package in resolver_packages - ] if split_exec_resolution else [] + exec_resolver_packages.append(dict(package, feature_resolutions = exec_resolution)) _resolve_possible_deps( resolver_packages, @@ -752,8 +749,6 @@ def resolve_cargo_workspace_members( return struct( cfg_match_cache = cfg_match_cache, exec_feature_resolutions_by_fq_crate = exec_feature_resolutions_by_fq_crate, - exec_platform_cfg_attrs = exec_platform_cfg_attrs, - exec_platform_cfg_attrs_by_triple = exec_platform_cfg_attrs_by_triple, feature_resolutions_by_fq_crate = feature_resolutions_by_fq_crate, platform_cfg_attrs = platform_cfg_attrs, platform_cfg_attrs_by_triple = platform_cfg_attrs_by_triple, diff --git a/rs/private/resolver.bzl b/rs/private/resolver.bzl index 5813d210..9a21983b 100644 --- a/rs/private/resolver.bzl +++ b/rs/private/resolver.bzl @@ -168,11 +168,7 @@ def _propagate_feature_enablement( if defer_build_dependency: deferred = feature_resolutions.deferred_build_dep_features[triple] - deferred_features = deferred.get(dep_name) - if deferred_features == None: - deferred_features = set() - deferred[dep_name] = deferred_features - deferred_features.add(dep_feature) + deferred.setdefault(dep_name, set()).add(dep_feature) else: dep_feature_resolutions = dep["feature_resolutions"] triple_features = dep_feature_resolutions.features_enabled[triple] @@ -222,33 +218,23 @@ def seed_exec_build_dependencies(packages, exec_cfg_attrs_by_triple): target_features = set() deferred_build_dep_features = {} - target_active = False + if not any(target_resolution.active.values()): + continue + for triple, active in target_resolution.active.items(): if not active: continue - target_active = True target_features.update(target_resolution.features_enabled[triple]) for dep_name, features in target_resolution.deferred_build_dep_features[triple].items(): - existing = deferred_build_dep_features.get(dep_name) - if existing == None: - existing = set() - deferred_build_dep_features[dep_name] = existing - existing.update(features) - - if not target_active: - continue + deferred_build_dep_features.setdefault(dep_name, set()).update(features) for dep in exec_resolution.possible_deps: - if dep.get("kind", "normal") != "build": - continue - - bazel_target = dep.get("bazel_target") - if not bazel_target: + if dep.get("kind", "normal") != "build" or not dep.get("bazel_target"): continue + bazel_target = dep["bazel_target"] dep_name = dep["name"] - prefixed_dep_alias = "dep:" + dep_name - if dep.get("optional", False) and dep_name not in target_features and prefixed_dep_alias not in target_features: + if dep.get("optional", False) and dep_name not in target_features and ("dep:" + dep_name) not in target_features: continue dep_resolution = dep["feature_resolutions"] @@ -260,9 +246,6 @@ def seed_exec_build_dependencies(packages, exec_cfg_attrs_by_triple): if "package" in dep: target_resolution.aliases[bazel_target] = dep_name.replace("-", "_") - if not dep_resolution.active[exec_triple]: - dep_resolution.active[exec_triple] = True - - dep_features = dep.get("features", []) - dep_resolution.features_enabled[exec_triple].update(dep_features) + dep_resolution.active[exec_triple] = True + dep_resolution.features_enabled[exec_triple].update(dep.get("features", [])) dep_resolution.features_enabled[exec_triple].update(deferred_build_dep_features.get(dep_name, [])) From 053ba7d1ef5a221a27b8915c081c741256134759 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 17:18:58 -0400 Subject: [PATCH 04/11] Remove redundant feature resolution helper --- rs/private/cargo_workspace_graph.bzl | 49 ++++++++++++---------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 1995f2f8..6b29b3a2 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -62,7 +62,7 @@ def cfg_match_info_for_target(target, platform_cfg_attrs, cfg_match_cache): cfg_match_cache[target] = match_info return match_info -def _new_feature_resolution(package_index, possible_deps, possible_features, platform_triples, build_dep_triples = None): +def new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples, build_dep_triples = None): if build_dep_triples == None: build_dep_triples = platform_triples return struct( @@ -77,25 +77,6 @@ def _new_feature_resolution(package_index, possible_deps, possible_features, pla possible_features = possible_features, ) -def _new_package_feature_resolution( - package_index, - package_info, - platform_triples, - dep_converter, - skip_internal_rustc_placeholder_crates, - build_dep_triples = None): - return _new_feature_resolution( - package_index, - prepare_possible_deps( - package_info.get("dependencies", []), - converter = dep_converter, - skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, - ), - package_info.get("features", {}), - platform_triples, - build_dep_triples = build_dep_triples, - ) - _INTERNAL_RUSTC_PLACEHOLDER_CRATES = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -409,12 +390,16 @@ def _resolve_packages( add_to_dict(versions_by_name, name, version) package_info = package_info_by_fq_crate[fq] - feature_resolutions = _new_package_feature_resolution( + possible_deps = prepare_possible_deps( + package_info.get("dependencies", []), + converter = dep_converter, + skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, + ) + feature_resolutions = new_feature_resolutions( package_index, - package_info, + possible_deps, + package_info.get("features", {}), platform_triples, - dep_converter, - skip_internal_rustc_placeholder_crates, build_dep_triples = build_dep_triples, ) package["feature_resolutions"] = feature_resolutions @@ -554,6 +539,13 @@ def resolve_cargo_workspace_members( else: resolver_versions_by_name[name] = [version] + possible_features = package.get("features", {}) + possible_deps = prepare_possible_deps( + package.get("dependencies", []), + converter = cargo_metadata_dep_to_dep_dict, + skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, + ) + package_index = len(resolver_packages) lockfile_pkg = workspace_members_by_key.get((name, version), {}) resolver_package = { @@ -562,12 +554,11 @@ def resolve_cargo_workspace_members( "dependencies": lockfile_pkg.get("dependencies", []), } - feature_resolutions = _new_package_feature_resolution( + feature_resolutions = new_feature_resolutions( package_index, - package, + possible_deps, + possible_features, platform_triples, - cargo_metadata_dep_to_dep_dict, - skip_internal_rustc_placeholder_crates, build_dep_triples = exec_platform_triples or platform_triples, ) resolver_package["feature_resolutions"] = feature_resolutions @@ -580,7 +571,7 @@ def resolve_cargo_workspace_members( if split_exec_resolution: for package in resolver_packages: target_resolution = package["feature_resolutions"] - exec_resolution = _new_feature_resolution( + exec_resolution = new_feature_resolutions( target_resolution.package_index, [dict(dep) for dep in target_resolution.possible_deps], target_resolution.possible_features, From a45d0ea2f289ab9dad7850c5b208fb8ba183e75c Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 18:02:59 -0400 Subject: [PATCH 05/11] Derive rust_crate compatibility from triples --- rs/extensions.bzl | 23 ---------- rs/private/git_crate_metadata_repository.bzl | 2 +- rs/private/repository_utils.bzl | 47 +++++++------------- rs/private/rustc_src_repository.bzl | 7 +-- rs/rust_crate.bzl | 6 +++ 5 files changed, 25 insertions(+), 60 deletions(-) diff --git a/rs/extensions.bzl b/rs/extensions.bzl index 85da01b9..941f440d 100644 --- a/rs/extensions.bzl +++ b/rs/extensions.bzl @@ -571,17 +571,6 @@ cargo_lints( if platform not in resolved_platforms: resolved_platforms.append(platform) - resolved_exec_platforms = [] - for triple in SUPPORTED_EXEC_TRIPLES: - platform = platform_label(triple, use_legacy_rules_rust_platforms) - if platform not in resolved_exec_platforms: - resolved_exec_platforms.append(platform) - - resolved_target_and_exec_platforms = resolved_platforms[:] - for platform in resolved_exec_platforms: - if platform not in resolved_target_and_exec_platforms: - resolved_target_and_exec_platforms.append(platform) - defs_bzl_contents = \ """load(":data.bzl", "DEP_DATA") load("@rules_rs//rs/private:all_crate_deps.bzl", _all_crate_deps = "all_crate_deps") @@ -621,21 +610,9 @@ RESOLVED_PLATFORMS = select({{ {target_compatible_with}, "//conditions:default": ["@platforms//:incompatible"], }}) - -RESOLVED_EXEC_PLATFORMS = select({{ - {exec_target_compatible_with}, - "//conditions:default": ["@platforms//:incompatible"], -}}) - -RESOLVED_TARGET_AND_EXEC_PLATFORMS = select({{ - {target_and_exec_compatible_with}, - "//conditions:default": ["@platforms//:incompatible"], -}}) """.format( platforms = render_string_list(resolved_platforms), target_compatible_with = ",\n ".join(['"%s": []' % platform for platform in resolved_platforms]), - exec_target_compatible_with = ",\n ".join(['"%s": []' % platform for platform in resolved_exec_platforms]), - target_and_exec_compatible_with = ",\n ".join(['"%s": []' % platform for platform in resolved_target_and_exec_platforms]), this_repo = repr("@" + hub_name + "//:"), ) diff --git a/rs/private/git_crate_metadata_repository.bzl b/rs/private/git_crate_metadata_repository.bzl index 7fa2d67a..8398864b 100644 --- a/rs/private/git_crate_metadata_repository.bzl +++ b/rs/private/git_crate_metadata_repository.bzl @@ -3,7 +3,7 @@ load(":repository_utils.bzl", "render_rust_crate_call", "rust_crate_attrs") def _git_crate_metadata_repository_implementation(rctx): rctx.file("crate.bzl", """\ load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") -load("@{hub_name}//:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") +load("@{hub_name}//:defs.bzl", "RESOLVED_PLATFORMS") def crate( crate_name, diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index 3014b673..5fb85161 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -302,29 +302,14 @@ def _render_rust_crate_call( skip_deps_verification_attr = skip_deps_verification_attr, ) -def _merge_resolution_selects(target_select, exec_select): - merged = dict(target_select) - differs = False - for triple, exec_items in exec_select.items(): - target_items = target_select.get(triple) - if target_items == None: - merged[triple] = exec_items - elif sorted(target_items) != sorted(exec_items): - differs = True - return merged, differs - -def _merge_aliases(target_aliases, exec_aliases): - merged = dict(target_aliases) - differs = False - for dependency, exec_alias in exec_aliases.items(): - target_alias = target_aliases.get(dependency) - if target_alias != None and target_alias != exec_alias: - differs = True - else: - merged[dependency] = exec_alias - return merged, differs +def _merge_resolution_values(target_values, exec_values): + return target_values | exec_values, any([ + key in target_values and target_values[key] != exec_value + for key, exec_value in exec_values.items() + ]) def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): + exec_active = getattr(attr, "exec_active", False) aliases = attr.aliases crate_features_select = attr.crate_features_select deps_select = attr.deps_select @@ -332,18 +317,18 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i target_compatible_with = "RESOLVED_PLATFORMS" binaries = values["binaries"] - if not attr.target_active and attr.exec_active: + if not getattr(attr, "target_active", True) and exec_active: aliases = attr.exec_aliases crate_features_select = attr.exec_crate_features_select deps_select = attr.exec_deps_select build_deps_select = attr.exec_build_script_deps_select - target_compatible_with = "RESOLVED_EXEC_PLATFORMS" + target_compatible_with = "None" binaries = "{}" - elif attr.exec_active: - merged_crate_features_select, crate_features_differ = _merge_resolution_selects(crate_features_select, attr.exec_crate_features_select) - merged_deps_select, deps_differ = _merge_resolution_selects(deps_select, attr.exec_deps_select) - merged_build_deps_select, build_deps_differ = _merge_resolution_selects(build_deps_select, attr.exec_build_script_deps_select) - merged_aliases, aliases_differ = _merge_aliases(aliases, attr.exec_aliases) + elif exec_active: + merged_crate_features_select, crate_features_differ = _merge_resolution_values(crate_features_select, attr.exec_crate_features_select) + merged_deps_select, deps_differ = _merge_resolution_values(deps_select, attr.exec_deps_select) + merged_build_deps_select, build_deps_differ = _merge_resolution_values(build_deps_select, attr.exec_build_script_deps_select) + merged_aliases, aliases_differ = _merge_resolution_values(aliases, attr.exec_aliases) if crate_features_differ or deps_differ or build_deps_differ or aliases_differ: target_call = _render_rust_crate_call( attr, @@ -368,7 +353,7 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i attr.exec_crate_features_select, attr.exec_deps_select, attr.exec_build_script_deps_select, - "RESOLVED_EXEC_PLATFORMS", + "None", "_exec", "_bs_exec", "{}", @@ -389,7 +374,7 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_features_select = merged_crate_features_select deps_select = merged_deps_select build_deps_select = merged_build_deps_select - target_compatible_with = "RESOLVED_TARGET_AND_EXEC_PLATFORMS" + target_compatible_with = "None" return _render_rust_crate_call( attr, @@ -418,7 +403,7 @@ def render_build_file_content(rctx, attr, values, bazel_metadata = {}): return """\ load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") load("@rules_rs//rs:rust_binary.bzl", "rust_binary") -load("@{hub_name}//:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") +load("@{hub_name}//:defs.bzl", "RESOLVED_PLATFORMS") {rust_crate_call}""".format( hub_name = attr.hub_name, diff --git a/rs/private/rustc_src_repository.bzl b/rs/private/rustc_src_repository.bzl index e8178517..6f5936c4 100644 --- a/rs/private/rustc_src_repository.bzl +++ b/rs/private/rustc_src_repository.bzl @@ -183,7 +183,7 @@ def _cargo_build_values(rctx, bazel_package, workspace_cargo_toml, target_name): def _render_crate_build_file(source_root, crate_attr, values, bazel_metadata): return """\ load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") -load("//{source_root}:defs.bzl", "RESOLVED_EXEC_PLATFORMS", "RESOLVED_PLATFORMS", "RESOLVED_TARGET_AND_EXEC_PLATFORMS") +load("//{source_root}:defs.bzl", "RESOLVED_PLATFORMS") {srcs_filegroup}{rust_crate_call}{package_metadata_bazel_additive_build_file_content}""".format( source_root = source_root, @@ -432,10 +432,7 @@ alias( rustc_srcs.add(_target_label(bazel_package, "srcs")) _prune_rustc_src(rctx, source_root) - rctx.file( - paths.join(source_root, "defs.bzl"), - "RESOLVED_EXEC_PLATFORMS = []\nRESOLVED_PLATFORMS = []\nRESOLVED_TARGET_AND_EXEC_PLATFORMS = []", - ) + rctx.file(paths.join(source_root, "defs.bzl"), "RESOLVED_PLATFORMS = []") return sorted(rustc_srcs) rustc_src_repository = repository_rule( diff --git a/rs/rust_crate.bzl b/rs/rust_crate.bzl index 4dae60f0..01fcf16d 100644 --- a/rs/rust_crate.bzl +++ b/rs/rust_crate.bzl @@ -58,6 +58,12 @@ def rust_crate( rustc_env = {}, skip_deps_verification = False, build_script_name = "_bs"): + if target_compatible_with == None: + target_compatible_with = select({ + _platform(triple, use_legacy_rules_rust_platforms): [] + for triple in triples + } | {"//conditions:default": ["@platforms//:incompatible"]}) + package_metadata( name = name + "_package_metadata", purl = purl, From 70ac8282e882724ce5a833c41c1d798c4bc8b80f Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 18:07:53 -0400 Subject: [PATCH 06/11] Remove redundant resolution state --- rs/extensions.bzl | 4 +-- rs/private/cargo_workspace_graph.bzl | 31 +++++++----------- rs/private/cargo_workspace_graph_test.bzl | 1 - rs/private/repository_utils.bzl | 38 ++++++++++------------- rs/private/resolver.bzl | 26 ++++++---------- 5 files changed, 40 insertions(+), 60 deletions(-) diff --git a/rs/extensions.bzl b/rs/extensions.bzl index 941f440d..7ef07d29 100644 --- a/rs/extensions.bzl +++ b/rs/extensions.bzl @@ -354,8 +354,8 @@ crate.annotation( crate_features = annotation.crate_features, crate_features_select = _select(feature_resolutions.features_enabled), exec_crate_features_select = _select(exec_feature_resolutions.features_enabled), - target_active = any(feature_resolutions.active.values()), - exec_active = any(exec_feature_resolutions.active.values()), + target_active = bool(feature_resolutions.active), + exec_active = bool(exec_feature_resolutions.active), use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, ) diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 6b29b3a2..079be622 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -63,15 +63,14 @@ def cfg_match_info_for_target(target, platform_cfg_attrs, cfg_match_cache): return match_info def new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples, build_dep_triples = None): - if build_dep_triples == None: - build_dep_triples = platform_triples + build_dep_triples = platform_triples if build_dep_triples == None else build_dep_triples return struct( - active = {triple: False for triple in platform_triples}, + active = set(), features_enabled = {triple: set() for triple in platform_triples}, build_deps = {triple: set() for triple in build_dep_triples}, deps = {triple: set() for triple in platform_triples}, aliases = {}, - deferred_build_dep_features = {triple: {} for triple in platform_triples}, + deferred_build_dep_features = {}, package_index = package_index, possible_deps = possible_deps, possible_features = possible_features, @@ -507,18 +506,13 @@ def resolve_cargo_workspace_members( skip_internal_rustc_placeholder_crates = True, watch_manifests = False, use_legacy_rules_rust_platforms = False): - split_exec_resolution = bool(exec_platform_triples) platform_cfg_attrs = [triple_to_cfg_attrs(triple) for triple in platform_triples] - platform_cfg_attrs_by_triple = {} - for cfg_attr in platform_cfg_attrs: - platform_cfg_attrs_by_triple[cfg_attr["_triple"]] = cfg_attr + platform_cfg_attrs_by_triple = {cfg_attr["_triple"]: cfg_attr for cfg_attr in platform_cfg_attrs} cfg_match_cache = {None: struct(matches = platform_triples, uses_feature_cfg = False)} exec_platform_cfg_attrs = [triple_to_cfg_attrs(triple) for triple in exec_platform_triples] - exec_platform_cfg_attrs_by_triple = {} - for cfg_attr in exec_platform_cfg_attrs: - exec_platform_cfg_attrs_by_triple[cfg_attr["_triple"]] = cfg_attr + exec_platform_cfg_attrs_by_triple = {cfg_attr["_triple"]: cfg_attr for cfg_attr in exec_platform_cfg_attrs} exec_cfg_match_cache = {None: struct(matches = exec_platform_triples, uses_feature_cfg = False)} workspace_member_keys = {} @@ -568,7 +562,7 @@ def resolve_cargo_workspace_members( exec_feature_resolutions_by_fq_crate = {} exec_resolver_packages = [] - if split_exec_resolution: + if exec_platform_triples: for package in resolver_packages: target_resolution = package["feature_resolutions"] exec_resolution = new_feature_resolutions( @@ -591,7 +585,7 @@ def resolve_cargo_workspace_members( dep_label_prefix, ) - if split_exec_resolution: + if exec_platform_triples: _resolve_possible_deps( exec_resolver_packages, resolver_versions_by_name, @@ -611,8 +605,7 @@ def resolve_cargo_workspace_members( ctx.watch(package["manifest_path"]) package_feature_resolutions = feature_resolutions_by_fq_crate[fq_crate(package["name"], package["version"])] - for triple in platform_triples: - package_feature_resolutions.active[triple] = True + package_feature_resolutions.active.update(platform_triples) if "default" in package.get("features", {}): for triple in platform_triples: package_feature_resolutions.features_enabled[triple].add("default") @@ -674,7 +667,7 @@ def resolve_cargo_workspace_members( continue if not is_first_party_dep or materialize_workspace_members: workspace_dep_labels_by_triple[triple].add(":" + dep_name) - feature_resolutions.active[triple] = True + feature_resolutions.active.add(triple) feature_resolutions.features_enabled[triple].update(features) for crate, annotation_versions in annotations.items(): @@ -695,7 +688,7 @@ def resolve_cargo_workspace_members( if triple in features_enabled: features_enabled[triple].update(features) - if split_exec_resolution: + if exec_platform_triples: exec_features_enabled = exec_feature_resolutions_by_fq_crate[fq_crate(crate, version)].features_enabled if annotation.crate_features: for triple in exec_platform_triples: @@ -710,10 +703,10 @@ def resolve_cargo_workspace_members( feature_resolutions_by_fq_crate, platform_cfg_attrs_by_triple, debug, - include_build_dependencies = not split_exec_resolution, + include_build_dependencies = not exec_platform_triples, ) - if split_exec_resolution: + if exec_platform_triples: seed_exec_build_dependencies(resolver_packages, exec_platform_cfg_attrs_by_triple) resolve( ctx, diff --git a/rs/private/cargo_workspace_graph_test.bzl b/rs/private/cargo_workspace_graph_test.bzl index 591997b0..c257eb41 100644 --- a/rs/private/cargo_workspace_graph_test.bzl +++ b/rs/private/cargo_workspace_graph_test.bzl @@ -262,7 +262,6 @@ def _resolve_package_facts_attaches_feature_resolutions_impl(ctx): asserts.equals(env, {"serde": ["1.0.0"]}, got.versions_by_name) asserts.true(env, "feature_resolutions" in packages[0]) asserts.equals(env, ["serde-1.0.0"], got.feature_resolutions_by_fq_crate.keys()) - asserts.equals(env, ["x86_64-unknown-linux-gnu"], got.feature_resolutions_by_fq_crate["serde-1.0.0"].active.keys()) asserts.equals(env, ["aarch64-apple-darwin"], got.feature_resolutions_by_fq_crate["serde-1.0.0"].build_deps.keys()) return unittest.end(env) diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index 5fb85161..541fe630 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -205,13 +205,6 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( {skip_deps_verification_attr}{indent}) """ -_RUST_CRATE_TARGET_EXEC_ALIAS = """{indent}rust_crate_target_exec_alias( -{indent} name = {name}, -{indent} crate_name = {crate_name}, -{indent} tags = {tags}, -{indent}) -""" - def _render_rust_crate_call( attr, values, @@ -221,7 +214,6 @@ def _render_rust_crate_call( build_deps_select, target_compatible_with, name_suffix, - build_script_name, binaries, bazel_metadata, extra_deps, @@ -298,15 +290,15 @@ def _render_rust_crate_call( has_lib = values["has_lib"], binaries = binaries, use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, - build_script_name = repr(build_script_name), + build_script_name = repr("_bs" + name_suffix), skip_deps_verification_attr = skip_deps_verification_attr, ) -def _merge_resolution_values(target_values, exec_values): - return target_values | exec_values, any([ - key in target_values and target_values[key] != exec_value +def _merge_target_and_exec(target_values, exec_values): + return target_values | exec_values if all([ + key not in target_values or target_values[key] == exec_value for key, exec_value in exec_values.items() - ]) + ]) else None def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): exec_active = getattr(attr, "exec_active", False) @@ -325,11 +317,11 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i target_compatible_with = "None" binaries = "{}" elif exec_active: - merged_crate_features_select, crate_features_differ = _merge_resolution_values(crate_features_select, attr.exec_crate_features_select) - merged_deps_select, deps_differ = _merge_resolution_values(deps_select, attr.exec_deps_select) - merged_build_deps_select, build_deps_differ = _merge_resolution_values(build_deps_select, attr.exec_build_script_deps_select) - merged_aliases, aliases_differ = _merge_resolution_values(aliases, attr.exec_aliases) - if crate_features_differ or deps_differ or build_deps_differ or aliases_differ: + merged_crate_features_select = _merge_target_and_exec(crate_features_select, attr.exec_crate_features_select) + merged_deps_select = _merge_target_and_exec(deps_select, attr.exec_deps_select) + merged_build_deps_select = _merge_target_and_exec(build_deps_select, attr.exec_build_script_deps_select) + merged_aliases = _merge_target_and_exec(aliases, attr.exec_aliases) + if None in [merged_crate_features_select, merged_deps_select, merged_build_deps_select, merged_aliases]: target_call = _render_rust_crate_call( attr, values, @@ -339,7 +331,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i build_deps_select, "RESOLVED_PLATFORMS", "_target", - "_bs_target", binaries, bazel_metadata, extra_deps, @@ -355,14 +346,18 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i attr.exec_build_script_deps_select, "None", "_exec", - "_bs_exec", "{}", bazel_metadata, extra_deps, indent, skip_deps_verification, ) - alias_call = _RUST_CRATE_TARGET_EXEC_ALIAS.format( + alias_call = """{indent}rust_crate_target_exec_alias( +{indent} name = {name}, +{indent} crate_name = {crate_name}, +{indent} tags = {tags}, +{indent}) +""".format( crate_name = values["crate_name"], indent = indent, name = values["name"], @@ -385,7 +380,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i build_deps_select, target_compatible_with, "", - "_bs", binaries, bazel_metadata, extra_deps, diff --git a/rs/private/resolver.bzl b/rs/private/resolver.bzl index 9a21983b..d04b099e 100644 --- a/rs/private/resolver.bzl +++ b/rs/private/resolver.bzl @@ -79,7 +79,7 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb match = dep["target"] for triple in match: - if not feature_resolutions.active[triple]: + if triple not in feature_resolutions.active: continue if optional: @@ -97,8 +97,8 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb triple_features = dep_feature_resolutions.features_enabled[triple] - if not dep_feature_resolutions.active[triple]: - dep_feature_resolutions.active[triple] = True + if triple not in dep_feature_resolutions.active: + dep_feature_resolutions.active.add(triple) new_dirty_package_indices.add(dep_feature_resolutions.package_index) dep_features = dep.get("features") @@ -125,7 +125,7 @@ def _propagate_feature_enablement( possible_features = feature_resolutions.possible_features for triple, feature_set in features_enabled.items(): - if not feature_resolutions.active[triple] or not feature_set: + if triple not in feature_resolutions.active or not feature_set: continue # Enable any features that are implied by previously-enabled features. @@ -167,8 +167,7 @@ def _propagate_feature_enablement( continue if defer_build_dependency: - deferred = feature_resolutions.deferred_build_dep_features[triple] - deferred.setdefault(dep_name, set()).add(dep_feature) + feature_resolutions.deferred_build_dep_features.setdefault(dep_name, set()).add(dep_feature) else: dep_feature_resolutions = dep["feature_resolutions"] triple_features = dep_feature_resolutions.features_enabled[triple] @@ -216,17 +215,12 @@ def seed_exec_build_dependencies(packages, exec_cfg_attrs_by_triple): target_resolution = package["feature_resolutions"] exec_resolution = package["exec_feature_resolutions"] - target_features = set() - deferred_build_dep_features = {} - if not any(target_resolution.active.values()): + if not target_resolution.active: continue - for triple, active in target_resolution.active.items(): - if not active: - continue + target_features = set() + for triple in target_resolution.active: target_features.update(target_resolution.features_enabled[triple]) - for dep_name, features in target_resolution.deferred_build_dep_features[triple].items(): - deferred_build_dep_features.setdefault(dep_name, set()).update(features) for dep in exec_resolution.possible_deps: if dep.get("kind", "normal") != "build" or not dep.get("bazel_target"): @@ -246,6 +240,6 @@ def seed_exec_build_dependencies(packages, exec_cfg_attrs_by_triple): if "package" in dep: target_resolution.aliases[bazel_target] = dep_name.replace("-", "_") - dep_resolution.active[exec_triple] = True + dep_resolution.active.add(exec_triple) dep_resolution.features_enabled[exec_triple].update(dep.get("features", [])) - dep_resolution.features_enabled[exec_triple].update(deferred_build_dep_features.get(dep_name, [])) + dep_resolution.features_enabled[exec_triple].update(target_resolution.deferred_build_dep_features.get(dep_name, [])) From 937f1be4d5f180231ea7ce7974e7a4facabf3041 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 18:13:11 -0400 Subject: [PATCH 07/11] Remove redundant split resolution plumbing --- rs/extensions.bzl | 7 +-- rs/private/cargo_workspace_graph.bzl | 66 ++++++-------------- rs/private/cargo_workspace_graph_test.bzl | 2 - rs/private/git_crate_metadata_repository.bzl | 2 +- rs/private/repository_utils.bzl | 19 ++---- rs/private/resolver.bzl | 6 +- rs/private/rustc_src_repository.bzl | 2 +- rs/rust_crate.bzl | 25 ++++---- 8 files changed, 43 insertions(+), 86 deletions(-) diff --git a/rs/extensions.bzl b/rs/extensions.bzl index 7ef07d29..d2a68594 100644 --- a/rs/extensions.bzl +++ b/rs/extensions.bzl @@ -251,12 +251,7 @@ def _generate_hub_and_spokes( facts_by_fq_crate[_fq_crate(name, version)] = fact - resolved_facts = resolve_package_facts( - packages, - facts_by_fq_crate, - platform_triples, - build_dep_triples = SUPPORTED_EXEC_TRIPLES, - ) + resolved_facts = resolve_package_facts(packages, facts_by_fq_crate, platform_triples) feature_resolutions_by_fq_crate = resolved_facts.feature_resolutions_by_fq_crate versions_by_name = resolved_facts.versions_by_name diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 079be622..2e8b9098 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -62,12 +62,11 @@ def cfg_match_info_for_target(target, platform_cfg_attrs, cfg_match_cache): cfg_match_cache[target] = match_info return match_info -def new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples, build_dep_triples = None): - build_dep_triples = platform_triples if build_dep_triples == None else build_dep_triples +def new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples): return struct( active = set(), features_enabled = {triple: set() for triple in platform_triples}, - build_deps = {triple: set() for triple in build_dep_triples}, + build_deps = {triple: set() for triple in platform_triples}, deps = {triple: set() for triple in platform_triples}, aliases = {}, deferred_build_dep_features = {}, @@ -370,13 +369,7 @@ def split_lockfile_packages(hub_name, cargo_metadata, workspace_cargo_toml, all_ workspace_members = workspace_members, ) -def _resolve_packages( - packages, - package_info_by_fq_crate, - platform_triples, - dep_converter = None, - build_dep_triples = None, - skip_internal_rustc_placeholder_crates = True): +def _resolve_packages(packages, package_info_by_fq_crate, platform_triples, dep_converter = None, skip_internal_rustc_placeholder_crates = True): feature_resolutions_by_fq_crate = {} versions_by_name = {} @@ -394,13 +387,7 @@ def _resolve_packages( converter = dep_converter, skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, ) - feature_resolutions = new_feature_resolutions( - package_index, - possible_deps, - package_info.get("features", {}), - platform_triples, - build_dep_triples = build_dep_triples, - ) + feature_resolutions = new_feature_resolutions(package_index, possible_deps, package_info.get("features", {}), platform_triples) package["feature_resolutions"] = feature_resolutions feature_resolutions_by_fq_crate[fq] = feature_resolutions @@ -409,12 +396,11 @@ def _resolve_packages( versions_by_name = versions_by_name, ) -def resolve_package_facts(packages, facts_by_fq_crate, platform_triples, build_dep_triples = None, skip_internal_rustc_placeholder_crates = True): +def resolve_package_facts(packages, facts_by_fq_crate, platform_triples, skip_internal_rustc_placeholder_crates = True): return _resolve_packages( packages, facts_by_fq_crate, platform_triples, - build_dep_triples = build_dep_triples, skip_internal_rustc_placeholder_crates = skip_internal_rustc_placeholder_crates, ) @@ -488,6 +474,11 @@ def _resolve_possible_deps( else: dep["target"] = set(match_info.matches) +def _apply_annotation_features(feature_resolutions, annotation): + for triple, features in feature_resolutions.features_enabled.items(): + features.update(annotation.crate_features) + features.update(annotation.crate_features_select.get(triple, [])) + def resolve_cargo_workspace_members( ctx, *, @@ -511,9 +502,7 @@ def resolve_cargo_workspace_members( cfg_match_cache = {None: struct(matches = platform_triples, uses_feature_cfg = False)} - exec_platform_cfg_attrs = [triple_to_cfg_attrs(triple) for triple in exec_platform_triples] - exec_platform_cfg_attrs_by_triple = {cfg_attr["_triple"]: cfg_attr for cfg_attr in exec_platform_cfg_attrs} - exec_cfg_match_cache = {None: struct(matches = exec_platform_triples, uses_feature_cfg = False)} + exec_platform_cfg_attrs_by_triple = {triple: triple_to_cfg_attrs(triple) for triple in exec_platform_triples} workspace_member_keys = {} for package in cargo_metadata["packages"]: @@ -548,13 +537,7 @@ def resolve_cargo_workspace_members( "dependencies": lockfile_pkg.get("dependencies", []), } - feature_resolutions = new_feature_resolutions( - package_index, - possible_deps, - possible_features, - platform_triples, - build_dep_triples = exec_platform_triples or platform_triples, - ) + feature_resolutions = new_feature_resolutions(package_index, possible_deps, possible_features, platform_triples) resolver_package["feature_resolutions"] = feature_resolutions feature_resolutions_by_fq_crate[fq_crate(name, version)] = feature_resolutions @@ -565,13 +548,13 @@ def resolve_cargo_workspace_members( if exec_platform_triples: for package in resolver_packages: target_resolution = package["feature_resolutions"] + target_resolution.build_deps.update({triple: set() for triple in exec_platform_triples}) exec_resolution = new_feature_resolutions( target_resolution.package_index, [dict(dep) for dep in target_resolution.possible_deps], target_resolution.possible_features, exec_platform_triples, ) - package["exec_feature_resolutions"] = exec_resolution exec_feature_resolutions_by_fq_crate[fq_crate(package["name"], package["version"])] = exec_resolution exec_resolver_packages.append(dict(package, feature_resolutions = exec_resolution)) @@ -591,8 +574,8 @@ def resolve_cargo_workspace_members( resolver_versions_by_name, exec_feature_resolutions_by_fq_crate, exec_platform_triples, - exec_platform_cfg_attrs, - exec_cfg_match_cache, + exec_platform_cfg_attrs_by_triple.values(), + {None: struct(matches = exec_platform_triples, uses_feature_cfg = False)}, dep_label_prefix, ) @@ -680,22 +663,11 @@ def resolve_cargo_workspace_members( if not annotation.crate_features and not annotation.crate_features_select: continue for version in target_versions: - features_enabled = feature_resolutions_by_fq_crate[fq_crate(crate, version)].features_enabled - if annotation.crate_features: - for triple in platform_triples: - features_enabled[triple].update(annotation.crate_features) - for triple, features in annotation.crate_features_select.items(): - if triple in features_enabled: - features_enabled[triple].update(features) + fq = fq_crate(crate, version) + _apply_annotation_features(feature_resolutions_by_fq_crate[fq], annotation) if exec_platform_triples: - exec_features_enabled = exec_feature_resolutions_by_fq_crate[fq_crate(crate, version)].features_enabled - if annotation.crate_features: - for triple in exec_platform_triples: - exec_features_enabled[triple].update(annotation.crate_features) - for triple, features in annotation.crate_features_select.items(): - if triple in exec_features_enabled: - exec_features_enabled[triple].update(features) + _apply_annotation_features(exec_feature_resolutions_by_fq_crate[fq], annotation) resolve( ctx, @@ -707,7 +679,7 @@ def resolve_cargo_workspace_members( ) if exec_platform_triples: - seed_exec_build_dependencies(resolver_packages, exec_platform_cfg_attrs_by_triple) + seed_exec_build_dependencies(resolver_packages, exec_resolver_packages, exec_platform_cfg_attrs_by_triple) resolve( ctx, exec_resolver_packages, diff --git a/rs/private/cargo_workspace_graph_test.bzl b/rs/private/cargo_workspace_graph_test.bzl index c257eb41..18bfd84a 100644 --- a/rs/private/cargo_workspace_graph_test.bzl +++ b/rs/private/cargo_workspace_graph_test.bzl @@ -256,13 +256,11 @@ def _resolve_package_facts_attaches_feature_resolutions_impl(ctx): }, }, ["x86_64-unknown-linux-gnu"], - build_dep_triples = ["aarch64-apple-darwin"], ) asserts.equals(env, {"serde": ["1.0.0"]}, got.versions_by_name) asserts.true(env, "feature_resolutions" in packages[0]) asserts.equals(env, ["serde-1.0.0"], got.feature_resolutions_by_fq_crate.keys()) - asserts.equals(env, ["aarch64-apple-darwin"], got.feature_resolutions_by_fq_crate["serde-1.0.0"].build_deps.keys()) return unittest.end(env) resolve_package_facts_attaches_feature_resolutions_test = unittest.make(_resolve_package_facts_attaches_feature_resolutions_impl) diff --git a/rs/private/git_crate_metadata_repository.bzl b/rs/private/git_crate_metadata_repository.bzl index 8398864b..4acf39cd 100644 --- a/rs/private/git_crate_metadata_repository.bzl +++ b/rs/private/git_crate_metadata_repository.bzl @@ -2,7 +2,7 @@ load(":repository_utils.bzl", "render_rust_crate_call", "rust_crate_attrs") def _git_crate_metadata_repository_implementation(rctx): rctx.file("crate.bzl", """\ -load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") +load("@rules_rs//rs:rust_crate.bzl", "rust_crate") load("@{hub_name}//:defs.bzl", "RESOLVED_PLATFORMS") def crate( diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index 541fe630..b278a007 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -202,7 +202,7 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( {indent} binaries = {binaries}, {indent} use_legacy_rules_rust_platforms = {use_legacy_rules_rust_platforms}, {indent} build_script_name = {build_script_name}, -{skip_deps_verification_attr}{indent}) +{target_exec_alias_name_attr}{skip_deps_verification_attr}{indent}) """ def _render_rust_crate_call( @@ -249,6 +249,7 @@ def _render_rust_crate_call( rustc_env = getattr(attr, "rustc_env", {}) rustc_env_attr = "%s rustc_env = %s,\n" % (indent, repr(rustc_env)) if rustc_env else "" skip_deps_verification_attr = "%s skip_deps_verification = True,\n" % indent if skip_deps_verification else "" + target_exec_alias_name_attr = "%s target_exec_alias_name = %s,\n" % (indent, values["name"]) if name_suffix == "_exec" else "" return _RUST_CRATE_MACRO_CALL.format( indent = indent, @@ -291,6 +292,7 @@ def _render_rust_crate_call( binaries = binaries, use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, build_script_name = repr("_bs" + name_suffix), + target_exec_alias_name_attr = target_exec_alias_name_attr, skip_deps_verification_attr = skip_deps_verification_attr, ) @@ -352,18 +354,7 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i indent, skip_deps_verification, ) - alias_call = """{indent}rust_crate_target_exec_alias( -{indent} name = {name}, -{indent} crate_name = {crate_name}, -{indent} tags = {tags}, -{indent}) -""".format( - crate_name = values["crate_name"], - indent = indent, - name = values["name"], - tags = repr(attr.crate_tags), - ) - return target_call + "\n" + exec_call + "\n" + alias_call + return target_call + "\n" + exec_call aliases = merged_aliases crate_features_select = merged_crate_features_select @@ -395,7 +386,7 @@ def render_build_file_content(rctx, attr, values, bazel_metadata = {}): additive_build_file_content += bazel_metadata.get("additive_build_file_content", "") return """\ -load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") +load("@rules_rs//rs:rust_crate.bzl", "rust_crate") load("@rules_rs//rs:rust_binary.bzl", "rust_binary") load("@{hub_name}//:defs.bzl", "RESOLVED_PLATFORMS") diff --git a/rs/private/resolver.bzl b/rs/private/resolver.bzl index d04b099e..0acc2f7d 100644 --- a/rs/private/resolver.bzl +++ b/rs/private/resolver.bzl @@ -209,11 +209,11 @@ def resolve(mctx, packages, feature_resolutions_by_fq_crate, cfg_attrs_by_triple fail("Resolution did not converge! This is likely a bug in rules_rs, please report it to github.com/hermeticbuild/rules_rs") -def seed_exec_build_dependencies(packages, exec_cfg_attrs_by_triple): +def seed_exec_build_dependencies(packages, exec_packages, exec_cfg_attrs_by_triple): """Seeds exec resolution from build dependencies of target-active packages.""" - for package in packages: + for package, exec_package in zip(packages, exec_packages): target_resolution = package["feature_resolutions"] - exec_resolution = package["exec_feature_resolutions"] + exec_resolution = exec_package["feature_resolutions"] if not target_resolution.active: continue diff --git a/rs/private/rustc_src_repository.bzl b/rs/private/rustc_src_repository.bzl index 6f5936c4..b67853fd 100644 --- a/rs/private/rustc_src_repository.bzl +++ b/rs/private/rustc_src_repository.bzl @@ -182,7 +182,7 @@ def _cargo_build_values(rctx, bazel_package, workspace_cargo_toml, target_name): def _render_crate_build_file(source_root, crate_attr, values, bazel_metadata): return """\ -load("@rules_rs//rs:rust_crate.bzl", "rust_crate", "rust_crate_target_exec_alias") +load("@rules_rs//rs:rust_crate.bzl", "rust_crate") load("//{source_root}:defs.bzl", "RESOLVED_PLATFORMS") {srcs_filegroup}{rust_crate_call}{package_metadata_bazel_additive_build_file_content}""".format( diff --git a/rs/rust_crate.bzl b/rs/rust_crate.bzl index 01fcf16d..3e66a59c 100644 --- a/rs/rust_crate.bzl +++ b/rs/rust_crate.bzl @@ -14,17 +14,6 @@ def _platform(triple, use_legacy_rules_rust_platforms): return "@rules_rust//rust/platform:" + triple.replace("-musl", "-gnu").replace("-gnullvm", "-msvc") return "@rules_rs//rs/platforms/config:" + triple -def rust_crate_target_exec_alias(name, crate_name, tags): - native.alias( - name = name, - actual = select({ - "@rules_rust//cargo/settings:use_exec_features_enabled": name + "_exec", - "//conditions:default": name + "_target", - }), - tags = ["crate-name=" + (crate_name or name)] + tags, - visibility = ["//visibility:public"], - ) - def rust_crate( name, crate_name, @@ -57,7 +46,8 @@ def rust_crate( extra_compile_data = [], rustc_env = {}, skip_deps_verification = False, - build_script_name = "_bs"): + build_script_name = "_bs", + target_exec_alias_name = None): if target_compatible_with == None: target_compatible_with = select({ _platform(triple, use_legacy_rules_rust_platforms): [] @@ -70,6 +60,17 @@ def rust_crate( visibility = ["//visibility:public"], ) + if target_exec_alias_name: + native.alias( + name = target_exec_alias_name, + actual = select({ + "@rules_rust//cargo/settings:use_exec_features_enabled": name, + "//conditions:default": target_exec_alias_name + "_target", + }), + tags = ["crate-name=" + (crate_name or target_exec_alias_name)] + tags, + visibility = ["//visibility:public"], + ) + compile_data = native.glob( include = ["**"], exclude = [ From 48e700a59b1ba0a5f5124dbcf58717f547ee8f12 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 18:18:10 -0400 Subject: [PATCH 08/11] Simplify exec dependency seeding --- rs/private/cargo_workspace_graph.bzl | 18 ++---------------- rs/private/resolver.bzl | 18 ++++++------------ test/build_exec_feature_split/src/lib.rs | 4 +--- .../vendor/consumer/src/lib.rs | 4 +--- 4 files changed, 10 insertions(+), 34 deletions(-) diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 2e8b9098..18f93a81 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -69,7 +69,6 @@ def new_feature_resolutions(package_index, possible_deps, possible_features, pla build_deps = {triple: set() for triple in platform_triples}, deps = {triple: set() for triple in platform_triples}, aliases = {}, - deferred_build_dep_features = {}, package_index = package_index, possible_deps = possible_deps, possible_features = possible_features, @@ -669,24 +668,11 @@ def resolve_cargo_workspace_members( if exec_platform_triples: _apply_annotation_features(exec_feature_resolutions_by_fq_crate[fq], annotation) - resolve( - ctx, - resolver_packages, - feature_resolutions_by_fq_crate, - platform_cfg_attrs_by_triple, - debug, - include_build_dependencies = not exec_platform_triples, - ) + resolve(ctx, resolver_packages, feature_resolutions_by_fq_crate, platform_cfg_attrs_by_triple, debug, include_build_dependencies = not exec_platform_triples) if exec_platform_triples: seed_exec_build_dependencies(resolver_packages, exec_resolver_packages, exec_platform_cfg_attrs_by_triple) - resolve( - ctx, - exec_resolver_packages, - exec_feature_resolutions_by_fq_crate, - exec_platform_cfg_attrs_by_triple, - debug, - ) + resolve(ctx, exec_resolver_packages, exec_feature_resolutions_by_fq_crate, exec_platform_cfg_attrs_by_triple, debug) for package in packages: feature_resolutions = package["feature_resolutions"] diff --git a/rs/private/resolver.bzl b/rs/private/resolver.bzl index 0acc2f7d..a984d545 100644 --- a/rs/private/resolver.bzl +++ b/rs/private/resolver.bzl @@ -167,7 +167,7 @@ def _propagate_feature_enablement( continue if defer_build_dependency: - feature_resolutions.deferred_build_dep_features.setdefault(dep_name, set()).add(dep_feature) + dep.setdefault("deferred_features", set()).add(dep_feature) else: dep_feature_resolutions = dep["feature_resolutions"] triple_features = dep_feature_resolutions.features_enabled[triple] @@ -193,13 +193,7 @@ def resolve(mctx, packages, feature_resolutions_by_fq_crate, cfg_attrs_by_triple for i in range(_MAX_ROUNDS): mctx.report_progress("Running round %s of dependency/feature resolution" % i) - dirty_package_indices = _resolve_one_round( - packages, - dirty_package_indices, - cfg_attrs_by_triple, - debug, - include_build_dependencies, - ) + dirty_package_indices = _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, debug, include_build_dependencies) if not dirty_package_indices: if debug: count = _count(feature_resolutions_by_fq_crate) @@ -222,11 +216,11 @@ def seed_exec_build_dependencies(packages, exec_packages, exec_cfg_attrs_by_trip for triple in target_resolution.active: target_features.update(target_resolution.features_enabled[triple]) - for dep in exec_resolution.possible_deps: - if dep.get("kind", "normal") != "build" or not dep.get("bazel_target"): + for target_dep, dep in zip(target_resolution.possible_deps, exec_resolution.possible_deps): + bazel_target = dep.get("bazel_target") + if dep.get("kind", "normal") != "build" or not bazel_target: continue - bazel_target = dep["bazel_target"] dep_name = dep["name"] if dep.get("optional", False) and dep_name not in target_features and ("dep:" + dep_name) not in target_features: continue @@ -242,4 +236,4 @@ def seed_exec_build_dependencies(packages, exec_packages, exec_cfg_attrs_by_trip dep_resolution.active.add(exec_triple) dep_resolution.features_enabled[exec_triple].update(dep.get("features", [])) - dep_resolution.features_enabled[exec_triple].update(target_resolution.deferred_build_dep_features.get(dep_name, [])) + dep_resolution.features_enabled[exec_triple].update(target_dep.get("deferred_features", [])) diff --git a/test/build_exec_feature_split/src/lib.rs b/test/build_exec_feature_split/src/lib.rs index cc90e757..075705e4 100644 --- a/test/build_exec_feature_split/src/lib.rs +++ b/test/build_exec_feature_split/src/lib.rs @@ -1,3 +1 @@ -pub fn run() { - feature_split_consumer::run(); -} +pub use feature_split_consumer::*; diff --git a/test/build_exec_feature_split/vendor/consumer/src/lib.rs b/test/build_exec_feature_split/vendor/consumer/src/lib.rs index c7972580..03b43c9b 100644 --- a/test/build_exec_feature_split/vendor/consumer/src/lib.rs +++ b/test/build_exec_feature_split/vendor/consumer/src/lib.rs @@ -1,3 +1 @@ -pub fn run() { - feature_split_shared::target_only(); -} +pub use feature_split_shared::target_only; From 48b13e724c969747f9d20914471a5578ee9462fe Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 18:24:47 -0400 Subject: [PATCH 09/11] Derive crate variant state from name_suffix --- rs/private/repository_utils.bzl | 19 +++++++------------ rs/rust_crate.bzl | 7 ++++--- test/build_exec_feature_split/Cargo.toml | 1 - test/build_exec_feature_split/src/lib.rs | 1 - 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index b278a007..cd0bfcc2 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -201,8 +201,8 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate( {indent} has_lib = {has_lib}, {indent} binaries = {binaries}, {indent} use_legacy_rules_rust_platforms = {use_legacy_rules_rust_platforms}, -{indent} build_script_name = {build_script_name}, -{target_exec_alias_name_attr}{skip_deps_verification_attr}{indent}) +{indent} name_suffix = {crate_name_suffix}, +{skip_deps_verification_attr}{indent}) """ def _render_rust_crate_call( @@ -212,13 +212,16 @@ def _render_rust_crate_call( crate_features_select, deps_select, build_deps_select, - target_compatible_with, name_suffix, binaries, bazel_metadata, extra_deps, indent, skip_deps_verification): + target_compatible_with = "RESOLVED_PLATFORMS" + if name_suffix == "_exec" or not getattr(attr, "target_active", True) or crate_features_select.keys() != attr.crate_features_select.keys(): + target_compatible_with = "None" + # We keep conditional_crate_features unrendered here because it must be treated specially for build scripts. # See `rust_crate.bzl` for details. crate_features, conditional_crate_features = compute_select( @@ -249,7 +252,6 @@ def _render_rust_crate_call( rustc_env = getattr(attr, "rustc_env", {}) rustc_env_attr = "%s rustc_env = %s,\n" % (indent, repr(rustc_env)) if rustc_env else "" skip_deps_verification_attr = "%s skip_deps_verification = True,\n" % indent if skip_deps_verification else "" - target_exec_alias_name_attr = "%s target_exec_alias_name = %s,\n" % (indent, values["name"]) if name_suffix == "_exec" else "" return _RUST_CRATE_MACRO_CALL.format( indent = indent, @@ -291,8 +293,7 @@ def _render_rust_crate_call( has_lib = values["has_lib"], binaries = binaries, use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, - build_script_name = repr("_bs" + name_suffix), - target_exec_alias_name_attr = target_exec_alias_name_attr, + crate_name_suffix = repr(name_suffix), skip_deps_verification_attr = skip_deps_verification_attr, ) @@ -308,7 +309,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_features_select = attr.crate_features_select deps_select = attr.deps_select build_deps_select = attr.build_script_deps_select - target_compatible_with = "RESOLVED_PLATFORMS" binaries = values["binaries"] if not getattr(attr, "target_active", True) and exec_active: @@ -316,7 +316,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_features_select = attr.exec_crate_features_select deps_select = attr.exec_deps_select build_deps_select = attr.exec_build_script_deps_select - target_compatible_with = "None" binaries = "{}" elif exec_active: merged_crate_features_select = _merge_target_and_exec(crate_features_select, attr.exec_crate_features_select) @@ -331,7 +330,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_features_select, deps_select, build_deps_select, - "RESOLVED_PLATFORMS", "_target", binaries, bazel_metadata, @@ -346,7 +344,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i attr.exec_crate_features_select, attr.exec_deps_select, attr.exec_build_script_deps_select, - "None", "_exec", "{}", bazel_metadata, @@ -360,7 +357,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_features_select = merged_crate_features_select deps_select = merged_deps_select build_deps_select = merged_build_deps_select - target_compatible_with = "None" return _render_rust_crate_call( attr, @@ -369,7 +365,6 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i crate_features_select, deps_select, build_deps_select, - target_compatible_with, "", binaries, bazel_metadata, diff --git a/rs/rust_crate.bzl b/rs/rust_crate.bzl index 3e66a59c..d9e39a3e 100644 --- a/rs/rust_crate.bzl +++ b/rs/rust_crate.bzl @@ -46,8 +46,8 @@ def rust_crate( extra_compile_data = [], rustc_env = {}, skip_deps_verification = False, - build_script_name = "_bs", - target_exec_alias_name = None): + name_suffix = ""): + build_script_name = "_bs" + name_suffix if target_compatible_with == None: target_compatible_with = select({ _platform(triple, use_legacy_rules_rust_platforms): [] @@ -60,7 +60,8 @@ def rust_crate( visibility = ["//visibility:public"], ) - if target_exec_alias_name: + if name_suffix == "_exec": + target_exec_alias_name = name.removesuffix(name_suffix) native.alias( name = target_exec_alias_name, actual = select({ diff --git a/test/build_exec_feature_split/Cargo.toml b/test/build_exec_feature_split/Cargo.toml index 943f9606..26dfdbb0 100644 --- a/test/build_exec_feature_split/Cargo.toml +++ b/test/build_exec_feature_split/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "build-exec-feature-split" version = "0.1.0" -edition = "2024" [workspace] resolver = "2" diff --git a/test/build_exec_feature_split/src/lib.rs b/test/build_exec_feature_split/src/lib.rs index 075705e4..e69de29b 100644 --- a/test/build_exec_feature_split/src/lib.rs +++ b/test/build_exec_feature_split/src/lib.rs @@ -1 +0,0 @@ -pub use feature_split_consumer::*; From 9858caa1d1c5ca6fc87182c8f5665d14bbaaf90f Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 22 Jun 2026 18:49:38 -0400 Subject: [PATCH 10/11] Clean up _render_rust_crate_call --- rs/private/repository_utils.bzl | 80 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index cd0bfcc2..676ea9fc 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -298,10 +298,10 @@ def _render_rust_crate_call( ) def _merge_target_and_exec(target_values, exec_values): - return target_values | exec_values if all([ - key not in target_values or target_values[key] == exec_value - for key, exec_value in exec_values.items() - ]) else None + for key, exec_value in exec_values.items(): + if key in target_values and target_values[key] != exec_value: + return None + return target_values | exec_values def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): exec_active = getattr(attr, "exec_active", False) @@ -324,32 +324,32 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i merged_aliases = _merge_target_and_exec(aliases, attr.exec_aliases) if None in [merged_crate_features_select, merged_deps_select, merged_build_deps_select, merged_aliases]: target_call = _render_rust_crate_call( - attr, - values, - aliases, - crate_features_select, - deps_select, - build_deps_select, - "_target", - binaries, - bazel_metadata, - extra_deps, - indent, - skip_deps_verification, + attr = attr, + values = values, + aliases = aliases, + crate_features_select = crate_features_select, + deps_select = deps_select, + build_deps_select = build_deps_select, + name_suffix = "_target", + binaries = binaries, + bazel_metadata = bazel_metadata, + extra_deps = extra_deps, + indent = indent, + skip_deps_verification = skip_deps_verification, ) exec_call = _render_rust_crate_call( - attr, - values, - attr.exec_aliases, - attr.exec_crate_features_select, - attr.exec_deps_select, - attr.exec_build_script_deps_select, - "_exec", - "{}", - bazel_metadata, - extra_deps, - indent, - skip_deps_verification, + attr = attr, + values = values, + aliases = attr.exec_aliases, + crate_features_select = attr.exec_crate_features_select, + deps_select = attr.exec_deps_select, + build_deps_select = attr.exec_build_script_deps_select, + name_suffix = "_exec", + binaries = "{}", + bazel_metadata = bazel_metadata, + extra_deps = extra_deps, + indent = indent, + skip_deps_verification = skip_deps_verification, ) return target_call + "\n" + exec_call @@ -359,18 +359,18 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i build_deps_select = merged_build_deps_select return _render_rust_crate_call( - attr, - values, - aliases, - crate_features_select, - deps_select, - build_deps_select, - "", - binaries, - bazel_metadata, - extra_deps, - indent, - skip_deps_verification, + attr = attr, + values = values, + aliases = aliases, + crate_features_select = crate_features_select, + deps_select = deps_select, + build_deps_select = build_deps_select, + name_suffix = "", + binaries = binaries, + bazel_metadata = bazel_metadata, + extra_deps = extra_deps, + indent = indent, + skip_deps_verification = skip_deps_verification, ) def render_build_file_content(rctx, attr, values, bazel_metadata = {}): From 8ead0ebb068e16f905d62c718d3a486d8d35a135 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Mon, 29 Jun 2026 19:20:58 -0400 Subject: [PATCH 11/11] Allow annotating proc macros to live in the exec universe for feature/dep unification --- rs/extensions.bzl | 14 ++++++- rs/private/annotations.bzl | 2 + rs/private/cargo_workspace_graph.bzl | 32 +++++++++++++--- rs/private/repository_utils.bzl | 15 ++++++++ rs/private/resolver.bzl | 37 ++++++++++++------- test/MODULE.bazel | 6 +++ test/MODULE.bazel.lock | 6 ++- test/build_exec_feature_split/Cargo.lock | 16 ++++++++ test/build_exec_feature_split/Cargo.toml | 4 +- .../vendor/consumer/Cargo.toml | 6 +++ .../vendor/consumer/src/lib.rs | 6 +++ .../vendor/proc-macro/Cargo.toml | 13 +++++++ .../vendor/proc-macro/src/lib.rs | 7 ++++ .../vendor/shared/Cargo.toml | 1 + .../vendor/shared/src/lib.rs | 6 +++ .../vendor/unannotated-proc-macro/Cargo.toml | 10 +++++ .../vendor/unannotated-proc-macro/src/lib.rs | 7 ++++ 17 files changed, 166 insertions(+), 22 deletions(-) create mode 100644 test/build_exec_feature_split/vendor/proc-macro/Cargo.toml create mode 100644 test/build_exec_feature_split/vendor/proc-macro/src/lib.rs create mode 100644 test/build_exec_feature_split/vendor/unannotated-proc-macro/Cargo.toml create mode 100644 test/build_exec_feature_split/vendor/unannotated-proc-macro/src/lib.rs diff --git a/rs/extensions.bzl b/rs/extensions.bzl index d2a68594..e935fa5b 100644 --- a/rs/extensions.bzl +++ b/rs/extensions.bzl @@ -27,7 +27,7 @@ load("//rs/private:git_crate_metadata_repository.bzl", "git_crate_metadata_repos load("//rs/private:lint_flags.bzl", "cargo_toml_lint_flags") load("//rs/private:registry_config_repository.bzl", "registry_config_repository") load("//rs/private:registry_utils.bzl", "CRATES_IO_REGISTRY", "registry_config_repo_name") -load("//rs/private:repository_utils.bzl", "render_select") +load("//rs/private:repository_utils.bzl", "exec_target_name", "render_select") load("//rs/private:toml2json.bzl", "run_toml2json") # Bazel 8 does not define attr.label_list_dict. @@ -353,6 +353,7 @@ crate.annotation( exec_active = bool(exec_feature_resolutions.active), use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms, ) + package["exec_target_name"] = exec_target_name(struct(**kwargs), crate_name) repo_name = _spoke_repo(hub_name, crate_name, version) package["target_repo_name"] = repo_name @@ -448,6 +449,14 @@ alias( actual = "{actual}", )""".format(name = name, version = version, actual = _target_label(target_repo_name, target_package_path, name))) + exec_target = package["exec_target_name"] + if exec_target: + hub_contents.append(""" +alias( + name = "{name}-{version}_exec", + actual = "{actual}", +)""".format(name = name, version = version, actual = _target_label(target_repo_name, target_package_path, exec_target))) + for binary in annotation.gen_binaries: hub_contents.append(""" alias( @@ -937,6 +946,9 @@ _annotation = tag_class( "crate_features_select": attr.string_list_dict( doc = "A list of strings to add to a crate's `rust_library::crate_features` attribute. Keys should be the platform triplet. Value should be a list of features.", ), + "is_proc_macro": attr.bool( + doc = "Resolve this proc-macro crate and its dependencies in the exec universe.", + ), "data": attr.label_list( doc = "A list of labels to add to a crate's `rust_library::data` attribute.", ), diff --git a/rs/private/annotations.bzl b/rs/private/annotations.bzl index 2e739c0c..5428493a 100644 --- a/rs/private/annotations.bzl +++ b/rs/private/annotations.bzl @@ -16,6 +16,7 @@ def _crate_annotation( tags = [], crate_features = [], crate_features_select = {}, + is_proc_macro = False, gen_binaries = [], extra_aliased_targets = {}, rustc_flags = [], @@ -43,6 +44,7 @@ def _crate_annotation( tags = tags, crate_features = crate_features, crate_features_select = crate_features_select, + is_proc_macro = is_proc_macro, gen_binaries = gen_binaries, extra_aliased_targets = extra_aliased_targets, rustc_flags = rustc_flags, diff --git a/rs/private/cargo_workspace_graph.bzl b/rs/private/cargo_workspace_graph.bzl index 18f93a81..0904db20 100644 --- a/rs/private/cargo_workspace_graph.bzl +++ b/rs/private/cargo_workspace_graph.bzl @@ -421,10 +421,12 @@ def _resolve_possible_deps( packages, resolver_versions_by_name, feature_resolutions_by_fq_crate, + proc_macro_fq_crates, platform_triples, platform_cfg_attrs, cfg_match_cache, - dep_label_prefix): + dep_label_prefix, + dep_label_suffix = ""): for package in packages: name = package["name"] deps_by_name = {} @@ -461,8 +463,10 @@ def _resolve_possible_deps( dep_fq = fq_crate(dep_package, resolved_version) if dep_fq not in feature_resolutions_by_fq_crate: fail("Resolved %s dependency %s but no crate metadata was available" % (name, dep_fq)) - dep["bazel_target"] = "%s%s" % (dep_label_prefix, dep_fq) + dep["bazel_target"] = "%s%s%s" % (dep_label_prefix, dep_fq, dep_label_suffix) dep["feature_resolutions"] = feature_resolutions_by_fq_crate[dep_fq] + if dep_fq in proc_macro_fq_crates: + dep["is_proc_macro"] = True target = dep.get("target") match_info = cfg_match_info_for_target(target, platform_cfg_attrs, cfg_match_cache) @@ -557,10 +561,22 @@ def resolve_cargo_workspace_members( exec_feature_resolutions_by_fq_crate[fq_crate(package["name"], package["version"])] = exec_resolution exec_resolver_packages.append(dict(package, feature_resolutions = exec_resolution)) + proc_macro_fq_crates = set() + for crate, annotation_versions in annotations.items(): + for version_key, annotation in annotation_versions.items(): + if not getattr(annotation, "is_proc_macro", False): + continue + versions = resolver_versions_by_name.get(crate, []) + if version_key != "*": + versions = [version_key] if version_key in versions else [] + for version in versions: + proc_macro_fq_crates.add(fq_crate(crate, version)) + _resolve_possible_deps( resolver_packages, resolver_versions_by_name, feature_resolutions_by_fq_crate, + proc_macro_fq_crates, platform_triples, platform_cfg_attrs, cfg_match_cache, @@ -572,10 +588,12 @@ def resolve_cargo_workspace_members( exec_resolver_packages, resolver_versions_by_name, exec_feature_resolutions_by_fq_crate, + proc_macro_fq_crates, exec_platform_triples, exec_platform_cfg_attrs_by_triple.values(), {None: struct(matches = exec_platform_triples, uses_feature_cfg = False)}, dep_label_prefix, + dep_label_suffix = "_exec", ) workspace_fq_deps = compute_workspace_fq_deps(workspace_members, resolver_versions_by_name) @@ -649,6 +667,8 @@ def resolve_cargo_workspace_members( continue if not is_first_party_dep or materialize_workspace_members: workspace_dep_labels_by_triple[triple].add(":" + dep_name) + if dep_fq in proc_macro_fq_crates: + continue feature_resolutions.active.add(triple) feature_resolutions.features_enabled[triple].update(features) @@ -663,10 +683,12 @@ def resolve_cargo_workspace_members( continue for version in target_versions: fq = fq_crate(crate, version) - _apply_annotation_features(feature_resolutions_by_fq_crate[fq], annotation) - - if exec_platform_triples: + if exec_platform_triples and getattr(annotation, "is_proc_macro", False): _apply_annotation_features(exec_feature_resolutions_by_fq_crate[fq], annotation) + else: + _apply_annotation_features(feature_resolutions_by_fq_crate[fq], annotation) + if exec_platform_triples: + _apply_annotation_features(exec_feature_resolutions_by_fq_crate[fq], annotation) resolve(ctx, resolver_packages, feature_resolutions_by_fq_crate, platform_cfg_attrs_by_triple, debug, include_build_dependencies = not exec_platform_triples) diff --git a/rs/private/repository_utils.bzl b/rs/private/repository_utils.bzl index 676ea9fc..9b0b7ff9 100644 --- a/rs/private/repository_utils.bzl +++ b/rs/private/repository_utils.bzl @@ -303,6 +303,21 @@ def _merge_target_and_exec(target_values, exec_values): return None return target_values | exec_values +def _target_and_exec_are_mergeable(attr): + return None not in [ + _merge_target_and_exec(attr.crate_features_select, attr.exec_crate_features_select), + _merge_target_and_exec(attr.deps_select, attr.exec_deps_select), + _merge_target_and_exec(attr.build_script_deps_select, attr.exec_build_script_deps_select), + _merge_target_and_exec(attr.aliases, attr.exec_aliases), + ] + +def exec_target_name(attr, name): + if not getattr(attr, "exec_active", False): + return None + if not getattr(attr, "target_active", True) or _target_and_exec_are_mergeable(attr): + return name + return name + "_exec" + def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", indent = "", skip_deps_verification = False): exec_active = getattr(attr, "exec_active", False) aliases = attr.aliases diff --git a/rs/private/resolver.bzl b/rs/private/resolver.bzl index a984d545..f36735f8 100644 --- a/rs/private/resolver.bzl +++ b/rs/private/resolver.bzl @@ -62,6 +62,8 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb if kind == "build" and not include_build_dependencies: continue + defer_proc_macro = dep.get("is_proc_macro", False) and not include_build_dependencies + dep_feature_resolutions = dep["feature_resolutions"] has_alias = "package" in dep @@ -95,6 +97,9 @@ def _resolve_one_round(packages, dirty_package_indices, cfg_attrs_by_triple, deb if has_alias: feature_resolutions.aliases[bazel_target] = dep_name.replace("-", "_") + if defer_proc_macro: + continue + triple_features = dep_feature_resolutions.features_enabled[triple] if triple not in dep_feature_resolutions.active: @@ -156,8 +161,8 @@ def _propagate_feature_enablement( if dep_name != dep["name"]: continue - defer_build_dependency = dep.get("kind", "normal") == "build" and not include_build_dependencies - if not defer_build_dependency and not _dep_target_matches_triple(dep, triple, feature_set, cfg_attrs_by_triple): + defer_dependency = not include_build_dependencies and (dep.get("kind", "normal") == "build" or dep.get("is_proc_macro", False)) + if not defer_dependency and not _dep_target_matches_triple(dep, triple, feature_set, cfg_attrs_by_triple): continue found = True @@ -166,7 +171,7 @@ def _propagate_feature_enablement( if optional_marker and dep_optional and dep_name not in feature_set and ("dep:" + dep_name) not in feature_set: continue - if defer_build_dependency: + if defer_dependency: dep.setdefault("deferred_features", set()).add(dep_feature) else: dep_feature_resolutions = dep["feature_resolutions"] @@ -204,7 +209,7 @@ def resolve(mctx, packages, feature_resolutions_by_fq_crate, cfg_attrs_by_triple fail("Resolution did not converge! This is likely a bug in rules_rs, please report it to github.com/hermeticbuild/rules_rs") def seed_exec_build_dependencies(packages, exec_packages, exec_cfg_attrs_by_triple): - """Seeds exec resolution from build dependencies of target-active packages.""" + """Seeds exec resolution from build dependencies and annotated proc macros.""" for package, exec_package in zip(packages, exec_packages): target_resolution = package["feature_resolutions"] exec_resolution = exec_package["feature_resolutions"] @@ -218,22 +223,28 @@ def seed_exec_build_dependencies(packages, exec_packages, exec_cfg_attrs_by_trip for target_dep, dep in zip(target_resolution.possible_deps, exec_resolution.possible_deps): bazel_target = dep.get("bazel_target") - if dep.get("kind", "normal") != "build" or not bazel_target: + is_build_dependency = dep.get("kind", "normal") == "build" + is_proc_macro = dep.get("is_proc_macro", False) + if not bazel_target or not (is_build_dependency or is_proc_macro): continue dep_name = dep["name"] if dep.get("optional", False) and dep_name not in target_features and ("dep:" + dep_name) not in target_features: continue - dep_resolution = dep["feature_resolutions"] - for exec_triple in dep["target"]: - if not _dep_target_matches_triple(dep, exec_triple, target_features, exec_cfg_attrs_by_triple): - continue - - target_resolution.build_deps[exec_triple].add(bazel_target) - if "package" in dep: - target_resolution.aliases[bazel_target] = dep_name.replace("-", "_") + target_bazel_target = target_dep.get("bazel_target") + if is_proc_macro and not any([target_bazel_target in deps for deps in target_resolution.deps.values()]): + continue + dep_resolution = dep["feature_resolutions"] + exec_triples = dep_resolution.features_enabled.keys() if is_proc_macro else dep["target"] + for exec_triple in exec_triples: + if is_build_dependency: + if not _dep_target_matches_triple(dep, exec_triple, target_features, exec_cfg_attrs_by_triple): + continue + target_resolution.build_deps[exec_triple].add(bazel_target) + if "package" in dep: + target_resolution.aliases[bazel_target] = dep_name.replace("-", "_") dep_resolution.active.add(exec_triple) dep_resolution.features_enabled[exec_triple].update(dep.get("features", [])) dep_resolution.features_enabled[exec_triple].update(target_dep.get("deferred_features", [])) diff --git a/test/MODULE.bazel b/test/MODULE.bazel index 5aea9601..e790997e 100644 --- a/test/MODULE.bazel +++ b/test/MODULE.bazel @@ -203,6 +203,12 @@ TESTS = [ [use_repo(crate, test) for test in TESTS] +crate.annotation( + crate = "feature-split-proc-macro", + is_proc_macro = True, + repositories = ["build_exec_feature_split"], +) + # Workspace member whose Cargo.toml lives at the bazel workspace root. crate.from_cargo( name = "root_package", diff --git a/test/MODULE.bazel.lock b/test/MODULE.bazel.lock index fc093cfa..8e1fa0c7 100644 --- a/test/MODULE.bazel.lock +++ b/test/MODULE.bazel.lock @@ -2435,8 +2435,10 @@ "paste_1.0.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"paste-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", "pastey_0.1.1": "{\"dependencies\":[],\"features\":{}}", "pastey_0.2.3": "{\"dependencies\":[],\"features\":{}}", - "path+build_exec_feature_split/vendor/consumer_feature-split-consumer": "{\"dependencies\":[{\"default_features\":true,\"features\":[\"target_feature\"],\"name\":\"feature-split-shared\",\"optional\":false},{\"default_features\":true,\"features\":[\"exec_feature\"],\"kind\":\"build\",\"name\":\"feature-split-shared\",\"optional\":false}],\"features\":{},\"strip_prefix\":\"\"}", - "path+build_exec_feature_split/vendor/shared_feature-split-shared": "{\"dependencies\":[],\"features\":{\"exec_feature\":[],\"target_feature\":[]},\"strip_prefix\":\"\"}", + "path+build_exec_feature_split/vendor/consumer_feature-split-consumer": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"feature-split-proc-macro\",\"optional\":true},{\"default_features\":true,\"features\":[\"target_feature\"],\"name\":\"feature-split-shared\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"feature-split-unannotated-proc-macro\",\"optional\":false},{\"default_features\":true,\"features\":[\"exec_feature\"],\"kind\":\"build\",\"name\":\"feature-split-shared\",\"optional\":false}],\"features\":{\"default\":[\"macros\"],\"macros\":[\"dep:feature-split-proc-macro\",\"feature-split-proc-macro?/exec_feature\"]},\"strip_prefix\":\"\"}", + "path+build_exec_feature_split/vendor/proc-macro_feature-split-proc-macro": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"feature-split-shared\",\"optional\":false}],\"features\":{\"exec_feature\":[\"feature-split-shared/exec_feature\"]},\"strip_prefix\":\"\"}", + "path+build_exec_feature_split/vendor/shared_feature-split-shared": "{\"dependencies\":[],\"features\":{\"exec_feature\":[],\"target_feature\":[],\"unannotated_proc_macro_feature\":[]},\"strip_prefix\":\"\"}", + "path+build_exec_feature_split/vendor/unannotated-proc-macro_feature-split-unannotated-proc-macro": "{\"dependencies\":[{\"default_features\":true,\"features\":[\"unannotated_proc_macro_feature\"],\"name\":\"feature-split-shared\",\"optional\":false}],\"features\":{},\"strip_prefix\":\"\"}", "path+cfg_feature_target_dep/inner_inner": "{\"dependencies\":[{\"name\":\"itoa\",\"req\":\"1.0.15\",\"target\":\"cfg(feature = \\\"with_itoa\\\")\"}],\"features\":{\"with_itoa\":[]},\"strip_prefix\":\"\"}", "path+first_party_feature_propagation/../first_party_feature_propagation.deps/dep_leaf_dep_leaf": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"itoa\",\"optional\":true,\"req\":\"1.0.15\"},{\"default_features\":true,\"features\":[],\"name\":\"ryu\",\"optional\":true,\"req\":\"1.0.20\"}],\"features\":{\"default\":[],\"default_extra\":[\"dep:itoa\"],\"with_extra\":[\"dep:ryu\"]},\"strip_prefix\":\"\"}", "path+vendored_crate_override/vendor/itoa_itoa": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"no-panic\",\"optional\":true,\"req\":\"0.1\"}],\"features\":{},\"strip_prefix\":\"\"}", diff --git a/test/build_exec_feature_split/Cargo.lock b/test/build_exec_feature_split/Cargo.lock index 048c9ae0..82c93d4c 100644 --- a/test/build_exec_feature_split/Cargo.lock +++ b/test/build_exec_feature_split/Cargo.lock @@ -12,6 +12,15 @@ dependencies = [ [[package]] name = "feature-split-consumer" version = "0.1.0" +dependencies = [ + "feature-split-proc-macro", + "feature-split-shared", + "feature-split-unannotated-proc-macro", +] + +[[package]] +name = "feature-split-proc-macro" +version = "0.1.0" dependencies = [ "feature-split-shared", ] @@ -19,3 +28,10 @@ dependencies = [ [[package]] name = "feature-split-shared" version = "0.1.0" + +[[package]] +name = "feature-split-unannotated-proc-macro" +version = "0.1.0" +dependencies = [ + "feature-split-shared", +] diff --git a/test/build_exec_feature_split/Cargo.toml b/test/build_exec_feature_split/Cargo.toml index 26dfdbb0..c473ecba 100644 --- a/test/build_exec_feature_split/Cargo.toml +++ b/test/build_exec_feature_split/Cargo.toml @@ -4,10 +4,12 @@ version = "0.1.0" [workspace] resolver = "2" -exclude = ["vendor/consumer", "vendor/shared"] +exclude = ["vendor/consumer", "vendor/proc-macro", "vendor/shared", "vendor/unannotated-proc-macro"] [dependencies] feature-split-consumer = { path = "vendor/consumer" } [patch.crates-io] +feature-split-proc-macro = { path = "vendor/proc-macro" } feature-split-shared = { path = "vendor/shared" } +feature-split-unannotated-proc-macro = { path = "vendor/unannotated-proc-macro" } diff --git a/test/build_exec_feature_split/vendor/consumer/Cargo.toml b/test/build_exec_feature_split/vendor/consumer/Cargo.toml index fb3bc49a..c8cf2d6f 100644 --- a/test/build_exec_feature_split/vendor/consumer/Cargo.toml +++ b/test/build_exec_feature_split/vendor/consumer/Cargo.toml @@ -3,9 +3,15 @@ name = "feature-split-consumer" version = "0.1.0" edition = "2024" +[features] +default = ["macros"] +macros = ["dep:feature-split-proc-macro", "feature-split-proc-macro?/exec_feature"] + [dependencies] # The target crate must not receive exec_feature from build-dependencies. feature-split-shared = { path = "../shared", features = ["target_feature"] } +feature-split-proc-macro = { path = "../proc-macro", optional = true } +feature-split-unannotated-proc-macro = { path = "../unannotated-proc-macro" } [build-dependencies] # The build script must not receive target_feature from dependencies. diff --git a/test/build_exec_feature_split/vendor/consumer/src/lib.rs b/test/build_exec_feature_split/vendor/consumer/src/lib.rs index 03b43c9b..4f2bd5ae 100644 --- a/test/build_exec_feature_split/vendor/consumer/src/lib.rs +++ b/test/build_exec_feature_split/vendor/consumer/src/lib.rs @@ -1 +1,7 @@ +#[feature_split_proc_macro::identity] +pub fn annotated_proc_macro() {} + +#[feature_split_unannotated_proc_macro::identity] +pub fn unannotated_proc_macro() {} + pub use feature_split_shared::target_only; diff --git a/test/build_exec_feature_split/vendor/proc-macro/Cargo.toml b/test/build_exec_feature_split/vendor/proc-macro/Cargo.toml new file mode 100644 index 00000000..463d0e8a --- /dev/null +++ b/test/build_exec_feature_split/vendor/proc-macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "feature-split-proc-macro" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[features] +exec_feature = ["feature-split-shared/exec_feature"] + +[dependencies] +feature-split-shared = { path = "../shared" } diff --git a/test/build_exec_feature_split/vendor/proc-macro/src/lib.rs b/test/build_exec_feature_split/vendor/proc-macro/src/lib.rs new file mode 100644 index 00000000..1b5784fb --- /dev/null +++ b/test/build_exec_feature_split/vendor/proc-macro/src/lib.rs @@ -0,0 +1,7 @@ +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream { + feature_split_shared::exec_only(); + item +} diff --git a/test/build_exec_feature_split/vendor/shared/Cargo.toml b/test/build_exec_feature_split/vendor/shared/Cargo.toml index 6cd26e08..81915506 100644 --- a/test/build_exec_feature_split/vendor/shared/Cargo.toml +++ b/test/build_exec_feature_split/vendor/shared/Cargo.toml @@ -6,3 +6,4 @@ edition = "2024" [features] exec_feature = [] target_feature = [] +unannotated_proc_macro_feature = [] diff --git a/test/build_exec_feature_split/vendor/shared/src/lib.rs b/test/build_exec_feature_split/vendor/shared/src/lib.rs index 799e33ec..58a9f6e8 100644 --- a/test/build_exec_feature_split/vendor/shared/src/lib.rs +++ b/test/build_exec_feature_split/vendor/shared/src/lib.rs @@ -1,8 +1,14 @@ #[cfg(all(feature = "exec_feature", feature = "target_feature"))] compile_error!("target and exec features were unified"); +#[cfg(all(feature = "exec_feature", feature = "unannotated_proc_macro_feature"))] +compile_error!("unannotated proc-macro features entered exec resolution"); + #[cfg(feature = "exec_feature")] pub fn exec_only() {} #[cfg(feature = "target_feature")] pub fn target_only() {} + +#[cfg(feature = "unannotated_proc_macro_feature")] +pub fn unannotated_proc_macro_only() {} diff --git a/test/build_exec_feature_split/vendor/unannotated-proc-macro/Cargo.toml b/test/build_exec_feature_split/vendor/unannotated-proc-macro/Cargo.toml new file mode 100644 index 00000000..7d249b60 --- /dev/null +++ b/test/build_exec_feature_split/vendor/unannotated-proc-macro/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "feature-split-unannotated-proc-macro" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +feature-split-shared = { path = "../shared", features = ["unannotated_proc_macro_feature"] } diff --git a/test/build_exec_feature_split/vendor/unannotated-proc-macro/src/lib.rs b/test/build_exec_feature_split/vendor/unannotated-proc-macro/src/lib.rs new file mode 100644 index 00000000..3c2bc3ea --- /dev/null +++ b/test/build_exec_feature_split/vendor/unannotated-proc-macro/src/lib.rs @@ -0,0 +1,7 @@ +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream { + feature_split_shared::unannotated_proc_macro_only(); + item +}