diff --git a/MODULE.bazel b/MODULE.bazel index a88f19e4db..8dae9e16c6 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -20,6 +20,9 @@ bazel_dep(name = "apple_support", version = "1.24.1", repo_name = "build_bazel_a internal_deps = use_extension("//rust/private:internal_extensions.bzl", "i") use_repo( internal_deps, + "rra", + "rra__miniz_oxide-0.9.1", + "rra__object-0.39.1", "rrra", "rrra__anyhow-1.0.102", "rrra__camino-1.2.2", diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index add64b028b..799be08755 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -26,6 +26,10 @@ load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies") rust_analyzer_dependencies() +load("@rules_rust//tools/auditable:deps.bzl", "auditable_dependencies") + +auditable_dependencies() + load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() diff --git a/cargo/private/BUILD.bazel b/cargo/private/BUILD.bazel index fd60c4ff62..cf06302b29 100644 --- a/cargo/private/BUILD.bazel +++ b/cargo/private/BUILD.bazel @@ -1,6 +1,10 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_skylib//rules:copy_file.bzl", "copy_file") -load("//rust:defs.bzl", "rust_binary") + +# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl) +# to avoid a dependency cycle with the auditable_injector. +# buildifier: disable=bzl-visibility +load("//rust/private:rust.bzl", "rust_binary") rust_binary( name = "copy_file", diff --git a/cargo/private/cargo_build_script_runner/BUILD.bazel b/cargo/private/cargo_build_script_runner/BUILD.bazel index caf12eb76e..eda46ef57f 100644 --- a/cargo/private/cargo_build_script_runner/BUILD.bazel +++ b/cargo/private/cargo_build_script_runner/BUILD.bazel @@ -1,4 +1,9 @@ -load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") +load("//rust:defs.bzl", "rust_library", "rust_test") + +# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl) +# to avoid a dependency cycle with the auditable_injector. +# buildifier: disable=bzl-visibility +load("//rust/private:rust.bzl", "rust_binary") rust_library( name = "cargo_build_script_runner", diff --git a/cargo/private/cargo_build_script_wrapper.bzl b/cargo/private/cargo_build_script_wrapper.bzl index bec53cf465..c2263d09d9 100644 --- a/cargo/private/cargo_build_script_wrapper.bzl +++ b/cargo/private/cargo_build_script_wrapper.bzl @@ -7,7 +7,11 @@ load( "name_to_pkg_name", _build_script_run = "cargo_build_script", ) -load("//rust:defs.bzl", "rust_binary") + +# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl) +# to avoid a dependency cycle with the auditable_injector. +# buildifier: disable=bzl-visibility +load("//rust/private:rust.bzl", "rust_binary") def cargo_build_script( *, diff --git a/cargo/private/cargo_toml_variable_extractor/BUILD.bazel b/cargo/private/cargo_toml_variable_extractor/BUILD.bazel index c91583b7c7..234a7fb3e0 100644 --- a/cargo/private/cargo_toml_variable_extractor/BUILD.bazel +++ b/cargo/private/cargo_toml_variable_extractor/BUILD.bazel @@ -1,4 +1,7 @@ -load("//rust:defs.bzl", "rust_binary") +# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl) +# to avoid a dependency cycle with the auditable_injector. +# buildifier: disable=bzl-visibility +load("//rust/private:rust.bzl", "rust_binary") rust_binary( name = "cargo_toml_variable_extractor", diff --git a/rust/defs.bzl b/rust/defs.bzl index 4f2ef72582..5b1d06a625 100644 --- a/rust/defs.bzl +++ b/rust/defs.bzl @@ -84,14 +84,23 @@ rust_library = _rust_library rust_static_library = _rust_static_library # See @rules_rust//rust/private:rust.bzl for a complete description. -rust_shared_library = _rust_shared_library -# See @rules_rust//rust/private:rust.bzl for a complete description. +_AUDITABLE_INJECTOR_SELECT = select({ + str(Label("//rust/settings:auditable_enabled")): str(Label("//tools/auditable:auditable_injector")), + "//conditions:default": None, +}) + +def rust_shared_library(name, **kwargs): + """Builds a Rust shared library. See @rules_rust//rust/private:rust.bzl for a complete description.""" + kwargs.setdefault("auditable_injector", _AUDITABLE_INJECTOR_SELECT) + _rust_shared_library(name = name, **kwargs) rust_proc_macro = _rust_proc_macro # See @rules_rust//rust/private:rust.bzl for a complete description. -rust_binary = _rust_binary -# See @rules_rust//rust/private:rust.bzl for a complete description. +def rust_binary(name, **kwargs): + """Builds a Rust binary crate. See @rules_rust//rust/private:rust.bzl for a complete description.""" + kwargs.setdefault("auditable_injector", _AUDITABLE_INJECTOR_SELECT) + _rust_binary(name = name, **kwargs) rust_library_group = _rust_library_group # See @rules_rust//rust/private:rust.bzl for a complete description. diff --git a/rust/private/common.bzl b/rust/private/common.bzl index 7339f842a1..0ac53f62ef 100644 --- a/rust/private/common.bzl +++ b/rust/private/common.bzl @@ -61,6 +61,12 @@ def _create_crate_info(**kwargs): kwargs.update({"rustc_env_files": []}) if not "data" in kwargs: kwargs.update({"data": depset([])}) + if not "pkg_name" in kwargs: + kwargs.update({"pkg_name": kwargs.get("name", "")}) + if not "version" in kwargs: + kwargs.update({"version": "0.0.0"}) + if not "source" in kwargs: + kwargs.update({"source": "Local"}) return CrateInfo(**kwargs) rust_common = struct( diff --git a/rust/private/internal_extensions.bzl b/rust/private/internal_extensions.bzl index 3691204017..2e8004e171 100644 --- a/rust/private/internal_extensions.bzl +++ b/rust/private/internal_extensions.bzl @@ -3,6 +3,7 @@ load("@bazel_features//:features.bzl", "bazel_features") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("//rust/private:repository_utils.bzl", "TINYJSON_KWARGS") +load("//tools/auditable:deps.bzl", "auditable_dependencies") load("//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies") def _internal_deps_impl(module_ctx): @@ -14,6 +15,7 @@ def _internal_deps_impl(module_ctx): direct_deps = [struct(repo = "rules_rust_tinyjson", is_dev_dep = False)] http_archive(**TINYJSON_KWARGS) + direct_deps.extend(auditable_dependencies()) direct_deps.extend(rust_analyzer_dependencies()) # is_dev_dep is ignored here. It's not relevant for internal_deps, as dev diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl index a531884d19..b2e8176dea 100644 --- a/rust/private/providers.bzl +++ b/rust/private/providers.bzl @@ -34,18 +34,21 @@ CrateInfo = provider( "name": "str: The name of this crate.", "output": "File: The output File that will be produced, depends on crate type.", "owner": "Label: The label of the target that produced this CrateInfo", + "pkg_name": "str: The Cargo package name, which may differ from the Rust crate name (e.g. 'rustls-webpki' vs 'webpki').", "proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.", "root": "File: The source File entrypoint to this crate, eg. lib.rs", "rustc_env": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.", "rustc_env_files": "[File]: Files containing additional environment variables to set for rustc.", "rustc_output": "File: The output from rustc from producing the output file. It is optional.", "rustc_rmeta_output": "File: The rmeta file produced for this crate. It is optional.", + "source": "str: The source of this crate (e.g. 'CratesIo', 'Git', 'Local', 'Registry').", "srcs": "depset[File]: All source Files that are part of the crate.", "std_dylib": "File: libstd.so file", "type": ( "str: The type of this crate " + "(see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit))." ), + "version": "str: The semver version of this crate.", "wrapped_crate_type": ( "str, optional: The original crate type for targets generated using a previously defined " + "crate (typically tests using the `rust_test::crate` attribute)" diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index bd784687d6..93ef126f5c 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -57,6 +57,17 @@ load( # TODO(marco): Separate each rule into its own file. +def _get_pkg_name(ctx): + """Extract the Cargo package name from a 'crate-name=...' tag. + + crate_universe adds this tag to vendored crates. Returns empty string if + not found, in which case _create_crate_info defaults to the crate name. + """ + for tag in getattr(ctx.attr, "tags", []): + if tag.startswith("crate-name="): + return tag[len("crate-name="):] + return "" + def _assert_no_deprecated_attributes(_ctx): """Forces a failure if any deprecated attributes were specified @@ -245,6 +256,9 @@ def _rust_library_common(ctx, crate_type): compile_data = depset(compile_data), compile_data_targets = depset(ctx.attr.compile_data), owner = ctx.label, + version = getattr(ctx.attr, "version", "0.0.0"), + source = getattr(ctx.attr, "source", "Local"), + pkg_name = _get_pkg_name(ctx), cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = False), ), ) @@ -309,6 +323,9 @@ def _rust_binary_impl(ctx): compile_data = depset(compile_data), compile_data_targets = depset(ctx.attr.compile_data), owner = ctx.label, + version = getattr(ctx.attr, "version", "0.0.0"), + source = getattr(ctx.attr, "source", "Local"), + pkg_name = _get_pkg_name(ctx), cfgs = _collect_cfgs(ctx, toolchain, crate_root, ctx.attr.crate_type, crate_is_test = False), ), ) @@ -431,6 +448,9 @@ def _rust_test_impl(ctx): compile_data_targets = compile_data_targets, wrapped_crate_type = crate.type, owner = ctx.label, + version = getattr(ctx.attr, "version", "0.0.0"), + source = getattr(ctx.attr, "source", "Local"), + pkg_name = _get_pkg_name(ctx), cfgs = _collect_cfgs(ctx, toolchain, crate.root, crate_type, crate_is_test = True), ) else: @@ -487,6 +507,9 @@ def _rust_test_impl(ctx): compile_data = depset(compile_data), compile_data_targets = depset(ctx.attr.compile_data), owner = ctx.label, + version = getattr(ctx.attr, "version", "0.0.0"), + source = getattr(ctx.attr, "source", "Local"), + pkg_name = _get_pkg_name(ctx), cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = True), ) @@ -605,6 +628,9 @@ RUSTC_ATTRS = { "_always_enable_metadata_output_groups": attr.label( default = Label("//rust/settings:always_enable_metadata_output_groups"), ), + "_auditable": attr.label( + default = Label("//rust/settings:auditable"), + ), "_error_format": attr.label( default = Label("//rust/settings:error_format"), ), @@ -791,6 +817,10 @@ _COMMON_ATTRS = { # "name": attr.string( # doc = "This name will also be used as the name of the crate built by this rule.", # `), + "source": attr.string( + doc = "The source of this crate (e.g. 'crates.io', 'git', 'local', 'registry'). Used for cargo-auditable dependency tracking.", + default = "Local", + ), "srcs": attr.label_list( doc = dedent("""\ List of Rust `.rs` source files used to build the library. @@ -1062,9 +1092,17 @@ _rust_shared_library_transition = transition( ], ) +_AUDITABLE_INJECTOR_ATTR = { + "auditable_injector": attr.label( + doc = "The auditable_injector tool for embedding cargo-auditable metadata. " + + "Set to @rules_rust//tools/auditable:auditable_injector to enable.", + cfg = "exec", + ), +} + rust_shared_library = rule( implementation = _rust_shared_library_impl, - attrs = _COMMON_ATTRS | _PLATFORM_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS, + attrs = _COMMON_ATTRS | _PLATFORM_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS | _AUDITABLE_INJECTOR_ATTR, fragments = ["cpp"], cfg = _rust_shared_library_transition, toolchains = [ @@ -1187,7 +1225,7 @@ _rust_binary_transition = transition( rust_binary = rule( implementation = _rust_binary_impl, provides = COMMON_PROVIDERS, - attrs = _COMMON_ATTRS | _RUST_BINARY_ATTRS | _PLATFORM_ATTRS, + attrs = _COMMON_ATTRS | _RUST_BINARY_ATTRS | _PLATFORM_ATTRS | _AUDITABLE_INJECTOR_ATTR, executable = True, fragments = ["cpp"], cfg = _rust_binary_transition, diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index c8f6ecc9e6..d5eba26452 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1447,6 +1447,19 @@ def rustc_compile_action( experimental_use_cc_common_link = experimental_use_cc_common_link, ) + # Generate cargo-auditable dependency metadata if the setting is enabled + # and this is a linkable crate type (binary or cdylib). + auditable_obj = None + if (hasattr(ctx.attr, "_auditable") and + ctx.attr._auditable[BuildSettingInfo].value and + crate_info.type in ["bin", "cdylib"] and + hasattr(ctx.attr, "auditable_injector") and + ctx.attr.auditable_injector): + auditable_obj = _create_auditable_object(ctx, crate_info, dep_info, toolchain) + if auditable_obj: + compile_inputs = depset([auditable_obj], transitive = [compile_inputs]) + rust_flags = list(rust_flags) + _auditable_rustc_flags(auditable_obj, toolchain) + # The types of rustc outputs to emit. # If we build metadata, we need to keep the command line of the two invocations # (rlib and rmeta) as similar as possible, otherwise rustc rejects the rmeta as @@ -2784,6 +2797,159 @@ extra_exec_rustc_flag = rule( def _per_crate_rustc_flag_impl(ctx): return PerCrateRustcFlagsInfo(per_crate_rustc_flags = [f for f in ctx.build_setting_value if f != ""]) +def _crate_key(crate_info): + """Return a unique key for a CrateInfo, used for deduplication and index lookup.""" + return crate_info.name + "@" + crate_info.version + +def _resolve_edges(dep_variant_infos, crate_to_idx): + """Resolve dependency edges from a list of DepVariantInfo structs. + + Handles two kinds of DepVariantInfo: + - crate_info: direct crate dependency + - crate_group_info: group of crates (e.g. from rust_prost_library) + + Proc-macro and build script dependencies are excluded since they are + compile-time tools not linked into the final binary. + + Args: + dep_variant_infos: list of DepVariantInfo structs. + crate_to_idx: dict mapping crate key to package index. + + Returns: + Sorted, deduplicated list of package indices. + """ + edges = [] + for dvi in dep_variant_infos: + if dvi.crate_info and _crate_key(dvi.crate_info) in crate_to_idx: + edges.append(crate_to_idx[_crate_key(dvi.crate_info)]) + elif hasattr(dvi, "crate_group_info") and dvi.crate_group_info: + for inner_dvi in dvi.crate_group_info.dep_variant_infos.to_list(): + if inner_dvi.crate_info and _crate_key(inner_dvi.crate_info) in crate_to_idx: + edges.append(crate_to_idx[_crate_key(inner_dvi.crate_info)]) + return sorted({e: None for e in edges}) + +def _create_auditable_object(ctx, crate_info, dep_info, toolchain): + """Generate a cargo-auditable compatible .dep-v0 object file. + + Collects dependency metadata from the transitive crate graph and invokes + the auditable_injector tool to create a platform-appropriate object file. + + Args: + ctx: The rule's context object. + crate_info: CrateInfo for the current crate. + dep_info: DepInfo with transitive dependency information. + toolchain: The Rust toolchain. + + Returns: + File: The generated audit_data object file. + """ + + # Filter out proc-macro crates -- they are compile-time tools that run + # during compilation but are not linked into the final binary. + # Sort by crate key for deterministic output across builds (depset + # iteration order is not guaranteed to be stable). + transitive_crates = sorted( + [c for c in dep_info.transitive_crates.to_list() if not _is_proc_macro(c)], + key = _crate_key, + ) + + # Build an index from crate identity to position in the packages list. + # If the root crate (binary/cdylib) already appears in transitive_crates + # (e.g. a binary depending on its own library crate), reuse that index + # instead of appending a duplicate entry. + crate_to_idx = {} + for i, dep_crate in enumerate(transitive_crates): + crate_to_idx[_crate_key(dep_crate)] = i + root_key = _crate_key(crate_info) + if root_key in crate_to_idx: + root_idx = crate_to_idx[root_key] + else: + root_idx = len(transitive_crates) + crate_to_idx[root_key] = root_idx + + # Resolve direct dependency edges for each runtime crate, including edges + # through CrateGroupInfo (e.g. rust_prost_library / prost toolchain). + # Proc-macro and build script deps are excluded since they are not linked. + dep_edges = [] + for dep_crate in transitive_crates: + dep_edges.append(_resolve_edges(dep_crate.deps.to_list(), crate_to_idx)) + + # Root crate's direct deps. + dep_edges.append(_resolve_edges(crate_info.deps.to_list(), crate_to_idx)) + + # Build the packages list as structured data, then encode with json.encode(). + # Use pkg_name (Cargo package name) for the "name" field so vulnerability + # scanners can match advisories. Falls back to crate name if pkg_name is empty. + packages = [] + for i, dep_crate in enumerate(transitive_crates): + entry = { + "dependencies": dep_edges[i], + "name": dep_crate.pkg_name or dep_crate.name, + "source": dep_crate.source, + "version": dep_crate.version, + } + if i == root_idx: + entry["root"] = True + + # Merge library and binary edges when the root was already in + # transitive_crates (binary depending on its own library crate). + merged = {e: None for e in dep_edges[i]} + merged.update({e: None for e in dep_edges[len(transitive_crates)]}) + entry["dependencies"] = sorted(merged) + packages.append(entry) + + # Root crate entry (only if not already in transitive_crates). + if root_idx == len(transitive_crates): + packages.append({ + "dependencies": dep_edges[root_idx], + "name": crate_info.pkg_name or crate_info.name, + "root": True, + "source": crate_info.source, + "version": crate_info.version, + }) + + json_content = json.encode({"format": 0, "packages": packages}) + + json_file = ctx.actions.declare_file(crate_info.name + "_audit_deps.json") + ctx.actions.write(output = json_file, content = json_content) + + audit_obj = ctx.actions.declare_file(crate_info.name + "_audit_data.o") + target_triple = toolchain.target_flag_value + + injector_info = ctx.attr.auditable_injector[DefaultInfo] + ctx.actions.run( + executable = injector_info.files_to_run.executable, + inputs = [json_file], + outputs = [audit_obj], + arguments = [target_triple, json_file.path, audit_obj.path], + tools = [injector_info.files_to_run], + mnemonic = "RustAuditable", + progress_message = "Generating cargo-auditable metadata for %{label}", + ) + + return audit_obj + +def _auditable_rustc_flags(audit_obj, toolchain): + """Return the rustc flags needed to link the auditable object file. + + Args: + audit_obj: The generated audit_data object file. + toolchain: The Rust toolchain. + + Returns: + list[str]: Rustc flags to pass. + """ + flags = ["--codegen=link-arg=" + audit_obj.path] + + if toolchain.target_os in ["macos", "darwin"]: + flags.append("--codegen=link-arg=-Wl,-u,_AUDITABLE_VERSION_INFO") + elif toolchain.target_os == "windows": + flags.append("--codegen=link-arg=/INCLUDE:AUDITABLE_VERSION_INFO") + elif toolchain.target_arch not in ("wasm32", "wasm64"): + flags.append("--codegen=link-arg=-Wl,-u,AUDITABLE_VERSION_INFO") + + return flags + per_crate_rustc_flag = rule( doc = ( "Add additional rustc_flag to matching crates from the command line with `--@rules_rust//rust/settings:experimental_per_crate_rustc_flag`. " + diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl index f302bab743..ea1aea6938 100644 --- a/rust/private/rustdoc.bzl +++ b/rust/private/rustdoc.bzl @@ -46,6 +46,9 @@ def _strip_crate_info_output(crate_info): compile_data = crate_info.compile_data, compile_data_targets = crate_info.compile_data_targets, data = crate_info.data, + version = crate_info.version, + source = crate_info.source, + pkg_name = crate_info.pkg_name, ) def rustdoc_compile_action( diff --git a/rust/private/rustdoc_test.bzl b/rust/private/rustdoc_test.bzl index 522c048cd1..63ed539c49 100644 --- a/rust/private/rustdoc_test.bzl +++ b/rust/private/rustdoc_test.bzl @@ -130,6 +130,9 @@ def _rust_doc_test_impl(ctx): compile_data_targets = crate.compile_data_targets, wrapped_crate_type = crate.type, owner = ctx.label, + version = crate.version, + source = crate.source, + pkg_name = crate.pkg_name, ) if toolchain.target_os == "windows": diff --git a/rust/settings/BUILD.bazel b/rust/settings/BUILD.bazel index d2b84debcc..eb58843fb6 100644 --- a/rust/settings/BUILD.bazel +++ b/rust/settings/BUILD.bazel @@ -2,6 +2,7 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load( ":settings.bzl", "always_enable_metadata_output_groups", + "auditable", "capture_clippy_output", "clippy_error_format", "clippy_flag", @@ -62,6 +63,14 @@ bzl_library( always_enable_metadata_output_groups() +auditable() + +config_setting( + name = "auditable_enabled", + flag_values = {":auditable": "True"}, + visibility = ["//visibility:public"], +) + capture_clippy_output() clippy_flag() diff --git a/rust/settings/settings.bzl b/rust/settings/settings.bzl index 2769272dc5..f6df15a491 100644 --- a/rust/settings/settings.bzl +++ b/rust/settings/settings.bzl @@ -557,6 +557,19 @@ def codegen_units(): build_setting_default = -1, ) +# buildifier: disable=unnamed-macro +def auditable(): + """When enabled, embeds cargo-auditable compatible dependency metadata \ + into a `.dep-v0` section of compiled Rust binaries. This allows tools \ + like `cargo audit` to detect dependency vulnerabilities in compiled binaries. + + To enable, set --@rules_rust//rust/settings:auditable=true in .bazelrc. + """ + bool_flag( + name = "auditable", + build_setting_default = False, + ) + # buildifier: disable=unnamed-macro def collect_cfgs(): """Enable collection of cfg flags with results stored in CrateInfo.cfgs. diff --git a/test/unit/auditable/BUILD.bazel b/test/unit/auditable/BUILD.bazel new file mode 100644 index 0000000000..57718a4d24 --- /dev/null +++ b/test/unit/auditable/BUILD.bazel @@ -0,0 +1,4 @@ +load(":auditable_test.bzl", "auditable_test_suite") + +############################ UNIT TESTS ############################# +auditable_test_suite(name = "auditable_test_suite") diff --git a/test/unit/auditable/auditable_test.bzl b/test/unit/auditable/auditable_test.bzl new file mode 100644 index 0000000000..1db7ef24cd --- /dev/null +++ b/test/unit/auditable/auditable_test.bzl @@ -0,0 +1,254 @@ +"""Analysis tests for the cargo-auditable integration.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load( + "//rust:defs.bzl", + "rust_binary", + "rust_library", +) +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _find_actions(tut, mnemonic): + return [a for a in tut.actions if a.mnemonic == mnemonic] + +def _get_json_content(tut): + """Return the content string written by the FileWrite that produces the audit JSON.""" + for a in tut.actions: + if a.mnemonic != "FileWrite": + continue + for out in a.outputs.to_list(): + if out.basename.endswith("_audit_deps.json"): + return a.content + return None + +# --------------------------------------------------------------------------- +# Test 1: RustAuditable action present when enabled +# --------------------------------------------------------------------------- + +def _auditable_action_present_test_impl(ctx): + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + auditable_actions = _find_actions(tut, "RustAuditable") + asserts.equals( + env, + 1, + len(auditable_actions), + "Expected exactly one RustAuditable action, got {}".format(len(auditable_actions)), + ) + return analysistest.end(env) + +auditable_action_present_test = analysistest.make( + _auditable_action_present_test_impl, + config_settings = {str(Label("//rust/settings:auditable")): True}, +) + +# --------------------------------------------------------------------------- +# Test 2: No RustAuditable action when setting is disabled +# --------------------------------------------------------------------------- + +def _auditable_action_absent_disabled_test_impl(ctx): + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + auditable_actions = _find_actions(tut, "RustAuditable") + asserts.equals( + env, + 0, + len(auditable_actions), + "Expected no RustAuditable action when setting is disabled", + ) + return analysistest.end(env) + +auditable_action_absent_disabled_test = analysistest.make( + _auditable_action_absent_disabled_test_impl, +) + +# --------------------------------------------------------------------------- +# Test 3: No RustAuditable on rust_library +# --------------------------------------------------------------------------- + +def _library_no_auditable_test_impl(ctx): + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + auditable_actions = _find_actions(tut, "RustAuditable") + asserts.equals( + env, + 0, + len(auditable_actions), + "rust_library should never produce RustAuditable actions", + ) + return analysistest.end(env) + +library_no_auditable_test = analysistest.make( + _library_no_auditable_test_impl, + config_settings = {str(Label("//rust/settings:auditable")): True}, +) + +# --------------------------------------------------------------------------- +# Test 4: Linker flags injected into Rustc action +# --------------------------------------------------------------------------- + +def _linker_flags_test_impl(ctx): + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + + rustc_actions = _find_actions(tut, "Rustc") + asserts.true(env, len(rustc_actions) > 0, "Expected at least one Rustc action") + + found = False + for action in rustc_actions: + for arg in action.argv: + if arg.startswith("--codegen=link-arg=") and "_audit_data.o" in arg: + found = True + break + if found: + break + asserts.true( + env, + found, + "Expected --codegen=link-arg=..._audit_data.o in a Rustc action's argv", + ) + return analysistest.end(env) + +linker_flags_test = analysistest.make( + _linker_flags_test_impl, + config_settings = {str(Label("//rust/settings:auditable")): True}, +) + +# --------------------------------------------------------------------------- +# Test 5: JSON content correctness +# --------------------------------------------------------------------------- + +def _json_content_test_impl(ctx): + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + + json = _get_json_content(tut) + asserts.true(env, json != None, "Expected a FileWrite action producing *_audit_deps.json") + + asserts.true( + env, + '"name":"lib_a"' in json, + "JSON should contain lib_a dependency", + ) + asserts.true( + env, + '"version":"1.0.0"' in json, + "JSON should contain lib_a's version 1.0.0", + ) + asserts.true( + env, + '"source":"CratesIo"' in json, + "JSON should contain lib_a's source CratesIo", + ) + asserts.true( + env, + '"name":"lib_b"' in json, + "JSON should contain transitive dep lib_b", + ) + asserts.true( + env, + '"root":true' in json, + "JSON should contain exactly one root entry", + ) + asserts.true( + env, + '"format":0' in json, + "JSON should contain format:0", + ) + + # The root crate should have non-empty dependencies (it depends on lib_a). + root_start = json.find('"root":true') + asserts.true(env, root_start > 0, "root entry should exist") + pkg_start = json.rfind("{", 0, root_start) + pkg_str = json[pkg_start:root_start + len('"root":true') + 1] + asserts.true( + env, + '"dependencies":[]' not in pkg_str, + "Root package should have non-empty dependencies, got: " + pkg_str, + ) + + return analysistest.end(env) + +json_content_test = analysistest.make( + _json_content_test_impl, + config_settings = {str(Label("//rust/settings:auditable")): True}, +) + +# --------------------------------------------------------------------------- +# Subjects and test suite +# --------------------------------------------------------------------------- + +def _auditable_test_subjects(): + """Create the test subject targets.""" + + rust_library( + name = "lib_b", + srcs = ["lib_b.rs"], + edition = "2021", + version = "0.1.0", + source = "CratesIo", + ) + + rust_library( + name = "lib_a", + srcs = ["lib.rs"], + edition = "2021", + deps = [":lib_b"], + version = "1.0.0", + source = "CratesIo", + ) + + rust_binary( + name = "auditable_bin", + srcs = ["main.rs"], + edition = "2021", + deps = [":lib_a"], + version = "2.0.0", + auditable_injector = "//tools/auditable:auditable_injector", + ) + +def auditable_test_suite(name): + """Entry-point macro called from the BUILD file. + + Args: + name: Name of the macro. + """ + _auditable_test_subjects() + + auditable_action_present_test( + name = "auditable_action_present_test", + target_under_test = ":auditable_bin", + ) + + auditable_action_absent_disabled_test( + name = "auditable_action_absent_disabled_test", + target_under_test = ":auditable_bin", + ) + + library_no_auditable_test( + name = "library_no_auditable_test", + target_under_test = ":lib_a", + ) + + linker_flags_test( + name = "linker_flags_test", + target_under_test = ":auditable_bin", + ) + + json_content_test( + name = "json_content_test", + target_under_test = ":auditable_bin", + ) + + native.test_suite( + name = name, + tests = [ + ":auditable_action_present_test", + ":auditable_action_absent_disabled_test", + ":library_no_auditable_test", + ":linker_flags_test", + ":json_content_test", + ], + ) diff --git a/test/unit/auditable/lib.rs b/test/unit/auditable/lib.rs new file mode 100644 index 0000000000..5bb70b73ad --- /dev/null +++ b/test/unit/auditable/lib.rs @@ -0,0 +1 @@ +pub fn lib_a() {} diff --git a/test/unit/auditable/lib_b.rs b/test/unit/auditable/lib_b.rs new file mode 100644 index 0000000000..5c6cf7c18e --- /dev/null +++ b/test/unit/auditable/lib_b.rs @@ -0,0 +1 @@ +pub fn lib_b() {} diff --git a/test/unit/auditable/main.rs b/test/unit/auditable/main.rs new file mode 100644 index 0000000000..f328e4d9d0 --- /dev/null +++ b/test/unit/auditable/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tools/auditable/3rdparty/BUILD.bazel b/tools/auditable/3rdparty/BUILD.bazel new file mode 100644 index 0000000000..1effb5153b --- /dev/null +++ b/tools/auditable/3rdparty/BUILD.bazel @@ -0,0 +1,56 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("//crate_universe:defs.bzl", "crate", "crates_vendor") + +crates_vendor( + name = "crates_vendor", + cargo_lockfile = "Cargo.Bazel.lock", + mode = "remote", + packages = { + "miniz_oxide": crate.spec( + version = "0.9", + ), + "object": crate.spec( + default_features = False, + features = [ + "coff", + "elf", + "macho", + "read_core", + "std", + "write", + ], + version = "0.39", + ), + }, + repository_name = "rra", + supported_platform_triples = [ + "aarch64-apple-darwin", + "aarch64-pc-windows-msvc", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-nixos-gnu", + "arm-unknown-linux-gnueabi", + "armv7-linux-androideabi", + "armv7-unknown-linux-gnueabi", + "i686-apple-darwin", + "i686-pc-windows-msvc", + "i686-unknown-freebsd", + "i686-unknown-linux-gnu", + "powerpc-unknown-linux-gnu", + "s390x-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-nixos-gnu", + ], + tags = ["manual"], +) + +bzl_library( + name = "bzl_lib", + srcs = [ + "//tools/auditable/3rdparty/crates:crates.bzl", + "//tools/auditable/3rdparty/crates:defs.bzl", + ], + visibility = ["//tools/auditable:__pkg__"], +) diff --git a/tools/auditable/3rdparty/Cargo.Bazel.lock b/tools/auditable/3rdparty/Cargo.Bazel.lock new file mode 100644 index 0000000000..91702b40ab --- /dev/null +++ b/tools/auditable/3rdparty/Cargo.Bazel.lock @@ -0,0 +1,90 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "direct-cargo-bazel-deps" +version = "0.0.1" +dependencies = [ + "miniz_oxide", + "object", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +dependencies = [ + "foldhash", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", + "memchr", +] diff --git a/tools/auditable/3rdparty/crates/BUILD.adler2-2.0.1.bazel b/tools/auditable/3rdparty/crates/BUILD.adler2-2.0.1.bazel new file mode 100644 index 0000000000..242f0c254f --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.adler2-2.0.1.bazel @@ -0,0 +1,74 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "adler2", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_root = "src/lib.rs", + edition = "2021", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=adler2", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "2.0.1", +) diff --git a/tools/auditable/3rdparty/crates/BUILD.bazel b/tools/auditable/3rdparty/crates/BUILD.bazel new file mode 100644 index 0000000000..a3cde92b86 --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.bazel @@ -0,0 +1,56 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +package(default_visibility = ["//visibility:public"]) + +exports_files( + [ + "cargo-bazel.json", + "crates.bzl", + "defs.bzl", + ] + glob( + include = ["*.bazel"], + allow_empty = True, + ), +) + +filegroup( + name = "srcs", + srcs = glob( + include = [ + "*.bazel", + "*.bzl", + ], + allow_empty = True, + ), +) + +# Workspace Member Dependencies +alias( + name = "miniz_oxide-0.9.1", + actual = "@rra__miniz_oxide-0.9.1//:miniz_oxide", + tags = ["manual"], +) + +alias( + name = "miniz_oxide", + actual = "@rra__miniz_oxide-0.9.1//:miniz_oxide", + tags = ["manual"], +) + +alias( + name = "object-0.39.1", + actual = "@rra__object-0.39.1//:object", + tags = ["manual"], +) + +alias( + name = "object", + actual = "@rra__object-0.39.1//:object", + tags = ["manual"], +) diff --git a/tools/auditable/3rdparty/crates/BUILD.cfg-if-1.0.4.bazel b/tools/auditable/3rdparty/crates/BUILD.cfg-if-1.0.4.bazel new file mode 100644 index 0000000000..42453ef18c --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.cfg-if-1.0.4.bazel @@ -0,0 +1,74 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "cfg_if", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_root = "src/lib.rs", + edition = "2018", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=cfg-if", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "1.0.4", +) diff --git a/tools/auditable/3rdparty/crates/BUILD.crc32fast-1.5.0.bazel b/tools/auditable/3rdparty/crates/BUILD.crc32fast-1.5.0.bazel new file mode 100644 index 0000000000..cf2d617c5a --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.crc32fast-1.5.0.bazel @@ -0,0 +1,146 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load( + "@rules_rust//cargo:defs.bzl", + "cargo_build_script", + "cargo_toml_env_vars", +) +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "crc32fast", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "std", + ], + crate_root = "src/lib.rs", + edition = "2021", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=crc32fast", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "1.5.0", + deps = [ + "@rra__cfg-if-1.0.4//:cfg_if", + "@rra__crc32fast-1.5.0//:build_script_build", + ], +) + +cargo_build_script( + name = "_bs", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + "**/*.rs", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "std", + ], + crate_name = "build_script_build", + crate_root = "build.rs", + data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + edition = "2021", + pkg_name = "crc32fast", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=crc32fast", + "manual", + "noclippy", + "norustfmt", + ], + version = "1.5.0", + visibility = ["//visibility:private"], +) + +alias( + name = "build_script_build", + actual = ":_bs", + tags = ["manual"], +) diff --git a/tools/auditable/3rdparty/crates/BUILD.equivalent-1.0.2.bazel b/tools/auditable/3rdparty/crates/BUILD.equivalent-1.0.2.bazel new file mode 100644 index 0000000000..5beadcc071 --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.equivalent-1.0.2.bazel @@ -0,0 +1,74 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "equivalent", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_root = "src/lib.rs", + edition = "2015", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=equivalent", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "1.0.2", +) diff --git a/tools/auditable/3rdparty/crates/BUILD.foldhash-0.2.0.bazel b/tools/auditable/3rdparty/crates/BUILD.foldhash-0.2.0.bazel new file mode 100644 index 0000000000..8b5fd452d5 --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.foldhash-0.2.0.bazel @@ -0,0 +1,74 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "foldhash", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_root = "src/lib.rs", + edition = "2021", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=foldhash", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "0.2.0", +) diff --git a/tools/auditable/3rdparty/crates/BUILD.hashbrown-0.17.0.bazel b/tools/auditable/3rdparty/crates/BUILD.hashbrown-0.17.0.bazel new file mode 100644 index 0000000000..04491294d7 --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.hashbrown-0.17.0.bazel @@ -0,0 +1,80 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "hashbrown", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "default-hasher", + ], + crate_root = "src/lib.rs", + edition = "2024", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=hashbrown", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "0.17.0", + deps = [ + "@rra__foldhash-0.2.0//:foldhash", + ], +) diff --git a/tools/auditable/3rdparty/crates/BUILD.indexmap-2.14.0.bazel b/tools/auditable/3rdparty/crates/BUILD.indexmap-2.14.0.bazel new file mode 100644 index 0000000000..b77045057b --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.indexmap-2.14.0.bazel @@ -0,0 +1,81 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "indexmap", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "std", + ], + crate_root = "src/lib.rs", + edition = "2024", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=indexmap", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "2.14.0", + deps = [ + "@rra__equivalent-1.0.2//:equivalent", + "@rra__hashbrown-0.17.0//:hashbrown", + ], +) diff --git a/tools/auditable/3rdparty/crates/BUILD.memchr-2.8.0.bazel b/tools/auditable/3rdparty/crates/BUILD.memchr-2.8.0.bazel new file mode 100644 index 0000000000..d410e86627 --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.memchr-2.8.0.bazel @@ -0,0 +1,78 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "memchr", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "alloc", + "std", + ], + crate_root = "src/lib.rs", + edition = "2021", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=memchr", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "2.8.0", +) diff --git a/tools/auditable/3rdparty/crates/BUILD.miniz_oxide-0.9.1.bazel b/tools/auditable/3rdparty/crates/BUILD.miniz_oxide-0.9.1.bazel new file mode 100644 index 0000000000..557c18b6fc --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.miniz_oxide-0.9.1.bazel @@ -0,0 +1,81 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars") +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "miniz_oxide", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "default", + "with-alloc", + ], + crate_root = "src/lib.rs", + edition = "2021", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=miniz_oxide", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "0.9.1", + deps = [ + "@rra__adler2-2.0.1//:adler2", + ], +) diff --git a/tools/auditable/3rdparty/crates/BUILD.object-0.39.1.bazel b/tools/auditable/3rdparty/crates/BUILD.object-0.39.1.bazel new file mode 100644 index 0000000000..6d28ef6fde --- /dev/null +++ b/tools/auditable/3rdparty/crates/BUILD.object-0.39.1.bazel @@ -0,0 +1,167 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### + +load( + "@rules_rust//cargo:defs.bzl", + "cargo_build_script", + "cargo_toml_env_vars", +) +load("@rules_rust//rust:defs.bzl", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +cargo_toml_env_vars( + name = "cargo_toml_env_vars", + src = "Cargo.toml", +) + +rust_library( + name = "object", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "coff", + "elf", + "macho", + "pe", + "read_core", + "std", + "write", + "write_core", + "write_std", + "xcoff", + ], + crate_root = "src/lib.rs", + edition = "2018", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=object", + "manual", + "noclippy", + "norustfmt", + ], + target_compatible_with = select({ + "@rules_rust//rust/platform:aarch64-apple-darwin": [], + "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [], + "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [], + "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:armv7-linux-androideabi": [], + "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [], + "@rules_rust//rust/platform:i686-apple-darwin": [], + "@rules_rust//rust/platform:i686-pc-windows-msvc": [], + "@rules_rust//rust/platform:i686-unknown-freebsd": [], + "@rules_rust//rust/platform:i686-unknown-linux-gnu": [], + "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [], + "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-apple-darwin": [], + "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [], + "@rules_rust//rust/platform:x86_64-unknown-freebsd": [], + "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [], + "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + version = "0.39.1", + deps = [ + "@rra__crc32fast-1.5.0//:crc32fast", + "@rra__hashbrown-0.17.0//:hashbrown", + "@rra__indexmap-2.14.0//:indexmap", + "@rra__memchr-2.8.0//:memchr", + "@rra__object-0.39.1//:build_script_build", + ], +) + +cargo_build_script( + name = "_bs", + srcs = glob( + include = ["**/*.rs"], + allow_empty = True, + ), + compile_data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + "**/*.rs", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + crate_features = [ + "coff", + "elf", + "macho", + "pe", + "read_core", + "std", + "write", + "write_core", + "write_std", + "xcoff", + ], + crate_name = "build_script_build", + crate_root = "build.rs", + data = glob( + include = ["**"], + allow_empty = True, + exclude = [ + "**/* *", + ".tmp_git_root/**/*", + "BUILD", + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), + edition = "2018", + pkg_name = "object", + rustc_env_files = [ + ":cargo_toml_env_vars", + ], + rustc_flags = [ + "--cap-lints=allow", + ], + tags = [ + "cargo-bazel", + "crate-name=object", + "manual", + "noclippy", + "norustfmt", + ], + version = "0.39.1", + visibility = ["//visibility:private"], +) + +alias( + name = "build_script_build", + actual = ":_bs", + tags = ["manual"], +) diff --git a/tools/auditable/3rdparty/crates/alias_rules.bzl b/tools/auditable/3rdparty/crates/alias_rules.bzl new file mode 100644 index 0000000000..14b04c1272 --- /dev/null +++ b/tools/auditable/3rdparty/crates/alias_rules.bzl @@ -0,0 +1,47 @@ +"""Alias that transitions its target to `compilation_mode=opt`. Use `transition_alias="opt"` to enable.""" + +load("@rules_cc//cc:defs.bzl", "CcInfo") +load("@rules_rust//rust:rust_common.bzl", "COMMON_PROVIDERS") + +def _transition_alias_impl(ctx): + # `ctx.attr.actual` is a list of 1 item due to the transition + providers = [ctx.attr.actual[0][provider] for provider in COMMON_PROVIDERS] + if CcInfo in ctx.attr.actual[0]: + providers.append(ctx.attr.actual[0][CcInfo]) + return providers + +def _change_compilation_mode(compilation_mode): + def _change_compilation_mode_impl(_settings, _attr): + return { + "//command_line_option:compilation_mode": compilation_mode, + } + + return transition( + implementation = _change_compilation_mode_impl, + inputs = [], + outputs = [ + "//command_line_option:compilation_mode", + ], + ) + +def _transition_alias_rule(compilation_mode): + return rule( + implementation = _transition_alias_impl, + provides = COMMON_PROVIDERS, + attrs = { + "actual": attr.label( + mandatory = True, + doc = "`rust_library()` target to transition to `compilation_mode=opt`.", + providers = COMMON_PROVIDERS, + cfg = _change_compilation_mode(compilation_mode), + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + doc = "Transitions a Rust library crate to the `compilation_mode=opt`.", + ) + +transition_alias_dbg = _transition_alias_rule("dbg") +transition_alias_fastbuild = _transition_alias_rule("fastbuild") +transition_alias_opt = _transition_alias_rule("opt") diff --git a/tools/auditable/3rdparty/crates/crates.bzl b/tools/auditable/3rdparty/crates/crates.bzl new file mode 100644 index 0000000000..3137bf663a --- /dev/null +++ b/tools/auditable/3rdparty/crates/crates.bzl @@ -0,0 +1,32 @@ +############################################################################### +# @generated +# This file is auto-generated by the cargo-bazel tool. +# +# DO NOT MODIFY: Local changes may be replaced in future executions. +############################################################################### +"""Rules for defining repositories for remote `crates_vendor` repositories""" + +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +# buildifier: disable=bzl-visibility +load("@rules_rust//crate_universe/private:crates_vendor.bzl", "crates_vendor_remote_repository") + +# buildifier: disable=bzl-visibility +load("//tools/auditable/3rdparty/crates:defs.bzl", _crate_repositories = "crate_repositories") + +def crate_repositories(): + """Generates repositories for vendored crates. + + Returns: + A list of repos visible to the module through the module extension. + """ + maybe( + crates_vendor_remote_repository, + name = "rra", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.bazel"), + defs_module = Label("//tools/auditable/3rdparty/crates:defs.bzl"), + ) + + direct_deps = [struct(repo = "rra", is_dev_dep = False)] + direct_deps.extend(_crate_repositories()) + return direct_deps diff --git a/tools/auditable/3rdparty/crates/defs.bzl b/tools/auditable/3rdparty/crates/defs.bzl new file mode 100644 index 0000000000..b998f30796 --- /dev/null +++ b/tools/auditable/3rdparty/crates/defs.bzl @@ -0,0 +1,493 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//tools/auditable/3rdparty:crates_vendor +############################################################################### +""" +# `crates_repository` API + +- [aliases](#aliases) +- [crate_deps](#crate_deps) +- [all_crate_deps](#all_crate_deps) +- [crate_repositories](#crate_repositories) + +""" + +load("@bazel_skylib//lib:selects.bzl", "selects") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +############################################################################### +# MACROS API +############################################################################### + +# An identifier that represent common dependencies (unconditional). +_COMMON_CONDITION = "" + +def _flatten_dependency_maps(all_dependency_maps): + """Flatten a list of dependency maps into one dictionary. + + Dependency maps have the following structure: + + ```python + DEPENDENCIES_MAP = { + # The first key in the map is a Bazel package + # name of the workspace this file is defined in. + "workspace_member_package": { + + # Not all dependencies are supported for all platforms. + # the condition key is the condition required to be true + # on the host platform. + "condition": { + + # An alias to a crate target. # The label of the crate target the + # Aliases are only crate names. # package name refers to. + "package_name": "@full//:label", + } + } + } + ``` + + Args: + all_dependency_maps (list): A list of dicts as described above + + Returns: + dict: A dictionary as described above + """ + dependencies = {} + + for workspace_deps_map in all_dependency_maps: + for pkg_name, conditional_deps_map in workspace_deps_map.items(): + if pkg_name not in dependencies: + non_frozen_map = dict() + for key, values in conditional_deps_map.items(): + non_frozen_map.update({key: dict(values.items())}) + dependencies.setdefault(pkg_name, non_frozen_map) + continue + + for condition, deps_map in conditional_deps_map.items(): + # If the condition has not been recorded, do so and continue + if condition not in dependencies[pkg_name]: + dependencies[pkg_name].setdefault(condition, dict(deps_map.items())) + continue + + # Alert on any miss-matched dependencies + inconsistent_entries = [] + for crate_name, crate_label in deps_map.items(): + existing = dependencies[pkg_name][condition].get(crate_name) + if existing and existing != crate_label: + inconsistent_entries.append((crate_name, existing, crate_label)) + dependencies[pkg_name][condition].update({crate_name: crate_label}) + + return dependencies + +def crate_deps(deps, package_name = None): + """Finds the fully qualified label of the requested crates for the package where this macro is called. + + Args: + deps (list): The desired list of crate targets. + package_name (str, optional): The package name of the set of dependencies to look up. + Defaults to `native.package_name()`. + + Returns: + list: A list of labels to generated rust targets (str) + """ + + if not deps: + return [] + + if package_name == None: + package_name = native.package_name() + + # Join both sets of dependencies + dependencies = _flatten_dependency_maps([ + _NORMAL_DEPENDENCIES, + _NORMAL_DEV_DEPENDENCIES, + _PROC_MACRO_DEPENDENCIES, + _PROC_MACRO_DEV_DEPENDENCIES, + _BUILD_DEPENDENCIES, + _BUILD_PROC_MACRO_DEPENDENCIES, + ]).pop(package_name, {}) + + # Combine all conditional packages so we can easily index over a flat list + # TODO: Perhaps this should actually return select statements and maintain + # the conditionals of the dependencies + flat_deps = {} + for deps_set in dependencies.values(): + for crate_name, crate_label in deps_set.items(): + flat_deps.update({crate_name: crate_label}) + + missing_crates = [] + crate_targets = [] + for crate_target in deps: + if crate_target not in flat_deps: + missing_crates.append(crate_target) + else: + crate_targets.append(flat_deps[crate_target]) + + if missing_crates: + fail("Could not find crates `{}` among dependencies of `{}`. Available dependencies were `{}`".format( + missing_crates, + package_name, + dependencies, + )) + + return crate_targets + +def all_crate_deps( + normal = False, + normal_dev = False, + proc_macro = False, + proc_macro_dev = False, + build = False, + build_proc_macro = False, + package_name = None): + """Finds the fully qualified label of all requested direct crate dependencies \ + for the package where this macro is called. + + If no parameters are set, all normal dependencies are returned. Setting any one flag will + otherwise impact the contents of the returned list. + + Args: + normal (bool, optional): If True, normal dependencies are included in the + output list. + normal_dev (bool, optional): If True, normal dev dependencies will be + included in the output list. + proc_macro (bool, optional): If True, proc_macro dependencies are included + in the output list. + proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are + included in the output list. + build (bool, optional): If True, build dependencies are included + in the output list. + build_proc_macro (bool, optional): If True, build proc_macro dependencies are + included in the output list. + package_name (str, optional): The package name of the set of dependencies to look up. + Defaults to `native.package_name()` when unset. + + Returns: + list: A list of labels to generated rust targets (str) + """ + + if package_name == None: + package_name = native.package_name() + + # Determine the relevant maps to use + all_dependency_maps = [] + if normal: + all_dependency_maps.append(_NORMAL_DEPENDENCIES) + if normal_dev: + all_dependency_maps.append(_NORMAL_DEV_DEPENDENCIES) + if proc_macro: + all_dependency_maps.append(_PROC_MACRO_DEPENDENCIES) + if proc_macro_dev: + all_dependency_maps.append(_PROC_MACRO_DEV_DEPENDENCIES) + if build: + all_dependency_maps.append(_BUILD_DEPENDENCIES) + if build_proc_macro: + all_dependency_maps.append(_BUILD_PROC_MACRO_DEPENDENCIES) + + # Default to always using normal dependencies + if not all_dependency_maps: + all_dependency_maps.append(_NORMAL_DEPENDENCIES) + + dependencies = _flatten_dependency_maps(all_dependency_maps).pop(package_name, None) + + if not dependencies: + if dependencies == None: + fail("Tried to get all_crate_deps for package " + package_name + " but that package had no Cargo.toml file") + else: + return [] + + crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values()) + for condition, deps in dependencies.items(): + crate_deps += selects.with_or({ + tuple(_CONDITIONS[condition]): deps.values(), + "//conditions:default": [], + }) + + return crate_deps + +def aliases( + normal = False, + normal_dev = False, + proc_macro = False, + proc_macro_dev = False, + build = False, + build_proc_macro = False, + package_name = None): + """Produces a map of Crate alias names to their original label + + If no dependency kinds are specified, `normal` and `proc_macro` are used by default. + Setting any one flag will otherwise determine the contents of the returned dict. + + Args: + normal (bool, optional): If True, normal dependencies are included in the + output list. + normal_dev (bool, optional): If True, normal dev dependencies will be + included in the output list.. + proc_macro (bool, optional): If True, proc_macro dependencies are included + in the output list. + proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are + included in the output list. + build (bool, optional): If True, build dependencies are included + in the output list. + build_proc_macro (bool, optional): If True, build proc_macro dependencies are + included in the output list. + package_name (str, optional): The package name of the set of dependencies to look up. + Defaults to `native.package_name()` when unset. + + Returns: + dict: The aliases of all associated packages + """ + if package_name == None: + package_name = native.package_name() + + # Determine the relevant maps to use + all_aliases_maps = [] + if normal: + all_aliases_maps.append(_NORMAL_ALIASES) + if normal_dev: + all_aliases_maps.append(_NORMAL_DEV_ALIASES) + if proc_macro: + all_aliases_maps.append(_PROC_MACRO_ALIASES) + if proc_macro_dev: + all_aliases_maps.append(_PROC_MACRO_DEV_ALIASES) + if build: + all_aliases_maps.append(_BUILD_ALIASES) + if build_proc_macro: + all_aliases_maps.append(_BUILD_PROC_MACRO_ALIASES) + + # Default to always using normal aliases + if not all_aliases_maps: + all_aliases_maps.append(_NORMAL_ALIASES) + all_aliases_maps.append(_PROC_MACRO_ALIASES) + + aliases = _flatten_dependency_maps(all_aliases_maps).pop(package_name, None) + + if not aliases: + return dict() + + common_items = aliases.pop(_COMMON_CONDITION, {}).items() + + # If there are only common items in the dictionary, immediately return them + if not len(aliases.keys()) == 1: + return dict(common_items) + + # Build a single select statement where each conditional has accounted for the + # common set of aliases. + crate_aliases = {"//conditions:default": dict(common_items)} + for condition, deps in aliases.items(): + condition_triples = _CONDITIONS[condition] + for triple in condition_triples: + if triple in crate_aliases: + crate_aliases[triple].update(deps) + else: + crate_aliases.update({triple: dict(deps.items() + common_items)}) + + return select(crate_aliases) + +############################################################################### +# WORKSPACE MEMBER DEPS AND ALIASES +############################################################################### + +_NORMAL_DEPENDENCIES = { + "": { + _COMMON_CONDITION: { + "miniz_oxide": Label("@rra//:miniz_oxide-0.9.1"), + "object": Label("@rra//:object-0.39.1"), + }, + }, +} + +_NORMAL_ALIASES = { + "": { + _COMMON_CONDITION: { + }, + }, +} + +_NORMAL_DEV_DEPENDENCIES = { + "": { + }, +} + +_NORMAL_DEV_ALIASES = { + "": { + }, +} + +_PROC_MACRO_DEPENDENCIES = { + "": { + }, +} + +_PROC_MACRO_ALIASES = { + "": { + }, +} + +_PROC_MACRO_DEV_DEPENDENCIES = { + "": { + }, +} + +_PROC_MACRO_DEV_ALIASES = { + "": { + }, +} + +_BUILD_DEPENDENCIES = { + "": { + }, +} + +_BUILD_ALIASES = { + "": { + }, +} + +_BUILD_PROC_MACRO_DEPENDENCIES = { + "": { + }, +} + +_BUILD_PROC_MACRO_ALIASES = { + "": { + }, +} + +_CONDITIONS = { + "aarch64-apple-darwin": ["@rules_rust//rust/platform:aarch64-apple-darwin"], + "aarch64-pc-windows-msvc": ["@rules_rust//rust/platform:aarch64-pc-windows-msvc"], + "aarch64-unknown-linux-gnu": ["@rules_rust//rust/platform:aarch64-unknown-linux-gnu"], + "aarch64-unknown-nixos-gnu": ["@rules_rust//rust/platform:aarch64-unknown-nixos-gnu"], + "arm-unknown-linux-gnueabi": ["@rules_rust//rust/platform:arm-unknown-linux-gnueabi"], + "armv7-linux-androideabi": ["@rules_rust//rust/platform:armv7-linux-androideabi"], + "armv7-unknown-linux-gnueabi": ["@rules_rust//rust/platform:armv7-unknown-linux-gnueabi"], + "i686-apple-darwin": ["@rules_rust//rust/platform:i686-apple-darwin"], + "i686-pc-windows-msvc": ["@rules_rust//rust/platform:i686-pc-windows-msvc"], + "i686-unknown-freebsd": ["@rules_rust//rust/platform:i686-unknown-freebsd"], + "i686-unknown-linux-gnu": ["@rules_rust//rust/platform:i686-unknown-linux-gnu"], + "powerpc-unknown-linux-gnu": ["@rules_rust//rust/platform:powerpc-unknown-linux-gnu"], + "s390x-unknown-linux-gnu": ["@rules_rust//rust/platform:s390x-unknown-linux-gnu"], + "x86_64-apple-darwin": ["@rules_rust//rust/platform:x86_64-apple-darwin"], + "x86_64-pc-windows-msvc": ["@rules_rust//rust/platform:x86_64-pc-windows-msvc"], + "x86_64-unknown-freebsd": ["@rules_rust//rust/platform:x86_64-unknown-freebsd"], + "x86_64-unknown-linux-gnu": ["@rules_rust//rust/platform:x86_64-unknown-linux-gnu"], + "x86_64-unknown-nixos-gnu": ["@rules_rust//rust/platform:x86_64-unknown-nixos-gnu"], +} + +############################################################################### + +def crate_repositories(): + """A macro for defining repositories for all generated crates. + + Returns: + A list of repos visible to the module through the module extension. + """ + maybe( + http_archive, + name = "rra__adler2-2.0.1", + sha256 = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa", + type = "tar.gz", + urls = ["https://static.crates.io/crates/adler2/2.0.1/download"], + strip_prefix = "adler2-2.0.1", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.adler2-2.0.1.bazel"), + ) + + maybe( + http_archive, + name = "rra__cfg-if-1.0.4", + sha256 = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801", + type = "tar.gz", + urls = ["https://static.crates.io/crates/cfg-if/1.0.4/download"], + strip_prefix = "cfg-if-1.0.4", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.cfg-if-1.0.4.bazel"), + ) + + maybe( + http_archive, + name = "rra__crc32fast-1.5.0", + sha256 = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511", + type = "tar.gz", + urls = ["https://static.crates.io/crates/crc32fast/1.5.0/download"], + strip_prefix = "crc32fast-1.5.0", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.crc32fast-1.5.0.bazel"), + ) + + maybe( + http_archive, + name = "rra__equivalent-1.0.2", + sha256 = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f", + type = "tar.gz", + urls = ["https://static.crates.io/crates/equivalent/1.0.2/download"], + strip_prefix = "equivalent-1.0.2", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.equivalent-1.0.2.bazel"), + ) + + maybe( + http_archive, + name = "rra__foldhash-0.2.0", + sha256 = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb", + type = "tar.gz", + urls = ["https://static.crates.io/crates/foldhash/0.2.0/download"], + strip_prefix = "foldhash-0.2.0", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.foldhash-0.2.0.bazel"), + ) + + maybe( + http_archive, + name = "rra__hashbrown-0.17.0", + sha256 = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51", + type = "tar.gz", + urls = ["https://static.crates.io/crates/hashbrown/0.17.0/download"], + strip_prefix = "hashbrown-0.17.0", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.hashbrown-0.17.0.bazel"), + ) + + maybe( + http_archive, + name = "rra__indexmap-2.14.0", + sha256 = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9", + type = "tar.gz", + urls = ["https://static.crates.io/crates/indexmap/2.14.0/download"], + strip_prefix = "indexmap-2.14.0", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.indexmap-2.14.0.bazel"), + ) + + maybe( + http_archive, + name = "rra__memchr-2.8.0", + sha256 = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79", + type = "tar.gz", + urls = ["https://static.crates.io/crates/memchr/2.8.0/download"], + strip_prefix = "memchr-2.8.0", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.memchr-2.8.0.bazel"), + ) + + maybe( + http_archive, + name = "rra__miniz_oxide-0.9.1", + sha256 = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c", + type = "tar.gz", + urls = ["https://static.crates.io/crates/miniz_oxide/0.9.1/download"], + strip_prefix = "miniz_oxide-0.9.1", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.miniz_oxide-0.9.1.bazel"), + ) + + maybe( + http_archive, + name = "rra__object-0.39.1", + sha256 = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b", + type = "tar.gz", + urls = ["https://static.crates.io/crates/object/0.39.1/download"], + strip_prefix = "object-0.39.1", + build_file = Label("//tools/auditable/3rdparty/crates:BUILD.object-0.39.1.bazel"), + ) + + return [ + struct(repo = "rra__miniz_oxide-0.9.1", is_dev_dep = False), + struct(repo = "rra__object-0.39.1", is_dev_dep = False), + ] diff --git a/tools/auditable/BUILD.bazel b/tools/auditable/BUILD.bazel new file mode 100644 index 0000000000..aa2dc7fbc7 --- /dev/null +++ b/tools/auditable/BUILD.bazel @@ -0,0 +1,22 @@ +# Loads rules directly from rust/private:rust.bzl (not //rust:defs.bzl) +# to avoid a dependency cycle: the defs.bzl macro sets auditable_injector +# via select(), which would make the injector depend on itself. +# buildifier: disable=bzl-visibility +load("//rust/private:rust.bzl", "rust_binary", "rust_test") + +rust_binary( + name = "auditable_injector", + srcs = ["main.rs"], + edition = "2021", + visibility = ["//visibility:public"], + deps = [ + "//tools/auditable/3rdparty/crates:miniz_oxide", + "//tools/auditable/3rdparty/crates:object", + ], +) + +rust_test( + name = "auditable_injector_test", + crate = ":auditable_injector", + edition = "2021", +) diff --git a/tools/auditable/deps.bzl b/tools/auditable/deps.bzl new file mode 100644 index 0000000000..8215fb4c80 --- /dev/null +++ b/tools/auditable/deps.bzl @@ -0,0 +1,7 @@ +"""Dependencies for the auditable_injector tool.""" + +load("//tools/auditable/3rdparty/crates:crates.bzl", "crate_repositories") + +def auditable_dependencies(): + """Define dependencies of the `auditable_injector` tool""" + return crate_repositories() diff --git a/tools/auditable/main.rs b/tools/auditable/main.rs new file mode 100644 index 0000000000..fd2fbdb5ca --- /dev/null +++ b/tools/auditable/main.rs @@ -0,0 +1,475 @@ +//! A tool to generate `.dep-v0` object files for cargo-auditable compatible +//! dependency tracking in Bazel-built Rust binaries. +//! +//! This tool reads a JSON dependency manifest, zlib-compresses it, and wraps +//! it in a platform-appropriate object file with a `.dep-v0` section and an +//! `AUDITABLE_VERSION_INFO` symbol. + +use miniz_oxide::deflate::compress_to_vec_zlib; +use object::write::{self, StandardSegment, Symbol, SymbolSection}; +use object::{ + elf, Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind, SymbolFlags, + SymbolKind, SymbolScope, +}; +use std::fs; +use std::process; + +const SYMBOL_NAME: &str = "AUDITABLE_VERSION_INFO"; + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() != 4 { + eprintln!("Usage: {} ", args[0]); + process::exit(1); + } + + let target_triple = &args[1]; + let input_path = &args[2]; + let output_path = &args[3]; + + let json = fs::read(input_path).unwrap_or_else(|e| { + eprintln!("Failed to read input file '{}': {}", input_path, e); + process::exit(1); + }); + + let compressed = compress_to_vec_zlib(&json, 7); + + let obj_bytes = if is_wasm(target_triple) { + create_wasm_file(&compressed) + } else { + match create_object_file(target_triple, &compressed) { + Some(bytes) => bytes, + None => { + eprintln!("Unsupported target triple: {}", target_triple); + process::exit(1); + } + } + }; + + fs::write(output_path, obj_bytes).unwrap_or_else(|e| { + eprintln!("Failed to write output file '{}': {}", output_path, e); + process::exit(1); + }); +} + +fn is_wasm(target_triple: &str) -> bool { + target_triple.starts_with("wasm32") || target_triple.starts_with("wasm64") +} + +fn is_apple(target_triple: &str) -> bool { + target_triple.contains("apple") || target_triple.contains("darwin") +} + +fn is_windows(target_triple: &str) -> bool { + target_triple.contains("windows") +} + +fn is_32bit(target_triple: &str) -> bool { + let arch = target_triple.split('-').next().unwrap_or(""); + matches!( + arch, + "i686" | "i586" | "armv7" | "arm" | "riscv32" | "wasm32" | "x86_64_x32" + ) || (arch == "aarch64" && target_triple.contains("ilp32")) +} + +fn create_wasm_file(contents: &[u8]) -> Vec { + let mut result: Vec = vec![0, b'a', b's', b'm', 1, 0, 0, 0]; + write_wasm_custom_section(&mut result, "linking", &[2]); + write_wasm_custom_section(&mut result, ".dep-v0", contents); + result +} + +fn write_wasm_custom_section(out: &mut Vec, name: &str, payload: &[u8]) { + let section_len = leb128_len(name.len()) + name.len() + payload.len(); + out.push(0); // custom section id + write_leb128(out, section_len); + write_leb128(out, name.len()); + out.extend_from_slice(name.as_bytes()); + out.extend_from_slice(payload); +} + +fn write_leb128(out: &mut Vec, mut value: usize) { + loop { + let mut byte = (value & 0x7f) as u8; + value >>= 7; + if value != 0 { + byte |= 0x80; + } + out.push(byte); + if value == 0 { + break; + } + } +} + +fn leb128_len(mut value: usize) -> usize { + let mut len = 0; + loop { + value >>= 7; + len += 1; + if value == 0 { + break; + } + } + len +} + +/// Adapted from cargo-auditable's object_file.rs, which is itself adapted from +/// the rustc codebase. +fn create_object_file(target_triple: &str, contents: &[u8]) -> Option> { + let parts: Vec<&str> = target_triple.split('-').collect(); + let arch_str = parts.first().copied().unwrap_or(""); + + let endianness = if (arch_str.contains("mips") && !arch_str.ends_with("el")) + || arch_str == "s390x" + || arch_str == "powerpc" + || arch_str == "powerpc64" + { + Endianness::Big + } else { + Endianness::Little + }; + + let architecture = match arch_str { + "arm" | "armv7" | "armv7s" | "thumbv7neon" => Architecture::Arm, + "aarch64" => { + if is_32bit(target_triple) { + Architecture::Aarch64_Ilp32 + } else { + Architecture::Aarch64 + } + } + "i686" | "i586" => Architecture::I386, + "s390x" => Architecture::S390x, + "mips" | "mipsel" => Architecture::Mips, + "mips64" | "mips64el" => Architecture::Mips64, + "x86_64" => { + if is_32bit(target_triple) { + Architecture::X86_64_X32 + } else { + Architecture::X86_64 + } + } + "powerpc" => Architecture::PowerPc, + "powerpc64" | "powerpc64le" => Architecture::PowerPc64, + "riscv32" => Architecture::Riscv32, + "riscv64" | "riscv64gc" => Architecture::Riscv64, + "sparc64" => Architecture::Sparc64, + "loongarch64" => Architecture::LoongArch64, + _ => return None, + }; + + let binary_format = if is_apple(target_triple) { + BinaryFormat::MachO + } else if is_windows(target_triple) { + BinaryFormat::Coff + } else { + BinaryFormat::Elf + }; + + let mut file = write::Object::new(binary_format, architecture, endianness); + + let mut e_flags: u32 = 0; + match architecture { + Architecture::Mips => { + let arch = if target_triple.contains("r6") { + elf::EF_MIPS_ARCH_32R6 + } else { + elf::EF_MIPS_ARCH_32R2 + }; + e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch; + if target_triple.contains("r6") { + e_flags |= elf::EF_MIPS_NAN2008; + } + } + Architecture::Mips64 => { + e_flags = elf::EF_MIPS_CPIC + | elf::EF_MIPS_PIC + | if target_triple.contains("r6") { + elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008 + } else { + elf::EF_MIPS_ARCH_64R2 + }; + } + Architecture::Riscv32 | Architecture::Riscv64 => { + let features = riscv_features_from_triple(target_triple); + if features.contains('c') { + e_flags |= elf::EF_RISCV_RVC; + } + if features.contains('d') { + e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE; + } else if features.contains('f') { + e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE; + } else { + e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT; + } + } + Architecture::LoongArch64 => { + e_flags = elf::EF_LARCH_OBJABI_V1; + if target_triple.contains("softfloat") { + e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT; + } else { + e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT; + } + } + _ => {} + } + + let os_abi = if target_triple.contains("freebsd") { + elf::ELFOSABI_FREEBSD + } else if target_triple.contains("solaris") { + elf::ELFOSABI_SOLARIS + } else if target_triple.contains("hermit") { + elf::ELFOSABI_STANDALONE + } else { + elf::ELFOSABI_NONE + }; + + file.flags = FileFlags::Elf { + os_abi, + abi_version: 0, + e_flags, + }; + + if binary_format == BinaryFormat::Coff { + let original_mangling = file.mangling(); + file.set_mangling(write::Mangling::None); + let mut feature: u64 = 0; + if architecture == Architecture::I386 { + feature |= 1; // IMAGE_FILE_SAFE_EXCEPTION_HANDLER + } + file.add_symbol(Symbol { + name: b"@feat.00".to_vec(), + value: feature, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Absolute, + flags: SymbolFlags::None, + }); + file.set_mangling(original_mangling); + } + + let section = file.add_section( + file.segment_name(StandardSegment::Data).to_vec(), + b".dep-v0".to_vec(), + SectionKind::ReadOnlyData, + ); + if let BinaryFormat::Elf = file.format() { + file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 }; + } + let offset = file.append_section_data(section, contents, 1); + + file.add_symbol(Symbol { + name: SYMBOL_NAME.as_bytes().to_vec(), + value: offset, + size: contents.len() as u64, + kind: SymbolKind::Data, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(section), + flags: SymbolFlags::None, + }); + + Some(file.write().unwrap()) +} + +fn riscv_features_from_triple(target_triple: &str) -> String { + let arch = target_triple.split('-').next().unwrap_or(""); + let prefix_len = if arch.starts_with("riscv32") || arch.starts_with("riscv64") { + 7 + } else { + return String::new(); + }; + let mut extensions = arch[prefix_len..].to_owned(); + if extensions.contains('g') { + extensions.push_str("imadf"); + } + if target_triple.contains("linux") || target_triple.contains("android") { + extensions.push_str("imadfc"); + } + extensions +} + +#[cfg(test)] +mod tests { + use super::*; + use miniz_oxide::inflate::decompress_to_vec_zlib; + use object::read::Object; + use object::{ObjectSection, ObjectSymbol}; + + /// Round-trip: compress JSON, generate ELF object, then read it back and + /// verify the `.dep-v0` section contains the original JSON. + fn roundtrip_elf(target_triple: &str) { + let json = br#"{"packages":[{"name":"foo","version":"1.0.0"}],"format":0}"#; + let compressed = compress_to_vec_zlib(json, 7); + let obj_bytes = create_object_file(target_triple, &compressed) + .unwrap_or_else(|| panic!("unsupported triple: {}", target_triple)); + + let file = object::File::parse(&*obj_bytes) + .unwrap_or_else(|e| panic!("failed to parse object for {}: {}", target_triple, e)); + + let section = file + .section_by_name(".dep-v0") + .unwrap_or_else(|| panic!(".dep-v0 section not found for {}", target_triple)); + let data = section.data().expect("could not read section data"); + assert!(!data.is_empty(), "section is empty for {}", target_triple); + + let decompressed = decompress_to_vec_zlib(data) + .unwrap_or_else(|e| panic!("decompression failed for {}: {:?}", target_triple, e)); + assert_eq!( + decompressed, json, + "round-trip mismatch for {}", + target_triple + ); + + // Verify the symbol exists + let sym = file + .symbols() + .find(|s| s.name() == Ok(SYMBOL_NAME)) + .unwrap_or_else(|| panic!("{} symbol not found for {}", SYMBOL_NAME, target_triple)); + assert_eq!(sym.size() as usize, compressed.len()); + } + + #[test] + fn test_elf_x86_64_linux() { + roundtrip_elf("x86_64-unknown-linux-gnu"); + } + + #[test] + fn test_elf_aarch64_linux() { + roundtrip_elf("aarch64-unknown-linux-gnu"); + } + + #[test] + fn test_elf_i686_linux() { + roundtrip_elf("i686-unknown-linux-gnu"); + } + + #[test] + fn test_elf_arm_linux() { + roundtrip_elf("armv7-unknown-linux-gnueabi"); + } + + #[test] + fn test_elf_riscv64_linux() { + roundtrip_elf("riscv64gc-unknown-linux-gnu"); + } + + #[test] + fn test_elf_s390x_linux() { + roundtrip_elf("s390x-unknown-linux-gnu"); + } + + /// Round-trip for formats where the symbol table may differ (Mach-O, COFF). + /// Verifies the section and its decompressed contents, but not symbol details. + fn roundtrip_section_only(target_triple: &str) { + let json = br#"{"packages":[{"name":"foo","version":"1.0.0"}],"format":0}"#; + let compressed = compress_to_vec_zlib(json, 7); + let obj_bytes = create_object_file(target_triple, &compressed) + .unwrap_or_else(|| panic!("unsupported triple: {}", target_triple)); + + let file = object::File::parse(&*obj_bytes) + .unwrap_or_else(|e| panic!("failed to parse object for {}: {}", target_triple, e)); + + let section = file + .section_by_name(".dep-v0") + .unwrap_or_else(|| panic!(".dep-v0 section not found for {}", target_triple)); + let data = section.data().expect("could not read section data"); + assert!(!data.is_empty(), "section is empty for {}", target_triple); + + let decompressed = decompress_to_vec_zlib(data) + .unwrap_or_else(|e| panic!("decompression failed for {}: {:?}", target_triple, e)); + assert_eq!( + decompressed, json, + "round-trip mismatch for {}", + target_triple + ); + } + + #[test] + fn test_macho_aarch64() { + roundtrip_section_only("aarch64-apple-darwin"); + } + + #[test] + fn test_coff_x86_64_windows() { + roundtrip_section_only("x86_64-pc-windows-msvc"); + } + + #[test] + fn test_unsupported_triple_returns_none() { + let compressed = compress_to_vec_zlib(b"test", 7); + assert!(create_object_file("unknown-unknown-unknown", &compressed).is_none()); + } + + #[test] + fn test_wasm_roundtrip() { + let json = br#"{"packages":[],"format":0}"#; + let compressed = compress_to_vec_zlib(json, 7); + let wasm = create_wasm_file(&compressed); + + // WASM magic: \0asm\1\0\0\0 + assert_eq!(&wasm[..8], &[0, b'a', b's', b'm', 1, 0, 0, 0]); + // Find .dep-v0 custom section by scanning for the name + let pos = wasm + .windows(7) + .position(|w| w == b".dep-v0") + .expect(".dep-v0 not found in WASM output"); + let payload_start = pos + 7; + let payload = &wasm[payload_start..payload_start + compressed.len()]; + let decompressed = decompress_to_vec_zlib(payload).expect("decompression failed for WASM"); + assert_eq!(decompressed, json); + } + + #[test] + fn test_is_wasm() { + assert!(is_wasm("wasm32-unknown-unknown")); + assert!(is_wasm("wasm64-unknown-unknown")); + assert!(!is_wasm("x86_64-unknown-linux-gnu")); + } + + #[test] + fn test_is_apple() { + assert!(is_apple("aarch64-apple-darwin")); + assert!(is_apple("x86_64-apple-darwin")); + assert!(!is_apple("x86_64-unknown-linux-gnu")); + } + + #[test] + fn test_is_windows() { + assert!(is_windows("x86_64-pc-windows-msvc")); + assert!(!is_windows("x86_64-unknown-linux-gnu")); + } + + #[test] + fn test_is_32bit() { + assert!(is_32bit("i686-unknown-linux-gnu")); + assert!(is_32bit("armv7-unknown-linux-gnueabi")); + assert!(is_32bit("riscv32-unknown-none-elf")); + assert!(!is_32bit("x86_64-unknown-linux-gnu")); + assert!(!is_32bit("aarch64-unknown-linux-gnu")); + } + + #[test] + fn test_riscv_features() { + assert!(riscv_features_from_triple("riscv64gc-unknown-linux-gnu").contains('c')); + assert!(riscv_features_from_triple("riscv64gc-unknown-linux-gnu").contains('d')); + assert_eq!(riscv_features_from_triple("x86_64-unknown-linux-gnu"), ""); + } + + #[test] + fn test_leb128_roundtrip() { + for &val in &[0, 1, 127, 128, 255, 16384, 65535] { + let mut buf = Vec::new(); + write_leb128(&mut buf, val); + assert_eq!( + leb128_len(val), + buf.len(), + "leb128_len mismatch for {}", + val + ); + } + } +}