diff --git a/MODULE.bazel b/MODULE.bazel index 9151647..2dacd52 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -52,6 +52,7 @@ use_repo( bazel_dep(name = "bazel_lib", version = "3.0.0") bazel_dep(name = "bazel_features", version = "1.45.0") bazel_dep(name = "bazel_skylib", version = "1.4.1") +bazel_dep(name = "hermetic_launcher", version = "0.0.8") bazel_dep(name = "package_metadata", version = "0.0.7") bazel_dep(name = "platforms", version = "1.1.0") bazel_dep(name = "protobuf", version = "34.0.bcr.1", repo_name = "com_google_protobuf") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index c08fed1..1c8d29f 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -72,6 +72,8 @@ "https://bcr.bazel.build/modules/buildifier_prebuilt/6.1.2/source.json": "19fb45ed3f0d55cbff94e402c39512940833ae3a68f9cbfd9518a1926b609c7c", "https://bcr.bazel.build/modules/buildozer/8.2.1/MODULE.bazel": "61e9433c574c2bd9519cad7fa66b9c1d2b8e8d5f3ae5d6528a2c2d26e68d874d", "https://bcr.bazel.build/modules/buildozer/8.2.1/source.json": "7c33f6a26ee0216f85544b4bca5e9044579e0219b6898dd653f5fb449cf2e484", + "https://bcr.bazel.build/modules/bzip2/1.0.8.bcr.3/MODULE.bazel": "29ecf4babfd3c762be00d7573c288c083672ab60e79c833ff7f49ee662e54471", + "https://bcr.bazel.build/modules/bzip2/1.0.8.bcr.3/source.json": "8be4a3ef2599693f759e5c0990a4cc5a246ac08db4c900a38f852ba25b5c39be", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/MODULE.bazel": "f1b7bb2dd53e8f2ef984b39485ec8a44e9076dda5c4b8efd2fb4c6a6e856a31d", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/source.json": "ebe931bfe362e4b41e59ee00a528db6074157ff2ced92eb9e970acab2e1089c9", "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", @@ -88,6 +90,8 @@ "https://bcr.bazel.build/modules/googletest/1.15.2/MODULE.bazel": "6de1edc1d26cafb0ea1a6ab3f4d4192d91a312fd2d360b63adaa213cd00b2108", "https://bcr.bazel.build/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46", "https://bcr.bazel.build/modules/googletest/1.17.0/source.json": "38e4454b25fc30f15439c0378e57909ab1fd0a443158aa35aec685da727cd713", + "https://bcr.bazel.build/modules/hermetic_launcher/0.0.8/MODULE.bazel": "3be7b0faca6f1e69e89197999e0b01ce058c42f3e764ef028466f7e0ff77c761", + "https://bcr.bazel.build/modules/hermetic_launcher/0.0.8/source.json": "8403718636114198fca6ea04b437fa001ac7167b4d485102defb0a6f7d11bc08", "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", "https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0", "https://bcr.bazel.build/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000", @@ -235,6 +239,8 @@ "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/MODULE.bazel": "b573395fe63aef4299ba095173e2f62ccfee5ad9bbf7acaa95dba73af9fc2b38", "https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/source.json": "3f3fbaeafecaf629877ad152a2c9def21f8d330d91aa94c5dc75bbb98c10b8b8", + "https://bcr.bazel.build/modules/xz/5.4.5.bcr.8/MODULE.bazel": "e48a69bd54053c2ec5fffc2a29fb70122afd3e83ab6c07068f63bc6553fa57cc", + "https://bcr.bazel.build/modules/xz/5.4.5.bcr.8/source.json": "bd7e928ccd63505b44f4784f7bbf12cc11f9ff23bf3ca12ff2c91cd74846099e", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", diff --git a/rs/experimental/miri/BUILD.bazel b/rs/experimental/miri/BUILD.bazel new file mode 100644 index 0000000..433d621 --- /dev/null +++ b/rs/experimental/miri/BUILD.bazel @@ -0,0 +1,25 @@ +load("@bazel_lib//:bzl_library.bzl", "bzl_library") + +package(default_visibility = ["//visibility:private"]) + +toolchain_type( + name = "toolchain_type", + visibility = ["//visibility:public"], +) + +toolchain_type( + name = "sysroot_toolchain_type", + visibility = ["//visibility:public"], +) + +bzl_library( + name = "miri_test", + srcs = ["miri_test.bzl"], + visibility = ["//visibility:public"], + deps = [ + "//rs/experimental/miri/private:compile", + "//rs/experimental/miri/private:providers", + "//rs/experimental/miri/private:toolchain", + "@hermetic_launcher//launcher:lib_bzl", + ], +) diff --git a/rs/experimental/miri/miri_test.bzl b/rs/experimental/miri/miri_test.bzl new file mode 100644 index 0000000..dd807f9 --- /dev/null +++ b/rs/experimental/miri/miri_test.bzl @@ -0,0 +1,207 @@ +"""Miri test rule.""" + +load("@hermetic_launcher//launcher:lib.bzl", "launcher") +load("//rs/experimental/miri/private:compile.bzl", "alias_for_dep", "miri_compile_aspect", "miri_extern_arg", "miri_process_wrapper_args", "miri_transitive_outputs") +load("//rs/experimental/miri/private:providers.bzl", "MiriCrateInfo") +load("//rs/experimental/miri/private:toolchain.bzl", "MIRI_TOOLCHAIN_TYPE") + +def _default_crate_name(label): + return label.name.replace("-", "_") + +def _crate_root(ctx): + if ctx.file.crate_root: + return ctx.file.crate_root + + for src in ctx.files.srcs: + if src.basename in ("main.rs", "lib.rs"): + return src + + if len(ctx.files.srcs) == 1: + return ctx.files.srcs[0] + + fail("miri_test requires crate_root when srcs does not contain main.rs or lib.rs") + +def _extern_arg(dep): + return ["--extern", "{}={}".format(dep.name, dep.output.short_path)] + +def _dirname(file): + return file.dirname + +def _short_path_dirname(file): + path = file.short_path + idx = path.rfind("/") + return path[:idx] if idx != -1 else "." + +def _direct_deps(ctx): + direct_deps = [] + for dep in ctx.attr.deps: + if MiriCrateInfo not in dep: + continue + miri_dep = dep[MiriCrateInfo] + compiled_crate = miri_dep.target + direct_deps.append(struct( + name = alias_for_dep(ctx.attr.aliases, dep, miri_dep.crate_info), + output = compiled_crate.output, + transitive_inputs = compiled_crate.transitive_inputs, + transitive_outputs = compiled_crate.transitive_outputs, + )) + return direct_deps + +def _declare_test_executable(ctx): + name = ctx.label.name + if ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]): + name += ".exe" + return ctx.actions.declare_file(name) + +def _miri_compile_args(ctx, toolchain, crate_root, crate_name, direct_deps): + args = ctx.actions.args() + args.add(crate_root) + args.add_all([ + "--crate-name", + crate_name, + "--crate-type", + "bin", + "--edition", + ctx.attr.edition, + "--test", + "--target", + toolchain.target_triple, + "--cfg=miri", + ]) + args.add_all([toolchain.miri_sysroot], format_each = "--sysroot=%s", expand_directories = False) + args.add_all(ctx.attr.rustc_flags) + args.add_all(direct_deps, map_each = miri_extern_arg) + args.add_all( + miri_transitive_outputs(direct_deps), + map_each = _dirname, + format_each = "-Ldependency=%s", + uniquify = True, + ) + return args + +def _emit_miri_check_action(ctx, toolchain, crate_root, crate_name, direct_deps): + output = ctx.actions.declare_file(ctx.label.name + ".miri_check.rmeta") + args = _miri_compile_args(ctx, toolchain, crate_root, crate_name, direct_deps) + args.add(output, format = "--emit=metadata=%s") + + ctx.actions.run( + executable = toolchain.process_wrapper, + arguments = miri_process_wrapper_args(ctx, toolchain, [], args), + env = ctx.attr.env | { + "MIRI_BE_RUSTC": "target", + "MIRI_SYSROOT": toolchain.miri_sysroot.path, + "REPOSITORY_NAME": ctx.label.workspace_name, + }, + inputs = depset( + direct = ctx.files.srcs + ctx.files.data + [crate_root], + transitive = [toolchain.all_files] + [dep.transitive_inputs for dep in direct_deps], + ), + outputs = [output], + mnemonic = "MiriTestCheck", + progress_message = "Compiling Miri test %{label}", + toolchain = MIRI_TOOLCHAIN_TYPE, + ) + return output + +def _miri_runfiles(ctx, toolchain, crate_root, args_file, check_output, direct_deps): + runfiles = ctx.runfiles( + files = ctx.files.srcs + ctx.files.data + [crate_root, args_file, check_output, ctx.executable._runner], + transitive_files = depset(transitive = [toolchain.all_files] + [dep.transitive_inputs for dep in direct_deps]), + ) + return runfiles.merge_all( + [ctx.attr._runner[DefaultInfo].default_runfiles] + + [data[DefaultInfo].default_runfiles for data in ctx.attr.data if DefaultInfo in data], + ) + +def _write_runner_args(ctx, toolchain, crate_root, crate_name, direct_deps): + args_file = ctx.actions.declare_file(ctx.label.name + ".miri_runner_args") + args = ctx.actions.args() + args.set_param_file_format("multiline") + args.add_all([ + toolchain.miri.short_path, + "--sysroot", + toolchain.miri_sysroot.short_path, + crate_root.short_path, + "--crate-name", + crate_name, + "--crate-type", + "bin", + "--edition", + ctx.attr.edition, + "--test", + "--target", + toolchain.target_triple, + "--cfg=miri", + ]) + args.add_all(direct_deps, map_each = _extern_arg) + args.add_all( + miri_transitive_outputs(direct_deps), + map_each = _short_path_dirname, + format_each = "-Ldependency=%s", + uniquify = True, + ) + args.add_all(ctx.attr.rustc_flags) + args.add_all(ctx.attr.miri_flags) + args.add("--") + args.add_all(ctx.attr.args) + ctx.actions.write(args_file, args) + return args_file + +def _launcher_args(ctx, args_file): + embedded_args, transformed_args = launcher.args_from_entrypoint(ctx.executable._runner) + embedded_args.append("@" + args_file.short_path) + return embedded_args, transformed_args + +def _miri_test_impl(ctx): + toolchain = ctx.toolchains[MIRI_TOOLCHAIN_TYPE] + crate_root = _crate_root(ctx) + crate_name = ctx.attr.crate_name or _default_crate_name(ctx.label) + direct_deps = _direct_deps(ctx) + check_output = _emit_miri_check_action(ctx, toolchain, crate_root, crate_name, direct_deps) + args_file = _write_runner_args(ctx, toolchain, crate_root, crate_name, direct_deps) + + executable = _declare_test_executable(ctx) + embedded_args, transformed_args = _launcher_args(ctx, args_file) + launcher.compile_stub( + ctx = ctx, + embedded_args = embedded_args, + transformed_args = transformed_args, + output_file = executable, + ) + + return [ + DefaultInfo( + executable = executable, + files = depset([executable, check_output]), + runfiles = _miri_runfiles(ctx, toolchain, crate_root, args_file, check_output, direct_deps), + ), + RunEnvironmentInfo(environment = ctx.attr.env), + ] + +miri_test = rule( + implementation = _miri_test_impl, + attrs = { + "crate_name": attr.string(doc = "Rust crate name. Defaults to the target name with '-' replaced by '_'."), + "crate_root": attr.label(allow_single_file = [".rs"]), + "aliases": attr.label_keyed_string_dict(doc = "Remap direct dependencies to another extern crate name."), + "data": attr.label_list(allow_files = True), + "deps": attr.label_list(aspects = [miri_compile_aspect]), + "edition": attr.string(default = "2021"), + "env": attr.string_dict(doc = "Environment variables set while running Miri."), + "miri_flags": attr.string_list(doc = "Extra flags passed to the Miri driver."), + "rustc_flags": attr.string_list(doc = "Extra rustc-compatible flags passed to Miri."), + "srcs": attr.label_list(allow_files = [".rs"], mandatory = True), + "_runner": attr.label( + default = Label("//rs/experimental/miri/private:miri_test_runner"), + executable = True, + cfg = "target", + ), + "_windows_constraint": attr.label(default = "@platforms//os:windows"), + }, + test = True, + toolchains = [ + MIRI_TOOLCHAIN_TYPE, + launcher.finalizer_toolchain_type, + launcher.template_toolchain_type, + ], +) diff --git a/rs/experimental/miri/private/BUILD.bazel b/rs/experimental/miri/private/BUILD.bazel new file mode 100644 index 0000000..48a70d6 --- /dev/null +++ b/rs/experimental/miri/private/BUILD.bazel @@ -0,0 +1,69 @@ +load("@bazel_lib//:bzl_library.bzl", "bzl_library") +load("//rs:rust_binary.bzl", "rust_binary") + +package(default_visibility = ["//rs/experimental/miri:__pkg__"]) + +rust_binary( + name = "miri_test_runner", + srcs = ["miri_test_runner.rs"], + crate_name = "miri_test_runner", + edition = "2024", +) + +bzl_library( + name = "providers", + srcs = ["providers.bzl"], +) + +bzl_library( + name = "toolchain", + srcs = ["toolchain.bzl"], + deps = [":providers"], +) + +bzl_library( + name = "compile", + srcs = ["compile.bzl"], + deps = [ + ":providers", + ":toolchain", + "@bazel_skylib//lib:structs", + "@rules_cc//cc/common", + "@rules_rust//rust:bzl_lib", + "@rules_rust//rust/platform:bzl_lib", + "@rules_rust//rust/private:bzl_lib", + ], +) + +bzl_library( + name = "sysroot", + srcs = ["sysroot.bzl"], + deps = [ + ":compile", + ":providers", + ":toolchain", + "@bazel_lib//lib:copy_to_directory", + ], +) + +bzl_library( + name = "declare_toolchains", + srcs = ["declare_toolchains.bzl"], + deps = [ + ":sysroot", + ":toolchain", + "//rs/platforms:triples", + "//rs/toolchains:toolchain_utils", + "@rules_rust//rust/platform:bzl_lib", + ], +) + +bzl_library( + name = "miri_repository", + srcs = ["miri_repository.bzl"], + visibility = ["//rs/toolchains:__pkg__"], + deps = [ + "//rs/private:rust_repository_utils", + "@rules_rust//rust/platform:bzl_lib", + ], +) diff --git a/rs/experimental/miri/private/compile.bzl b/rs/experimental/miri/private/compile.bzl new file mode 100644 index 0000000..1a2a805 --- /dev/null +++ b/rs/experimental/miri/private/compile.bzl @@ -0,0 +1,525 @@ +"""Miri compile aspects.""" + +load("@bazel_skylib//lib:structs.bzl", "structs") +load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") +load( + "@rules_rust//rust:rust_common.bzl", + "BuildInfo", + "CrateGroupInfo", + "CrateInfo", + "DepInfo", + "DepVariantInfo", + "TestCrateInfo", +) +load("@rules_rust//rust/platform:triple.bzl", _parse_triple = "triple") +load("@rules_rust//rust/private:rustc.bzl", "rustc_compile") +load("@rules_rust//rust/private:utils.bzl", "determine_output_hash") +load("//rs/experimental/miri/private:providers.bzl", "MiriCrateInfo") +load("//rs/experimental/miri/private:toolchain.bzl", "MIRI_SYSROOT_TOOLCHAIN_TYPE", "MIRI_TOOLCHAIN_TYPE") + +_CC_TOOLCHAIN_TYPE = "@bazel_tools//tools/cpp:toolchain_type" +_RUST_TOOLCHAIN_TYPE = "@rules_rust//rust:toolchain_type" + +def _find_library_dir(rust_srcs): + for src in rust_srcs: + if src.path.endswith("/library/Cargo.toml"): + return src.dirname + fail("rust_srcs must contain library/Cargo.toml from the Rust source tree") + +def _sanitize_for_rustc_metadata(value): + allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + result = [] + for char in value.elems(): + result.append(char if char in allowed else "_") + return "".join(result) + +def _crate_provider(target): + if CrateInfo in target: + return target[CrateInfo] + if TestCrateInfo in target: + return target[TestCrateInfo].crate + return None + +def _rustc_files(target, crate): + env_files = list(crate.rustc_env_files) + flag_files = [] + if DepInfo not in target: + return struct(env_files = env_files, flag_files = flag_files) + + dep_env = target[DepInfo].dep_env + if dep_env: + env_files.append(dep_env) + + for build_info in target[DepInfo].transitive_build_infos.to_list(): + if build_info.rustc_env: + env_files.append(build_info.rustc_env) + if build_info.flags: + flag_files.append(build_info.flags) + + return struct(env_files = env_files, flag_files = flag_files) + +def alias_for_dep(aliases, dep, dep_crate): + for candidate in [ + dep, + dep.label, + dep_crate.owner, + ]: + if candidate in aliases: + return aliases[candidate] + return dep_crate.name + +def _direct_crate_aliases(target): + if DepInfo not in target: + return {} + + aliases = {} + for dep in target[DepInfo].direct_crates.to_list(): + aliases[dep.dep.owner] = dep.name + return aliases + +def _miri_direct_dep_infos(owner_crate, deps, aliases, direct_crate_aliases, use_host_outputs = False): + resolved_aliases = dict(owner_crate.aliases) + resolved_aliases.update(aliases) + resolved_aliases.update(direct_crate_aliases) + + direct_deps = [] + for dep in deps: + if MiriCrateInfo not in dep: + continue + miri_dep = dep[MiriCrateInfo] + compiled_crate = miri_dep.host if use_host_outputs else miri_dep.target + direct_deps.append(struct( + name = alias_for_dep(resolved_aliases, dep, miri_dep.crate_info), + output = compiled_crate.output, + transitive_inputs = compiled_crate.transitive_inputs, + transitive_outputs = compiled_crate.transitive_outputs, + )) + return direct_deps + +def _miri_dep_variants(deps, use_host_outputs = False, proc_macro_only = False): + variants = [] + for dep in deps: + crate_info = None + dep_info = None + if MiriCrateInfo in dep: + if proc_macro_only and dep[MiriCrateInfo].crate_info.type != "proc-macro": + continue + compiled_crate = dep[MiriCrateInfo].host if use_host_outputs else dep[MiriCrateInfo].target + crate_info = compiled_crate.crate_info + dep_info = compiled_crate.dep_info + elif CrateInfo in dep: + if proc_macro_only and dep[CrateInfo].type != "proc-macro": + continue + fail("{} does not provide MiriCrateInfo".format(dep.label)) + + if crate_info or BuildInfo in dep or CcInfo in dep or CrateGroupInfo in dep: + variants.append(DepVariantInfo( + build_info = dep[BuildInfo] if BuildInfo in dep else None, + cc_info = dep[CcInfo] if CcInfo in dep else None, + crate_group_info = dep[CrateGroupInfo] if CrateGroupInfo in dep else None, + crate_info = crate_info, + dep_info = dep_info, + )) + return variants + +def miri_transitive_outputs(direct_deps): + return depset(transitive = [dep.transitive_outputs for dep in direct_deps]) + +def miri_extern_arg(dep): + return ["--extern", "{}={}".format(dep.name, dep.output.path)] + +def _dirname(file): + return file.dirname + +def miri_process_wrapper_args(ctx, toolchain, rustc_env_files, miri_args): + process_wrapper_args = ctx.actions.args() + process_wrapper_args.add_all(rustc_env_files, before_each = "--env-file") + process_wrapper_args.add("--subst", "pwd=${pwd}") + process_wrapper_args.add("--subst", "exec_root=${exec_root}") + process_wrapper_args.add("--subst", "output_base=${output_base}") + + miri_path = ctx.actions.args() + miri_path.add("--") + miri_path.add(toolchain.miri) + + return [process_wrapper_args, miri_path, miri_args] + +def _target_rlib_output(ctx, crate, prefix): + metadata = _sanitize_for_rustc_metadata(str(ctx.label)) + return ctx.actions.declare_file("{}/{}/lib{}-{}.rlib".format( + prefix, + ctx.label.name, + crate.name, + metadata, + )) + +def _host_output(ctx, crate): + if crate.type == "proc-macro": + return ctx.actions.declare_file("miri_host/{}/{}".format( + ctx.label.name, + crate.output.basename, + )) + return _target_rlib_output(ctx, crate, "miri_host") + +def _host_output_hash(ctx, crate): + if crate.type == "proc-macro": + return determine_output_hash(crate.root, ctx.label) + return _sanitize_for_rustc_metadata(str(ctx.label)) + +def _emit_miri_rustc_action( + *, + ctx, + crate, + toolchain, + direct_deps, + output, + rustc_env_files, + rustc_flag_files, + target_triple, + sysroot, + env, + mnemonic, + progress_message, + cfg_miri = False): + metadata = _sanitize_for_rustc_metadata(str(ctx.label)) + + args = ctx.actions.args() + args.add(crate.root) + args.add(crate.name, format = "--crate-name=%s") + args.add(crate.type, format = "--crate-type=%s") + args.add(crate.edition, format = "--edition=%s") + args.add(target_triple, format = "--target=%s") + args.add_all([sysroot], format_each = "--sysroot=%s", expand_directories = False) + if cfg_miri: + args.add("--cfg=miri") + args.add(metadata, format = "-Cmetadata=%s") + args.add(metadata, format = "-Cextra-filename=-%s") + args.add(output, format = "--emit=link=%s") + args.add_all(ctx.rule.attr.crate_features, before_each = "--cfg", format_each = 'feature="%s"') + args.add_all(ctx.rule.attr.rustc_flags) + args.add_all(rustc_flag_files, format_each = "@%s") + + args.add_all(direct_deps, map_each = miri_extern_arg) + args.add_all( + miri_transitive_outputs(direct_deps), + map_each = _dirname, + format_each = "-Ldependency=%s", + uniquify = True, + ) + + transitive_inputs = [dep.transitive_inputs for dep in direct_deps] + transitive_outputs = [dep.transitive_outputs for dep in direct_deps] + direct_inputs = [crate.root, sysroot] + rustc_env_files + rustc_flag_files + compile_inputs = depset( + direct = direct_inputs, + transitive = [ + crate.srcs, + crate.compile_data, + toolchain.all_files, + ] + transitive_inputs, + ) + + ctx.actions.run( + executable = toolchain.process_wrapper, + arguments = miri_process_wrapper_args(ctx, toolchain, rustc_env_files, args), + env = env, + inputs = compile_inputs, + outputs = [output], + mnemonic = mnemonic, + progress_message = progress_message, + toolchain = MIRI_TOOLCHAIN_TYPE, + ) + + return struct( + transitive_inputs = transitive_inputs, + transitive_outputs = transitive_outputs, + ) + +def _compiled_crate(output, transitive_inputs, transitive_outputs): + return struct( + output = output, + transitive_inputs = depset( + [output], + transitive = transitive_inputs, + ), + transitive_outputs = depset([output], transitive = transitive_outputs), + ) + +def _miri_compile_action( + *, + ctx, + crate, + toolchain, + direct_deps, + output, + rustc_files, + target_triple, + sysroot, + env, + mnemonic, + progress_message, + cfg_miri = False): + action = _emit_miri_rustc_action( + ctx = ctx, + crate = crate, + toolchain = toolchain, + direct_deps = direct_deps, + output = output, + rustc_env_files = rustc_files.env_files, + rustc_flag_files = rustc_files.flag_files, + target_triple = target_triple, + sysroot = sysroot, + env = env, + mnemonic = mnemonic, + progress_message = progress_message, + cfg_miri = cfg_miri, + ) + return _compiled_crate(output, action.transitive_inputs, action.transitive_outputs) + +def _miri_rustc_compile_toolchain(toolchain, rust_toolchain, sysroot, target_triple): + parsed_target = _parse_triple(target_triple) + fields = structs.to_dict(rust_toolchain) + fields.update({ + "all_files": depset([sysroot], transitive = [toolchain.all_files]), + "channel": "nightly", + "process_wrapper": toolchain.process_wrapper, + "rust_std": [], + "rustc": toolchain.miri, + "target_abi": parsed_target.abi, + "target_arch": parsed_target.arch, + "target_flag_value": target_triple, + "target_json": None, + "target_os": parsed_target.system, + "target_triple": parsed_target, + "_bootstrapping": False, + "_toolchain_generated_sysroot": False, + }) + return struct(**fields) + +def _miri_host_crate_info_dict(crate, output, deps, proc_macro_deps): + crate_info = structs.to_dict(crate) + crate_info.update({ + "deps": deps, + "metadata": None, + "metadata_supports_pipelining": False, + "output": output, + "proc_macro_deps": proc_macro_deps, + "rustc_output": None, + "rustc_rmeta_output": None, + "srcs": crate.srcs.to_list(), + }) + return crate_info + +def _miri_host_compile_action(ctx, crate, toolchain, rust_toolchain, deps, proc_macro_deps): + output = _host_output(ctx, crate) + rust_flags = ctx.actions.args() + rust_flags.add_all([toolchain.host_miri_sysroot], format_each = "--sysroot=%s", expand_directories = False) + + result = rustc_compile( + ctx = ctx, + attr = ctx.rule.attr, + rust_toolchain = _miri_rustc_compile_toolchain( + toolchain, + rust_toolchain, + toolchain.host_miri_sysroot, + toolchain.exec_triple, + ), + tool_file = toolchain.miri, + toolchain = MIRI_TOOLCHAIN_TYPE, + crate_info_dict = _miri_host_crate_info_dict(crate, output, deps, proc_macro_deps), + env = { + "MIRI_BE_RUSTC": "host", + "MIRI_SYSROOT": toolchain.host_miri_sysroot.path, + "REPOSITORY_NAME": ctx.label.workspace_name, + }, + file = ctx.rule.file, + files = ctx.rule.files, + include_coverage = False, + mnemonic = "MiriHostRustc", + output_hash = _host_output_hash(ctx, crate), + progress_message = "Compiling Rust host dependency for Miri %{label}", + rust_flags = rust_flags, + skip_expanding_rustc_env = True, + ) + + return struct( + crate_info = result.crate_info, + dep_info = result.dep_info, + output = result.crate_info.output, + transitive_inputs = depset([result.crate_info.output], transitive = [result.compile_inputs]), + transitive_outputs = depset([result.crate_info.output], transitive = [result.dep_info.transitive_crate_outputs]), + ) + +def _miri_compile_aspect_impl(target, ctx): + crate = _crate_provider(target) + if not crate: + return [] + + if crate.type not in ("lib", "rlib", "proc-macro"): + return [] + + toolchain = ctx.toolchains[MIRI_TOOLCHAIN_TYPE] + rust_toolchain = ctx.toolchains[_RUST_TOOLCHAIN_TYPE] + rustc_files = _rustc_files(target, crate) + deps = ctx.rule.attr.deps + proc_macro_deps = ctx.rule.attr.proc_macro_deps + aliases = ctx.rule.attr.aliases + direct_crate_aliases = _direct_crate_aliases(target) + direct_deps = _miri_direct_dep_infos( + crate, + deps, + aliases, + direct_crate_aliases, + ) + host = _miri_host_compile_action( + ctx = ctx, + crate = crate, + toolchain = toolchain, + rust_toolchain = rust_toolchain, + deps = _miri_dep_variants(deps, use_host_outputs = True), + proc_macro_deps = _miri_dep_variants(proc_macro_deps, use_host_outputs = True, proc_macro_only = True), + ) + + if crate.type == "proc-macro": + host = struct( + output = host.output, + transitive_inputs = depset(transitive = [host.transitive_inputs, crate.data]), + transitive_outputs = host.transitive_outputs, + ) + return [MiriCrateInfo( + crate_info = crate, + host = host, + target = host, + )] + + # Miri uses MIRI_BE_RUSTC=target to compile dependency rlibs with + # MIR-preserving defaults, then runs normally for the root crate. + target = _miri_compile_action( + ctx = ctx, + crate = crate, + toolchain = toolchain, + direct_deps = direct_deps, + output = _target_rlib_output(ctx, crate, "miri"), + rustc_files = rustc_files, + target_triple = toolchain.target_triple, + sysroot = toolchain.miri_sysroot, + cfg_miri = True, + env = crate.rustc_env | { + "MIRI_BE_RUSTC": "target", + "MIRI_SYSROOT": toolchain.miri_sysroot.path, + "REPOSITORY_NAME": ctx.label.workspace_name, + }, + mnemonic = "MiriRustc", + progress_message = "Compiling Rust dependency for Miri %{label}", + ) + + return [MiriCrateInfo( + crate_info = crate, + host = host, + target = target, + )] + +miri_compile_aspect = aspect( + implementation = _miri_compile_aspect_impl, + attr_aspects = ["deps", "proc_macro_deps"], + fragments = ["cpp"], + toolchains = [ + MIRI_TOOLCHAIN_TYPE, + _RUST_TOOLCHAIN_TYPE, + _CC_TOOLCHAIN_TYPE, + ], +) + +def _miri_sysroot_compile_aspect_impl(target, ctx): + crate = _crate_provider(target) + if not crate: + return [] + + if crate.type == "proc-macro": + fail("Miri sysroot build does not support proc-macro crate targets: {}".format(ctx.label)) + + if crate.type not in ("lib", "rlib"): + return [] + + toolchain = ctx.toolchains[MIRI_SYSROOT_TOOLCHAIN_TYPE] + direct_deps = _miri_direct_dep_infos( + crate, + ctx.rule.attr.deps, + ctx.rule.attr.aliases, + _direct_crate_aliases(target), + ) + transitive_outputs = [dep.transitive_outputs for dep in direct_deps] + transitive_inputs = [dep.transitive_inputs for dep in direct_deps] + + metadata = _sanitize_for_rustc_metadata(str(ctx.label)) + output = ctx.actions.declare_file("miri_sysroot/{}/lib{}-{}.rlib".format(ctx.label.name, crate.name, metadata)) + rustc_files = _rustc_files(target, crate) + + args = ctx.actions.args() + args.add(crate.root) + args.add(crate.name, format = "--crate-name=%s") + args.add(crate.type, format = "--crate-type=%s") + args.add(crate.edition, format = "--edition=%s") + args.add(toolchain.target_triple, format = "--target=%s") + args.add("--cfg=miri") + args.add("-Zforce-unstable-if-unmarked") + args.add("-Aunexpected_cfgs") + args.add("-Cdebug-assertions=off") + args.add("-Coverflow-checks=on") + args.add("-Cpanic=abort" if crate.name == "panic_abort" else "-Cpanic=unwind") + args.add(metadata, format = "-Cmetadata=%s") + args.add(metadata, format = "-Cextra-filename=-%s") + args.add(output, format = "--emit=link=%s") + args.add_all(ctx.rule.attr.crate_features, before_each = "--cfg", format_each = 'feature="%s"') + args.add_all(ctx.rule.attr.rustc_flags) + args.add_all(rustc_files.flag_files, format_each = "@%s") + + args.add_all(direct_deps, map_each = miri_extern_arg) + args.add_all( + miri_transitive_outputs(direct_deps), + map_each = _dirname, + format_each = "-Ldependency=%s", + uniquify = True, + ) + + library_dir = _find_library_dir(toolchain.rustc_srcs) + env = crate.rustc_env | { + "MIRI_BE_RUSTC": "target", + "MIRI_CALLED_FROM_SETUP": "1", + "MIRI_LIB_SRC": library_dir, + "MIRI_SYSROOT": output.dirname, + "REPOSITORY_NAME": ctx.label.workspace_name, + } + + compile_inputs = depset( + direct = [crate.root] + rustc_files.env_files + rustc_files.flag_files, + transitive = [ + crate.srcs, + crate.compile_data, + toolchain.all_files, + ] + transitive_inputs, + ) + + ctx.actions.run( + executable = toolchain.process_wrapper, + arguments = miri_process_wrapper_args(ctx, toolchain, rustc_files.env_files, args), + env = env, + inputs = compile_inputs, + outputs = [output], + mnemonic = "MiriSysrootRustc", + progress_message = "Compiling Rust sysroot crate for Miri %{label}", + toolchain = MIRI_SYSROOT_TOOLCHAIN_TYPE, + ) + + compiled = _compiled_crate(output, transitive_inputs, transitive_outputs) + return [MiriCrateInfo( + crate_info = crate, + host = compiled, + target = compiled, + )] + +miri_sysroot_compile_aspect = aspect( + implementation = _miri_sysroot_compile_aspect_impl, + attr_aspects = ["deps"], + toolchains = [MIRI_SYSROOT_TOOLCHAIN_TYPE], +) diff --git a/rs/experimental/miri/private/declare_toolchains.bzl b/rs/experimental/miri/private/declare_toolchains.bzl new file mode 100644 index 0000000..c35ae9b --- /dev/null +++ b/rs/experimental/miri/private/declare_toolchains.bzl @@ -0,0 +1,82 @@ +load("@rules_rust//rust/platform:triple.bzl", _parse_triple = "triple") +load("//rs/experimental/miri/private:sysroot.bzl", "miri_sysroot") +load("//rs/experimental/miri/private:toolchain.bzl", "miri_sysroot_toolchain", "miri_toolchain") +load("//rs/platforms:triples.bzl", "ALL_TARGET_TRIPLES", "SUPPORTED_EXEC_TRIPLES", "triple_to_constraint_set") +load("//rs/toolchains:toolchain_utils.bzl", "sanitize_triple", "sanitize_version") + +def declare_miri_toolchains( + *, + version, + execs = SUPPORTED_EXEC_TRIPLES): + version_key = sanitize_version(version) + selected_target_triple = select({ + "@rules_rs//rs/platforms/config:" + target_triple: target_triple + for target_triple in ALL_TARGET_TRIPLES + }) + source_sysroot_name = "miri_sysroot_" + version_key + generated_stdlib_root = "@rustc_src_%s//src:sysroot" % version_key + rust_src_repo_label = "@rustc_src_%s//src:rustc_srcs" % version_key + + miri_sysroot( + name = source_sysroot_name, + roots = [generated_stdlib_root], + ) + + for triple in execs: + exec_triple = _parse_triple(triple) + triple_suffix = exec_triple.system + "_" + exec_triple.arch + exec_compatible_with = [ + "@platforms//os:" + exec_triple.system, + "@platforms//cpu:" + exec_triple.arch, + ] + + miri_repo_label = "@miri_%s_%s//:" % (triple_suffix, version_key) + toolchain_kwargs = dict( + exec_triple = triple, + miri = miri_repo_label + "miri", + rustc_lib = miri_repo_label + "rustc_lib", + target_triple = selected_target_triple, + ) + + sysroot_toolchain_name = "%s_%s_miri_sysroot_toolchain" % (triple_suffix, version_key) + miri_sysroot_toolchain( + name = sysroot_toolchain_name, + rustc_srcs = rust_src_repo_label, + **toolchain_kwargs + ) + + host_source_sysroot_name = "%s_%s_miri_host_sysroot" % (triple_suffix, version_key) + miri_sysroot( + name = host_source_sysroot_name, + srcs = ["@rust_stdlib_%s_%s//:rust_std-%s" % (sanitize_triple(triple), version_key, triple)], + ) + + toolchain_name = "%s_%s_miri_toolchain" % (triple_suffix, version_key) + miri_toolchain( + name = toolchain_name, + host_source_sysroot = host_source_sysroot_name, + source_sysroot = source_sysroot_name, + **toolchain_kwargs + ) + + for target_triple in ALL_TARGET_TRIPLES: + target_key = sanitize_triple(target_triple) + target_compatible_with = triple_to_constraint_set(target_triple) + + native.toolchain( + name = "%s_to_%s_%s_miri_sysroot" % (triple_suffix, target_key, version_key), + exec_compatible_with = exec_compatible_with, + target_compatible_with = target_compatible_with, + toolchain = sysroot_toolchain_name, + toolchain_type = "@rules_rs//rs/experimental/miri:sysroot_toolchain_type", + visibility = ["//visibility:public"], + ) + + native.toolchain( + name = "%s_to_%s_%s_miri" % (triple_suffix, target_key, version_key), + exec_compatible_with = exec_compatible_with, + target_compatible_with = target_compatible_with, + toolchain = toolchain_name, + toolchain_type = "@rules_rs//rs/experimental/miri:toolchain_type", + visibility = ["//visibility:public"], + ) diff --git a/rs/experimental/miri/private/miri_repository.bzl b/rs/experimental/miri/private/miri_repository.bzl new file mode 100644 index 0000000..3fd3c89 --- /dev/null +++ b/rs/experimental/miri/private/miri_repository.bzl @@ -0,0 +1,54 @@ +load("@rules_rust//rust/platform:triple.bzl", "triple") +load("@rules_rust//rust/platform:triple_mappings.bzl", "system_to_binary_ext", "system_to_dylib_ext") +load("//rs/private:rust_repository_utils.bzl", "RUST_REPOSITORY_COMMON_ATTR", "rust_tool_archive") + +_BUILD_FOR_MIRI_TEMPLATE = """\ +filegroup( + name = "miri", + srcs = ["bin/miri{binary_ext}"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "rustc_lib", + srcs = glob( + [ + "bin/*{dylib_ext}", + "lib/*{dylib_ext}*", + "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}", + "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", + "lib/rustlib/{target_triple}/lib/*.rmeta", + ], + allow_empty = True, + ), + visibility = ["//visibility:public"], +) +""" + +def _miri_repository_impl(rctx): + exec_triple = triple(rctx.attr.triple) + miri = rust_tool_archive(rctx, "miri", "miri-preview", exec_triple) + rustc = rust_tool_archive(rctx, "rustc", "rustc", exec_triple, sha256 = rctx.attr.rustc_sha256) + miri_download = rctx.download(miri.urls, miri.output, sha256 = miri.sha256, auth = miri.auth, block = False) + rustc_download = rctx.download(rustc.urls, rustc.output, sha256 = rustc.sha256, auth = rustc.auth, block = False) + miri_download.wait() + rustc_download.wait() + rctx.extract(miri.output, strip_prefix = miri.strip_prefix) + rctx.extract(rustc.output, strip_prefix = rustc.strip_prefix) + rctx.file( + "BUILD.bazel", + _BUILD_FOR_MIRI_TEMPLATE.format( + binary_ext = system_to_binary_ext(exec_triple.system), + dylib_ext = system_to_dylib_ext(exec_triple.system), + target_triple = exec_triple.str, + ), + ) + + return rctx.repo_metadata(reproducible = True) + +miri_repository = repository_rule( + implementation = _miri_repository_impl, + attrs = { + "rustc_sha256": attr.string(mandatory = True), + } | RUST_REPOSITORY_COMMON_ATTR, +) diff --git a/rs/experimental/miri/private/miri_test_runner.rs b/rs/experimental/miri/private/miri_test_runner.rs new file mode 100644 index 0000000..c92aaae --- /dev/null +++ b/rs/experimental/miri/private/miri_test_runner.rs @@ -0,0 +1,50 @@ +use std::env; +use std::ffi::OsString; +use std::fs; +use std::path::PathBuf; +use std::process::{self, Command}; + +fn load_args_file() -> Result, String> { + let args = env::args_os().skip(1).collect::>(); + if args.len() != 1 { + return Err("usage: miri_test_runner @args-file".to_owned()); + } + + let arg = args[0].to_string_lossy(); + let Some(args_file) = arg.strip_prefix('@') else { + return Err(format!("expected @args-file, got {}", args[0].to_string_lossy())); + }; + if args_file.is_empty() { + return Err("missing args file after @".to_owned()); + } + + let args_file = PathBuf::from(args_file); + let content = fs::read_to_string(&args_file) + .map_err(|err| format!("failed to read {}: {err}", args_file.display()))?; + Ok(content.lines().map(OsString::from).collect()) +} + +fn run() -> Result { + let mut args = load_args_file()?.into_iter(); + let miri = args + .next() + .ok_or_else(|| "missing miri path in args file".to_owned())?; + + let mut command = Command::new(miri); + command.args(args); + + let status = command + .status() + .map_err(|err| format!("failed to run Miri: {err}"))?; + Ok(status.code().unwrap_or(1)) +} + +fn main() { + match run() { + Ok(code) => process::exit(code), + Err(err) => { + eprintln!("{err}"); + process::exit(1); + } + } +} diff --git a/rs/experimental/miri/private/providers.bzl b/rs/experimental/miri/private/providers.bzl new file mode 100644 index 0000000..cbe8cfc --- /dev/null +++ b/rs/experimental/miri/private/providers.bzl @@ -0,0 +1,18 @@ +"""Providers for Miri rules.""" + +MiriCrateInfo = provider( + doc = "A Rust crate compiled by Miri-as-rustc for use by miri_test.", + fields = { + "crate_info": "The source crate's rules_rust CrateInfo.", + "host": "Compiled crate struct for Miri host-mode rustc/proc-macro use.", + "target": "Compiled crate struct for Miri target-mode use.", + }, +) + +MiriSysrootInfo = provider( + doc = "A sysroot prepared for Miri execution.", + fields = { + "sysroot": "The sysroot directory artifact.", + "target_triple": "The target triple the sysroot was built for.", + }, +) diff --git a/rs/experimental/miri/private/sysroot.bzl b/rs/experimental/miri/private/sysroot.bzl new file mode 100644 index 0000000..f71b371 --- /dev/null +++ b/rs/experimental/miri/private/sysroot.bzl @@ -0,0 +1,78 @@ +"""Miri sysroot rules.""" + +load("@bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory_bin_action") +load("//rs/experimental/miri/private:compile.bzl", "miri_sysroot_compile_aspect") +load("//rs/experimental/miri/private:providers.bzl", "MiriCrateInfo", "MiriSysrootInfo") +load("//rs/experimental/miri/private:toolchain.bzl", "MIRI_SYSROOT_TOOLCHAIN_TYPE") + +_COPY_TO_DIRECTORY_TOOLCHAIN_TYPE = "@bazel_lib//lib:copy_to_directory_toolchain_type" + +def _repository_relative_path(file): + if file.short_path.startswith("../"): + return file.short_path[file.short_path.find("/", 3) + 1:] + return file.short_path + +def _miri_sysroot_impl(ctx): + toolchain = ctx.toolchains[MIRI_SYSROOT_TOOLCHAIN_TYPE] + srcs_by_path = {src.short_path: src for src in ctx.files.srcs} + + for root in ctx.attr.roots: + if MiriCrateInfo not in root: + fail("{} must provide MiriCrateInfo; only Rust library roots can form a Miri sysroot".format(root.label)) + for output in root[MiriCrateInfo].target.transitive_outputs.to_list(): + srcs_by_path[output.short_path] = output + + if not srcs_by_path: + fail("miri_sysroot requires at least one src or root") + + sysroot = ctx.actions.declare_directory(ctx.label.name) + lib_dir = "lib/rustlib/{}/lib".format(toolchain.target_triple) + srcs = [srcs_by_path[path] for path in sorted(srcs_by_path.keys())] + replace_prefixes = {} + basenames = set() + for src in srcs: + if src.basename in basenames: + fail("Miri sysroot input basename '{}' is not unique".format(src.basename)) + basenames.add(src.basename) + replace_prefixes[_repository_relative_path(src)] = "{}/{}".format(lib_dir, src.basename) + + copy_to_directory_bin_action( + ctx, + name = ctx.label.name, + dst = sysroot, + copy_to_directory_bin = ctx.toolchains[_COPY_TO_DIRECTORY_TOOLCHAIN_TYPE].copy_to_directory_info.bin, + files = srcs, + root_paths = [], + include_external_repositories = ["**"], + replace_prefixes = replace_prefixes, + hardlink = "off", + ) + + return [ + MiriSysrootInfo( + sysroot = sysroot, + target_triple = toolchain.target_triple, + ), + DefaultInfo( + files = depset([sysroot]), + runfiles = ctx.runfiles([sysroot]), + ), + ] + +miri_sysroot = rule( + implementation = _miri_sysroot_impl, + attrs = { + "roots": attr.label_list( + aspects = [miri_sysroot_compile_aspect], + doc = "Root Rust libraries from the Rust source graph whose Miri-compiled transitive outputs form the sysroot.", + ), + "srcs": attr.label_list( + allow_files = True, + doc = "Rust sysroot crate artifacts to place under lib/rustlib//lib.", + ), + }, + toolchains = [ + MIRI_SYSROOT_TOOLCHAIN_TYPE, + _COPY_TO_DIRECTORY_TOOLCHAIN_TYPE, + ], +) diff --git a/rs/experimental/miri/private/toolchain.bzl b/rs/experimental/miri/private/toolchain.bzl new file mode 100644 index 0000000..39ae112 --- /dev/null +++ b/rs/experimental/miri/private/toolchain.bzl @@ -0,0 +1,90 @@ +"""Miri toolchain rules.""" + +load("//rs/experimental/miri/private:providers.bzl", "MiriSysrootInfo") + +MIRI_TOOLCHAIN_TYPE = str(Label("//rs/experimental/miri:toolchain_type")) +MIRI_SYSROOT_TOOLCHAIN_TYPE = str(Label("//rs/experimental/miri:sysroot_toolchain_type")) + +_COMMON_ATTRS = { + "exec_triple": attr.string(mandatory = True), + "miri": attr.label(allow_single_file = True, cfg = "exec", mandatory = True), + "process_wrapper": attr.label( + default = Label("@rules_rust//util/process_wrapper"), + cfg = "exec", + executable = True, + doc = "Executable wrapper used to apply rustc env files and execute Miri-as-rustc.", + ), + "rustc_lib": attr.label(allow_files = True, cfg = "exec", mandatory = True), + "target_triple": attr.string(mandatory = True), +} + +def _all_files(ctx, direct = [], transitive = []): + return depset( + [ctx.file.miri] + direct, + transitive = [ + depset(ctx.files.rustc_lib), + ] + transitive, + ) + +def _toolchain_info(ctx, all_files, extra = None): + fields = { + "all_files": all_files, + "exec_triple": ctx.attr.exec_triple, + "miri": ctx.file.miri, + "process_wrapper": ctx.executable.process_wrapper, + "target_triple": ctx.attr.target_triple, + } + fields.update(extra or {}) + return platform_common.ToolchainInfo(**fields) + +def _miri_toolchain_impl(ctx): + source_sysroot = ctx.attr.source_sysroot[MiriSysrootInfo] + if source_sysroot.target_triple != ctx.attr.target_triple: + fail("source_sysroot target triple {} does not match toolchain target triple {}".format( + source_sysroot.target_triple, + ctx.attr.target_triple, + )) + host_source_sysroot = ctx.attr.host_source_sysroot[MiriSysrootInfo] + if host_source_sysroot.target_triple != ctx.attr.exec_triple: + fail("host_source_sysroot target triple {} does not match toolchain execution triple {}".format( + host_source_sysroot.target_triple, + ctx.attr.exec_triple, + )) + + return [ + _toolchain_info(ctx, _all_files(ctx, direct = [source_sysroot.sysroot]), { + "host_miri_sysroot": host_source_sysroot.sysroot, + "miri_sysroot": source_sysroot.sysroot, + }), + ] + +miri_toolchain = rule( + implementation = _miri_toolchain_impl, + attrs = _COMMON_ATTRS | { + "host_source_sysroot": attr.label( + cfg = "exec", + providers = [MiriSysrootInfo], + mandatory = True, + doc = "Bazel-prepared Miri sysroot for host-mode rustc work such as proc macros.", + ), + "source_sysroot": attr.label( + providers = [MiriSysrootInfo], + mandatory = True, + doc = "Bazel-prepared Miri sysroot.", + ), + }, +) + +def _miri_sysroot_toolchain_impl(ctx): + return [ + _toolchain_info(ctx, _all_files(ctx, transitive = [depset(ctx.files.rustc_srcs)]), { + "rustc_srcs": ctx.files.rustc_srcs, + }), + ] + +miri_sysroot_toolchain = rule( + implementation = _miri_sysroot_toolchain_impl, + attrs = _COMMON_ATTRS | { + "rustc_srcs": attr.label(allow_files = True, mandatory = True), + }, +) diff --git a/rs/private/rust_repository_utils.bzl b/rs/private/rust_repository_utils.bzl index 0fbf0a7..563ff8b 100644 --- a/rs/private/rust_repository_utils.bzl +++ b/rs/private/rust_repository_utils.bzl @@ -6,17 +6,33 @@ load( "produce_tool_suburl", ) -def download_and_extract(rctx, tool, dir, triple, sha256 = None): +def _archive_extension(urls): + url = urls[0] if urls else "" + if url.endswith(".tar.gz"): + return ".tar.gz" + if url.endswith(".tar.xz"): + return ".tar.xz" + return "" + +def rust_tool_archive(rctx, tool, dir, triple, sha256 = None): tool_suburl = produce_tool_suburl(tool, triple, rctx.attr.version, rctx.attr.iso_date) urls = [url.format(tool_suburl) for url in rctx.attr.urls] - tool_path = produce_tool_path(tool, rctx.attr.version, triple) - - rctx.download_and_extract( - urls, - sha256 = sha256 or rctx.attr.sha256, + return struct( auth = get_auth(rctx, urls), + output = tool_suburl.replace("/", "_").replace(":", "_") + _archive_extension(urls), + sha256 = sha256 or rctx.attr.sha256, strip_prefix = "{}/{}".format(tool_path, dir), + urls = urls, + ) + +def download_and_extract(rctx, tool, dir, triple, sha256 = None): + archive = rust_tool_archive(rctx, tool, dir, triple, sha256 = sha256) + rctx.download_and_extract( + archive.urls, + sha256 = archive.sha256, + auth = archive.auth, + strip_prefix = archive.strip_prefix, ) RUST_REPOSITORY_COMMON_ATTR = { diff --git a/rs/private/rustc_src_repository.bzl b/rs/private/rustc_src_repository.bzl index c1b962a..5ffcbb0 100644 --- a/rs/private/rustc_src_repository.bzl +++ b/rs/private/rustc_src_repository.bzl @@ -4,7 +4,7 @@ load( "@rules_rust//rust/private:repository_utils.bzl", "DEFAULT_STATIC_RUST_URL_TEMPLATES", ) -load("//rs/platforms:triples.bzl", "SUPPORTED_TIER_3_TRIPLES") +load("//rs/platforms:triples.bzl", "ALL_TARGET_TRIPLES") load( "//rs/private:cargo_workspace_graph.bzl", "fq_crate", @@ -53,10 +53,24 @@ def _rustc_src_tool_suburl(version, iso_date = None): path = _rustc_src_tool_path(version) return iso_date + "/" + path if (iso_date and version in ("beta", "nightly")) else path -_SRCS_FILEGROUP = """\ +def _srcs_filegroup(extra_srcs = None): + srcs = 'glob(["**/*"])' + if extra_srcs: + srcs = "%s + %s" % (srcs, repr(extra_srcs)) + + return """\ filegroup( name = "srcs", - srcs = glob(["**/*"]), + srcs = %s, + visibility = ["//visibility:public"], +) +""" % srcs + +def _rustc_srcs_filegroup(): + return """\ +filegroup( + name = "rustc_srcs", + srcs = [":srcs"], visibility = ["//visibility:public"], ) """ @@ -172,7 +186,7 @@ load("//{source_root}:defs.bzl", "RESOLVED_PLATFORMS") {srcs_filegroup}{rust_crate_call}{package_metadata_bazel_additive_build_file_content}""".format( source_root = source_root, - srcs_filegroup = _SRCS_FILEGROUP, + srcs_filegroup = _srcs_filegroup(), rust_crate_call = render_rust_crate_call( crate_attr, values, @@ -257,14 +271,16 @@ source_stdlib( ) """, ] - _generate_source_stdlib_build_files(rctx, _SOURCE_ROOT, root_build) + rustc_srcs = _generate_source_stdlib_build_files(rctx, _SOURCE_ROOT, root_build) + root_build.extend([ + _srcs_filegroup(extra_srcs = rustc_srcs), + _rustc_srcs_filegroup(), + ]) rctx.file(paths.join(_SOURCE_ROOT, "BUILD.bazel"), "\n".join(root_build)) return rctx.repo_metadata(reproducible = True) def _generate_source_stdlib_build_files(rctx, source_root, root_build): - platform_triples = sorted(SUPPORTED_TIER_3_TRIPLES) - cargo = rctx.path(rctx.attr.cargo) rustc = rctx.path(rctx.attr.rustc) result = rctx.execute( @@ -295,7 +311,7 @@ def _generate_source_stdlib_build_files(rctx, source_root, root_build): package_metadata_info = resolve_cargo_metadata_packages( source_packages, cargo_metadata, - platform_triples, + ALL_TARGET_TRIPLES, skip_internal_rustc_placeholder_crates = False, ) resolution = resolve_cargo_workspace_members( @@ -313,7 +329,7 @@ def _generate_source_stdlib_build_files(rctx, source_root, root_build): ), }, }, - platform_triples = platform_triples, + platform_triples = ALL_TARGET_TRIPLES, materialize_workspace_members = True, dep_label_prefix = "//{}:".format(source_root), skip_internal_rustc_placeholder_crates = False, @@ -324,13 +340,14 @@ def _generate_source_stdlib_build_files(rctx, source_root, root_build): cfg_match_cache = resolution.cfg_match_cache, feature_resolutions_by_fq_crate = resolution.feature_resolutions_by_fq_crate, platform_cfg_attrs = resolution.platform_cfg_attrs, - platform_triples = platform_triples, + platform_triples = ALL_TARGET_TRIPLES, repo_root = workspace_root, use_legacy_rules_rust_platforms = False, workspace_package = source_root, ) crate_package_dirs = set() + rustc_srcs = set() for package in workspace_cargo_metadata["packages"]: name = package["name"] @@ -343,6 +360,7 @@ def _generate_source_stdlib_build_files(rctx, source_root, root_build): continue if package_dir: crate_package_dirs.add(package_dir) + rustc_srcs.add(_target_label(bazel_package, "srcs")) root_build.append("""\ alias( @@ -364,11 +382,11 @@ alias( crate_attr = _crate_attr( aliases = dep_data["aliases"], build_script_deps = dep_data["build_deps"], - build_script_deps_select = _select_by_triple(platform_triples, dep_data["build_deps_by_platform"]), + build_script_deps_select = _select_by_triple(ALL_TARGET_TRIPLES, dep_data["build_deps_by_platform"]), crate_features = dep_data["crate_features"], - crate_features_select = _select_by_triple(platform_triples, dep_data["crate_features_by_platform"]), + crate_features_select = _select_by_triple(ALL_TARGET_TRIPLES, dep_data["crate_features_by_platform"]), deps = dep_data["deps"], - deps_select = _select_by_triple(platform_triples, dep_data["deps_by_platform"]), + deps_select = _select_by_triple(ALL_TARGET_TRIPLES, dep_data["deps_by_platform"]), extra_compile_data = _extra_compile_data(name, source_root), ) rctx.file(paths.join(bazel_package, "BUILD.bazel"), _render_crate_build_file(source_root, crate_attr, cargo.values, cargo.bazel_metadata)) @@ -379,6 +397,7 @@ alias( fq = fq_crate(name, version) bazel_package = package["bazel_package"] target_name = package["target_name"] + rustc_srcs.add(_target_label(bazel_package, "srcs")) root_build.append("""\ alias( name = "{fq}", @@ -396,17 +415,19 @@ alias( bazel_package, workspace_cargo_toml, target_name, - crate_attr = _resolved_crate_attr(package["feature_resolutions"], platform_triples), + crate_attr = _resolved_crate_attr(package["feature_resolutions"], ALL_TARGET_TRIPLES), ), ) for package_dir in sorted(_SOURCE_PACKAGE_DIRS.values()): bazel_package = _source_package(source_root, package_dir) if package_dir not in crate_package_dirs: - rctx.file(paths.join(bazel_package, "BUILD.bazel"), _SRCS_FILEGROUP) + rctx.file(paths.join(bazel_package, "BUILD.bazel"), _srcs_filegroup()) + rustc_srcs.add(_target_label(bazel_package, "srcs")) _prune_rustc_src(rctx, source_root) rctx.file(paths.join(source_root, "defs.bzl"), "RESOLVED_PLATFORMS = []") + return sorted(rustc_srcs) rustc_src_repository = repository_rule( implementation = _rustc_src_repository_impl, diff --git a/rs/rules_rust.bzl b/rs/rules_rust.bzl index 4db00c9..790fc1c 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-aROt7MxNPUOELpgM4BZEQpptsoT6t2qjFpAzyZRF0LE=", - strip_prefix = "rules_rust-1c00622c3ba0269a6d5306772fe2e9aff4bf89a0", - url = "https://github.com/hermeticbuild/rules_rust/archive/1c00622c3ba0269a6d5306772fe2e9aff4bf89a0.tar.gz", + integrity = "sha256-58gffm6oKlSzAgS7oht5nszfrQAa2ImkBFpOHAK2Yi0=", + strip_prefix = "rules_rust-9da4630822e76c6a870927bf02c5201c0bbff9ee", + url = "https://github.com/hermeticbuild/rules_rust/archive/9da4630822e76c6a870927bf02c5201c0bbff9ee.tar.gz", patches = patches, patch_strip = strip, ) diff --git a/rs/toolchains/BUILD.bazel b/rs/toolchains/BUILD.bazel index 266937f..064f3ab 100644 --- a/rs/toolchains/BUILD.bazel +++ b/rs/toolchains/BUILD.bazel @@ -41,6 +41,7 @@ bzl_library( srcs = ["module_extension.bzl"], deps = [ ":toolchain_utils", + "//rs/experimental/miri/private:miri_repository", "//rs/platforms:triples", "//rs/private:cargo_repository", "//rs/private:clippy_repository", diff --git a/rs/toolchains/module_extension.bzl b/rs/toolchains/module_extension.bzl index 488d785..3f4caa7 100644 --- a/rs/toolchains/module_extension.bzl +++ b/rs/toolchains/module_extension.bzl @@ -7,6 +7,7 @@ load( "check_version_valid", "produce_tool_suburl", ) +load("//rs/experimental/miri/private:miri_repository.bzl", "miri_repository") load("//rs/platforms:triples.bzl", "SUPPORTED_EXEC_TRIPLES", "SUPPORTED_TIER_1_AND_2_TRIPLES") load("//rs/private:cargo_repository.bzl", "cargo_repository") load("//rs/private:clippy_repository.bzl", "clippy_repository") @@ -101,6 +102,49 @@ def _parse_version(version): return base_version, iso_date +_EXPERIMENTAL_MIRI_TAG = tag_class( + attrs = { + "name": attr.string( + default = "miri_toolchains", + doc = "Name of the generated Miri toolchain repo.", + ), + "version": attr.string( + mandatory = True, + doc = "Miri version (e.g. nightly/2026-04-20).", + ), + "exec_triples": attr.string_list( + default = SUPPORTED_EXEC_TRIPLES, + doc = "Execution triples to declare Miri toolchains for.", + ), + }, +) + +def _miri_toolchains_repository_impl(rctx): + rctx.file( + "BUILD.bazel", + """\ +load("@rules_rs//rs/experimental/miri/private:declare_toolchains.bzl", "declare_miri_toolchains") + +declare_miri_toolchains( + version = {version}, + execs = {execs}, +) +""".format( + version = repr(rctx.attr.version), + execs = repr(rctx.attr.execs), + ), + ) + + return rctx.repo_metadata(reproducible = True) + +_miri_toolchains_repository = repository_rule( + implementation = _miri_toolchains_repository_impl, + attrs = { + "version": attr.string(mandatory = True), + "execs": attr.string_list(mandatory = True), + }, +) + def _toolchains_impl(mctx): root_module_name = None for mod in mctx.modules: @@ -135,6 +179,32 @@ def _toolchains_impl(mctx): rustfmt_versions.add(tag.rustfmt_version or tag.version) rust_analyzer_versions.add(tag.rust_analyzer_version or tag.version) + miri_versions = set([]) + miri_repo_configs = {} + miri_repo_tags = [] + for mod in mctx.modules: + for tag in mod.tags.experimental_miri: + _parse_version(tag.version) + if not tag.exec_triples: + fail("Miri toolchain repo {} must declare at least one exec triple".format(tag.name)) + for exec_triple in tag.exec_triples: + if exec_triple not in SUPPORTED_EXEC_TRIPLES: + fail("Miri exec triple {} is not supported".format(exec_triple)) + + existing = miri_repo_configs.get(tag.name) + if existing and ( + existing.version != tag.version or + existing.exec_triples != tag.exec_triples + ): + fail("Miri toolchain repo {} has conflicting tag configurations".format(tag.name)) + + if not existing: + miri_repo_configs[tag.name] = tag + miri_repo_tags.append(tag) + miri_versions.add(tag.version) + + rust_versions = versions | miri_versions + existing_facts = getattr(mctx, "facts", {}) or {} pending_downloads = {} new_facts = {} @@ -165,13 +235,17 @@ def _toolchains_impl(mctx): ) # First pass: enqueue all sha downloads we don't already have. - for version in versions: + for version in rust_versions: base_version, iso_date = _parse_version(version) for triple in SUPPORTED_EXEC_TRIPLES: exec_triple = _parse_triple(triple) - for tool_name in ["rustc", "clippy", "cargo"]: + for tool_name in ["rustc", "cargo"]: _request_sha(tool_name, base_version, iso_date, exec_triple) + if version in versions: + _request_sha("clippy", base_version, iso_date, exec_triple) + if version in miri_versions: + _request_sha("miri", base_version, iso_date, exec_triple) for target_triple in SUPPORTED_TIER_1_AND_2_TRIPLES: _request_sha("rust-std", base_version, iso_date, _parse_triple(target_triple)) @@ -214,10 +288,10 @@ def _toolchains_impl(mctx): host_os = _normalize_os_name(mctx.os.name) host_arch = _normalize_arch_name(mctx.os.arch) - host_cargo_repo = None - host_rustc_repo = None + host_cargo_repos = {} + host_rustc_repos = {} - for version in versions | rustfmt_versions | rust_analyzer_versions: + for version in rust_versions | rustfmt_versions | rust_analyzer_versions: version_key = sanitize_version(version) base_version, iso_date = _parse_version(version) @@ -235,12 +309,11 @@ def _toolchains_impl(mctx): sha256 = _sha_for("rustc", base_version, iso_date, exec_triple), ) - if version in versions: + if version in rust_versions: cargo_name = "cargo_{}_{}".format(triple_suffix, version_key) - if host_cargo_repo == None and exec_triple.arch == host_arch and exec_triple.system == host_os: - host_cargo_repo = cargo_name - if host_rustc_repo == None and exec_triple.arch == host_arch and exec_triple.system == host_os: - host_rustc_repo = rustc_name + if exec_triple.arch == host_arch and exec_triple.system == host_os: + host_cargo_repos[version] = cargo_name + host_rustc_repos[version] = rustc_name cargo_repository( name = cargo_name, @@ -250,6 +323,7 @@ def _toolchains_impl(mctx): sha256 = _sha_for("cargo", base_version, iso_date, exec_triple), ) + if version in versions: clippy_repository( name = "clippy_{}_{}".format(triple_suffix, version_key), triple = triple, @@ -259,7 +333,17 @@ def _toolchains_impl(mctx): rustc_sha256 = _sha_for("rustc", base_version, iso_date, exec_triple), ) - if version in versions: + if version in miri_versions: + miri_repository( + name = "miri_{}_{}".format(triple_suffix, version_key), + triple = triple, + version = base_version, + iso_date = iso_date, + sha256 = _sha_for("miri", base_version, iso_date, exec_triple), + rustc_sha256 = _sha_for("rustc", base_version, iso_date, exec_triple), + ) + + if version in rust_versions: for target_triple in SUPPORTED_TIER_1_AND_2_TRIPLES: stdlib_repository( name = "rust_stdlib_{}_{}".format(sanitize_triple(target_triple), version_key), @@ -309,21 +393,14 @@ def _toolchains_impl(mctx): sha256 = _sha_for("rust-analyzer", base_version, iso_date, exec_triple), ) - if host_cargo_repo == None: + if len(host_cargo_repos) != len(rust_versions): fail("Could not find host Cargo repository for {}-{}".format(host_os, host_arch)) - if host_rustc_repo == None: + if len(host_rustc_repos) != len(rust_versions): fail("Could not find host rustc repository for {}-{}".format(host_os, host_arch)) host_exe_suffix = ".exe" if host_os == "windows" else "" - host_cargo = "@{}//:bin/cargo{}".format( - host_cargo_repo, - host_exe_suffix, - ) - host_rustc = "@{}//:bin/rustc{}".format( - host_rustc_repo, - host_exe_suffix, - ) + host_cargo = "@{}//:bin/cargo{}".format(host_cargo_repos[version_tags[0].version], host_exe_suffix) - for version in versions: + for version in rust_versions: version_key = sanitize_version(version) base_version, iso_date = _parse_version(version) rustc_src_repository( @@ -331,8 +408,8 @@ def _toolchains_impl(mctx): version = base_version, iso_date = iso_date, sha256 = new_facts[_rustc_src_archive_path(base_version, iso_date)], - cargo = host_cargo, - rustc = host_rustc, + cargo = "@{}//:bin/cargo{}".format(host_cargo_repos[version], host_exe_suffix), + rustc = "@{}//:bin/rustc{}".format(host_rustc_repos[version], host_exe_suffix), ) host_tools_repository( @@ -379,6 +456,22 @@ def _toolchains_impl(mctx): elif repo_name not in direct_deps: direct_deps.append(repo_name) + for tag in miri_repo_tags: + if tag.name in repo_configs: + fail("Miri toolchain repo {} conflicts with a Rust toolchain repo of the same name".format(tag.name)) + + _miri_toolchains_repository( + name = tag.name, + version = tag.version, + execs = tag.exec_triples, + ) + + if mctx.is_dev_dependency(tag): + if tag.name not in direct_dev_deps: + direct_dev_deps.append(tag.name) + elif tag.name not in direct_deps: + direct_deps.append(tag.name) + kwargs = dict( reproducible = True, root_module_direct_deps = direct_deps, @@ -392,5 +485,8 @@ def _toolchains_impl(mctx): toolchains = module_extension( implementation = _toolchains_impl, - tag_classes = {"toolchain": _TOOLCHAIN_TAG}, + tag_classes = { + "experimental_miri": _EXPERIMENTAL_MIRI_TAG, + "toolchain": _TOOLCHAIN_TAG, + }, ) diff --git a/test/MODULE.bazel b/test/MODULE.bazel index 6182fe1..c776c2b 100644 --- a/test/MODULE.bazel +++ b/test/MODULE.bazel @@ -112,10 +112,13 @@ toolchains.toolchain( edition = "2024", version = "1.92.0", ) -use_repo(toolchains, "default_rust_toolchains") +toolchains.experimental_miri(version = "nightly/2026-04-20") +use_repo(toolchains, "default_rust_toolchains", "miri_toolchains") register_toolchains("@default_rust_toolchains//:all") +register_toolchains("@miri_toolchains//:all") + register_toolchains( "@rules_rust//extensions/prost:default_prost_toolchain", "@rules_rust//extensions/pyo3/toolchains:rust_toolchain", diff --git a/test/MODULE.bazel.lock b/test/MODULE.bazel.lock index 12018d3..5ac2ec5 100644 --- a/test/MODULE.bazel.lock +++ b/test/MODULE.bazel.lock @@ -106,6 +106,8 @@ "https://bcr.bazel.build/modules/googletest/1.17.0.bcr.2/MODULE.bazel": "827f54f492a3ce549c940106d73de332c2b30cebd0c20c0bc5d786aba7f116cb", "https://bcr.bazel.build/modules/googletest/1.17.0.bcr.2/source.json": "3664514073a819992320ffbce5825e4238459df344d8b01748af2208f8d2e1eb", "https://bcr.bazel.build/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46", + "https://bcr.bazel.build/modules/hermetic_launcher/0.0.8/MODULE.bazel": "3be7b0faca6f1e69e89197999e0b01ce058c42f3e764ef028466f7e0ff77c761", + "https://bcr.bazel.build/modules/hermetic_launcher/0.0.8/source.json": "8403718636114198fca6ea04b437fa001ac7167b4d485102defb0a6f7d11bc08", "https://bcr.bazel.build/modules/jemalloc/5.3.0-bcr.alpha.4/MODULE.bazel": "2eeba189b444e56233f86a8d53ce112acaddd41891f907b5ab77c76607a021f9", "https://bcr.bazel.build/modules/jemalloc/5.3.0-bcr.alpha.4/source.json": "f075aa09eef05c187e2c7d6f6b305994af6d0f3fb3ff8b0a6ed84cfd5dd6c4e2", "https://bcr.bazel.build/modules/jq.bzl/0.1.0/MODULE.bazel": "2ce69b1af49952cd4121a9c3055faa679e748ce774c7f1fda9657f936cae902f", @@ -128,7 +130,8 @@ "https://bcr.bazel.build/modules/libunwind/1.8.1/MODULE.bazel": "d9b947b15135786aed51671c22872a93e2637c74caa20e94f2061fd3b1efd6c3", "https://bcr.bazel.build/modules/libunwind/1.8.1/source.json": "2b4e58e2fa6ed7204862ab1a4e5978e4107340884c3217596ff7910b8dfae919", "https://bcr.bazel.build/modules/llvm/0.7.7/MODULE.bazel": "0eeaf1814feca77abc7af3523e2b9d3735f92e2583043f8d6a2cc0fb5f479a28", - "https://bcr.bazel.build/modules/llvm/0.7.7/source.json": "7c8910307329462a21b7bdcc52710360da3de8284738dca52241bb15302a00dc", + "https://bcr.bazel.build/modules/llvm/0.8.0/MODULE.bazel": "72e043f9ad3cdd721659c74010fff87a4ce970abf8d47c04f48a0371234191e3", + "https://bcr.bazel.build/modules/llvm/0.8.0/source.json": "bbe84a7c74b4bbb964a5dad5071c80e4ea36da42f798f70844658b8fd3b236af", "https://bcr.bazel.build/modules/mbedtls/3.6.5/MODULE.bazel": "f68edc3db5a01add21c85c2dc2211ecef43a974756656268d8171ad4fb1f30e3", "https://bcr.bazel.build/modules/mbedtls/3.6.5/source.json": "e94d5925d46562f393784d0a5ea66635cb2f15740b6dc1d7cb1371f01fcf0e0e", "https://bcr.bazel.build/modules/mimalloc/2.2.4.bcr.1/MODULE.bazel": "0a31b9ed52ff6c21aebf0794e18bd6ad4638273d7240d324b4b0843f8bcf5365", @@ -294,9 +297,10 @@ "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f", "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b", + "https://bcr.bazel.build/modules/tar.bzl/0.10.4/MODULE.bazel": "e8f9ff79199e8d9eaad7f1b0a77ad74b30bb82d794b87d8ca942bead5de83ae9", + "https://bcr.bazel.build/modules/tar.bzl/0.10.4/source.json": "20143442376c03426f6135292ba02d825cb75308aa47e6bf42dd4cc5a435c2ff", "https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468", "https://bcr.bazel.build/modules/tar.bzl/0.9.0/MODULE.bazel": "452a22d7f02b1c9d7a22ab25edf20f46f3e1101f0f67dc4bfbf9a474ddf02445", - "https://bcr.bazel.build/modules/tar.bzl/0.9.0/source.json": "c732760a374831a2cf5b08839e4be75017196b4d796a5aa55235272ee17cd839", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9", "https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/MODULE.bazel": "b573395fe63aef4299ba095173e2f62ccfee5ad9bbf7acaa95dba73af9fc2b38", @@ -2126,12 +2130,12 @@ "password-hash_0.4.2": "{\"dependencies\":[{\"name\":\"base64ct\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2\"}],\"features\":{\"alloc\":[\"base64ct/alloc\"],\"default\":[\"rand_core\"],\"std\":[\"alloc\",\"base64ct/std\",\"rand_core/std\"]}}", "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\":{}}", - "path+cfg_feature_target_dep/inner_inner": "{\"dependencies\":[{\"name\":\"itoa\",\"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},{\"default_features\":true,\"features\":[],\"name\":\"ryu\",\"optional\":true}],\"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}],\"features\":{},\"strip_prefix\":\"\"}", - "path+vendored_workspace_dep/vendor/itoa_itoa": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"mylib\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"no-panic\",\"optional\":true}],\"features\":{},\"strip_prefix\":\"\"}", - "path+workspace_member_annotation_features/../workspace_member_annotation_features.deps/dep_leaf_dep_leaf": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"itoa\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"ryu\",\"optional\":true}],\"features\":{\"default\":[],\"with_extra\":[\"dep:ryu\"]},\"strip_prefix\":\"\"}", - "path+workspace_path_dependency/../vendored_crate_override/vendor/itoa_itoa": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"no-panic\",\"optional\":true}],\"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\":\"\"}", + "path+vendored_workspace_dep/vendor/itoa_itoa": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"mylib\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"no-panic\",\"optional\":true,\"req\":\"0.1\"}],\"features\":{},\"strip_prefix\":\"\"}", + "path+workspace_member_annotation_features/../workspace_member_annotation_features.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\":[],\"with_extra\":[\"dep:ryu\"]},\"strip_prefix\":\"\"}", + "path+workspace_path_dependency/../vendored_crate_override/vendor/itoa_itoa": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"no-panic\",\"optional\":true,\"req\":\"0.1\"}],\"features\":{},\"strip_prefix\":\"\"}", "path-clean_0.1.0": "{\"dependencies\":[],\"features\":{}}", "path-clean_1.0.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.2\"}],\"features\":{}}", "path-slash_0.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"}],\"features\":{}}", @@ -3243,6 +3247,94 @@ "zvariant_utils_3.2.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.81\"},{\"name\":\"quote\",\"req\":\"^1.0.36\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.200\"},{\"features\":[\"extra-traits\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.64\"},{\"name\":\"winnow\",\"req\":\"^0.7\"}],\"features\":{\"default\":[],\"gvariant\":[]}}" }, "@@rules_rs+//rs/toolchains:module_extension.bzl%toolchains": { + "2026-04-20/cargo-nightly-aarch64-apple-darwin.tar.xz": "b0f79e3f1e56cf3f774c887e55ccaba2a85321d2b4a96233481e1f3368d09814", + "2026-04-20/cargo-nightly-aarch64-pc-windows-msvc.tar.xz": "cc913e840cec0e12faaa7232695de78f1791b89cfd68a09f3d87a3f1c59a8fa7", + "2026-04-20/cargo-nightly-aarch64-unknown-linux-gnu.tar.xz": "6111f2e2eed1b92f384ed7c234dea2f97398cb2a8130e228d731e15befb6e3c6", + "2026-04-20/cargo-nightly-x86_64-apple-darwin.tar.xz": "96753c55261202e449c48ab2351ffc55c06d3a7cd50ba86f3fdb5ae07e950ec6", + "2026-04-20/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "5c1939084a6a418025d290d84631fbc29848f2a69cf0cd2aff9290f09b88f7e6", + "2026-04-20/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "6c23e14109146a368de316b86eaf57cc17754993ccac57ea480b0754abf53ccc", + "2026-04-20/miri-nightly-aarch64-apple-darwin.tar.xz": "0bd4134903dcf573bde2e130becfea5cb1b8ec666eb6af841c65d345ab039885", + "2026-04-20/miri-nightly-aarch64-pc-windows-msvc.tar.xz": "627325504450f255126348c4eb554bbce0cb4489435ea6f5f25ef1dc8aa681f0", + "2026-04-20/miri-nightly-aarch64-unknown-linux-gnu.tar.xz": "0a947415156ddc2e51d996026f95378e9aa99dc7bd26836a4aadc0a07c2029f0", + "2026-04-20/miri-nightly-x86_64-apple-darwin.tar.xz": "5364c4ed154922ad79da7180bb9d9e65149755886a99a2be88a4e5fb8676460d", + "2026-04-20/miri-nightly-x86_64-pc-windows-msvc.tar.xz": "ce48a24cb0b9cbd33601ac5c5eeda133a56b40cb324fdfc461baedb76f826395", + "2026-04-20/miri-nightly-x86_64-unknown-linux-gnu.tar.xz": "690cbe23b3aa3d81fe227275018ff8b9f3caeab2be39f0e38b554e46818c1582", + "2026-04-20/rust-std-nightly-aarch64-apple-darwin.tar.xz": "4a91d56a43ff60562744ee64f9cfde006240bf7ac75ebe974f3083ef41a55f5d", + "2026-04-20/rust-std-nightly-aarch64-apple-ios-macabi.tar.xz": "47cc4621fd45feb923b62d5e6df59ba6e90965d06f1f108d5b21947e549dfc11", + "2026-04-20/rust-std-nightly-aarch64-apple-ios-sim.tar.xz": "288d16484e23502d73c9347bd971c6e644b1b1467bddea4088f746dac4939fa0", + "2026-04-20/rust-std-nightly-aarch64-apple-ios.tar.xz": "261d26c340ba13bc94dd05c5ed68fcee2a54ee2fb675d96c6661abdf1132cc82", + "2026-04-20/rust-std-nightly-aarch64-linux-android.tar.xz": "bb4cf0f1f376f14b2c32c2b393f4c28c30e88515a336953507f19c2a3a39c37c", + "2026-04-20/rust-std-nightly-aarch64-pc-windows-gnullvm.tar.xz": "cd65d6f1be6d1d5e7629a895bb45f3eca8dcff7e08a42cdf594031267eccf10c", + "2026-04-20/rust-std-nightly-aarch64-pc-windows-msvc.tar.xz": "fe68c7c441ee07f2b590038fdbe7c3bcd71a284aaa4549835d3ca2e9977099da", + "2026-04-20/rust-std-nightly-aarch64-unknown-fuchsia.tar.xz": "01b0604be2b37f4fcb55d15f2963f918df198e283cd7c3a1c401fe04826d4ada", + "2026-04-20/rust-std-nightly-aarch64-unknown-linux-gnu.tar.xz": "c3b985d33c29b10be530b90bde112056e854921700652ed818160ca52ea609e7", + "2026-04-20/rust-std-nightly-aarch64-unknown-linux-musl.tar.xz": "28d39529691b21816bc1f8bdbaebb52e97283691afb3910680a0d1ae0b513d21", + "2026-04-20/rust-std-nightly-aarch64-unknown-none-softfloat.tar.xz": "69506a4602f6e530fbe97886ed66dc2041eb961a309c0a5fab6278ee242ad0bd", + "2026-04-20/rust-std-nightly-aarch64-unknown-none.tar.xz": "b38e7dcba8cce0a603ab0bb61119ba2f755c4a10f665dc2349eee90deeda16c7", + "2026-04-20/rust-std-nightly-aarch64-unknown-uefi.tar.xz": "1adb247db0725439591d8733065c9ebc7b999bba8ba6582c26929efb487a4949", + "2026-04-20/rust-std-nightly-arm-linux-androideabi.tar.xz": "cd5245ce6c47da6ed08f9fb6c84d295b1315c9846a82a09fc6ab84ea8bf76cc4", + "2026-04-20/rust-std-nightly-arm-unknown-linux-gnueabi.tar.xz": "f64beb6ba876b5f754749bba9b8376ea11bc6af789d10824dc56a976fa6a48e6", + "2026-04-20/rust-std-nightly-arm-unknown-linux-gnueabihf.tar.xz": "1c4ba88c0369b23c8010760b7ef991c6723fe9ae3e1d84d895aeff5297244ba9", + "2026-04-20/rust-std-nightly-arm-unknown-linux-musleabi.tar.xz": "4c65f34e5d6ad215304cb8fe8bdc58f2db1b45f175ab8755a05003e93c0fd20a", + "2026-04-20/rust-std-nightly-arm-unknown-linux-musleabihf.tar.xz": "c63c237dc9fb3e57745f35537ef1145fe12a450370f7da7d4cf572cf667b6d7d", + "2026-04-20/rust-std-nightly-armv7-linux-androideabi.tar.xz": "c1696871e455200afc863efc403afc6e137f05fda6551367c3d5a83b4d4be4a8", + "2026-04-20/rust-std-nightly-armv7-unknown-linux-gnueabi.tar.xz": "6a2d2703ed95ec59a027093080403f91cafb39330832b6c948b7ffa89d6c1709", + "2026-04-20/rust-std-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "3a2022f6aaf37f876b6eb899258bc9b0aeb4741fd86f7f76c9509e664f39d39c", + "2026-04-20/rust-std-nightly-armv7-unknown-linux-musleabi.tar.xz": "b7564c037b99a1321109a202501ea9876e4f3af0c4990356a3156a6ffe1031bd", + "2026-04-20/rust-std-nightly-armv7-unknown-linux-musleabihf.tar.xz": "9136765abe4dd641a54bf41527330cc706173f8f0052f83ee305d2751f0dba72", + "2026-04-20/rust-std-nightly-i686-linux-android.tar.xz": "abde07896240267bf0f08c7a73933db94ac68bf670b1f2019a5d7c2c6eb12ece", + "2026-04-20/rust-std-nightly-i686-pc-windows-gnu.tar.xz": "df9c29b14f1e75079a191a807d58d729926e43a2ea3e861b5117b86d3f4d1013", + "2026-04-20/rust-std-nightly-i686-pc-windows-gnullvm.tar.xz": "baf2955440749dff5bf6b1bbd130c8ec6c14209db19be548184a0c29b86241e7", + "2026-04-20/rust-std-nightly-i686-pc-windows-msvc.tar.xz": "82bb86a1be03ec6486210b9625db8fd60af51c559e18c869333abe2e5bef8822", + "2026-04-20/rust-std-nightly-i686-unknown-freebsd.tar.xz": "2bccb95688a6ec64db57f546a1caee131f780ccce4a51703edb49113defb379e", + "2026-04-20/rust-std-nightly-i686-unknown-linux-gnu.tar.xz": "451e3b8c2e4c70e561c39643eeef598a64a7fdf0d57ae8cebf8c77410f281402", + "2026-04-20/rust-std-nightly-i686-unknown-linux-musl.tar.xz": "a882300d1156bea0cccf32148169a989ce7c9a2d4839d97899ace13738cc53d2", + "2026-04-20/rust-std-nightly-i686-unknown-uefi.tar.xz": "28d7b86a0bf0c75a96175d4d15e9209fa2a80bdd8a0e823c5bd02b3f8ef48583", + "2026-04-20/rust-std-nightly-loongarch64-unknown-linux-gnu.tar.xz": "5a5026f1d8bc0b7c533f7f505ef5017f6fb47504639e4aa7c8a93ebdd6818f64", + "2026-04-20/rust-std-nightly-loongarch64-unknown-linux-musl.tar.xz": "005b53a702a02248261f546d4c4b34b9b2e384a4ab3bb10b691f4e06754634b7", + "2026-04-20/rust-std-nightly-loongarch64-unknown-none.tar.xz": "4cc4834a03a5ed7e01704968d0a04177c1bd6ae5aace734e092cc351759638f7", + "2026-04-20/rust-std-nightly-powerpc-unknown-linux-gnu.tar.xz": "f52effd2cbc8c7e52861c2e037b8582b469c2ef2e42e0cee9cc4f2bc2ba212b2", + "2026-04-20/rust-std-nightly-powerpc64-unknown-linux-gnu.tar.xz": "6b64e6815545135a95c7f22e3a0cc69efb46cfdc118226bd5ee31508f9a50cfd", + "2026-04-20/rust-std-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "0f9c38e5959552e5721a29954bf1b3443d1b7cc25300790955ac084f4cca21a4", + "2026-04-20/rust-std-nightly-powerpc64le-unknown-linux-musl.tar.xz": "8d7d843db710fc39bea646a5cf4f2e5c5f4eaaebe4ad0386f65ed8b456b94e42", + "2026-04-20/rust-std-nightly-riscv32imc-unknown-none-elf.tar.xz": "27a6765f37c9136e3d1dd0d8a0edbb4eab4d3508c8ae35246ac4307a8b37d75d", + "2026-04-20/rust-std-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "4c9d9e9608b9088b4bd575ff122a528924056ea66053ec16181073bd2825c1d3", + "2026-04-20/rust-std-nightly-riscv64gc-unknown-linux-musl.tar.xz": "fb9365ca0416f77b22c4210f88077735d9cb11c6ed8dca49cbd35306c263e6f5", + "2026-04-20/rust-std-nightly-riscv64gc-unknown-none-elf.tar.xz": "444fa83b28285a45c0ce9d65b2e97e0e1ac223180238deaccd4fefa9aed6cc17", + "2026-04-20/rust-std-nightly-s390x-unknown-linux-gnu.tar.xz": "39dc240bb34cffde2587d6c561e7c1e59861377aa12d0cbd45417b01c993a2c2", + "2026-04-20/rust-std-nightly-sparc64-unknown-linux-gnu.tar.xz": "9c86e5c2d0ec72481b8154e3c157864f71a84751fd6b2c997695b081cfdde4f8", + "2026-04-20/rust-std-nightly-thumbv6m-none-eabi.tar.xz": "c54a993f775fe54098c295128913a24e2c8eb96499e08a212eca403e15e860ae", + "2026-04-20/rust-std-nightly-thumbv7em-none-eabi.tar.xz": "264067ff5aa98046f624e6375ed6c0198fd395081de1114ca21ae20be414e091", + "2026-04-20/rust-std-nightly-thumbv7em-none-eabihf.tar.xz": "0ebddc4f3a05d6dd8c2b0c38708c55ddd8ec0c7a72902133d6fc1d2c88856e33", + "2026-04-20/rust-std-nightly-thumbv7m-none-eabi.tar.xz": "6c7cbcbe5af855d64722463c542e9c661c33b70d56f00e3d003860c5025ba64b", + "2026-04-20/rust-std-nightly-thumbv8m.main-none-eabi.tar.xz": "934858d2cc18babfaf20059cab2d3fcc8812e73136d32711fae3fcdb0c898dc1", + "2026-04-20/rust-std-nightly-thumbv8m.main-none-eabihf.tar.xz": "348316791bfdc2643f5206fe90b51be03727e24cc4a5b25398881d56f92b1a94", + "2026-04-20/rust-std-nightly-wasm32-unknown-emscripten.tar.xz": "2b91bbc4e9129538e1a173f2c89798539b043149851b0ff9b8e99b718e9f1623", + "2026-04-20/rust-std-nightly-wasm32-unknown-unknown.tar.xz": "e54863e8c1974839a578c48aebf771a3fff251f3b1117fc1e4d793229510dfda", + "2026-04-20/rust-std-nightly-wasm32-wasip1-threads.tar.xz": "4c58392ae5480b8670f68c1a1055056082e2081b855839608393044e95d2f8a3", + "2026-04-20/rust-std-nightly-wasm32-wasip1.tar.xz": "3b7723d7cb44c7fe1252bf8a483843d747a9dfa03efa822f851c4a6ed9ba83cf", + "2026-04-20/rust-std-nightly-wasm32-wasip2.tar.xz": "ec35bf581f30996af7c250933a93fc8009f59a62ad6253a7207e418eb425546d", + "2026-04-20/rust-std-nightly-x86_64-apple-darwin.tar.xz": "86b9cbce61b3c0bdba43ca7666e0d0492a9db10fdf47272fe779c7c899ff641f", + "2026-04-20/rust-std-nightly-x86_64-apple-ios-macabi.tar.xz": "98f9d62106a50dc95a874a23fa5f30ed1820f4a915080f3541de76748c792d0a", + "2026-04-20/rust-std-nightly-x86_64-apple-ios.tar.xz": "b543d064c58c99c3f8ed80f9a063bdb0f8d9358f7f9df3f2088f79294f3c6727", + "2026-04-20/rust-std-nightly-x86_64-linux-android.tar.xz": "2f7917c02bd50de08f3348444e8a5f0d69cf8f8c852f7c457274e0838775038b", + "2026-04-20/rust-std-nightly-x86_64-pc-windows-gnu.tar.xz": "7ce032d9fce2e733623607dea56a848698e726960f309f70cf699e40cb0529aa", + "2026-04-20/rust-std-nightly-x86_64-pc-windows-gnullvm.tar.xz": "9b6b3932a9e05dda7af7dee09d09902b331dbc6b30c2db8489350e867601713f", + "2026-04-20/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "78258e91e4e027ce5d6655e317577c65ea601fe6aad5d330affe70d374a60be6", + "2026-04-20/rust-std-nightly-x86_64-unknown-freebsd.tar.xz": "3c742c7065f233366c686d34e407e65502674d38161573a6fe7f8ea78b002147", + "2026-04-20/rust-std-nightly-x86_64-unknown-fuchsia.tar.xz": "0f1df98a39375fd0ae5abd71688b4d1bc5b7248735f308976e6ed84f6cc83dd4", + "2026-04-20/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "440bb1a20d036cb8a63fe309ba670fc4ef1f46c934116c035c1fe4abe6cea291", + "2026-04-20/rust-std-nightly-x86_64-unknown-linux-musl.tar.xz": "d3ceca4950478f1402f3a23e35b25e694c83d504440dc05ecea239be8d43e639", + "2026-04-20/rust-std-nightly-x86_64-unknown-netbsd.tar.xz": "6c2b4993e3fe98d91cdcef392f9e924b90887b1570693733bb3f14489c6581c1", + "2026-04-20/rust-std-nightly-x86_64-unknown-none.tar.xz": "ea9676a7c5d37f0e0277d27be5bbbfc10bd06212f8379a65ac8ceaae6e1b605d", + "2026-04-20/rust-std-nightly-x86_64-unknown-uefi.tar.xz": "2a3534c21e3b46c188d70757c6aee1fe704d6ec65aa442ca7d4413bbb15ec4da", + "2026-04-20/rustc-nightly-aarch64-apple-darwin.tar.xz": "8d4ad96e5a7d093e8a30de89e0c0363afca73c943d1ad243a81e8c2b255a6b78", + "2026-04-20/rustc-nightly-aarch64-pc-windows-msvc.tar.xz": "63cbbd5cd570c64564654086d8f595206970969ff3b619437ca27258eaab9ad5", + "2026-04-20/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz": "acc677720fc501706bee6769370551e200e6fe78a366d94119f17484802e86b5", + "2026-04-20/rustc-nightly-src.tar.xz": "29ec502b55dbe13fb7be4f38a5c20c9d2177459113cad86d0ce8315f13b143dc", + "2026-04-20/rustc-nightly-x86_64-apple-darwin.tar.xz": "72eb57f91f17ebd1ae5e8191257ca1003e6c709b636ad9255c7a8f2964b78c97", + "2026-04-20/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "cd263dd5930ba551da011c0570260cd01abb504dff3d19e4cbb5b0a5aa53fca5", + "2026-04-20/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "5f521dc115c18c4c3b45165a5cb4ffc4af1fd151743dc2695515d8366ab0c551", "cargo-1.92.0-aarch64-apple-darwin.tar.xz": "bce6e7def37240c5a63115828017a9fc0ebcb31e64115382f5943b62b71aa34a", "cargo-1.92.0-aarch64-pc-windows-msvc.tar.xz": "3fc47cc29781190c5075f807836c4b98d411aaf27c59598fbfb2aa781ccf6190", "cargo-1.92.0-aarch64-unknown-linux-gnu.tar.xz": "cb2ce6be6411b986e25c71ad8a813f9dfbe3461738136fd684e3644f8dd75df4", @@ -3334,6 +3426,7 @@ "rustc-1.92.0-aarch64-apple-darwin.tar.xz": "15dee753c9217dff4cf45d734b29dc13ce6017d8a55fe34eed75022b39a63ff0", "rustc-1.92.0-aarch64-pc-windows-msvc.tar.xz": "07ba5606143de3bc916455714f67732fca05805591396a8190d5e88b8863eea3", "rustc-1.92.0-aarch64-unknown-linux-gnu.tar.xz": "7c8706fad4c038b5eacab0092e15db54d2b365d5f3323ca046fe987f814e7826", + "rustc-1.92.0-src.tar.xz": "ebee170bfe4c4dfc59521a101de651e5534f4dae889756a5c97ca9ea40d0c307", "rustc-1.92.0-x86_64-apple-darwin.tar.xz": "0facbd5d2742c8e97c53d59c9b5b81db6088cfc285d9ecb99523a50d6765fc5c", "rustc-1.92.0-x86_64-pc-windows-msvc.tar.xz": "6aec5aba5384ce1041538e71ebebf671d82e29f4fe697bc7844626d94b0dbfe6", "rustc-1.92.0-x86_64-unknown-linux-gnu.tar.xz": "78b2dd9c6b1fcd2621fa81c611cf5e2d6950690775038b585c64f364422886e0", diff --git a/test/miri/BUILD.bazel b/test/miri/BUILD.bazel new file mode 100644 index 0000000..e3b9cd5 --- /dev/null +++ b/test/miri/BUILD.bazel @@ -0,0 +1,84 @@ +load("@rules_rs//rs:rust_library.bzl", "rust_library") +load("@rules_rs//rs:rust_proc_macro.bzl", "rust_proc_macro") +load("@rules_rs//rs:rust_test.bzl", "rust_test") +load( + "@rules_rs//rs/experimental/miri:miri_test.bzl", + "miri_test", +) + +package(default_visibility = ["//visibility:public"]) + +NOT_WINDOWS = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], +}) + +miri_test( + name = "miri_cfg_test", + timeout = "long", + srcs = ["miri_cfg_test.rs"], + edition = "2024", + target_compatible_with = NOT_WINDOWS, +) + +rust_library( + name = "miri_leaf", + srcs = ["miri_leaf.rs"], + crate_name = "miri_leaf", + edition = "2024", +) + +rust_library( + name = "miri_dep", + srcs = ["miri_dep.rs"], + crate_name = "miri_dep", + edition = "2024", + deps = [":miri_leaf"], +) + +miri_test( + name = "miri_dep_graph_test", + timeout = "long", + srcs = ["miri_dep_graph_test.rs"], + edition = "2024", + target_compatible_with = NOT_WINDOWS, + deps = [":miri_dep"], +) + +rust_proc_macro( + name = "miri_proc_macro", + srcs = ["miri_proc_macro.rs"], + edition = "2024", +) + +miri_test( + name = "miri_proc_macro_test", + timeout = "long", + srcs = ["miri_proc_macro_test.rs"], + edition = "2024", + target_compatible_with = NOT_WINDOWS, + deps = [":miri_proc_macro"], +) + +miri_test( + name = "miri_detects_use_after_free", + timeout = "long", + srcs = ["miri_detects_use_after_free.rs"], + edition = "2024", + tags = ["manual"], + target_compatible_with = NOT_WINDOWS, +) + +rust_test( + name = "miri_detects_use_after_free_failure_test", + timeout = "long", + srcs = ["expect_miri_failure.rs"], + args = [ + "$(rootpath :miri_detects_use_after_free)", + "Undefined Behavior", + ], + data = [":miri_detects_use_after_free"], + edition = "2024", + target_compatible_with = NOT_WINDOWS, + use_libtest_harness = False, +) diff --git a/test/miri/expect_miri_failure.rs b/test/miri/expect_miri_failure.rs new file mode 100644 index 0000000..8bd77f8 --- /dev/null +++ b/test/miri/expect_miri_failure.rs @@ -0,0 +1,37 @@ +use std::env; +use std::process::Command; + +fn run() -> Result<(), String> { + let args = env::args().collect::>(); + if args.len() < 3 { + return Err("usage: expect_miri_failure ".to_owned()); + } + + let output = Command::new(&args[1]) + .output() + .map_err(|err| format!("failed to run {}: {err}", args[1]))?; + if output.status.success() { + return Err("Miri test passed, but failure was expected".to_owned()); + } + + let combined = format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + if !combined.contains(&args[2]) { + return Err(format!( + "Miri failure output did not contain expected text `{}`\n{combined}", + args[2], + )); + } + + Ok(()) +} + +fn main() { + if let Err(err) = run() { + eprintln!("{err}"); + std::process::exit(1); + } +} diff --git a/test/miri/miri_cfg_test.rs b/test/miri/miri_cfg_test.rs new file mode 100644 index 0000000..3e93c1d --- /dev/null +++ b/test/miri/miri_cfg_test.rs @@ -0,0 +1,11 @@ +#[cfg(miri)] +#[test] +fn runs_under_miri() { + assert_eq!(2 + 2, 4); +} + +#[cfg(not(miri))] +#[test] +fn would_fail_without_miri_cfg() { + panic!("miri_test must pass cfg(miri)"); +} diff --git a/test/miri/miri_dep.rs b/test/miri/miri_dep.rs new file mode 100644 index 0000000..f2504c1 --- /dev/null +++ b/test/miri/miri_dep.rs @@ -0,0 +1,3 @@ +pub fn value_from_miri_dep() -> i32 { + miri_leaf::leaf_value() + 1 +} diff --git a/test/miri/miri_dep_graph_test.rs b/test/miri/miri_dep_graph_test.rs new file mode 100644 index 0000000..7a7ceec --- /dev/null +++ b/test/miri/miri_dep_graph_test.rs @@ -0,0 +1,10 @@ +#[cfg(not(miri))] +compile_error!("miri_dep_graph_test must run with cfg(miri)"); + +#[cfg(miri)] +const _: () = (); + +#[test] +fn uses_miri_compiled_dependency() { + assert_eq!(miri_dep::value_from_miri_dep() + 1, 42); +} diff --git a/test/miri/miri_detects_use_after_free.rs b/test/miri/miri_detects_use_after_free.rs new file mode 100644 index 0000000..551a57c --- /dev/null +++ b/test/miri/miri_detects_use_after_free.rs @@ -0,0 +1,9 @@ +#[test] +fn detects_use_after_free() { + let ptr = Box::into_raw(Box::new(123_u8)); + + unsafe { + drop(Box::from_raw(ptr)); + let _ = std::ptr::read(ptr); + } +} diff --git a/test/miri/miri_leaf.rs b/test/miri/miri_leaf.rs new file mode 100644 index 0000000..493001e --- /dev/null +++ b/test/miri/miri_leaf.rs @@ -0,0 +1,3 @@ +pub fn leaf_value() -> i32 { + 40 +} diff --git a/test/miri/miri_proc_macro.rs b/test/miri/miri_proc_macro.rs new file mode 100644 index 0000000..41e76b6 --- /dev/null +++ b/test/miri/miri_proc_macro.rs @@ -0,0 +1,10 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn make_miri_value(_input: TokenStream) -> TokenStream { + "fn proc_macro_generated_value() -> i32 { 7 }" + .parse() + .unwrap() +} diff --git a/test/miri/miri_proc_macro_test.rs b/test/miri/miri_proc_macro_test.rs new file mode 100644 index 0000000..62d0372 --- /dev/null +++ b/test/miri/miri_proc_macro_test.rs @@ -0,0 +1,9 @@ +#[cfg(not(miri))] +compile_error!("miri_proc_macro_test must run with cfg(miri)"); + +miri_proc_macro::make_miri_value!(); + +#[test] +fn uses_proc_macro_dependency() { + assert_eq!(proc_macro_generated_value(), 7); +}