diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index ea0e92c01d..15b1d2aab4 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -630,6 +630,12 @@ tasks: - "--config=no_std_alloc" test_flags: - "--config=no_std_alloc" + unstable_rust_features_ubuntu2204: + name: Build with unstable_rust_features + platform: ubuntu2204 + working_directory: test/integration/unstable_rust_features + build_targets: + - "//..." bzlmod_repo_mapping_runfiles: name: bzlmod repo mapping test platform: ubuntu2204 diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl index 4e180f317c..f90722752f 100644 --- a/rust/private/providers.bzl +++ b/rust/private/providers.bzl @@ -229,3 +229,8 @@ AllocatorLibrariesImplInfo = provider( "static_archive": "Optional[File]: the allocator library archive (typically .a file).", }, ) + +UnstableRustFeaturesInfo = provider( + doc = "UnstableRustFeaturesInfo contains a function mapping build targets to unstable features approved for use. Only works on nightly toolchains. May return the special value \"__all__\" to allow all unstable features for the target.", + fields = {"unstable_rust_features_config": "Callable[[Label], List[string]] Returns a list of unstable features approved for use for the given build target."}, +) diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index cc87477b19..bd784687d6 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -24,6 +24,7 @@ load( "CrateGroupInfo", "CrateInfo", "LintsInfo", + "UnstableRustFeaturesInfo", ) load( ":rust_allocator_libraries.bzl", @@ -807,6 +808,11 @@ _COMMON_ATTRS = { "stamp": _stamp_attribute( default_value = 0, ), + "unstable_rust_features_config": attr.label( + doc = "Controls which unstable features are allowed to be used by this target. Setting this to anything other than None requires a nightly toolchain.", + providers = [UnstableRustFeaturesInfo], + default = None, + ), "version": attr.string( doc = "A version to inject in the cargo environment variable.", default = "0.0.0", diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index d12900cb28..f43ddaba67 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -34,6 +34,7 @@ load( "AlwaysEnableMetadataOutputGroupsInfo", "LintsInfo", "RustcOutputDiagnosticsInfo", + "UnstableRustFeaturesInfo", _BuildInfo = "BuildInfo", ) load(":rustc_resource_set.bzl", "get_rustc_resource_set", "is_codegen_units_enabled") @@ -904,6 +905,15 @@ def _should_add_oso_prefix(toolchain): return True +def _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features): + other_flags = [] + for flag in rust_flags: + if flag.startswith("-Zallow-features="): + all_allowed_unstable_features.extend(flag.removeprefix("-Zallow-features=").split(",")) + else: + other_flags.append(flag) + return other_flags + def construct_arguments( *, ctx, @@ -933,7 +943,8 @@ def construct_arguments( force_depend_on_objects = False, skip_expanding_rustc_env = False, require_explicit_unstable_features = False, - error_format = None): + error_format = None, + allowed_unstable_rust_features = None): """Builds an Args object containing common rustc flags Args: @@ -969,6 +980,7 @@ def construct_arguments( skip_expanding_rustc_env (bool): Whether to skip expanding CrateInfo.rustc_env_attr require_explicit_unstable_features (bool): Whether to require all unstable features to be explicitly opted in to using `-Zallow-features=...`. error_format (str, optional): Error format to pass to the `--error-format` command line argument. If set to None, uses the "_error_format" entry in `attr`. + allowed_unstable_rust_features (list, optional): List of unstable Rust language features allowed for this target. Returns: tuple: A tuple of the following items @@ -996,8 +1008,12 @@ def construct_arguments( process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file") - if require_explicit_unstable_features: - process_wrapper_flags.add("--require-explicit-unstable-features", "true") + all_allowed_unstable_features = [] + if getattr(ctx.attr, "unstable_rust_features_config", None): + all_allowed_unstable_features.extend(ctx.attr.unstable_rust_features_config[UnstableRustFeaturesInfo].unstable_rust_features_config(ctx.label)) + + if allowed_unstable_rust_features != None: + all_allowed_unstable_features.extend(allowed_unstable_rust_features) # Certain rust build processes expect to find files from the environment # variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera, @@ -1124,7 +1140,11 @@ def construct_arguments( # Tell Rustc where to find the standard library (or libcore) rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s") - rustc_flags.add_all(rust_flags, map_each = map_flag) + + rustc_flags.add_all( + _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features), + map_each = map_flag, + ) # Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test # Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681 @@ -1244,7 +1264,18 @@ def construct_arguments( if hasattr(ctx.attr, "_extra_exec_rustc_env") and is_exec_configuration(ctx): env.update(ctx.attr._extra_exec_rustc_env[ExtraExecRustcEnvInfo].extra_exec_rustc_env) - rustc_flags.add_all(collect_extra_rustc_flags(ctx, toolchain, crate_info.root, crate_info.type), map_each = map_flag) + extra_rustc_flags = _extract_allowed_unstable_features_from_flags( + collect_extra_rustc_flags(ctx, toolchain, crate_info.root, crate_info.type), + all_allowed_unstable_features, + ) + if getattr(ctx.attr, "unstable_rust_features_config", None) and not "__all__" in all_allowed_unstable_features: + all_allowed_unstable_features = {f: None for f in all_allowed_unstable_features}.keys() + extra_rustc_flags.append("-Zallow-features=" + ",".join(all_allowed_unstable_features)) + + # require_explicit_unstable_features makes no sense when all features are allowed anyway + if require_explicit_unstable_features: + process_wrapper_flags.add("--require-explicit-unstable-features", "true") + rustc_flags.add_all(extra_rustc_flags, map_each = map_flag) if is_no_std(ctx, toolchain, crate_info.is_test): rustc_flags.add('--cfg=feature="no_std"') @@ -1323,7 +1354,8 @@ def rustc_compile_action( force_all_deps_direct = False, crate_info_dict = None, skip_expanding_rustc_env = False, - include_coverage = True): + include_coverage = True, + allowed_unstable_rust_features = None): """Create and run a rustc compile action based on the current rule's attributes Args: @@ -1337,6 +1369,8 @@ def rustc_compile_action( crate_info_dict: A mutable dict used to create CrateInfo provider skip_expanding_rustc_env (bool, optional): Whether to expand CrateInfo.rustc_env include_coverage (bool, optional): Whether to generate coverage information or not. + allowed_unstable_rust_features (list, optional): A list of unstable Rust language features + that are allowed to be used in the crate. Returns: list: A list of the following providers: @@ -1460,6 +1494,7 @@ def rustc_compile_action( use_json_output = bool(build_metadata) or bool(rustc_output) or bool(rustc_rmeta_output), skip_expanding_rustc_env = skip_expanding_rustc_env, require_explicit_unstable_features = require_explicit_unstable_features, + allowed_unstable_rust_features = allowed_unstable_rust_features, ) args_metadata = None @@ -1487,6 +1522,7 @@ def rustc_compile_action( use_json_output = True, build_metadata = True, require_explicit_unstable_features = require_explicit_unstable_features, + allowed_unstable_rust_features = allowed_unstable_rust_features, ) env = dict(ctx.configuration.default_shell_env) diff --git a/rust/rust_common.bzl b/rust/rust_common.bzl index a75da204a4..267923cd49 100644 --- a/rust/rust_common.bzl +++ b/rust/rust_common.bzl @@ -27,6 +27,7 @@ load( _DepInfo = "DepInfo", _DepVariantInfo = "DepVariantInfo", _TestCrateInfo = "TestCrateInfo", + _UnstableRustFeaturesInfo = "UnstableRustFeaturesInfo", ) BuildInfo = _BuildInfo @@ -36,5 +37,6 @@ CrateInfo = _CrateInfo DepInfo = _DepInfo DepVariantInfo = _DepVariantInfo TestCrateInfo = _TestCrateInfo +UnstableRustFeaturesInfo = _UnstableRustFeaturesInfo COMMON_PROVIDERS = _COMMON_PROVIDERS diff --git a/test/integration/unstable_rust_features/.bazelrc b/test/integration/unstable_rust_features/.bazelrc new file mode 100644 index 0000000000..c3188d63e3 --- /dev/null +++ b/test/integration/unstable_rust_features/.bazelrc @@ -0,0 +1,12 @@ +############################################################################### +## Incompatibility flags +############################################################################### + +# https://github.com/bazelbuild/bazel/issues/8195 +build --incompatible_disallow_empty_glob=true + +# https://github.com/bazelbuild/bazel/issues/12821 +build --nolegacy_external_runfiles + +# https://github.com/bazelbuild/bazel/issues/23043. +build --incompatible_autoload_externally= diff --git a/test/integration/unstable_rust_features/BUILD.bazel b/test/integration/unstable_rust_features/BUILD.bazel new file mode 100644 index 0000000000..92b53986db --- /dev/null +++ b/test/integration/unstable_rust_features/BUILD.bazel @@ -0,0 +1,10 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary") +load(":unstable_features_for_test.bzl", "unstable_rust_features_for_test_rule") + +unstable_rust_features_for_test_rule(name = "unstable_rust_features_for_test") + +rust_binary( + name = "unstable_features_test", + srcs = ["main.rs"], + unstable_rust_features_config = ":unstable_rust_features_for_test", +) diff --git a/test/integration/unstable_rust_features/MODULE.bazel b/test/integration/unstable_rust_features/MODULE.bazel new file mode 100644 index 0000000000..6ecda9db45 --- /dev/null +++ b/test/integration/unstable_rust_features/MODULE.bazel @@ -0,0 +1,38 @@ +module( + name = "rules_rust_test_unstable_rust_features", + version = "0.0.0", +) + +bazel_dep(name = "rules_rust", version = "0.0.0") +local_path_override( + module_name = "rules_rust", + path = "../../..", +) + +bazel_dep(name = "rules_cc", version = "0.2.4") +bazel_dep(name = "bazel_skylib", version = "1.8.2") +bazel_dep(name = "platforms", version = "1.0.0") + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain( + edition = "2018", +) + +# Use a nightly version where `core_intrinsics` is an unstable feature. +VERSION = "nightly/2026-04-15" + +rust.repository_set( + name = "nightly_rust_x86_64", + allocator_library = "@rules_rust//ffi/rs:empty", + edition = "2021", + exec_triple = "x86_64-unknown-linux-gnu", + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], + target_triple = "x86_64-unknown-linux-gnu", + versions = [VERSION], +) +use_repo(rust, "rust_toolchains") + +register_toolchains("@rust_toolchains//:all") diff --git a/test/integration/unstable_rust_features/WORKSPACE.bazel b/test/integration/unstable_rust_features/WORKSPACE.bazel new file mode 100644 index 0000000000..9b7902f70f --- /dev/null +++ b/test/integration/unstable_rust_features/WORKSPACE.bazel @@ -0,0 +1 @@ +workspace(name = "rules_rust_test_unstable_rust_features") diff --git a/test/integration/unstable_rust_features/main.rs b/test/integration/unstable_rust_features/main.rs new file mode 100644 index 0000000000..1385ae88cf --- /dev/null +++ b/test/integration/unstable_rust_features/main.rs @@ -0,0 +1,7 @@ +#![feature(core_intrinsics)] +#![allow(internal_features)] +use std::intrinsics; + +fn main() { + intrinsics::abort(); +} diff --git a/test/integration/unstable_rust_features/unstable_features_for_test.bzl b/test/integration/unstable_rust_features/unstable_features_for_test.bzl new file mode 100644 index 0000000000..6769ba58aa --- /dev/null +++ b/test/integration/unstable_rust_features/unstable_features_for_test.bzl @@ -0,0 +1,19 @@ +"""Defines a test rule providing UnstableRustFeaturesInfo""" + +load("@rules_rust//rust:rust_common.bzl", "UnstableRustFeaturesInfo") + +def _unstable_rust_features_for_test_impl_fn(label): + if str(label).endswith(":unstable_features_test"): + return ["core_intrinsics"] + return [] + +def _unstable_rust_features_for_test_impl(_ctx): + return UnstableRustFeaturesInfo( + unstable_rust_features_config = _unstable_rust_features_for_test_impl_fn, + ) + +unstable_rust_features_for_test_rule = rule( + attrs = {}, + provides = [UnstableRustFeaturesInfo], + implementation = _unstable_rust_features_for_test_impl, +)