diff --git a/.bazelrc b/.bazelrc index 496f91b705..6140072ff4 100644 --- a/.bazelrc +++ b/.bazelrc @@ -46,6 +46,10 @@ build:unpretty --output_groups=+rust_unpretty # https://github.com/rust-lang/rust/issues/43364 build:unpretty --config=nightly +# Disable cc toolchains to test rust targets can be built without one. +build:no_cc_toolchain --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 +build:no_cc_toolchain --repo_env=BAZEL_NO_APPLE_CPP_TOOLCHAIN=1 + ############################################################################### ## Incompatibility flags ############################################################################### diff --git a/cargo/private/BUILD.bazel b/cargo/private/BUILD.bazel index 15d6286820..d11e456cbc 100644 --- a/cargo/private/BUILD.bazel +++ b/cargo/private/BUILD.bazel @@ -8,6 +8,36 @@ rust_binary( visibility = ["//visibility:public"], ) +rust_binary( + name = "no_ar", + srcs = ["no_binary.rs"], + edition = "2021", + rustc_env = { + "BINARY_ENV": "AR", + }, + visibility = ["//visibility:public"], +) + +rust_binary( + name = "no_cc", + srcs = ["no_binary.rs"], + edition = "2021", + rustc_env = { + "BINARY_ENV": "CC", + }, + visibility = ["//visibility:public"], +) + +rust_binary( + name = "no_cxx", + srcs = ["no_binary.rs"], + edition = "2021", + rustc_env = { + "BINARY_ENV": "CXX", + }, + visibility = ["//visibility:public"], +) + bzl_library( name = "bzl_lib", srcs = glob(["**/*.bzl"]), diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl index 6226cc2dc9..9262881a3c 100644 --- a/cargo/private/cargo_build_script.bzl +++ b/cargo/private/cargo_build_script.bzl @@ -3,7 +3,6 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") -load("@rules_cc//cc:find_cc_toolchain.bzl", find_cpp_toolchain = "find_cc_toolchain") load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load("//rust:defs.bzl", "rust_common") load("//rust:rust_common.bzl", "BuildInfo", "CrateGroupInfo", "DepInfo") @@ -367,8 +366,6 @@ def _cargo_build_script_impl(ctx): toolchain_tools = [toolchain.all_files] - cc_toolchain = find_cpp_toolchain(ctx) - env = {} if ctx.attr.use_default_shell_env == -1: @@ -417,18 +414,25 @@ def _cargo_build_script_impl(ctx): # Pull in env vars which may be required for the cc_toolchain to work (e.g. on OSX, the SDK version). # We hope that the linker env is sufficient for the whole cc_toolchain. cc_toolchain, feature_configuration = find_cc_toolchain(ctx) - linker, link_args, linker_env = get_linker_and_args(ctx, "bin", cc_toolchain, feature_configuration, None) + linker, _, link_args, linker_env = get_linker_and_args(ctx, "bin", toolchain, cc_toolchain, feature_configuration, None) env.update(**linker_env) env["LD"] = linker env["LDFLAGS"] = " ".join(_pwd_flags(link_args)) - # MSVC requires INCLUDE to be set - cc_c_args, cc_cxx_args, cc_env = get_cc_compile_args_and_env(cc_toolchain, feature_configuration) - include = cc_env.get("INCLUDE") - if include: - env["INCLUDE"] = include + # Defaults for cxx flags. + env["CC"] = "${{pwd}}/{}".format(ctx.executable._fallback_cc.path) + env["CXX"] = "${{pwd}}/{}".format(ctx.executable._fallback_cxx.path) + env["AR"] = "${{pwd}}/{}".format(ctx.executable._fallback_ar.path) + env["CFLAGS"] = "" + env["CXXFLAGS"] = "" if cc_toolchain: + # MSVC requires INCLUDE to be set + cc_c_args, cc_cxx_args, cc_env = get_cc_compile_args_and_env(cc_toolchain, feature_configuration) + include = cc_env.get("INCLUDE") + if include: + env["INCLUDE"] = include + toolchain_tools.append(cc_toolchain.all_files) env["CC"] = cc_common.get_tool_for_action( @@ -503,6 +507,9 @@ def _cargo_build_script_impl(ctx): direct = [ script, ctx.executable._cargo_build_script_runner, + ctx.executable._fallback_cc, + ctx.executable._fallback_cxx, + ctx.executable._fallback_ar, ] + ([toolchain.target_json] if toolchain.target_json else []), transitive = script_data + script_tools + toolchain_tools, ) @@ -725,6 +732,21 @@ cargo_build_script = rule( "_experimental_symlink_execroot": attr.label( default = Label("//cargo/settings:experimental_symlink_execroot"), ), + "_fallback_ar": attr.label( + cfg = "exec", + executable = True, + default = Label("//cargo/private:no_ar"), + ), + "_fallback_cc": attr.label( + cfg = "exec", + executable = True, + default = Label("//cargo/private:no_cc"), + ), + "_fallback_cxx": attr.label( + cfg = "exec", + executable = True, + default = Label("//cargo/private:no_cxx"), + ), "_incompatible_runfiles_cargo_manifest_dir": attr.label( default = Label("//cargo/settings:incompatible_runfiles_cargo_manifest_dir"), ), @@ -732,7 +754,7 @@ cargo_build_script = rule( fragments = ["cpp"], toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], ) diff --git a/cargo/private/no_binary.rs b/cargo/private/no_binary.rs new file mode 100644 index 0000000000..c3487b103a --- /dev/null +++ b/cargo/private/no_binary.rs @@ -0,0 +1,6 @@ +//! A cross platform implementation of `/bin/false` + +fn main() { + eprintln!(concat!("No binary provided for ", env!("BINARY_ENV"))); + std::process::exit(1); +} diff --git a/extensions/bindgen/private/bindgen.bzl b/extensions/bindgen/private/bindgen.bzl index deaa7e4e7a..426cdd48d1 100644 --- a/extensions/bindgen/private/bindgen.bzl +++ b/extensions/bindgen/private/bindgen.bzl @@ -363,7 +363,7 @@ def _rust_bindgen_impl(ctx): for define in ctx.attr.cc_lib[CcInfo].compilation_context.defines.to_list(): args.add("-D" + define) - _, _, linker_env = get_linker_and_args(ctx, "bin", cc_toolchain, feature_configuration, None) + _, _, _, linker_env = get_linker_and_args(ctx, "bin", rust_toolchain, cc_toolchain, feature_configuration, None) env.update(**linker_env) # Set the dynamic linker search path so that clang uses the libstdcxx from the toolchain. diff --git a/extensions/prost/private/prost.bzl b/extensions/prost/private/prost.bzl index 9cdb3e5a8d..4eecf85a06 100644 --- a/extensions/prost/private/prost.bzl +++ b/extensions/prost/private/prost.bzl @@ -392,7 +392,7 @@ rust_prost_aspect = aspect( fragments = ["cpp"], toolchains = [ TOOLCHAIN_TYPE, - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), "@rules_rust//rust:toolchain_type", "@rules_rust//rust/rustfmt:toolchain_type", ], diff --git a/extensions/protobuf/proto.bzl b/extensions/protobuf/proto.bzl index 40fa81d9e6..8c67a39ecb 100644 --- a/extensions/protobuf/proto.bzl +++ b/extensions/protobuf/proto.bzl @@ -343,7 +343,7 @@ rust_proto_library = rule( toolchains = [ str(Label("//:toolchain_type")), str(Label("@rules_rust//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = """\ Builds a Rust library crate from a set of `proto_library`s. @@ -435,7 +435,7 @@ rust_grpc_library = rule( toolchains = [ str(Label("//:toolchain_type")), str(Label("@rules_rust//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = """\ Builds a Rust library crate from a set of `proto_library`s suitable for gRPC. diff --git a/extensions/wasm_bindgen/private/wasm_bindgen_test.bzl b/extensions/wasm_bindgen/private/wasm_bindgen_test.bzl index 2502a19f54..8a2dcb5738 100644 --- a/extensions/wasm_bindgen/private/wasm_bindgen_test.bzl +++ b/extensions/wasm_bindgen/private/wasm_bindgen_test.bzl @@ -8,9 +8,6 @@ load("@rules_rust//rust/private:rust.bzl", "RUSTC_ATTRS", "get_rust_test_flags") # buildifier: disable=bzl-visibility load("@rules_rust//rust/private:rustc.bzl", "rustc_compile_action") -# buildifier: disable=bzl-visibility -# load("@rules_rust//rust/private:toolchain_utils.bzl", "get_coverage_env") - # buildifier: disable=bzl-visibility load( "@rules_rust//rust/private:utils.bzl", @@ -334,7 +331,7 @@ rust_wasm_bindgen_test = rule( toolchains = [ str(Label("//:toolchain_type")), "@rules_rust//rust:toolchain_type", - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], test = True, ) diff --git a/ffi/rs/BUILD.bazel b/ffi/rs/BUILD.bazel index 1fb2abebec..4a659ea86a 100644 --- a/ffi/rs/BUILD.bazel +++ b/ffi/rs/BUILD.bazel @@ -1,7 +1,7 @@ load("@rules_cc//cc:cc_library.bzl", "cc_library") # buildifier: disable=bzl-visibility -load("@rules_rust//rust/private:rust.bzl", "rust_allocator_libraries") +load("@rules_rust//rust/private:rust_allocator_libraries.bzl", "rust_allocator_libraries") rust_allocator_libraries( name = "allocator_libraries_with_mangling_support", diff --git a/rust/platform/BUILD.bazel b/rust/platform/BUILD.bazel index d5513b1e79..439be46d2d 100644 --- a/rust/platform/BUILD.bazel +++ b/rust/platform/BUILD.bazel @@ -21,6 +21,22 @@ constraint_value( constraint_setting = ":wasi_version", ) +# ABI constraint settings +constraint_setting( + name = "abi", + default_constraint_value = ":gnu", +) + +constraint_value( + name = "gnu", + constraint_setting = ":abi", +) + +constraint_value( + name = "musl", + constraint_setting = ":abi", +) + package_group( name = "function_transition_allowlist", packages = [ diff --git a/rust/platform/triple_mappings.bzl b/rust/platform/triple_mappings.bzl index 6070591e6b..796d73e952 100644 --- a/rust/platform/triple_mappings.bzl +++ b/rust/platform/triple_mappings.bzl @@ -69,6 +69,7 @@ SUPPORTED_T2_PLATFORM_TRIPLES = { "x86_64-linux-android": _support(std = True, host_tools = False), "x86_64-unknown-freebsd": _support(std = True, host_tools = True), "x86_64-unknown-fuchsia": _support(std = True, host_tools = False), + "x86_64-unknown-linux-musl": _support(std = True, host_tools = True), "x86_64-unknown-none": _support(std = True, host_tools = False), "x86_64-unknown-uefi": _support(std = True, host_tools = False), } diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl index d073c8a5f8..318c05af8d 100644 --- a/rust/private/clippy.bzl +++ b/rust/private/clippy.bzl @@ -360,7 +360,7 @@ rust_clippy_aspect = aspect( ], toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], implementation = _clippy_aspect_impl, doc = """\ diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl index ee31d60339..b90caaefca 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -19,7 +19,13 @@ load("//rust/private:common.bzl", "DEFAULT_NIGHTLY_ISO_DATE") DEFAULT_TOOLCHAIN_NAME_PREFIX = "toolchain_for" DEFAULT_STATIC_RUST_URL_TEMPLATES = ["https://static.rust-lang.org/dist/{}.tar.xz"] DEFAULT_NIGHTLY_VERSION = "nightly/{}".format(DEFAULT_NIGHTLY_ISO_DATE) -DEFAULT_EXTRA_TARGET_TRIPLES = ["wasm32-unknown-unknown", "wasm32-wasip1", "wasm32-wasip2"] +DEFAULT_EXTRA_TARGET_TRIPLES = [ + "wasm32-unknown-unknown", + "wasm32-wasip1", + "wasm32-wasip2", + "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-musl", +] TINYJSON_KWARGS = dict( name = "rules_rust_tinyjson", @@ -44,8 +50,6 @@ filegroup( "bin/*{dylib_ext}", "lib/*{dylib_ext}*", "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}", - "lib/rustlib/{target_triple}/bin/gcc-ld/*", - "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}", "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", "lib/rustlib/{target_triple}/lib/*.rmeta", ], @@ -61,21 +65,52 @@ filegroup( ) """ -def BUILD_for_compiler(target_triple): +_build_file_for_linker_template = """\ +filegroup( + name = "rust-lld", + srcs = ["lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}"], + data = glob( + include = [ + "lib/rustlib/{target_triple}/bin/*-ld{binary_ext}", + "lib/rustlib/{target_triple}/bin/gcc-ld/*", + ], + exclude = [ + "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}", + ], + allow_empty = True, + ), + visibility = ["//visibility:public"], +) +""" + +def BUILD_for_compiler(target_triple, include_linker = False): """Emits a BUILD file the compiler archive. Args: target_triple (str): The triple of the target platform + include_linker (bool): Whether to generate targets for linkers. Returns: str: The contents of a BUILD file """ - return _build_file_for_compiler_template.format( + content = [_build_file_for_compiler_template.format( binary_ext = system_to_binary_ext(target_triple.system), staticlib_ext = system_to_staticlib_ext(target_triple.system), dylib_ext = system_to_dylib_ext(target_triple.system), target_triple = target_triple.str, - ) + )] + + if include_linker: + content.append( + _build_file_for_linker_template.format( + binary_ext = system_to_binary_ext(target_triple.system), + staticlib_ext = system_to_staticlib_ext(target_triple.system), + dylib_ext = system_to_dylib_ext(target_triple.system), + target_triple = target_triple.str, + ), + ) + + return "\n".join(content) _build_file_for_cargo_template = """\ filegroup( @@ -280,6 +315,8 @@ rust_toolchain( rust_doc = "//:rustdoc", rust_std = "//:rust_std-{target_triple}", rustc = "//:rustc", + linker = {linker_label}, + linker_type = {linker_type}, rustfmt = {rustfmt_label}, cargo = "//:cargo", clippy_driver = "//:clippy_driver_bin", @@ -316,6 +353,7 @@ def BUILD_for_rust_toolchain( default_edition, include_rustfmt, include_llvm_tools, + include_linker, stdlib_linkflags = None, extra_rustc_flags = None, extra_exec_rustc_flags = None, @@ -335,6 +373,7 @@ def BUILD_for_rust_toolchain( default_edition (str): Default Rust edition. include_rustfmt (bool): Whether rustfmt is present in the toolchain. include_llvm_tools (bool): Whether llvm-tools are present in the toolchain. + include_linker (bool): Whether a linker is available in the toolchain. stdlib_linkflags (list, optional): Overridden flags needed for linking to rust stdlib, akin to BAZEL_LINKLIBS. Defaults to None. @@ -366,6 +405,12 @@ def BUILD_for_rust_toolchain( if global_allocator_library: global_allocator_library_label = "\"{global_allocator_library}\"".format(global_allocator_library = global_allocator_library) + linker_label = "None" + linker_type = "None" + if include_linker: + linker_label = "\"//:rust-lld\"" + linker_type = "\"direct\"" + return _build_file_for_rust_toolchain_template.format( toolchain_name = name, binary_ext = system_to_binary_ext(target_triple.system), @@ -381,6 +426,8 @@ def BUILD_for_rust_toolchain( llvm_cov_label = llvm_cov_label, llvm_profdata_label = llvm_profdata_label, llvm_lib_label = llvm_lib_label, + linker_label = linker_label, + linker_type = linker_type, extra_rustc_flags = extra_rustc_flags, extra_exec_rustc_flags = extra_exec_rustc_flags, opt_level = opt_level, @@ -404,9 +451,36 @@ def BUILD_for_toolchain( toolchain, toolchain_type, target_settings, + target_settings_select, target_compatible_with, exec_compatible_with): - target_settings_value = "target_settings = {},".format(json.encode(target_settings)) if target_settings else "# target_settings = []" + """Generates BUILD file content for a Bazel toolchain target. + + Args: + name (str): The name of the toolchain target. + toolchain (str): Label of the toolchain implementation (e.g., rust_toolchain target). + toolchain_type (str): The toolchain type label (e.g., "@rules_rust//rust:toolchain"). + target_settings (list): A list of config_setting labels that must be satisfied for + this toolchain to be selected. + target_settings_select (dict): A dictionary mapping config_setting labels to lists of + additional target_settings, used to generate a select() statement. If empty, no + select() is generated. + target_compatible_with (list): A list of constraint values for the target platform. + exec_compatible_with (list): A list of constraint values for the execution platform. + + Returns: + str: The generated BUILD file content as a string. + """ + target_settings_value = "# target_settings = []" + + if target_settings or target_settings_select: + target_settings_value = "target_settings = {}".format( + json.encode(target_settings), + ) + if target_settings_select: + target_settings_value += " + select({})".format( + json.encode(target_settings_select), + ) return _build_file_for_toolchain_template.format( name = name, @@ -465,7 +539,7 @@ def load_rustfmt(ctx, target_triple, version, iso_date): return BUILD_for_rustfmt(target_triple), sha256 -def load_rust_compiler(ctx, iso_date, target_triple, version): +def load_rust_compiler(ctx, iso_date, target_triple, version, include_linker = False): """Loads a rust compiler and yields corresponding BUILD for it Args: @@ -473,6 +547,7 @@ def load_rust_compiler(ctx, iso_date, target_triple, version): iso_date (str): The date of the tool (or None, if the version is a specific version). target_triple (struct): The Rust-style target that this compiler runs on. version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. + include_linker (bool): Whether to include linker targets in the output BUILD contents. Returns: Tuple[str, Dict[str, str]]: The BUILD file contents for this compiler and compiler library @@ -488,7 +563,7 @@ def load_rust_compiler(ctx, iso_date, target_triple, version): version = version, ) - return BUILD_for_compiler(target_triple), sha256 + return BUILD_for_compiler(target_triple, include_linker), sha256 def load_clippy(ctx, iso_date, target_triple, version): """Loads Clippy and yields corresponding BUILD for it @@ -983,44 +1058,58 @@ def select_rust_version(versions): return current -_build_file_for_toolchain_hub_template = """ -toolchain( - name = "{name}", - exec_compatible_with = {exec_constraint_sets_serialized}, - target_compatible_with = {target_constraint_sets_serialized}, - target_settings = {target_settings_serialized}, - toolchain = "{toolchain}", - toolchain_type = "{toolchain_type}", - visibility = ["//visibility:public"], -) -""" - def BUILD_for_toolchain_hub( toolchain_names, toolchain_labels, toolchain_types, target_settings, + target_settings_select, target_compatible_with, exec_compatible_with): - return "\n".join([_build_file_for_toolchain_hub_template.format( - name = toolchain_name, - exec_constraint_sets_serialized = json.encode(exec_compatible_with[toolchain_name]), - target_constraint_sets_serialized = json.encode(target_compatible_with[toolchain_name]), - target_settings_serialized = json.encode(target_settings[toolchain_name]) if toolchain_name in target_settings else "None", - toolchain = toolchain_labels[toolchain_name], - toolchain_type = toolchain_types[toolchain_name], - ) for toolchain_name in toolchain_names]) + """Generates BUILD file content for a toolchain hub repository. + + Uses BUILD_for_toolchain to generate each toolchain declaration with proper + target_settings and target_settings_select handling. + + Args: + toolchain_names (list): List of toolchain names. + toolchain_labels (dict): Map of toolchain name to toolchain label. + toolchain_types (dict): Map of toolchain name to toolchain type. + target_settings (dict): Map of toolchain name to target_settings list. + target_settings_select (dict): Map of toolchain name to target_settings_select dict. + target_compatible_with (dict): Map of toolchain name to target constraints. + exec_compatible_with (dict): Map of toolchain name to exec constraints. + + Returns: + str: The generated BUILD file content. + """ + return "\n".join([ + BUILD_for_toolchain( + name = toolchain_name, + toolchain = toolchain_labels[toolchain_name], + toolchain_type = toolchain_types[toolchain_name], + target_settings = target_settings.get(toolchain_name, []), + target_settings_select = target_settings_select.get(toolchain_name, {}), + target_compatible_with = target_compatible_with[toolchain_name], + exec_compatible_with = exec_compatible_with[toolchain_name], + ) + for toolchain_name in toolchain_names + ]) def _toolchain_repository_hub_impl(repository_ctx): repository_ctx.file("WORKSPACE.bazel", """workspace(name = "{}")""".format( repository_ctx.name, )) + # Decode the JSON-encoded target_settings_select + target_settings_select = json.decode(repository_ctx.attr.target_settings_select) + repository_ctx.file("BUILD.bazel", BUILD_for_toolchain_hub( toolchain_names = repository_ctx.attr.toolchain_names, toolchain_labels = repository_ctx.attr.toolchain_labels, toolchain_types = repository_ctx.attr.toolchain_types, target_settings = repository_ctx.attr.target_settings, + target_settings_select = target_settings_select, target_compatible_with = repository_ctx.attr.target_compatible_with, exec_compatible_with = repository_ctx.attr.exec_compatible_with, )) @@ -1040,9 +1129,13 @@ toolchain_repository_hub = repository_rule( mandatory = True, ), "target_settings": attr.string_list_dict( - doc = "A list of config_settings that must be satisfied by the target configuration in order for this toolchain to be selected during toolchain resolution.", + doc = "A list of config_settings that must be satisfied by the target configuration in order for this toolchain to be selected during toolchain resolution, keyed by toolchain name.", mandatory = True, ), + "target_settings_select": attr.string( + doc = "JSON-encoded dictionary mapping toolchain name to a dict of config_setting labels to lists of additional target_settings, used to generate select() statements. For internal use only.", + default = "{}", + ), "toolchain_labels": attr.string_dict( doc = "The name of the toolchain implementation target, keyed by toolchain name.", mandatory = True, diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index 2f695cbbb9..adfd8ce93f 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -17,19 +17,26 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") -load("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common") +load(":common.bzl", "COMMON_PROVIDERS", "rust_common") load( - "//rust/private:providers.bzl", - "AllocatorLibrariesImplInfo", - "AllocatorLibrariesInfo", + ":providers.bzl", "BuildInfo", "CrateGroupInfo", "CrateInfo", "LintsInfo", ) -load("//rust/private:rustc.bzl", "collect_extra_rustc_flags", "is_no_std", "rustc_compile_action") load( - "//rust/private:utils.bzl", + ":rust_allocator_libraries.bzl", + "RUSTC_ALLOCATOR_LIBRARIES_ATTRS", +) +load( + ":rustc.bzl", + "collect_extra_rustc_flags", + "is_no_std", + "rustc_compile_action", +) +load( + ":utils.bzl", "can_build_metadata", "can_use_metadata_for_pipelining", "compute_crate_name", @@ -650,19 +657,6 @@ RUSTC_ATTRS = { ), } -# Attributes for rust-based allocator library support. -# Can't add it directly to RUSTC_ATTRS above, as those are used as -# aspect parameters and only support simple types ('bool', 'int' or 'string'). -_rustc_allocator_libraries_attrs = { - # This is really internal. Not prefixed with `_` since we need to adapt this - # in bootstrapping situations, e.g., when building the process wrapper - # or allocator libraries themselves. - "allocator_libraries": attr.label( - default = "//ffi/rs:default_allocator_libraries", - providers = [AllocatorLibrariesInfo], - ), -} - _common_attrs = { "aliases": attr.label_keyed_string_dict( doc = dedent("""\ @@ -826,7 +820,7 @@ _common_attrs = { doc = "A setting used to determine whether or not the `--stamp` flag is enabled", default = Label("//rust/private:stamp"), ), -} | RUSTC_ATTRS | _rustc_allocator_libraries_attrs +} | RUSTC_ATTRS | RUSTC_ALLOCATOR_LIBRARIES_ATTRS _coverage_attrs = { "_collect_cc_coverage": attr.label( @@ -933,7 +927,7 @@ rust_library = rule( fragments = ["cpp"], toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = dedent("""\ Builds a Rust library crate. @@ -1031,7 +1025,7 @@ rust_static_library = rule( cfg = _rust_static_library_transition, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], provides = [ CcInfo, @@ -1081,7 +1075,7 @@ rust_shared_library = rule( cfg = _rust_shared_library_transition, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], provides = [ CcInfo, @@ -1137,7 +1131,7 @@ rust_proc_macro = rule( fragments = ["cpp"], toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = dedent("""\ Builds a Rust proc-macro crate. @@ -1222,7 +1216,7 @@ rust_binary = rule( cfg = _rust_binary_transition, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = dedent("""\ Builds a Rust binary crate. @@ -1344,38 +1338,103 @@ def _common_attrs_for_binary_without_process_wrapper(attrs): return new_attr +def _rust_binary_without_process_wrapper_transition_impl(_settings, _attr): + # For process wrapper bootstrapping, we force musl on Linux for better portability + # and set the linker preference to use Rust's linker + return { + str(Label("//rust/settings:toolchain_linker_preference")): "rust", + str(Label("//rust/settings:platform_linux_musl")): True, + } + +_rust_binary_without_process_wrapper_transition = transition( + implementation = _rust_binary_without_process_wrapper_transition_impl, + inputs = [], + outputs = [ + str(Label("//rust/settings:toolchain_linker_preference")), + str(Label("//rust/settings:platform_linux_musl")), + ], +) + +_RustBuiltWithoutProcessWrapperInfo = provider( + doc = "A provider identifying the target having been built using a `*_without_process_wrapper` rule variant.", + fields = {}, +) + +def _rust_binary_without_process_wrapper_impl(ctx): + providers = _rust_binary_impl(ctx) + return providers + [_RustBuiltWithoutProcessWrapperInfo()] + # Provides an internal rust_{binary,library} to use that we can use to build the process # wrapper, this breaks the dependency of rust_* on the process wrapper by # setting it to None, which the functions in rustc detect and build accordingly. rust_binary_without_process_wrapper = rule( - implementation = _rust_binary_impl, - provides = COMMON_PROVIDERS, - attrs = _common_attrs_for_binary_without_process_wrapper(_common_attrs | _rust_binary_attrs | { - "platform": attr.label( - doc = "Optional platform to transition the binary to.", - default = None, - ), - "_allowlist_function_transition": attr.label( - default = "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - }), + implementation = _rust_binary_without_process_wrapper_impl, + doc = "A variant of `rust_binary` that uses a minimal process wrapper for `Rustc` actions.", + provides = COMMON_PROVIDERS + [_RustBuiltWithoutProcessWrapperInfo], + attrs = _common_attrs_for_binary_without_process_wrapper(_common_attrs | _rust_binary_attrs), + cfg = _rust_binary_without_process_wrapper_transition, executable = True, fragments = ["cpp"], - cfg = _rust_binary_transition, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], ) +def _rust_library_without_process_wrapper_impl(ctx): + providers = _rust_library_impl(ctx) + return providers + [_RustBuiltWithoutProcessWrapperInfo()] + rust_library_without_process_wrapper = rule( - implementation = _rust_library_impl, - provides = COMMON_PROVIDERS, + implementation = _rust_library_without_process_wrapper_impl, + doc = "A variant of `rust_library` that uses a minimal process wrapper for `Rustc` actions.", + provides = COMMON_PROVIDERS + [_RustBuiltWithoutProcessWrapperInfo], attrs = dict(_common_attrs_for_binary_without_process_wrapper(_common_attrs).items()), fragments = ["cpp"], toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), + ], +) + +def _test_attrs_for_binary_without_process_wrapper(attrs): + new_attrs = {} + new_attrs.update(attrs) + + # Require that `crate` has the correct internal provider. + new_attrs["crate"] = attr.label( + mandatory = False, + providers = [_RustBuiltWithoutProcessWrapperInfo], + doc = dedent("""\ + Target inline tests declared in the given crate + + These tests are typically those that would be held out under + `#[cfg(test)]` declarations. + """), + ) + + return new_attrs + +def _rust_test_without_process_wrapper_test_impl(ctx): + if ctx.attr.srcs: + fail("`rust_test_without_process_wrapper_test.srcs` is not allowed. Remove it from {}".format( + ctx.label, + )) + providers = _rust_test_impl(ctx) + return providers + +rust_test_without_process_wrapper_test = rule( + implementation = _rust_test_without_process_wrapper_test_impl, + doc = "Unlike other `*_without_process_wrapper` rules, this rule does use the process wrapper but requires it's dependencies were not built with one.", + provides = COMMON_PROVIDERS, + attrs = _test_attrs_for_binary_without_process_wrapper(_common_attrs | _rust_test_attrs), + executable = True, + cfg = _rust_binary_without_process_wrapper_transition, + fragments = ["cpp"], + test = True, + toolchains = [ + str(Label("//rust:toolchain_type")), + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], ) @@ -1412,7 +1471,7 @@ rust_test = rule( test = True, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = dedent("""\ Builds a Rust test crate. @@ -1676,47 +1735,6 @@ rust_library_group = rule( """), ) -def _rust_allocator_libraries_impl(ctx): - toolchain = find_toolchain(ctx) - allocator_library = ctx.attr.allocator_library[AllocatorLibrariesImplInfo] if ctx.attr.allocator_library else None - global_allocator_library = ctx.attr.global_allocator_library[AllocatorLibrariesImplInfo] if ctx.attr.global_allocator_library else None - - make_ccinfo = lambda info, std: toolchain.make_libstd_and_allocator_ccinfo( - ctx.label, - ctx.actions, - struct(allocator_libraries_impl_info = info), - std, - ) - - providers = [AllocatorLibrariesInfo( - allocator_library = allocator_library, - global_allocator_library = global_allocator_library, - libstd_and_allocator_ccinfo = make_ccinfo(allocator_library, "std"), - libstd_and_global_allocator_ccinfo = make_ccinfo(global_allocator_library, "std"), - nostd_and_global_allocator_ccinfo = make_ccinfo(global_allocator_library, "no_std_with_alloc"), - )] - - return providers - -rust_allocator_libraries = rule( - implementation = _rust_allocator_libraries_impl, - provides = [AllocatorLibrariesInfo], - attrs = { - "allocator_library": attr.label( - doc = "An optional library to provide when a default rust allocator is used.", - providers = [AllocatorLibrariesImplInfo], - ), - "global_allocator_library": attr.label( - doc = "An optional library to provide when a default rust allocator is used.", - providers = [AllocatorLibrariesImplInfo], - ), - }, - toolchains = [ - str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", - ], -) - def _replace_illlegal_chars(name): """Replaces illegal characters in a name with underscores. diff --git a/rust/private/rust_allocator_libraries.bzl b/rust/private/rust_allocator_libraries.bzl new file mode 100644 index 0000000000..f6edd0a0bf --- /dev/null +++ b/rust/private/rust_allocator_libraries.bzl @@ -0,0 +1,301 @@ +"""Rust allocator library rules""" + +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") +load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") +load( + "//rust/private:utils.bzl", + "dedent", + "find_toolchain", +) +load( + ":common.bzl", + "rust_common", +) +load( + ":providers.bzl", + "AllocatorLibrariesImplInfo", + "AllocatorLibrariesInfo", +) + +def _ltl(library, actions, cc_toolchain, feature_configuration): + """A helper to generate `LibraryToLink` objects + + Args: + library (File): A rust library file to link. + actions: The rule's ctx.actions object. + cc_toolchain (CcToolchainInfo): A cc toolchain provider to be used (can be None). + feature_configuration (feature_configuration): feature_configuration to be queried (can be None). + + Returns: + LibraryToLink: A provider containing information about libraries to link. + """ + return cc_common.create_library_to_link( + actions = actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + static_library = library, + pic_static_library = library, + ) + +def make_libstd_and_allocator_ccinfo( + *, + cc_toolchain, + feature_configuration, + label, + actions, + experimental_link_std_dylib, + rust_std, + allocator_library, + std = "std"): + """Make the CcInfo (if possible) for libstd and allocator libraries. + + Args: + cc_toolchain (CcToolchainInfo): A cc toolchain provider to be used. + feature_configuration (feature_configuration): feature_configuration to be queried. + label (Label): The rule's label. + actions: The rule's ctx.actions object. + experimental_link_std_dylib (boolean): The value of the standard library's `_experimental_link_std_dylib(ctx)`. + rust_std: The Rust standard library. + allocator_library (struct): The target to use for providing allocator functions. + This should be a struct with either: + * a cc_info field of type CcInfo + * an allocator_libraries_impl_info field, which should be None or of type AllocatorLibrariesImplInfo. + std: Standard library flavor. Currently only "std" and "no_std_with_alloc" are supported, + accompanied with the default panic behavior. + + + Returns: + A CcInfo object for the required libraries, or None if no such libraries are available. + """ + cc_infos = [] + if not type(allocator_library) == "struct": + fail("Unexpected type of allocator_library, it must be a struct.") + if not any([hasattr(allocator_library, field) for field in ["cc_info", "allocator_libraries_impl_info"]]): + fail("Unexpected contents of allocator_library, it must provide either a cc_info or an allocator_libraries_impl_info.") + + if not rust_common.stdlib_info in rust_std: + fail(dedent("""\ + {} -- + The `rust_lib` ({}) must be a target providing `rust_common.stdlib_info` + (typically `rust_stdlib_filegroup` rule from @rules_rust//rust:defs.bzl). + See https://github.com/bazelbuild/rules_rust/pull/802 for more information. + """).format(label, rust_std)) + rust_stdlib_info = rust_std[rust_common.stdlib_info] + + if rust_stdlib_info.self_contained_files: + compilation_outputs = cc_common.create_compilation_outputs( + objects = depset(rust_stdlib_info.self_contained_files), + ) + + # Include C++ toolchain files as additional inputs for cross-compilation scenarios + additional_inputs = [] + if cc_toolchain and cc_toolchain.all_files: + additional_inputs = cc_toolchain.all_files.to_list() + + linking_context, _linking_outputs = cc_common.create_linking_context_from_compilation_outputs( + name = label.name, + actions = actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + compilation_outputs = compilation_outputs, + additional_inputs = additional_inputs, + ) + + cc_infos.append(CcInfo( + linking_context = linking_context, + )) + + if rust_stdlib_info.std_rlibs: + allocator_library_inputs = [] + + if hasattr(allocator_library, "allocator_libraries_impl_info") and allocator_library.allocator_libraries_impl_info: + static_archive = allocator_library.allocator_libraries_impl_info.static_archive + allocator_library_inputs = [depset( + [_ltl(static_archive, actions, cc_toolchain, feature_configuration)], + )] + + alloc_inputs = depset( + [_ltl(f, actions, cc_toolchain, feature_configuration) for f in rust_stdlib_info.alloc_files], + transitive = allocator_library_inputs, + order = "topological", + ) + between_alloc_and_core_inputs = depset( + [_ltl(f, actions, cc_toolchain, feature_configuration) for f in rust_stdlib_info.between_alloc_and_core_files], + transitive = [alloc_inputs], + order = "topological", + ) + core_inputs = depset( + [_ltl(f, actions, cc_toolchain, feature_configuration) for f in rust_stdlib_info.core_files], + transitive = [between_alloc_and_core_inputs], + order = "topological", + ) + + # The libraries panic_abort and panic_unwind are alternatives. + # The std by default requires panic_unwind. + # Exclude panic_abort if panic_unwind is present. + # TODO: Provide a setting to choose between panic_abort and panic_unwind. + filtered_between_core_and_std_files = rust_stdlib_info.between_core_and_std_files + has_panic_unwind = [ + f + for f in filtered_between_core_and_std_files + if "panic_unwind" in f.basename + ] + if has_panic_unwind: + filtered_between_core_and_std_files = [ + f + for f in filtered_between_core_and_std_files + if "abort" not in f.basename + ] + core_alloc_and_panic_inputs = depset( + [ + _ltl(f, actions, cc_toolchain, feature_configuration) + for f in rust_stdlib_info.panic_files + if "unwind" not in f.basename + ], + transitive = [core_inputs], + order = "topological", + ) + else: + core_alloc_and_panic_inputs = depset( + [ + _ltl(f, actions, cc_toolchain, feature_configuration) + for f in rust_stdlib_info.panic_files + if "unwind" not in f.basename + ], + transitive = [core_inputs], + order = "topological", + ) + memchr_inputs = depset( + [ + _ltl(f, actions, cc_toolchain, feature_configuration) + for f in rust_stdlib_info.memchr_files + ], + transitive = [core_inputs], + order = "topological", + ) + between_core_and_std_inputs = depset( + [ + _ltl(f, actions, cc_toolchain, feature_configuration) + for f in filtered_between_core_and_std_files + ], + transitive = [memchr_inputs], + order = "topological", + ) + + if experimental_link_std_dylib: + # std dylib has everything so that we do not need to include all std_files + std_inputs = depset( + [cc_common.create_library_to_link( + actions = actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + dynamic_library = rust_stdlib_info.std_dylib, + )], + ) + else: + std_inputs = depset( + [ + _ltl(f, actions, cc_toolchain, feature_configuration) + for f in rust_stdlib_info.std_files + ], + transitive = [between_core_and_std_inputs], + order = "topological", + ) + + test_inputs = depset( + [ + _ltl(f, actions, cc_toolchain, feature_configuration) + for f in rust_stdlib_info.test_files + ], + transitive = [std_inputs], + order = "topological", + ) + + if std == "std": + link_inputs = cc_common.create_linker_input( + owner = rust_std.label, + libraries = test_inputs, + ) + elif std == "no_std_with_alloc": + link_inputs = cc_common.create_linker_input( + owner = rust_std.label, + libraries = core_alloc_and_panic_inputs, + ) + else: + fail("Requested '{}' std mode is currently not supported.".format(std)) + + allocator_inputs = None + if hasattr(allocator_library, "cc_info"): + allocator_inputs = [allocator_library.cc_info.linking_context.linker_inputs] + + cc_infos.append(CcInfo( + linking_context = cc_common.create_linking_context( + linker_inputs = depset( + [link_inputs], + transitive = allocator_inputs, + order = "topological", + ), + ), + )) + + if cc_infos: + return cc_common.merge_cc_infos( + direct_cc_infos = cc_infos, + ) + return None + +# Attributes for rust-based allocator library support. +# Can't add it directly to RUSTC_ATTRS above, as those are used as +# aspect parameters and only support simple types ('bool', 'int' or 'string'). +RUSTC_ALLOCATOR_LIBRARIES_ATTRS = { + # This is really internal. Not prefixed with `_` since we need to adapt this + # in bootstrapping situations, e.g., when building the process wrapper + # or allocator libraries themselves. + "allocator_libraries": attr.label( + default = "//ffi/rs:default_allocator_libraries", + providers = [AllocatorLibrariesInfo], + ), +} + +def _rust_allocator_libraries_impl(ctx): + allocator_library = ctx.attr.allocator_library[AllocatorLibrariesImplInfo] if ctx.attr.allocator_library else None + global_allocator_library = ctx.attr.global_allocator_library[AllocatorLibrariesImplInfo] if ctx.attr.global_allocator_library else None + + toolchain = find_toolchain(ctx) + + def make_cc_info(info, std): + return toolchain.make_libstd_and_allocator_ccinfo( + ctx.label, + ctx.actions, + struct(allocator_libraries_impl_info = info), + std, + ) + + providers = [AllocatorLibrariesInfo( + allocator_library = allocator_library, + global_allocator_library = global_allocator_library, + libstd_and_allocator_ccinfo = make_cc_info(allocator_library, "std"), + libstd_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "std"), + nostd_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "no_std_with_alloc"), + )] + + return providers + +rust_allocator_libraries = rule( + implementation = _rust_allocator_libraries_impl, + provides = [AllocatorLibrariesInfo], + attrs = { + "allocator_library": attr.label( + doc = "An optional library to provide when a default rust allocator is used.", + providers = [AllocatorLibrariesImplInfo], + ), + "global_allocator_library": attr.label( + doc = "An optional library to provide when a default rust allocator is used.", + providers = [AllocatorLibrariesImplInfo], + ), + }, + toolchains = [ + str(Label("//rust:toolchain_type")), + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), + ], +) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 48be035043..ba0f9b0cda 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -212,7 +212,7 @@ def _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation # - For shared libraries - we use `pic`. This covers `dylib`, `cdylib` and `proc-macro` crate types. # - In `fastbuild` and `dbg` mode we use `pic` by default. # - In `opt` mode we use `nopic` outputs to build binaries. - if crate_type in ("cdylib", "dylib", "proc-macro"): + if cc_toolchain and crate_type in ("cdylib", "dylib", "proc-macro"): return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) elif compilation_mode in ("fastbuild", "dbg"): return True @@ -406,12 +406,13 @@ def get_cc_user_link_flags(ctx): """ return ctx.fragments.cpp.linkopts -def get_linker_and_args(ctx, crate_type, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = False): +def get_linker_and_args(ctx, crate_type, toolchain, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = False): """Gathers cc_common linker information Args: ctx (ctx): The current target's context object crate_type (str): The target crate's type (i.e. "bin", "proc-macro", etc.). + toolchain (rust_toolchain): The current Rust toolchain. cc_toolchain (CcToolchain): cc_toolchain for which we are creating build variables. feature_configuration (FeatureConfiguration): Feature configuration to be queried. rpaths (depset): Depset of directories where loader will look for libraries at runtime. @@ -421,50 +422,114 @@ def get_linker_and_args(ctx, crate_type, cc_toolchain, feature_configuration, rp Returns: tuple: A tuple of the following items: - (str): The tool path for given action. + - (bool): Whether or not the linker is a direct driver (e.g. `ld`) vs a wrapper (e.g. `gcc`). - (sequence): A flattened command line flags for given action. - (dict): Environment variables to be set for given action. """ user_link_flags = get_cc_user_link_flags(ctx) - if crate_type in ("bin") or add_flags_for_binary: - is_linking_dynamic_library = False - action_name = CPP_LINK_EXECUTABLE_ACTION_NAME - elif crate_type in ("dylib"): - is_linking_dynamic_library = True - action_name = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME - elif crate_type in ("staticlib"): - is_linking_dynamic_library = False - action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME - elif crate_type in ("cdylib", "proc-macro"): - # Proc macros get compiled as shared libraries to be loaded by the compiler. - is_linking_dynamic_library = True - action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME - elif crate_type in ("lib", "rlib"): - fail("Invalid `crate_type` for linking action: {}".format(crate_type)) - else: - fail("Unknown `crate_type`: {}".format(crate_type)) + ld = None + ld_is_direct_driver = False + link_args = [] + link_env = {} + + if cc_toolchain: + if crate_type in ("bin") or add_flags_for_binary: + is_linking_dynamic_library = False + action_name = CPP_LINK_EXECUTABLE_ACTION_NAME + elif crate_type in ("dylib"): + is_linking_dynamic_library = True + action_name = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME + elif crate_type in ("staticlib"): + is_linking_dynamic_library = False + action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME + elif crate_type in ("cdylib", "proc-macro"): + # Proc macros get compiled as shared libraries to be loaded by the compiler. + is_linking_dynamic_library = True + action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME + elif crate_type in ("lib", "rlib"): + fail("Invalid `crate_type` for linking action: {}".format(crate_type)) + else: + fail("Unknown `crate_type`: {}".format(crate_type)) + + link_variables = cc_common.create_link_variables( + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + is_linking_dynamic_library = is_linking_dynamic_library, + runtime_library_search_directories = rpaths, + user_link_flags = user_link_flags, + ) + link_args.extend(cc_common.get_memory_inefficient_command_line( + feature_configuration = feature_configuration, + action_name = action_name, + variables = link_variables, + )) + link_env = cc_common.get_environment_variables( + feature_configuration = feature_configuration, + action_name = action_name, + variables = link_variables, + ) + ld = cc_common.get_tool_for_action( + feature_configuration = feature_configuration, + action_name = action_name, + ) + ld_is_direct_driver = False + + if not ld or toolchain.linker_preference == "rust": + ld = toolchain.linker.path + ld_is_direct_driver = toolchain.linker_type == "direct" + + # When using rust-lld directly, we still need library search paths from cc_toolchain + # to find system libraries that rustc's stdlib depends on (like -lgcc_s, -lutil, etc.) + # Filter link_args to only include flags that help locate libraries. + if cc_toolchain and link_args: + filtered_args = [] + skip_next = False + for i, arg in enumerate(link_args): + if skip_next: + skip_next = False + continue + + # Strip -Wl, prefix if using direct driver (it's only for compiler drivers) + processed_arg = arg + if ld_is_direct_driver and arg.startswith("-Wl,"): + # Remove -Wl, prefix and split on commas (e.g., "-Wl,-rpath,/path" -> ["-rpath", "/path"]) + # For now, we'll handle common cases; complex -Wl, args might need more sophisticated handling + processed_arg = arg[4:] # Strip "-Wl," + + # Handle macOS version flag: convert -mmacos-version-min=X.Y to -macos_version_min X.Y + if processed_arg.startswith("-mmacos-version-min="): + version = processed_arg.split("=", 1)[1] + filtered_args.append("-macos_version_min") + filtered_args.append(version) + # Keep library search path flags + + elif processed_arg.startswith("-L"): + filtered_args.append(processed_arg) + # Keep sysroot flags (as single or two-part arguments) + + elif processed_arg == "--sysroot" or processed_arg.startswith("--sysroot="): + filtered_args.append(processed_arg) + if processed_arg == "--sysroot" and i + 1 < len(link_args): + # Two-part argument, keep the next arg too + filtered_args.append(link_args[i + 1]) + skip_next = True + + # Keep dynamic linker flags + elif processed_arg.startswith("--dynamic-linker") or processed_arg == "--dynamic-linker": + filtered_args.append(processed_arg) + if processed_arg == "--dynamic-linker" and i + 1 < len(link_args): + filtered_args.append(link_args[i + 1]) + skip_next = True + + # Keep rpath-related flags + elif processed_arg.startswith("-rpath") or processed_arg.startswith("--rpath"): + filtered_args.append(processed_arg) + link_args = filtered_args + + if not ld: + fail("No linker available for rustc. Either `rust_toolchain.linker` must be set or a `cc_toolchain` configured for the current configuration.") - link_variables = cc_common.create_link_variables( - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - is_linking_dynamic_library = is_linking_dynamic_library, - runtime_library_search_directories = rpaths, - user_link_flags = user_link_flags, - ) - link_args = cc_common.get_memory_inefficient_command_line( - feature_configuration = feature_configuration, - action_name = action_name, - variables = link_variables, - ) - link_env = cc_common.get_environment_variables( - feature_configuration = feature_configuration, - action_name = action_name, - variables = link_variables, - ) - ld = cc_common.get_tool_for_action( - feature_configuration = feature_configuration, - action_name = action_name, - ) if "LIB" in link_env: # Needed to ensure that link.exe will use msvcrt.lib from the cc_toolchain, # and not a non-hermetic system version. @@ -472,12 +537,12 @@ def get_linker_and_args(ctx, crate_type, cc_toolchain, feature_configuration, rp # I don't see a good way to stop rustc from adding the non-hermetic library search path, # so put our cc_toolchain library search path on the command line where it has # precedence over the non-hermetic path injected by rustc. - link_args = link_args + [ + link_args.extend([ "-LIBPATH:" + element for element in link_env["LIB"].split(";") - ] + ]) - return ld, link_args, link_env + return ld, ld_is_direct_driver, link_args, link_env def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib): """Constructs a disambiguating symlink for a library dependency. @@ -678,7 +743,9 @@ def collect_inputs( # rules_rust is not coupled with Bazel release. Remove conditional and change to # _linker_files once Starlark CcToolchainInfo is visible to Bazel. # https://github.com/bazelbuild/rules_rust/issues/2425 - if hasattr(cc_toolchain, "_linker_files"): + if not cc_toolchain: + linker_depset = depset() + elif hasattr(cc_toolchain, "_linker_files"): linker_depset = cc_toolchain._linker_files else: linker_depset = cc_toolchain.linker_files() @@ -1026,6 +1093,10 @@ def construct_arguments( _add_lto_flags(ctx, toolchain, rustc_flags, crate_info) _add_codegen_units_flags(toolchain, emit, rustc_flags) + # Use linker_type to determine whether to use direct or indirect linker invocation + # If linker_type is not explicitly set, infer from which linker is actually being used + ld_is_direct_driver = False + # Link! if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary: # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc @@ -1038,7 +1109,15 @@ def construct_arguments( else: rpaths = depset() - ld, link_args, link_env = get_linker_and_args(ctx, crate_info.type, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = add_flags_for_binary) + ld, ld_is_direct_driver, link_args, link_env = get_linker_and_args( + ctx, + crate_info.type, + toolchain, + cc_toolchain, + feature_configuration, + rpaths, + add_flags_for_binary = add_flags_for_binary, + ) env.update(link_env) rustc_flags.add(ld, format = "--codegen=linker=%s") @@ -1047,7 +1126,19 @@ def construct_arguments( # Additional context: https://github.com/rust-lang/rust/pull/36574 rustc_flags.add_all(link_args, format_each = "--codegen=link-arg=%s") - _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = include_link_flags) + _add_native_link_flags( + rustc_flags, + dep_info, + linkstamp_outs, + ambiguous_libs, + crate_info.type, + toolchain, + cc_toolchain, + feature_configuration, + compilation_mode, + ld_is_direct_driver, + include_link_flags = include_link_flags, + ) use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects) @@ -1241,7 +1332,7 @@ def rustc_compile_action( # One or more of the transitive deps is a cc_library / cc_import extra_disabled_features = [] cc_toolchain, feature_configuration = find_cc_toolchain(ctx, extra_disabled_features) - if not _are_linkstamps_supported( + if not cc_toolchain or not _are_linkstamps_supported( feature_configuration = feature_configuration, has_grep_includes = hasattr(ctx.attr, "_use_grep_includes"), ): @@ -1767,8 +1858,8 @@ def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_co interface_library (File): Optional interface library for cdylib crates on Windows. Returns: - list: A list containing the CcInfo provider and optionally AllocatorLibrariesImplInfo provider used when this crate is used as the rust allocator library implementation. - + list: A list containing the `CcInfo` provider and optionally `AllocatorLibrariesImplInfo` + provider used when this crate is used as the rust allocator library implementation. """ # A test will not need to produce CcInfo as nothing can depend on test targets @@ -1785,47 +1876,51 @@ def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_co return [] dot_a = None + library_to_link = None if crate_info.type == "staticlib": - library_to_link = cc_common.create_library_to_link( - actions = ctx.actions, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - static_library = crate_info.output, - # TODO(hlopko): handle PIC/NOPIC correctly - pic_static_library = crate_info.output, - alwayslink = getattr(attr, "alwayslink", False), - ) + if cc_toolchain: + library_to_link = cc_common.create_library_to_link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + static_library = crate_info.output, + # TODO(hlopko): handle PIC/NOPIC correctly + pic_static_library = crate_info.output, + alwayslink = getattr(attr, "alwayslink", False), + ) elif crate_info.type in ("rlib", "lib"): # bazel hard-codes a check for endswith((".a", ".pic.a", # ".lib")) in create_library_to_link, so we work around that # by creating a symlink to the .rlib with a .a extension. dot_a = make_static_lib_symlink(ctx.label.package, ctx.actions, crate_info.output) - # TODO(hlopko): handle PIC/NOPIC correctly - library_to_link = cc_common.create_library_to_link( - actions = ctx.actions, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - static_library = dot_a, + if cc_toolchain: # TODO(hlopko): handle PIC/NOPIC correctly - pic_static_library = dot_a, - alwayslink = getattr(attr, "alwayslink", False), - ) + library_to_link = cc_common.create_library_to_link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + static_library = dot_a, + # TODO(hlopko): handle PIC/NOPIC correctly + pic_static_library = dot_a, + alwayslink = getattr(attr, "alwayslink", False), + ) elif crate_info.type == "cdylib": - library_to_link = cc_common.create_library_to_link( - actions = ctx.actions, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - dynamic_library = crate_info.output, - interface_library = interface_library, - ) + if cc_toolchain: + library_to_link = cc_common.create_library_to_link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + dynamic_library = crate_info.output, + interface_library = interface_library, + ) else: fail("Unexpected case") link_input = cc_common.create_linker_input( owner = ctx.label, - libraries = depset([library_to_link]), + libraries = depset([library_to_link]) if library_to_link else depset(), ) linking_context = cc_common.create_linking_context( @@ -2147,72 +2242,135 @@ def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows def _add_user_link_flags(ret, linker_input): ret.extend(["--codegen=link-arg={}".format(flag) for flag in linker_input.user_link_flags]) -def _make_link_flags_windows(make_link_flags_args, flavor_msvc): +def _make_link_flags_windows(make_link_flags_args, flavor_msvc, use_direct_driver): linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args ret = [] + prefix = "" if use_direct_driver else "-Wl," for lib in linker_input.libraries: if lib.alwayslink: if flavor_msvc: - ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path]) + ret.append("-Clink-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path) else: ret.extend([ - "-C", - "link-arg=-Wl,--whole-archive", - "-C", - ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path), - "-C", - "link-arg=-Wl,--no-whole-archive", + ("-Clink-arg=%s--whole-archive" % prefix), + ("-Clink-arg=%s" % get_preferred_artifact(lib, use_pic).path), + ("-Clink-arg=%s--no-whole-archive" % prefix), ]) elif include_link_flags: ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc)) _add_user_link_flags(ret, linker_input) return ret -def _make_link_flags_windows_msvc(make_link_flags_args): - return _make_link_flags_windows(make_link_flags_args, flavor_msvc = True) +def _make_link_flags_windows_msvc(make_link_flags_args, use_direct_driver): + return _make_link_flags_windows(make_link_flags_args, flavor_msvc = True, use_direct_driver = use_direct_driver) -def _make_link_flags_windows_gnu(make_link_flags_args): - return _make_link_flags_windows(make_link_flags_args, flavor_msvc = False) +def _make_link_flags_windows_gnu(make_link_flags_args, use_direct_driver): + return _make_link_flags_windows(make_link_flags_args, flavor_msvc = False, use_direct_driver = use_direct_driver) -def _make_link_flags_darwin(make_link_flags_args): +def _make_link_flags_darwin(make_link_flags_args, use_direct_driver): linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args ret = [] + prefix = "" if use_direct_driver else "-Wl," for lib in linker_input.libraries: if lib.alwayslink: ret.extend([ - "-C", - ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path), + ("-Clink-arg=%s-force_load" % (prefix)), + ("-Clink-arg=%s%s" % (prefix, get_preferred_artifact(lib, use_pic).path)), ]) elif include_link_flags: ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_darwin = True)) _add_user_link_flags(ret, linker_input) return ret -def _make_link_flags_default(make_link_flags_args): +def _make_link_flags_default(make_link_flags_args, use_direct_driver): linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args ret = [] + prefix = "" if use_direct_driver else "-Wl," for lib in linker_input.libraries: if lib.alwayslink: ret.extend([ - "-C", - "link-arg=-Wl,--whole-archive", - "-C", - ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path), - "-C", - "link-arg=-Wl,--no-whole-archive", + ("-Clink-arg=%s--whole-archive" % prefix), + ("-Clink-arg=%s" % get_preferred_artifact(lib, use_pic).path), + ("-Clink-arg=%s--no-whole-archive" % prefix), ]) elif include_link_flags: ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default)) _add_user_link_flags(ret, linker_input) return ret +def _make_link_flags_default_indirect(make_link_flags_args): + return _make_link_flags_default(make_link_flags_args, False) + +def _make_link_flags_default_direct(make_link_flags_args): + return _make_link_flags_default(make_link_flags_args, True) + +def _make_link_flags_darwin_indirect(make_link_flags_args): + return _make_link_flags_darwin(make_link_flags_args, False) + +def _make_link_flags_darwin_direct(make_link_flags_args): + return _make_link_flags_darwin(make_link_flags_args, True) + +def _make_link_flags_windows_msvc_indirect(make_link_flags_args): + return _make_link_flags_windows_msvc(make_link_flags_args, False) + +def _make_link_flags_windows_msvc_direct(make_link_flags_args): + return _make_link_flags_windows_msvc(make_link_flags_args, True) + +def _make_link_flags_windows_gnu_indirect(make_link_flags_args): + return _make_link_flags_windows_gnu(make_link_flags_args, False) + +def _make_link_flags_windows_gnu_direct(make_link_flags_args): + return _make_link_flags_windows_gnu(make_link_flags_args, True) + +def _get_make_link_flag_funcs(target_os, target_abi, use_direct_link_driver): + """Select the appropriate functions for producing link arguments and library names. + + Args: + target_os (str): The target platform triple system component. + target_abi (str): The target platform triple abi component. + use_direct_link_driver (bool): Whether or not linking is done directly via + a link driver (`rust-lld`, `lld`, `lld-link.exe`, etc) vs indirectly via + a compiler (`gcc`, `clang`, `clang-cl.exe`, etc) + + Returns: + tuple: + - callable: The function for producing link args. + - callable: The function for formatting link library names. + """ + if target_os == "windows": + make_link_flags_windows_msvc = _make_link_flags_windows_msvc_direct if use_direct_link_driver else _make_link_flags_windows_msvc_indirect + make_link_flags_windows_gnu = _make_link_flags_windows_gnu_direct if use_direct_link_driver else _make_link_flags_windows_gnu_indirect + make_link_flags = make_link_flags_windows_msvc if target_abi == "msvc" else make_link_flags_windows_gnu + get_lib_name = get_lib_name_for_windows + elif target_os.startswith(("mac", "darwin", "ios")): + make_link_flags_darwin = _make_link_flags_darwin_direct if use_direct_link_driver else _make_link_flags_darwin_indirect + make_link_flags = make_link_flags_darwin + get_lib_name = get_lib_name_default + else: + make_link_flags_default = _make_link_flags_default_direct if use_direct_link_driver else _make_link_flags_default_indirect + make_link_flags = make_link_flags_default + get_lib_name = get_lib_name_default + + return (make_link_flags, get_lib_name) + def _libraries_dirnames(make_link_flags_args): link_input, use_pic, _, _ = make_link_flags_args # De-duplicate names. return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list() -def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = True): +def _add_native_link_flags( + args, + dep_info, + linkstamp_outs, + ambiguous_libs, + crate_type, + toolchain, + cc_toolchain, + feature_configuration, + compilation_mode, + use_direct_link_driver, + include_link_flags = True): """Adds linker flags for all dependencies of the current target. Args: @@ -2225,6 +2383,7 @@ def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate cc_toolchain (CcToolchainInfo): The current `cc_toolchain` feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain compilation_mode (bool): The compilation mode for this build. + use_direct_link_driver (bool): Whether the linker is a direct driver (e.g. `ld`, `wasm-ld`) vs a wrapper (e.g. `clang`, `gcc`). include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library. """ if crate_type in ["lib", "rlib"]: @@ -2232,15 +2391,11 @@ def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode) - if toolchain.target_os == "windows": - make_link_flags = _make_link_flags_windows_msvc if toolchain.target_triple.abi == "msvc" else _make_link_flags_windows_gnu - get_lib_name = get_lib_name_for_windows - elif toolchain.target_os.startswith(("mac", "darwin", "ios")): - make_link_flags = _make_link_flags_darwin - get_lib_name = get_lib_name_default - else: - make_link_flags = _make_link_flags_default - get_lib_name = get_lib_name_default + make_link_flags, get_lib_name = _get_make_link_flag_funcs( + target_os = toolchain.target_os, + target_abi = toolchain.target_abi, + use_direct_link_driver = use_direct_link_driver, + ) # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0 make_link_flags_args = [(arg, use_pic, ambiguous_libs, include_link_flags) for arg in dep_info.transitive_noncrates.to_list()] @@ -2255,34 +2410,35 @@ def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate args.add_all(linkstamp_outs, before_each = "-C", format_each = "link-args=%s") - if crate_type in ["dylib", "cdylib"]: - # For shared libraries we want to link C++ runtime library dynamically - # (for example libstdc++.so or libc++.so). - args.add_all( - cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration), - map_each = _get_dirname, - format_each = "-Lnative=%s", - ) - if include_link_flags: + if cc_toolchain: + if crate_type in ["dylib", "cdylib"]: + # For shared libraries we want to link C++ runtime library dynamically + # (for example libstdc++.so or libc++.so). args.add_all( cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration), - map_each = get_lib_name, - format_each = "-ldylib=%s", + map_each = _get_dirname, + format_each = "-Lnative=%s", ) - else: - # For all other crate types we want to link C++ runtime library statically - # (for example libstdc++.a or libc++.a). - args.add_all( - cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration), - map_each = _get_dirname, - format_each = "-Lnative=%s", - ) - if include_link_flags: + if include_link_flags: + args.add_all( + cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration), + map_each = get_lib_name, + format_each = "-ldylib=%s", + ) + else: + # For all other crate types we want to link C++ runtime library statically + # (for example libstdc++.a or libc++.a). args.add_all( cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration), - map_each = get_lib_name, - format_each = "-lstatic=%s", + map_each = _get_dirname, + format_each = "-Lnative=%s", ) + if include_link_flags: + args.add_all( + cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration), + map_each = get_lib_name, + format_each = "-lstatic=%s", + ) def _get_dirname(file): """A helper function for `_add_native_link_flags`. diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl index 84ee415321..0ffe808fd1 100644 --- a/rust/private/rustdoc.bzl +++ b/rust/private/rustdoc.bzl @@ -384,6 +384,6 @@ rust_doc = rule( }, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], ) diff --git a/rust/private/rustdoc_test.bzl b/rust/private/rustdoc_test.bzl index 36afda488d..522c048cd1 100644 --- a/rust/private/rustdoc_test.bzl +++ b/rust/private/rustdoc_test.bzl @@ -250,7 +250,7 @@ rust_doc_test = rule( fragments = ["cpp"], toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = dedent("""\ Runs Rust documentation tests. diff --git a/rust/private/unpretty.bzl b/rust/private/unpretty.bzl index b7257b6821..be111c83a3 100644 --- a/rust/private/unpretty.bzl +++ b/rust/private/unpretty.bzl @@ -244,7 +244,7 @@ rust_unpretty_aspect = aspect( } | RUSTC_ATTRS, toolchains = [ str(Label("//rust:toolchain_type")), - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], required_providers = [ [rust_common.crate_info], diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl index a759119e7c..8a82126d53 100644 --- a/rust/private/utils.bzl +++ b/rust/private/utils.bzl @@ -70,17 +70,25 @@ def find_toolchain(ctx): """ return ctx.toolchains[Label("//rust:toolchain_type")] +# A global kill switch to test without a cc toolchain present. +_FORCE_DISABLE_CC_TOOLCHAIN = False + def find_cc_toolchain(ctx, extra_unsupported_features = tuple()): """Extracts a CcToolchain from the current target's context Args: ctx (ctx): The current target's rule context object - extra_unsupported_features (sequence of str): Extra featrures to disable + extra_unsupported_features (sequence of str): Extra features to disable Returns: tuple: A tuple of (CcToolchain, FeatureConfiguration) """ - cc_toolchain = find_rules_cc_toolchain(ctx) + if _FORCE_DISABLE_CC_TOOLCHAIN: + return None, None + + cc_toolchain = find_rules_cc_toolchain(ctx, mandatory = False) + if not cc_toolchain: + return None, None feature_configuration = cc_common.configure_features( ctx = ctx, diff --git a/rust/repositories.bzl b/rust/repositories.bzl index 37f872f319..a9d9a28919 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -42,14 +42,18 @@ load_arbitrary_tool = _load_arbitrary_tool # Note: Code in `.github/workflows/crate_universe.yaml` looks for this line, if you remove it or change its format, you will also need to update that code. DEFAULT_TOOLCHAIN_TRIPLES = { "aarch64-apple-darwin": "rust_macos_aarch64", + # "aarch64-pc-windows-gnu": "rust_windows_aarch64_gnu", "aarch64-pc-windows-msvc": "rust_windows_aarch64", "aarch64-unknown-linux-gnu": "rust_linux_aarch64", + # "aarch64-unknown-linux-musl": "rust_linux_aarch64_gnu", "powerpc64le-unknown-linux-gnu": "rust_linux_powerpc64le", "s390x-unknown-linux-gnu": "rust_linux_s390x", "x86_64-apple-darwin": "rust_macos_x86_64", + # "x86_64-pc-windows-gnu": "rust_windows_x86_64_gnu", "x86_64-pc-windows-msvc": "rust_windows_x86_64", "x86_64-unknown-freebsd": "rust_freebsd_x86_64", "x86_64-unknown-linux-gnu": "rust_linux_x86_64", + # "x86_64-unknown-linux-musl": "rust_linux_x86_64_musl", } _COMPACT_WINDOWS_NAMES = True @@ -233,6 +237,7 @@ def rust_register_toolchains( toolchain_names = [] toolchain_labels = {} toolchain_target_settings = {} + toolchain_target_settings_select = {} toolchain_types = {} exec_compatible_with_by_toolchain = {} target_compatible_with_by_toolchain = {} @@ -294,6 +299,7 @@ def rust_register_toolchains( target_compatible_with_by_toolchain[toolchain.name] = toolchain.target_constraints toolchain_types[toolchain.name] = "@rules_rust//rust:toolchain" toolchain_target_settings[toolchain.name] = ["@rules_rust//rust/toolchain/channel:{}".format(toolchain.channel.name)] + target_settings + toolchain_target_settings_select[toolchain.name] = _get_abi_target_settings_select(toolchain.target_triple) for exec_triple, name in rustfmt_toolchain_triples.items(): rustfmt_repo_name = "rustfmt_{}__{}".format(rustfmt_version.replace("/", "-"), exec_triple) @@ -318,6 +324,7 @@ def rust_register_toolchains( toolchain_labels[rustfmt_repo_name] = "@{}_tools//:rustfmt_toolchain".format(rustfmt_repo_name) exec_compatible_with_by_toolchain[rustfmt_repo_name] = triple_to_constraint_set(exec_triple) target_compatible_with_by_toolchain[rustfmt_repo_name] = [] + toolchain_target_settings_select[rustfmt_repo_name] = {} toolchain_types[rustfmt_repo_name] = "@rules_rust//rust/rustfmt:toolchain_type" if aliases: @@ -331,6 +338,7 @@ def rust_register_toolchains( exec_compatible_with_by_toolchain[name] = info["exec_compatible_with"] target_compatible_with_by_toolchain[name] = info["target_compatible_with"] toolchain_target_settings[name] = info["target_settings"] + toolchain_target_settings_select[name] = info["target_settings_select"] toolchain_types[name] = info["toolchain_type"] toolchain_repository_hub( @@ -339,6 +347,7 @@ def rust_register_toolchains( toolchain_labels = toolchain_labels, toolchain_types = toolchain_types, target_settings = toolchain_target_settings, + target_settings_select = json.encode(toolchain_target_settings_select), exec_compatible_with = exec_compatible_with_by_toolchain, target_compatible_with = target_compatible_with_by_toolchain, ) @@ -436,11 +445,14 @@ def _rust_toolchain_tools_repository_impl(ctx): exec_triple = triple(ctx.attr.exec_triple) + include_linker = True + rustc_content, rustc_sha256 = load_rust_compiler( ctx = ctx, iso_date = iso_date, target_triple = exec_triple, version = version, + include_linker = include_linker, ) clippy_content, clippy_sha256 = load_clippy( ctx = ctx, @@ -519,6 +531,7 @@ def _rust_toolchain_tools_repository_impl(ctx): default_edition = ctx.attr.edition, include_rustfmt = not (not ctx.attr.rustfmt_version), include_llvm_tools = include_llvm_tools, + include_linker = include_linker, extra_rustc_flags = ctx.attr.extra_rustc_flags, extra_exec_rustc_flags = ctx.attr.extra_exec_rustc_flags, opt_level = ctx.attr.opt_level if ctx.attr.opt_level else None, @@ -573,6 +586,7 @@ def _toolchain_repository_proxy_impl(repository_ctx): name = "toolchain", toolchain = repository_ctx.attr.toolchain, target_settings = repository_ctx.attr.target_settings, + target_settings_select = repository_ctx.attr.target_settings_select, toolchain_type = repository_ctx.attr.toolchain_type, target_compatible_with = repository_ctx.attr.target_compatible_with, exec_compatible_with = repository_ctx.attr.exec_compatible_with, @@ -593,6 +607,9 @@ toolchain_repository_proxy = repository_rule( "target_settings": attr.string_list( doc = "A list of config_settings that must be satisfied by the target configuration in order for this toolchain to be selected during toolchain resolution.", ), + "target_settings_select": attr.string_list_dict( + doc = "A dictionary mapping config_setting labels to lists of additional target_settings, used to generate a select() statement. If empty, no select() is generated.", + ), "toolchain": attr.string( doc = "The name of the toolchain implementation target.", mandatory = True, @@ -608,6 +625,42 @@ toolchain_repository_proxy = repository_rule( # For legacy support rust_toolchain_repository_proxy = toolchain_repository_proxy +def _get_abi_target_settings_select(target_triple): + """Returns the ABI-specific config_settings for use in a select() statement. + + Args: + target_triple (str): The target triple to check + + Returns: + dict: A dictionary mapping config_setting labels to lists of target_settings, + for use in a select() statement. Empty dict if no ABI-specific settings apply. + """ + triple_struct = triple(target_triple) + + # Check for linux + musl combination + if triple_struct.system == "linux": + return { + "@rules_rust//rust/settings:experimental_use_platform_abi_settings_enabled": [ + "@rules_rust//rust/settings:platform_linux_musl_{}".format( + "enabled" if triple_struct.abi == "musl" else "disabled", + ), + ], + "//conditions:default": [], + } + + # Check for windows + gnu combination + if triple_struct.system == "windows": + return { + "@rules_rust//rust/settings:experimental_use_platform_abi_settings_enabled": [ + "@rules_rust//rust/settings:platform_windows_gnu_{}".format( + "enabled" if triple_struct.abi == "gnu" else "disabled", + ), + ], + "//conditions:default": [], + } + + return {} + # N.B. A "proxy repository" is needed to allow for registering the toolchain (with constraints) # without actually downloading the toolchain. def rust_toolchain_repository( @@ -701,6 +754,7 @@ def rust_toolchain_repository( ) channel_target_settings = ["@rules_rust//rust/toolchain/channel:{}".format(channel)] if channel else [] + abi_target_settings_select = _get_abi_target_settings_select(target_triple) tools_toolchain_label = "@{}//:rust_toolchain".format(tools_repo_name) @@ -710,6 +764,7 @@ def rust_toolchain_repository( name = name, toolchain = tools_toolchain_label, target_settings = channel_target_settings + target_settings, + target_settings_select = abi_target_settings_select, toolchain_type = toolchain_type, exec_compatible_with = exec_compatible_with, target_compatible_with = target_compatible_with, @@ -719,7 +774,8 @@ def rust_toolchain_repository( "exec_compatible_with": exec_compatible_with, "name": name, "target_compatible_with": target_compatible_with, - "target_settings": target_settings, + "target_settings": channel_target_settings + target_settings, + "target_settings_select": abi_target_settings_select, "toolchain_label": "@{name}//:toolchain".format(name = name), "toolchain_type": toolchain_type, "tools_toolchain_label": tools_toolchain_label, @@ -753,6 +809,7 @@ _RUST_ANALYZER_TOOLCHAIN_TOOLS_REPOSITORY_ATTRS = { def _rust_analyzer_toolchain_tools_repository_impl(repository_ctx): sha256s = dict(repository_ctx.attr.sha256s) + include_linker = True iso_date = None version = repository_ctx.attr.version @@ -777,6 +834,7 @@ def _rust_analyzer_toolchain_tools_repository_impl(repository_ctx): iso_date = iso_date, target_triple = host_triple, version = version, + include_linker = include_linker, ) build_contents = [rustc_content] sha256s.update(rustc_sha256) @@ -911,6 +969,8 @@ def _rustfmt_toolchain_tools_repository_impl(repository_ctx): repository_ctx.name, )) + include_linker = True + iso_date = None version = repository_ctx.attr.version version_array = version.split("/") @@ -929,6 +989,7 @@ def _rustfmt_toolchain_tools_repository_impl(repository_ctx): iso_date = iso_date, target_triple = exec_triple, version = version, + include_linker = include_linker, ) rustfmt_content, rustfmt_sha256 = load_rustfmt( ctx = repository_ctx, diff --git a/rust/settings/BUILD.bazel b/rust/settings/BUILD.bazel index a93b91cc64..24df7a6df2 100644 --- a/rust/settings/BUILD.bazel +++ b/rust/settings/BUILD.bazel @@ -17,6 +17,7 @@ load( "experimental_use_cc_common_link", "experimental_use_coverage_metadata_files", "experimental_use_global_allocator", + "experimental_use_platform_abi_settings", "experimental_use_sh_toolchain_for_bootstrap_process_wrapper", "extra_exec_rustc_env", "extra_exec_rustc_flag", @@ -30,11 +31,14 @@ load( "lto", "no_std", "pipelined_compilation", + "platform_linux_musl", + "platform_windows_gnu", "rename_first_party_crates", "rustc_output_diagnostics", "rustfmt_toml", "third_party_dir", "toolchain_generated_sysroot", + "toolchain_linker_preference", "unpretty", "use_real_import_macro", ) @@ -91,6 +95,8 @@ experimental_use_allocator_libraries_with_mangled_symbols( name = "experimental_use_allocator_libraries_with_mangled_symbols", ) +experimental_use_platform_abi_settings() + experimental_use_sh_toolchain_for_bootstrap_process_wrapper() extra_exec_rustc_env() @@ -117,6 +123,10 @@ no_std() pipelined_compilation() +platform_linux_musl() + +platform_windows_gnu() + rename_first_party_crates() rustc_output_diagnostics() @@ -127,6 +137,8 @@ third_party_dir() toolchain_generated_sysroot() +toolchain_linker_preference() + unpretty() use_real_import_macro() diff --git a/rust/settings/settings.bzl b/rust/settings/settings.bzl index abedffa4c0..030b58e7f3 100644 --- a/rust/settings/settings.bzl +++ b/rust/settings/settings.bzl @@ -234,6 +234,102 @@ def experimental_use_sh_toolchain_for_bootstrap_process_wrapper(): build_setting_default = False, ) +def toolchain_linker_preference(): + """A flag to control which linker is preferred for linking Rust binaries. + + Accepts three values: + - "rust": Use `rust_toolchain.linker` always. + - "cc": Use the linker provided by the configured `cc_toolchain` + - "none": Default to `cc` being the preference and falling back to `rust`. + """ + string_flag( + name = "toolchain_linker_preference", + build_setting_default = "none", + values = ["rust", "cc", "none"], + ) + +# buildifier: disable=unnamed-macro +def experimental_use_platform_abi_settings(): + """An experimental flag to enable platform ABI-based toolchain selection. + + When enabled, this enables the use of platform_linux_musl and platform_windows_gnu + settings for toolchain selection. This is particularly useful for the process wrapper + bootstrap transition, which will select musl-based toolchains on Linux when this + experimental flag is enabled. + + Note: The individual platform flags (platform_linux_musl, platform_windows_gnu) can + still be set manually regardless of this experimental flag. + """ + bool_flag( + name = "experimental_use_platform_abi_settings", + build_setting_default = True, + ) + + # Config setting used in select() statements for conditional target_settings. + native.config_setting( + name = "experimental_use_platform_abi_settings_enabled", + flag_values = { + ":experimental_use_platform_abi_settings": "True", + }, + ) + +# buildifier: disable=unnamed-macro +def platform_linux_musl(): + """A flag to select Linux musl toolchains. + + When enabled, this will select Rust toolchains targeting Linux with musl libc. + This is primarily used by the process wrapper bootstrap transition to ensure + static linking on Linux for better portability. + """ + bool_flag( + name = "platform_linux_musl", + build_setting_default = False, + ) + + # Config setting that checks if platform_linux_musl is enabled. + # Used in toolchain target_settings (wrapped in select() based on experimental flag). + native.config_setting( + name = "platform_linux_musl_enabled", + flag_values = { + ":platform_linux_musl": "True", + }, + ) + + native.config_setting( + name = "platform_linux_musl_disabled", + flag_values = { + ":platform_linux_musl": "False", + }, + ) + +# buildifier: disable=unnamed-macro +def platform_windows_gnu(): + """A flag to select Windows GNU toolchains. + + When enabled, this will select Rust toolchains targeting Windows with GNU ABI + (MinGW) instead of MSVC. + """ + bool_flag( + name = "platform_windows_gnu", + build_setting_default = False, + ) + + # Config setting that checks if platform_windows_gnu is enabled. + # Used in toolchain target_settings (wrapped in select() based on experimental flag). + native.config_setting( + name = "platform_windows_gnu_enabled", + flag_values = { + ":platform_windows_gnu": "True", + }, + ) + + native.config_setting( + name = "platform_windows_gnu_disabled", + flag_values = { + ":platform_windows_gnu": "False", + }, + ) + # buildifier: disable=unnamed-macro def clippy_toml(): """This setting is used by the clippy rules. See https://bazelbuild.github.io/rules_rust/rust_clippy.html diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index f3ef2f2987..79ca34f532 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -9,6 +9,10 @@ load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") load("//rust/platform:triple.bzl", "triple") load("//rust/private:common.bzl", "rust_common") load("//rust/private:lto.bzl", "RustLtoInfo") +load( + "//rust/private:rust_allocator_libraries.bzl", + "make_libstd_and_allocator_ccinfo", +) load( "//rust/private:rust_analyzer.bzl", _current_rust_analyzer_toolchain = "current_rust_analyzer_toolchain", @@ -21,7 +25,6 @@ load( ) load( "//rust/private:utils.bzl", - "dedent", "deduplicate", "find_cc_toolchain", "is_exec_configuration", @@ -129,244 +132,26 @@ rust_stdlib_filegroup = rule( }, ) -def _ltl(library, actions, cc_toolchain, feature_configuration): - """A helper to generate `LibraryToLink` objects - - Args: - library (File): A rust library file to link. - actions: The rule's ctx.actions object. - cc_toolchain (CcToolchainInfo): A cc toolchain provider to be used. - feature_configuration (feature_configuration): feature_configuration to be queried. - - Returns: - LibraryToLink: A provider containing information about libraries to link. - """ - return cc_common.create_library_to_link( - actions = actions, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - static_library = library, - pic_static_library = library, - ) - -def _make_libstd_and_allocator_ccinfo( - cc_toolchain, - feature_configuration, - label, - actions, - experimental_link_std_dylib, - rust_std, - allocator_library, - std = "std"): - """Make the CcInfo (if possible) for libstd and allocator libraries. - - Args: - cc_toolchain (CcToolchainInfo): A cc toolchain provider to be used. - feature_configuration (feature_configuration): feature_configuration to be queried. - label (Label): The rule's label. - actions: The rule's ctx.actions object. - experimental_link_std_dylib (boolean): The value of the standard library's `_experimental_link_std_dylib(ctx)`. - rust_std: The Rust standard library. - allocator_library (struct): The target to use for providing allocator functions. - This should be a struct with either: - * a cc_info field of type CcInfo - * an allocator_libraries_impl_info field, which should be None or of type AllocatorLibrariesImplInfo. - std: Standard library flavor. Currently only "std" and "no_std_with_alloc" are supported, - accompanied with the default panic behavior. - - - Returns: - A CcInfo object for the required libraries, or None if no such libraries are available. - """ - cc_infos = [] - if not type(allocator_library) == "struct": - fail("Unexpected type of allocator_library, it must be a struct.") - if not any([hasattr(allocator_library, field) for field in ["cc_info", "allocator_libraries_impl_info"]]): - fail("Unexpected contents of allocator_library, it must provide either a cc_info or an allocator_libraries_impl_info.") - - if not rust_common.stdlib_info in rust_std: - fail(dedent("""\ - {} -- - The `rust_lib` ({}) must be a target providing `rust_common.stdlib_info` - (typically `rust_stdlib_filegroup` rule from @rules_rust//rust:defs.bzl). - See https://github.com/bazelbuild/rules_rust/pull/802 for more information. - """).format(label, rust_std)) - rust_stdlib_info = rust_std[rust_common.stdlib_info] - - if rust_stdlib_info.self_contained_files: - compilation_outputs = cc_common.create_compilation_outputs( - objects = depset(rust_stdlib_info.self_contained_files), - ) - - # Include C++ toolchain files as additional inputs for cross-compilation scenarios - additional_inputs = [] - if cc_toolchain and cc_toolchain.all_files: - additional_inputs = cc_toolchain.all_files.to_list() - - linking_context, _linking_outputs = cc_common.create_linking_context_from_compilation_outputs( - name = label.name, - actions = actions, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - compilation_outputs = compilation_outputs, - additional_inputs = additional_inputs, - ) - - cc_infos.append(CcInfo( - linking_context = linking_context, - )) - - if rust_stdlib_info.std_rlibs: - allocator_library_inputs = [] - - if hasattr(allocator_library, "allocator_libraries_impl_info") and allocator_library.allocator_libraries_impl_info: - static_archive = allocator_library.allocator_libraries_impl_info.static_archive - allocator_library_inputs = [depset( - [_ltl(static_archive, actions, cc_toolchain, feature_configuration)], - )] - - alloc_inputs = depset( - [_ltl(f, actions, cc_toolchain, feature_configuration) for f in rust_stdlib_info.alloc_files], - transitive = allocator_library_inputs, - order = "topological", - ) - between_alloc_and_core_inputs = depset( - [_ltl(f, actions, cc_toolchain, feature_configuration) for f in rust_stdlib_info.between_alloc_and_core_files], - transitive = [alloc_inputs], - order = "topological", - ) - core_inputs = depset( - [_ltl(f, actions, cc_toolchain, feature_configuration) for f in rust_stdlib_info.core_files], - transitive = [between_alloc_and_core_inputs], - order = "topological", - ) - - # The libraries panic_abort and panic_unwind are alternatives. - # The std by default requires panic_unwind. - # Exclude panic_abort if panic_unwind is present. - # TODO: Provide a setting to choose between panic_abort and panic_unwind. - filtered_between_core_and_std_files = rust_stdlib_info.between_core_and_std_files - has_panic_unwind = [ - f - for f in filtered_between_core_and_std_files - if "panic_unwind" in f.basename - ] - if has_panic_unwind: - filtered_between_core_and_std_files = [ - f - for f in filtered_between_core_and_std_files - if "abort" not in f.basename - ] - core_alloc_and_panic_inputs = depset( - [ - _ltl(f, actions, cc_toolchain, feature_configuration) - for f in rust_stdlib_info.panic_files - if "unwind" not in f.basename - ], - transitive = [core_inputs], - order = "topological", - ) - else: - core_alloc_and_panic_inputs = depset( - [ - _ltl(f, actions, cc_toolchain, feature_configuration) - for f in rust_stdlib_info.panic_files - if "unwind" not in f.basename - ], - transitive = [core_inputs], - order = "topological", - ) - memchr_inputs = depset( - [ - _ltl(f, actions, cc_toolchain, feature_configuration) - for f in rust_stdlib_info.memchr_files - ], - transitive = [core_inputs], - order = "topological", - ) - between_core_and_std_inputs = depset( - [ - _ltl(f, actions, cc_toolchain, feature_configuration) - for f in filtered_between_core_and_std_files - ], - transitive = [memchr_inputs], - order = "topological", - ) - - if experimental_link_std_dylib: - # std dylib has everything so that we do not need to include all std_files - std_inputs = depset( - [cc_common.create_library_to_link( - actions = actions, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - dynamic_library = rust_stdlib_info.std_dylib, - )], - ) - else: - std_inputs = depset( - [ - _ltl(f, actions, cc_toolchain, feature_configuration) - for f in rust_stdlib_info.std_files - ], - transitive = [between_core_and_std_inputs], - order = "topological", - ) - - test_inputs = depset( - [ - _ltl(f, actions, cc_toolchain, feature_configuration) - for f in rust_stdlib_info.test_files - ], - transitive = [std_inputs], - order = "topological", - ) - - if std == "std": - link_inputs = cc_common.create_linker_input( - owner = rust_std.label, - libraries = test_inputs, - ) - elif std == "no_std_with_alloc": - link_inputs = cc_common.create_linker_input( - owner = rust_std.label, - libraries = core_alloc_and_panic_inputs, - ) - else: - fail("Requested '{}' std mode is currently not supported.".format(std)) - - allocator_inputs = None - if hasattr(allocator_library, "cc_info"): - allocator_inputs = [allocator_library.cc_info.linking_context.linker_inputs] - - cc_infos.append(CcInfo( - linking_context = cc_common.create_linking_context( - linker_inputs = depset( - [link_inputs], - transitive = allocator_inputs, - order = "topological", - ), - ), - )) - - if cc_infos: - return cc_common.merge_cc_infos( - direct_cc_infos = cc_infos, - ) - return None +def _experimental_link_std_dylib(ctx): + return not is_exec_configuration(ctx) and \ + ctx.attr.experimental_link_std_dylib[BuildSettingInfo].value and \ + ctx.attr.rust_std[rust_common.stdlib_info].std_dylib != None -def _symlink_sysroot_tree(ctx, name, target): +def _symlink_sysroot_tree(ctx, name, target, target_files = None): """Generate a set of symlinks to files from another target Args: ctx (ctx): The toolchain's context object name (str): The name of the sysroot directory (typically `ctx.label.name`) target (Target): A target owning files to symlink + target_files (depset): An optional depset to use in place of `target.files`. Returns: depset[File]: A depset of the generated symlink files """ tree_files = [] + if target_files == None: + target_files = target.files for file in target.files.to_list(): # Parse the path to the file relative to the workspace root so a # symlink matching this path can be created within the sysroot. @@ -425,7 +210,8 @@ def _generate_sysroot( cargo_clippy = None, llvm_tools = None, rust_std = None, - rustfmt = None): + rustfmt = None, + linker = None): """Generate a rust sysroot from collection of toolchain components Args: @@ -439,6 +225,7 @@ def _generate_sysroot( llvm_tools (Target, optional): A collection of llvm tools used by `rustc`. rust_std (Target, optional): A collection of Files containing Rust standard library components. rustfmt (File, optional): The path to a `rustfmt` executable. + linker (Target, optional): The linker target (e.g. `rust-lld`). Returns: struct: A struct of generated files representing the new sysroot @@ -487,6 +274,27 @@ def _generate_sysroot( sysroot_rustfmt = _symlink_sysroot_bin(ctx, name, "bin", rustfmt) direct_files.extend([sysroot_rustfmt]) + # Linker + sysroot_linker = None + if linker: + linker_files = linker[DefaultInfo].files.to_list() + if not len(linker_files) == 1: + fail("`rust_toolchain.linker` is expected to be represted by one file. Found {}. Please update {}".format( + len(linker_files), + linker.label, + )) + linker_bin = linker_files[0] + dest = "bin" + if "/bin/" in linker_bin.path: + _, _, subdir = linker_bin.path.partition("/bin/") + if subdir: + dest = "bin/{}".format(subdir[:-len("/" + linker_bin.basename)]).rstrip("/") + + sysroot_linker = _symlink_sysroot_bin(ctx, name, dest, linker_bin) + sysroot_linker_files = _symlink_sysroot_tree(ctx, name, linker, linker[DefaultInfo].default_runfiles.files) + direct_files.extend([sysroot_linker]) + transitive_file_sets.extend([sysroot_linker_files]) + # Llvm tools sysroot_llvm_tools = None if llvm_tools: @@ -510,6 +318,7 @@ def _generate_sysroot( "cargo: {}".format(cargo), "clippy: {}".format(clippy), "cargo-clippy: {}".format(cargo_clippy), + "linker: {}".format(linker), "llvm_tools: {}".format(llvm_tools), "rust_std: {}".format(rust_std), "rustc_lib: {}".format(rustc_lib), @@ -525,8 +334,9 @@ def _generate_sysroot( return struct( all_files = all_files, cargo = sysroot_cargo, - clippy = sysroot_clippy, cargo_clippy = sysroot_cargo_clippy, + clippy = sysroot_clippy, + linker = sysroot_linker, rust_std = sysroot_rust_std, rustc = sysroot_rustc, rustc_lib = sysroot_rustc_lib, @@ -606,6 +416,7 @@ def _rust_toolchain_impl(ctx): cargo = ctx.file.cargo, cargo_clippy = ctx.file.cargo_clippy, llvm_tools = ctx.attr.llvm_tools, + linker = ctx.attr.linker, ) # Determine the path and short_path of the sysroot @@ -672,11 +483,13 @@ def _rust_toolchain_impl(ctx): target_json = None target_arch = None target_os = None + target_abi = None if ctx.attr.target_triple: target_triple = triple(ctx.attr.target_triple) target_arch = target_triple.arch target_os = target_triple.system + target_abi = target_triple.abi elif ctx.attr.target_json: # Ensure the data provided is valid json @@ -692,24 +505,58 @@ def _rust_toolchain_impl(ctx): target_arch = target_json_content["arch"] if "os" in target_json_content: target_os = target_json_content["os"] + if "env" in target_json_content: + target_abi = target_json_content["env"] else: fail("Either `target_triple` or `target_json` must be provided. Please update {}".format( ctx.label, )) + cc_toolchain, feature_configuration = find_cc_toolchain(ctx) + + linker_preference = None + if ctx.attr.linker_preference: + linker_preference = ctx.attr.linker_preference + else: + value = ctx.attr._linker_preference[BuildSettingInfo].value + if value != "none": + linker_preference = value + + # Validate linker_preference configuration + if linker_preference == "rust": + if not ctx.attr.linker: + fail("When `rust_toolchain.linker_preference == \"rust\"`, a `rust_toolchain.linker` must be provided. Please update: {}".format( + ctx.label, + )) + elif linker_preference == "cc": + if not cc_toolchain: + fail("When `rust_toolchain.linker_preference == \"cc\"`, a `cc_toolchain` must be configured. Please update: {}".format( + ctx.label, + )) + experimental_link_std_dylib = _experimental_link_std_dylib(ctx) - make_ccinfo = lambda label, actions, allocator_library, std: ( - _make_libstd_and_allocator_ccinfo(cc_toolchain, feature_configuration, label, actions, experimental_link_std_dylib, rust_std, allocator_library, std) - ) - make_local_ccinfo = lambda allocator_library, std: make_ccinfo( - ctx.label, - ctx.actions, - struct(cc_info = allocator_library), - std, - ) + + def make_ccinfo(label, actions, allocator_library, std): + return make_libstd_and_allocator_ccinfo( + cc_toolchain = cc_toolchain, + feature_configuration = feature_configuration, + label = label, + actions = actions, + experimental_link_std_dylib = experimental_link_std_dylib, + rust_std = rust_std, + allocator_library = allocator_library, + std = std, + ) + + def make_local_ccinfo(allocator_library, std): + return make_ccinfo( + ctx.label, + ctx.actions, + struct(cc_info = allocator_library), + std, + ) # Include C++ toolchain files to ensure tools like 'ar' are available for cross-compilation - cc_toolchain, _ = find_cc_toolchain(ctx) all_files_depsets = [sysroot.all_files] if cc_toolchain and cc_toolchain.all_files: all_files_depsets.append(cc_toolchain.all_files) @@ -752,7 +599,11 @@ def _rust_toolchain_impl(ctx): target_flag_value = target_json.path if target_json else target_triple.str, target_json = target_json, target_os = target_os, + target_abi = target_abi, target_triple = target_triple, + linker = sysroot.linker, + linker_preference = linker_preference, + linker_type = ctx.attr.linker_type or None, # Experimental and incompatible flags _rename_first_party_crates = rename_first_party_crates, @@ -775,11 +626,6 @@ def _rust_toolchain_impl(ctx): make_variable_info, ] -def _experimental_link_std_dylib(ctx): - return not is_exec_configuration(ctx) and \ - ctx.attr.experimental_link_std_dylib[BuildSettingInfo].value and \ - ctx.attr.rust_std[rust_common.stdlib_info].std_dylib != None - rust_toolchain = rule( implementation = _rust_toolchain_impl, fragments = ["cpp"], @@ -869,6 +715,22 @@ rust_toolchain = rule( doc = "Target that provides allocator functions for when a global allocator is present.", default = "@rules_rust//ffi/cc/global_allocator_library", ), + "linker": attr.label( + doc = "The label to an explicit linker to use (e.g. `rust-lld`, `ld`, `link-ld.exe`, etc...)", + # `target` cfg is used so a linker can be chose based on the target + # platform. Linker binaries are still required to be runnable in the + # `exec` configuration. + cfg = "target", + allow_single_file = True, + ), + "linker_preference": attr.string( + doc = "The preferred linker to use. If unspecified, `cc` is preferred and `rust` is used as a fallback whenever `linker` is provided.", + values = ["cc", "rust"], + ), + "linker_type": attr.string( + doc = "The type of linker invocation: 'direct' (ld, rust-lld) or 'indirect' (via compiler like clang/gcc). If unset, defaults based on linker_preference.", + values = ["direct", "indirect"], + ), "llvm_cov": attr.label( doc = "The location of the `llvm-cov` binary. Can be a direct source or a filegroup containing one item. If None, rust code is not instrumented for coverage.", allow_single_file = True, @@ -991,6 +853,9 @@ rust_toolchain = rule( default = Label("//rust/settings:incompatible_do_not_include_data_in_compile_data"), doc = "Label to a boolean build setting that controls whether to include data files in compile_data.", ), + "_linker_preference": attr.label( + default = Label("//rust/settings:toolchain_linker_preference"), + ), "_no_std": attr.label( default = Label("//rust/settings:no_std"), ), @@ -1012,7 +877,7 @@ rust_toolchain = rule( ), }, toolchains = [ - "@bazel_tools//tools/cpp:toolchain_type", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], doc = """Declares a Rust toolchain for use. diff --git a/test/integration/cc_common_link/unit/cc_common_link_test.bzl b/test/integration/cc_common_link/unit/cc_common_link_test.bzl index bab3588408..7a1c52403f 100644 --- a/test/integration/cc_common_link/unit/cc_common_link_test.bzl +++ b/test/integration/cc_common_link/unit/cc_common_link_test.bzl @@ -315,6 +315,10 @@ def _codegen_units_test_targets(): name = "mock_rustdoc", out = "mock_rustdoc.exe", ) + write_file( + name = "mock_rust-lld", + out = "mock_rust-lld.exe", + ) rust_toolchain( name = "codegen_units_toolchain_impl", binary_ext = "", @@ -324,6 +328,8 @@ def _codegen_units_test_targets(): rust_doc = ":mock_rustdoc", rust_std = ":std_libs", rustc = ":mock_rustc", + linker = ":mock_rust-lld", + linker_type = "direct", staticlib_ext = ".a", stdlib_linkflags = [], extra_rustc_flags = ["-Ccodegen-units=%s" % _TOOLCHAIN_EXTRA_RUSTC_FLAGS_CODEGEN_UNITS], diff --git a/test/toolchain/toolchain_test.bzl b/test/toolchain/toolchain_test.bzl index 8eb59de8e4..3140064b32 100644 --- a/test/toolchain/toolchain_test.bzl +++ b/test/toolchain/toolchain_test.bzl @@ -172,6 +172,12 @@ def _define_targets(): content = [], is_executable = True, ) + write_file( + name = "mock_rust_lld", + out = "mock_rust_lld.exe", + content = [], + is_executable = True, + ) rust_toolchain( name = "rust_extra_flags_toolchain", @@ -182,6 +188,7 @@ def _define_targets(): rust_doc = ":mock_rustdoc", rust_std = ":std_libs", rustc = ":mock_rustc", + linker = ":mock_rust_lld", staticlib_ext = ".a", stdlib_linkflags = [], extra_rustc_flags = [TOOLCHAIN_FLAG], diff --git a/test/unit/native_deps/native_deps_test.bzl b/test/unit/native_deps/native_deps_test.bzl index ab97be6598..eed73ba262 100644 --- a/test/unit/native_deps/native_deps_test.bzl +++ b/test/unit/native_deps/native_deps_test.bzl @@ -16,13 +16,70 @@ load( def _get_toolchain(ctx): return ctx.attr._toolchain[platform_common.ToolchainInfo] +def _get_bin_dir_from_action(action): + """Extract the bin directory from an action's outputs. + + This handles config transitions that add suffixes like -ST-. + + Args: + action: The action to extract the bin directory from. + + Returns: + The bin directory path as a string. + """ + bin_dir = action.outputs.to_list()[0].dirname + if "/bin/" in bin_dir: + bin_dir = bin_dir.split("/bin/")[0] + "/bin" + return bin_dir + def _get_darwin_component(arg): - # path/to/darwin_x86_64-fastbuild-fastbuild/package -> darwin_x86_64-fastbuild + """Extract darwin component from a path. + + Args: + arg: Path like "path/to/darwin_x86_64-fastbuild-ST-abc123/package" + + Returns: + Darwin component like "darwin" (ignoring arch and compilation mode) + """ + + # path/to/darwin_x86_64-fastbuild-ST-abc123/package -> darwin_x86_64-fastbuild-ST-abc123 darwin_component = [x for x in arg.split("/") if x.startswith("darwin")][0] - # darwin_x86_64-fastbuild -> darwin + # darwin_x86_64-fastbuild-ST-abc123 -> darwin return darwin_component.split("-")[0] +def _assert_bin_dir_structure(env, ctx, bin_dir, toolchain): + """Validate bin_dir structure, ignoring ST-{hash} suffix from config transitions. + + Args: + env: The analysis test environment + ctx: The test context + bin_dir: The bin directory path to validate + toolchain: The toolchain info + """ + compilation_mode = ctx.var["COMPILATION_MODE"] + + # bin_dir should be like: bazel-out/{platform}-{mode}[-ST-{hash}]/bin + asserts.true(env, bin_dir.startswith("bazel-out/"), "bin_dir should start with bazel-out/") + asserts.true(env, bin_dir.endswith("/bin"), "bin_dir should end with /bin") + + # Validate it contains compilation mode (ignoring potential ST-{hash}) + bin_dir_components = bin_dir.split("/")[1] # Get the platform-mode component + asserts.true( + env, + compilation_mode in bin_dir_components, + "bin_dir should contain compilation mode: expected '{}' in '{}'".format(compilation_mode, bin_dir_components), + ) + + # For Darwin platforms, validate darwin component + if toolchain.target_os in ["macos", "darwin"] and "darwin" in bin_dir: + darwin_component = _get_darwin_component(bin_dir) + asserts.true( + env, + darwin_component.startswith("darwin"), + "darwin component should start with 'darwin', got '{}'".format(darwin_component), + ) + def _rlib_has_no_native_libs_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) @@ -129,60 +186,86 @@ def _extract_linker_args(argv): ) ] -def _get_workspace_prefix(ctx): - return "" if ctx.workspace_name in ["rules_rust", "_main"] else "external/rules_rust/" - -def _bin_has_native_dep_and_alwayslink_test_impl(ctx): +def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) action = tut.actions[0] toolchain = _get_toolchain(ctx) - compilation_mode = ctx.var["COMPILATION_MODE"] - workspace_prefix = _get_workspace_prefix(ctx) link_args = _extract_linker_args(action.argv) + bin_dir = _get_bin_dir_from_action(action) + + # Validate bin_dir structure (ignoring ST-{hash} suffix from config transitions) + _assert_bin_dir_structure(env, ctx, bin_dir, toolchain) + if toolchain.target_os in ["macos", "darwin"]: - darwin_component = _get_darwin_component(link_args[-1]) - want = [ - "-lstatic=native_dep", - "-lnative_dep", - "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(darwin_component, compilation_mode, workspace_prefix), - ] + if use_cc_linker: + # When using CC linker, args are passed with -Wl, prefix as separate arguments + want = [ + "-lstatic=native_dep", + "-lnative_dep", + "-Wl,-force_load", + "-Wl,{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), + ] + else: + # When using rust-lld directly, args are passed without prefix as separate arguments + want = [ + "-lstatic=native_dep", + "-lnative_dep", + "-force_load", + "{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), + ] assert_list_contains_adjacent_elements(env, link_args, want) elif toolchain.target_os == "windows": if toolchain.target_triple.abi == "msvc": want = [ "-lstatic=native_dep", "native_dep.lib", - "/WHOLEARCHIVE:bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), + "/WHOLEARCHIVE:{}/test/unit/native_deps/alwayslink.lo.lib".format(bin_dir), ] - else: + elif use_cc_linker: want = [ "-lstatic=native_dep", "native_dep.lib", "-Wl,--whole-archive", - "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), + "{}/test/unit/native_deps/alwayslink.lo.lib".format(bin_dir), "-Wl,--no-whole-archive", ] + else: + want = [ + "-lstatic=native_dep", + "native_dep.lib", + "--whole-archive", + "{}/test/unit/native_deps/alwayslink.lo.lib".format(bin_dir), + "--no-whole-archive", + ] elif toolchain.target_arch == "s390x": want = [ "-lstatic=native_dep", "link-arg=-Wl,--whole-archive", - "link-arg=bazel-out/s390x-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(compilation_mode, workspace_prefix), + "link-arg={}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), "link-arg=-Wl,--no-whole-archive", ] - else: + elif use_cc_linker: want = [ "-lstatic=native_dep", "-lnative_dep", "-Wl,--whole-archive", - "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(compilation_mode, workspace_prefix), + "{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), "-Wl,--no-whole-archive", ] + else: + want = [ + "-lstatic=native_dep", + "-lnative_dep", + "--whole-archive", + "{}/test/unit/native_deps/libalwayslink.lo".format(bin_dir), + "--no-whole-archive", + ] assert_list_contains_adjacent_elements(env, link_args, want) return analysistest.end(env) -def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx): +def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): toolchain = _get_toolchain(ctx) env = analysistest.begin(ctx) @@ -190,48 +273,79 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx): action = tut.actions[0] linker_args = _extract_linker_args(action.argv) + bin_dir = _get_bin_dir_from_action(action) + + # Validate bin_dir structure (ignoring ST-{hash} suffix from config transitions) + _assert_bin_dir_structure(env, ctx, bin_dir, toolchain) - toolchain = _get_toolchain(ctx) compilation_mode = ctx.var["COMPILATION_MODE"] - workspace_prefix = _get_workspace_prefix(ctx) pic_suffix = _get_pic_suffix(ctx, compilation_mode) + if toolchain.target_os in ["macos", "darwin"]: - darwin_component = _get_darwin_component(linker_args[-1]) - want = [ - "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), - "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(darwin_component, compilation_mode, workspace_prefix, pic_suffix), - ] + if use_cc_linker: + # When using CC linker, args are passed with -Wl, prefix as separate arguments + want = [ + "-lstatic=native_dep{}".format(pic_suffix), + "-lnative_dep{}".format(pic_suffix), + "-Wl,-force_load", + "-Wl,{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), + ] + else: + # When using rust-lld directly, args are passed without prefix as separate arguments + want = [ + "-lstatic=native_dep{}".format(pic_suffix), + "-lnative_dep{}".format(pic_suffix), + "-force_load", + "{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), + ] elif toolchain.target_os == "windows": if toolchain.target_triple.abi == "msvc": want = [ "-lstatic=native_dep", "native_dep.lib", - "/WHOLEARCHIVE:bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), + "/WHOLEARCHIVE:{}/test/unit/native_deps/alwayslink.lo.lib".format(bin_dir), ] - else: + elif use_cc_linker: want = [ "-lstatic=native_dep", "native_dep.lib", "-Wl,--whole-archive", - "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), + "{}/test/unit/native_deps/alwayslink.lo.lib".format(bin_dir), "-Wl,--no-whole-archive", ] + else: + want = [ + "-lstatic=native_dep", + "native_dep.lib", + "--whole-archive", + "{}/test/unit/native_deps/alwayslink.lo.lib".format(bin_dir), + "--no-whole-archive", + ] elif toolchain.target_arch == "s390x": want = [ "-lstatic=native_dep{}".format(pic_suffix), "link-arg=-Wl,--whole-archive", - "link-arg=bazel-out/s390x-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(compilation_mode, workspace_prefix, pic_suffix), + "link-arg={}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), "link-arg=-Wl,--no-whole-archive", ] - else: + elif use_cc_linker: + # CC linker uses -Wl, prefix but arguments are separate want = [ "-lstatic=native_dep{}".format(pic_suffix), "-lnative_dep{}".format(pic_suffix), "-Wl,--whole-archive", - "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(compilation_mode, workspace_prefix, pic_suffix), + "{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), "-Wl,--no-whole-archive", ] + else: + # rust-lld doesn't use -Wl, prefix, so flags and path are separate + want = [ + "-lstatic=native_dep{}".format(pic_suffix), + "-lnative_dep{}".format(pic_suffix), + "--whole-archive", + "{}/test/unit/native_deps/libalwayslink{}.lo".format(bin_dir, pic_suffix), + "--no-whole-archive", + ] assert_list_contains_adjacent_elements(env, linker_args, want) return analysistest.end(env) @@ -252,14 +366,72 @@ proc_macro_has_native_libs_test = analysistest.make(_proc_macro_has_native_libs_ "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), }) bin_has_native_libs_test = analysistest.make(_bin_has_native_libs_test_impl, attrs = { + "_linker_preference": attr.label(default = Label("//rust/settings:toolchain_linker_preference")), "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), }) -bin_has_native_dep_and_alwayslink_test = analysistest.make(_bin_has_native_dep_and_alwayslink_test_impl, attrs = { - "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), -}) -cdylib_has_native_dep_and_alwayslink_test = analysistest.make(_cdylib_has_native_dep_and_alwayslink_test_impl, attrs = { - "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), -}) + +def _bin_has_native_dep_and_alwayslink_rust_linker_test_impl(ctx): + return _bin_has_native_dep_and_alwayslink_test_impl(ctx, False) + +bin_has_native_dep_and_alwayslink_rust_linker_test = analysistest.make( + _bin_has_native_dep_and_alwayslink_rust_linker_test_impl, + attrs = { + "_linker_preference": attr.label(default = Label("//rust/settings:toolchain_linker_preference")), + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), + }, + config_settings = { + str(Label("//rust/settings:experimental_use_platform_abi_settings")): True, + str(Label("//rust/settings:platform_linux_musl")): True, + str(Label("//rust/settings:toolchain_linker_preference")): "rust", + # Coverage is not supported on musl so it should be disabled for these targets. + "//command_line_option:collect_code_coverage": False, + }, +) + +def _bin_has_native_dep_and_alwayslink_cc_linker_test_impl(ctx): + return _bin_has_native_dep_and_alwayslink_test_impl(ctx, True) + +bin_has_native_dep_and_alwayslink_cc_linker_test = analysistest.make( + _bin_has_native_dep_and_alwayslink_cc_linker_test_impl, + attrs = { + "_linker_preference": attr.label(default = Label("//rust/settings:toolchain_linker_preference")), + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), + }, + config_settings = { + str(Label("//rust/settings:toolchain_linker_preference")): "cc", + }, +) + +def _cdylib_has_native_dep_and_alwayslink_rust_linker_test_impl(ctx): + return _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, False) + +cdylib_has_native_dep_and_alwayslink_rust_linker_test = analysistest.make( + _cdylib_has_native_dep_and_alwayslink_rust_linker_test_impl, + attrs = { + "_linker_preference": attr.label(default = Label("//rust/settings:toolchain_linker_preference")), + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), + }, + config_settings = { + str(Label("//rust/settings:experimental_use_platform_abi_settings")): True, + str(Label("//rust/settings:platform_linux_musl")): True, + str(Label("//rust/settings:toolchain_linker_preference")): "rust", + "//command_line_option:collect_code_coverage": False, + }, +) + +def _cdylib_has_native_dep_and_alwayslink_cc_linker_test_impl(ctx): + return _cdylib_has_native_dep_and_alwayslink_test_impl(ctx, True) + +cdylib_has_native_dep_and_alwayslink_cc_linker_test = analysistest.make( + _cdylib_has_native_dep_and_alwayslink_cc_linker_test_impl, + attrs = { + "_linker_preference": attr.label(default = Label("//rust/settings:toolchain_linker_preference")), + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), + }, + config_settings = { + str(Label("//rust/settings:toolchain_linker_preference")): "cc", + }, +) def _native_dep_test(): rust_library( @@ -323,6 +495,9 @@ def _native_dep_test(): deps = [":native_dep", ":alwayslink"], ) + # Note: cdylib tests use the same target but we'll force the setting via config_setting + # The test will check the _linker_preference build setting to determine expected behavior + rlib_has_no_native_libs_test( name = "rlib_has_no_native_libs_test", target_under_test = ":rlib_has_no_native_dep", @@ -343,12 +518,20 @@ def _native_dep_test(): name = "bin_has_native_libs_test", target_under_test = ":bin_has_native_dep", ) - bin_has_native_dep_and_alwayslink_test( - name = "bin_has_native_dep_and_alwayslink_test", + bin_has_native_dep_and_alwayslink_rust_linker_test( + name = "bin_has_native_dep_and_alwayslink_rust_linker_test", + target_under_test = ":bin_has_native_dep_and_alwayslink", + ) + bin_has_native_dep_and_alwayslink_cc_linker_test( + name = "bin_has_native_dep_and_alwayslink_cc_linker_test", target_under_test = ":bin_has_native_dep_and_alwayslink", ) - cdylib_has_native_dep_and_alwayslink_test( - name = "cdylib_has_native_dep_and_alwayslink_test", + cdylib_has_native_dep_and_alwayslink_rust_linker_test( + name = "cdylib_has_native_dep_and_alwayslink_rust_linker_test", + target_under_test = ":cdylib_has_native_dep_and_alwayslink", + ) + cdylib_has_native_dep_and_alwayslink_cc_linker_test( + name = "cdylib_has_native_dep_and_alwayslink_cc_linker_test", target_under_test = ":cdylib_has_native_dep_and_alwayslink", ) @@ -473,10 +656,12 @@ def native_deps_test_suite(name): name = name, tests = [ ":bin_has_additional_deps_test", - ":bin_has_native_dep_and_alwayslink_test", + ":bin_has_native_dep_and_alwayslink_rust_linker_test", + ":bin_has_native_dep_and_alwayslink_cc_linker_test", ":bin_has_native_libs_test", ":cdylib_has_additional_deps_test", - ":cdylib_has_native_dep_and_alwayslink_test", + ":cdylib_has_native_dep_and_alwayslink_rust_linker_test", + ":cdylib_has_native_dep_and_alwayslink_cc_linker_test", ":cdylib_has_native_libs_test", ":lib_has_no_additional_deps_test", ":native_linkopts_propagate_test", diff --git a/test/unit/pipelined_compilation/wrap.bzl b/test/unit/pipelined_compilation/wrap.bzl index 4a0bd994b6..f24a0e421a 100644 --- a/test/unit/pipelined_compilation/wrap.bzl +++ b/test/unit/pipelined_compilation/wrap.bzl @@ -104,6 +104,9 @@ wrap = rule( cfg = "exec", ), }, - toolchains = ["@rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type"], + toolchains = [ + "@rules_rust//rust:toolchain", + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), + ], fragments = ["cpp"], ) diff --git a/test/unit/toolchain/toolchain_test.bzl b/test/unit/toolchain/toolchain_test.bzl index a0a0258b87..67232aadef 100644 --- a/test/unit/toolchain/toolchain_test.bzl +++ b/test/unit/toolchain/toolchain_test.bzl @@ -118,6 +118,13 @@ def _define_test_targets(): is_executable = True, ) + write_file( + name = "mock_rust_lld", + out = "mock_rust_lld.exe", + content = [], + is_executable = True, + ) + write_file( name = "mock_rustdoc", out = "mock_rustdoc.exe", @@ -133,6 +140,7 @@ def _define_test_targets(): rust_doc = ":mock_rustdoc", rust_std = ":std_libs", rustc = ":mock_rustc", + linker = ":mock_rust_lld", staticlib_ext = ".a", stdlib_linkflags = [], target_triple = "toolchain-test-triple", @@ -148,6 +156,7 @@ def _define_test_targets(): rust_doc = ":mock_rustdoc", rust_std = ":std_libs", rustc = ":mock_rustc", + linker = ":mock_rust_lld", staticlib_ext = ".a", stdlib_linkflags = [], target_json = encoded_target_json, @@ -161,6 +170,7 @@ def _define_test_targets(): rust_doc = ":mock_rustdoc", rust_std = ":std_libs", rustc = ":mock_rustc", + linker = ":mock_rust_lld", staticlib_ext = ".a", stdlib_linkflags = [], target_json = json.encode( @@ -182,6 +192,7 @@ def _define_test_targets(): rust_doc = ":mock_rustdoc", rust_std = ":std_libs", rustc = ":mock_rustc", + linker = ":mock_rust_lld", staticlib_ext = ".a", stdlib_linkflags = ["test:$(location :stdlib_srcs)", "test:sysroot=$(RUST_SYSROOT)"], extra_rustc_flags = ["extra_rustc_flags:$(location :stdlib_srcs)", "extra_rustc_flags:sysroot=$(RUST_SYSROOT)"], diff --git a/util/process_wrapper/BUILD.bazel b/util/process_wrapper/BUILD.bazel index a946776e41..af56264e4c 100644 --- a/util/process_wrapper/BUILD.bazel +++ b/util/process_wrapper/BUILD.bazel @@ -1,8 +1,7 @@ load("@bazel_skylib//lib:selects.bzl", "selects") -load("//rust:defs.bzl", "rust_test") # buildifier: disable=bzl-visibility -load("//rust/private:rust.bzl", "rust_binary_without_process_wrapper") +load("//rust/private:rust.bzl", "rust_binary_without_process_wrapper", "rust_test_without_process_wrapper_test") load("//util/process_wrapper/private:bootstrap_process_wrapper.bzl", "bootstrap_process_wrapper") config_setting( @@ -50,7 +49,7 @@ rust_binary_without_process_wrapper( ], ) -rust_test( +rust_test_without_process_wrapper_test( name = "process_wrapper_test", crate = ":process_wrapper", edition = "2018",