From 6a36c15349f7c314dcac64a953717dd596bef650 Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Mon, 1 Dec 2025 07:54:26 -0800 Subject: [PATCH 1/6] Moved `rust_allocator_libraries` to it's own `.bzl` file --- ffi/rs/BUILD.bazel | 2 +- rust/private/rust.bzl | 62 +---- rust/private/rust_allocator_libraries.bzl | 298 ++++++++++++++++++++++ rust/toolchain.bzl | 260 ++----------------- 4 files changed, 328 insertions(+), 294 deletions(-) create mode 100644 rust/private/rust_allocator_libraries.bzl 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/private/rust.bzl b/rust/private/rust.bzl index 4b457ccf79..da0d4a81bd 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -20,8 +20,6 @@ load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") load("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common") load( "//rust/private:providers.bzl", - "AllocatorLibrariesImplInfo", - "AllocatorLibrariesInfo", "BuildInfo", "CrateGroupInfo", "CrateInfo", @@ -46,6 +44,10 @@ load( "transform_deps", "transform_sources", ) +load( + ":rust_allocator_libraries.bzl", + "RUSTC_ALLOCATOR_LIBRARIES_ATTRS", +) # TODO(marco): Separate each rule into its own file. @@ -650,19 +652,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("""\ @@ -836,7 +825,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( @@ -1683,47 +1672,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..a7dc1cb0aa --- /dev/null +++ b/rust/private/rust_allocator_libraries.bzl @@ -0,0 +1,298 @@ +"""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. + 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 + +# 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 = Label("//ffi/rs:default_allocator_libraries"), + providers = [AllocatorLibrariesInfo], + ), +} + +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", + ], +) diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index 4d9de9925a..3360815272 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,232 +132,6 @@ 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 _symlink_sysroot_tree(ctx, name, target): """Generate a set of symlinks to files from another target @@ -701,15 +478,26 @@ def _rust_toolchain_impl(ctx): )) cc_toolchain, feature_configuration = find_cc_toolchain(ctx) 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) From f3e784ff1aef1cf34b443bb9d8d518be8877d6b8 Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Fri, 21 Nov 2025 12:21:52 -0800 Subject: [PATCH 2/6] Add support for optional cc_toolchain --- .bazelrc | 4 + cargo/Cargo.lock | 580 ++++++++++++++++++ cargo/private/BUILD.bazel | 30 + cargo/private/cargo_build_script.bzl | 42 +- cargo/private/no_binary.rs | 6 + extensions/bindgen/private/bindgen.bzl | 2 +- extensions/prost/private/prost.bzl | 2 +- extensions/protobuf/proto.bzl | 4 +- .../private/wasm_bindgen_test.bzl | 5 +- rust/private/clippy.bzl | 2 +- rust/private/repository_utils.bzl | 105 +++- rust/private/rust.bzl | 111 +++- rust/private/rust_allocator_libraries.bzl | 29 +- rust/private/rustc.bzl | 415 +++++++++---- rust/private/rustdoc.bzl | 2 +- rust/private/rustdoc_test.bzl | 2 +- rust/private/unpretty.bzl | 2 +- rust/private/utils.bzl | 12 +- rust/repositories.bzl | 9 + rust/settings/BUILD.bazel | 3 + rust/settings/settings.bzl | 14 + rust/toolchain.bzl | 97 ++- .../unit/cc_common_link_test.bzl | 6 + test/toolchain/toolchain_test.bzl | 7 + test/unit/native_deps/native_deps_test.bzl | 291 +++++++-- test/unit/pipelined_compilation/wrap.bzl | 5 +- test/unit/toolchain/toolchain_test.bzl | 11 + util/process_wrapper/BUILD.bazel | 5 +- 28 files changed, 1526 insertions(+), 277 deletions(-) create mode 100644 cargo/Cargo.lock create mode 100644 cargo/private/no_binary.rs diff --git a/.bazelrc b/.bazelrc index 35af4019d0..52fc278d16 100644 --- a/.bazelrc +++ b/.bazelrc @@ -49,6 +49,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/Cargo.lock b/cargo/Cargo.lock new file mode 100644 index 0000000000..a87f55badf --- /dev/null +++ b/cargo/Cargo.lock @@ -0,0 +1,580 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "cargo-util-schemas" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161d4f7828830c7893180528e121e701bb8070b80c0f97f793a930d285b315a6" +dependencies = [ + "semver", + "serde", + "serde-untagged", + "serde-value", + "thiserror", + "toml", + "unicode-xid", + "url", +] + +[[package]] +name = "cargo_toml" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88da5a13c620b4ca0078845707ea9c3faf11edbc3ffd8497d11d686211cd1ac0" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "cargo_toml_info" +version = "0.1.0" +dependencies = [ + "cargo_toml", +] + +[[package]] +name = "cargo_toml_variable_extractor" +version = "0.1.0" +dependencies = [ + "cargo-util-schemas", + "pathdiff", + "semver", + "toml", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "pathdiff" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3bf70094d203e07844da868b634207e71bfab254fe713171fae9a6e751ccf31" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] 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 3572675a0c..13dd45027a 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") @@ -373,8 +372,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: @@ -423,18 +420,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_ar_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_ar_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( @@ -515,6 +519,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, ) @@ -737,6 +744,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"), ), @@ -744,7 +766,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/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 e81d005ffe..e29bc3bb76 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -57,8 +57,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", ], @@ -74,21 +72,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( @@ -293,6 +322,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", @@ -329,6 +360,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, @@ -348,6 +380,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. @@ -379,6 +412,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), @@ -394,6 +433,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, @@ -478,7 +519,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: @@ -486,6 +527,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 @@ -501,7 +543,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 @@ -1000,18 +1042,6 @@ 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, @@ -1019,14 +1049,33 @@ def BUILD_for_toolchain_hub( target_settings, 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 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_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_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( @@ -1057,7 +1106,7 @@ 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, ), "toolchain_labels": attr.string_dict( diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index da0d4a81bd..7313c8f075 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -17,17 +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", + ":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", @@ -44,10 +53,6 @@ load( "transform_deps", "transform_sources", ) -load( - ":rust_allocator_libraries.bzl", - "RUSTC_ALLOCATOR_LIBRARIES_ATTRS", -) # TODO(marco): Separate each rule into its own file. @@ -931,7 +936,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. @@ -1029,7 +1034,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, @@ -1078,7 +1083,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, @@ -1134,7 +1139,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. @@ -1218,7 +1223,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. @@ -1340,38 +1345,84 @@ def _common_attrs_for_binary_without_process_wrapper(attrs): return new_attr +_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), 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, + fragments = ["cpp"], + test = True, + toolchains = [ + str(Label("//rust:toolchain_type")), + config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False), ], ) @@ -1408,7 +1459,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. diff --git a/rust/private/rust_allocator_libraries.bzl b/rust/private/rust_allocator_libraries.bzl index a7dc1cb0aa..ef344e1d47 100644 --- a/rust/private/rust_allocator_libraries.bzl +++ b/rust/private/rust_allocator_libraries.bzl @@ -23,8 +23,8 @@ def _ltl(library, actions, cc_toolchain, feature_configuration): 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. + 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. @@ -38,6 +38,7 @@ def _ltl(library, actions, cc_toolchain, feature_configuration): ) def make_libstd_and_allocator_ccinfo( + *, cc_toolchain, feature_configuration, label, @@ -257,23 +258,25 @@ RUSTC_ALLOCATOR_LIBRARIES_ATTRS = { } 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, - ) + 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_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"), + 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 @@ -293,6 +296,6 @@ rust_allocator_libraries = 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/rustc.bzl b/rust/private/rustc.bzl index f2fe58c2c5..d55d94b833 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -209,7 +209,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 @@ -403,12 +403,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. @@ -418,50 +419,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. @@ -469,12 +534,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. @@ -675,7 +740,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() @@ -1036,6 +1103,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 @@ -1048,7 +1119,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") @@ -1057,7 +1136,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) @@ -1251,7 +1342,10 @@ 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(feature_configuration = feature_configuration): + if not cc_toolchain or not _are_linkstamps_supported( + feature_configuration = feature_configuration, + has_grep_includes = hasattr(ctx.attr, "_use_grep_includes"), + ): linkstamps = depset([]) # Determine if the build is currently running with --stamp @@ -1789,8 +1883,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 @@ -1807,47 +1901,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( @@ -2169,72 +2267,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: @@ -2247,6 +2408,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"]: @@ -2254,15 +2416,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()] @@ -2277,20 +2435,35 @@ def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate args.add_all(linkstamp_outs, format_each = "-Clink-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). - runtime_libs = cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration) - args.add_all(runtime_libs, map_each = _get_dirname, format_each = "-Lnative=%s") - if include_link_flags: - args.add_all(runtime_libs, 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). - runtime_libs = cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration) - args.add_all(runtime_libs, map_each = _get_dirname, format_each = "-Lnative=%s") - if include_link_flags: - args.add_all(runtime_libs, map_each = get_lib_name, format_each = "-lstatic=%s") + 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_dirname, + format_each = "-Lnative=%s", + ) + 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_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 71305a752b..48930e5839 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -450,11 +450,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, @@ -532,6 +535,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, @@ -766,6 +770,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 @@ -790,6 +795,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) @@ -924,6 +930,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("/") @@ -942,6 +950,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 129131feb8..55338dd452 100644 --- a/rust/settings/BUILD.bazel +++ b/rust/settings/BUILD.bazel @@ -36,6 +36,7 @@ load( "rustfmt_toml", "third_party_dir", "toolchain_generated_sysroot", + "toolchain_linker_preference", "unpretty", "use_real_import_macro", ) @@ -130,6 +131,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 e1886923df..4e826621b0 100644 --- a/rust/settings/settings.bzl +++ b/rust/settings/settings.bzl @@ -247,6 +247,20 @@ 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 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 3360815272..cba11b94af 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -132,18 +132,26 @@ rust_stdlib_filegroup = rule( }, ) -def _symlink_sysroot_tree(ctx, name, target): +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, 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. @@ -202,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: @@ -216,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 @@ -264,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: @@ -287,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), @@ -302,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, @@ -386,6 +419,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 @@ -452,11 +486,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 @@ -472,11 +508,35 @@ 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) def make_ccinfo(label, actions, allocator_library, std): @@ -500,7 +560,6 @@ def _rust_toolchain_impl(ctx): ) # 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) @@ -520,6 +579,9 @@ def _rust_toolchain_impl(ctx): libstd_and_global_allocator_ccinfo = make_local_ccinfo(ctx.attr.global_allocator_library[CcInfo], "std"), nostd_and_global_allocator_ccinfo = make_local_ccinfo(ctx.attr.global_allocator_library[CcInfo], "no_std_with_alloc"), make_libstd_and_allocator_ccinfo = make_ccinfo, + linker = sysroot.linker, + linker_preference = linker_preference, + linker_type = ctx.attr.linker_type or None, llvm_cov = ctx.file.llvm_cov, llvm_profdata = ctx.file.llvm_profdata, llvm_lib = ctx.files.llvm_lib, @@ -543,6 +605,7 @@ 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, require_explicit_unstable_features = _require_explicit_unstable_features(ctx), @@ -567,11 +630,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"], @@ -661,6 +719,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, @@ -790,6 +864,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"), ), @@ -811,7 +888,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..37755a21c7 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,68 @@ 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: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: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 +491,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 +514,36 @@ 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", + # TODO: https://github.com/bazelbuild/rules_rust/issues/390 + # Without a cc toolchain static rust is unable to find gnu + # libraries. A favorable alternative would be to use musl + # but there are no constraints to define this. + target_compatible_with = select({ + "@platforms//os:linux": ["@platforms//:incompatible"], + "//conditions:default": [], + }), + ) + 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", + # TODO: https://github.com/bazelbuild/rules_rust/issues/390 + # Without a cc toolchain static rust is unable to find gnu + # libraries. A favorable alternative would be to use musl + # but there are no constraints to define this. + target_compatible_with = select({ + "@platforms//os:linux": ["@platforms//:incompatible"], + "//conditions:default": [], + }), + ) + 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 +668,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", From 57d3485998f0dc495b769e7ba7c3758612e6ce3c Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Thu, 27 Nov 2025 08:54:35 -0800 Subject: [PATCH 3/6] PR feedback --- rust/private/repository_utils.bzl | 48 +++++++++++++++---------------- rust/settings/settings.bzl | 9 ++++-- rust/toolchain.bzl | 40 +++++++++++++++++--------- 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl index e29bc3bb76..05b741947d 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -395,46 +395,46 @@ def BUILD_for_rust_toolchain( if stdlib_linkflags == None: stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(target_triple.system)]) - rustfmt_label = "None" + rustfmt_label = None if include_rustfmt: - rustfmt_label = "\"//:rustfmt_bin\"" - llvm_cov_label = "None" - llvm_profdata_label = "None" - llvm_lib_label = "None" + rustfmt_label = "//:rustfmt_bin" + llvm_cov_label = None + llvm_profdata_label = None + llvm_lib_label = None if include_llvm_tools: - llvm_cov_label = "\"//:llvm_cov_bin\"" - llvm_profdata_label = "\"//:llvm_profdata_bin\"" - llvm_lib_label = "\"//:llvm_lib\"" - allocator_library_label = "None" + llvm_cov_label = "//:llvm_cov_bin" + llvm_profdata_label = "//:llvm_profdata_bin" + llvm_lib_label = "//:llvm_lib" + allocator_library_label = None if allocator_library: - allocator_library_label = "\"{allocator_library}\"".format(allocator_library = allocator_library) - global_allocator_library_label = "None" + allocator_library_label = allocator_library + global_allocator_library_label = None if global_allocator_library: - global_allocator_library_label = "\"{global_allocator_library}\"".format(global_allocator_library = global_allocator_library) + global_allocator_library_label = global_allocator_library - linker_label = "None" - linker_type = "None" + linker_label = None + linker_type = None if include_linker: - linker_label = "\"//:rust-lld\"" - linker_type = "\"direct\"" + 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), staticlib_ext = system_to_staticlib_ext(target_triple.system), dylib_ext = system_to_dylib_ext(target_triple.system), - allocator_library = allocator_library_label, - global_allocator_library = global_allocator_library_label, + allocator_library = repr(allocator_library_label), + global_allocator_library = repr(global_allocator_library_label), stdlib_linkflags = stdlib_linkflags, default_edition = default_edition, exec_triple = exec_triple.str, target_triple = target_triple.str, - rustfmt_label = rustfmt_label, - 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, + rustfmt_label = repr(rustfmt_label), + llvm_cov_label = repr(llvm_cov_label), + llvm_profdata_label = repr(llvm_profdata_label), + llvm_lib_label = repr(llvm_lib_label), + linker_label = repr(linker_label), + linker_type = repr(linker_type), extra_rustc_flags = extra_rustc_flags, extra_exec_rustc_flags = extra_exec_rustc_flags, opt_level = opt_level, diff --git a/rust/settings/settings.bzl b/rust/settings/settings.bzl index 4e826621b0..671a451c4f 100644 --- a/rust/settings/settings.bzl +++ b/rust/settings/settings.bzl @@ -251,9 +251,12 @@ 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`. + - "rust": Use `rust_toolchain.linker` always (e.g., `rust-lld`). This uses rustc to invoke + the linker directly. + - "cc": Use the linker provided by the configured `cc_toolchain`. This uses rustc to invoke + the C++ toolchain's linker (e.g., `clang`, `gcc`, `link.exe`). + - "none": Default to `cc` being the preference and falling back to `rust` if no `cc_toolchain` + is available. """ string_flag( name = "toolchain_linker_preference", diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index cba11b94af..5af30df398 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -238,7 +238,7 @@ def _generate_sysroot( # Rustc sysroot_rustc = _symlink_sysroot_bin(ctx, name, "bin", rustc) - direct_files.extend([sysroot_rustc]) + direct_files.append(sysroot_rustc) # Rustc dependencies sysroot_rustc_lib = None @@ -248,31 +248,31 @@ def _generate_sysroot( # Rustdoc sysroot_rustdoc = _symlink_sysroot_bin(ctx, name, "bin", rustdoc) - direct_files.extend([sysroot_rustdoc]) + direct_files.append(sysroot_rustdoc) # Clippy sysroot_clippy = None if clippy: sysroot_clippy = _symlink_sysroot_bin(ctx, name, "bin", clippy) - direct_files.extend([sysroot_clippy]) + direct_files.append(sysroot_clippy) # Cargo sysroot_cargo = None if cargo: sysroot_cargo = _symlink_sysroot_bin(ctx, name, "bin", cargo) - direct_files.extend([sysroot_cargo]) + direct_files.append(sysroot_cargo) # Cargo-clippy sysroot_cargo_clippy = None if cargo_clippy: sysroot_cargo_clippy = _symlink_sysroot_bin(ctx, name, "bin", cargo_clippy) - direct_files.extend([sysroot_cargo_clippy]) + direct_files.append(sysroot_cargo_clippy) # Rustfmt sysroot_rustfmt = None if rustfmt: sysroot_rustfmt = _symlink_sysroot_bin(ctx, name, "bin", rustfmt) - direct_files.extend([sysroot_rustfmt]) + direct_files.append(sysroot_rustfmt) # Linker sysroot_linker = None @@ -292,8 +292,8 @@ def _generate_sysroot( 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]) + direct_files.append(sysroot_linker) + transitive_file_sets.append(sysroot_linker_files) # Llvm tools sysroot_llvm_tools = None @@ -720,11 +720,8 @@ rust_toolchain = rule( 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", + doc = "The label to an explicit linker to use (e.g. rust-lld, ld, link-ld.exe, etc.). Linker binaries must be runnable in the exec configuration, so cfg = \"exec\" is used. To choose a linker based on the target platform, use a select() when providing this attribute. The select() will be evaluated against the target platform before the exec transition is applied, allowing platform-specific linker selection while ensuring the selected linker is built for the exec platform.", + cfg = "exec", allow_single_file = True, ), "linker_preference": attr.string( @@ -935,5 +932,22 @@ it to the `"--extra_toolchains"` flag for Bazel, and it will be used. See `@rules_rust//rust:repositories.bzl` for examples of defining the `@rust_cpuX` repository \ with the actual binaries and libraries. + +To use a platform-specific linker, you can use a `select()` in the `linker` attribute: + +```python +rust_toolchain( + name = "rust_toolchain_impl", + # ... other attributes ... + linker = select({ + "@platforms//os:linux": "//tools:rust-lld-linux", + "@platforms//os:windows": "//tools:rust-lld-windows", + "//conditions:default": "//tools:rust-lld", + }), +) +``` + +The `select()` is evaluated against the target platform before the exec transition is applied, \ +allowing platform-specific linker selection while ensuring the selected linker is built for the exec platform. """, ) From a2ad88a0cc37c006adfa61bd05ed3b3b206c3ecc Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Tue, 2 Dec 2025 07:53:40 -0800 Subject: [PATCH 4/6] Added cross_compile examples --- .bazelci/presubmit.yml | 45 ++++- examples/cross_compile/.bazelrc | 77 +++++++++ examples/cross_compile/.gitignore | 2 + examples/cross_compile/BUILD.bazel | 54 ++++++ examples/cross_compile/Cargo.lock | 166 +++++++++++++++++++ examples/cross_compile/Cargo.toml | 7 + examples/cross_compile/MODULE.bazel | 50 ++++++ examples/cross_compile/WORKSPACE.bazel | 0 examples/cross_compile/platforms/BUILD.bazel | 65 ++++++++ examples/cross_compile/src/lib.rs | 3 + examples/cross_compile/src/main.rs | 3 + examples/cross_compile/tools/BUILD.bazel | 11 ++ examples/cross_compile/tools/infer_test.bzl | 52 ++++++ examples/cross_compile/tools/infer_tester.rs | 48 ++++++ rust/settings/settings.bzl | 15 ++ 15 files changed, 591 insertions(+), 7 deletions(-) create mode 100644 examples/cross_compile/.bazelrc create mode 100644 examples/cross_compile/.gitignore create mode 100644 examples/cross_compile/BUILD.bazel create mode 100644 examples/cross_compile/Cargo.lock create mode 100644 examples/cross_compile/Cargo.toml create mode 100644 examples/cross_compile/MODULE.bazel create mode 100644 examples/cross_compile/WORKSPACE.bazel create mode 100644 examples/cross_compile/platforms/BUILD.bazel create mode 100644 examples/cross_compile/src/lib.rs create mode 100644 examples/cross_compile/src/main.rs create mode 100644 examples/cross_compile/tools/BUILD.bazel create mode 100644 examples/cross_compile/tools/infer_test.bzl create mode 100644 examples/cross_compile/tools/infer_tester.rs diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 05469f3fe2..0737a82e75 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -723,19 +723,43 @@ tasks: working_directory: examples/compile_opt build_targets: - "//..." - # TODO: https://github.com/bazelbuild/rules_rust/issues/2075 - # cross_compile_zig: - # name: Cross compile example with Zig + # TODO: This test requires musl or for rustc to provide necessary gnu libraries for + # linking gnu libraries. + # https://github.com/bazelbuild/rules_rust/issues/390 + # cross_compile_linux: + # name: Cross compile example on Linux # platform: ubuntu2204 - # working_directory: examples/cross_compile_zig - # build_targets: + # working_directory: examples/cross_compile + # shell_commands: + # - sed -i 's/_FORCE_DISABLE_CC_TOOLCHAIN = False/_FORCE_DISABLE_CC_TOOLCHAIN = True/' ../../rust/private/utils.bzl + # test_flags: + # - "--keep_going" + # test_targets: # - "//..." + cross_compile_macos: + name: Cross compile example on MacOS + platform: macos_arm64 + working_directory: examples/cross_compile + shell_commands: + - sed -i '' 's/_FORCE_DISABLE_CC_TOOLCHAIN = False/_FORCE_DISABLE_CC_TOOLCHAIN = True/' ../../rust/private/utils.bzl + test_flags: + - "--keep_going" + test_targets: + - "//..." + cross_compile_windows: + name: Cross compile example on Windows + platform: windows + working_directory: examples/cross_compile + batch_commands: + - powershell -Command "(Get-Content rust/private/utils.bzl) -replace '_FORCE_DISABLE_CC_TOOLCHAIN = False', '_FORCE_DISABLE_CC_TOOLCHAIN = True' | Set-Content ../../rust/private/utils.bzl" + test_flags: + - "--keep_going" + test_targets: + - "//..." cross_compile_musl_macos_to_linux: name: Cross compile example Musl from macOS to Linux platform: macos_arm64 working_directory: examples/cross_compile_musl - build_targets: - - "//..." test_targets: - "//..." cross_compile_musl_linux_to_linux: @@ -775,6 +799,13 @@ tasks: - "//:all" test_targets: - "//..." + # TODO: https://github.com/bazelbuild/rules_rust/issues/2075 + # cross_compile_zig: + # name: Cross compile example with Zig + # platform: ubuntu2204 + # working_directory: examples/cross_compile_zig + # build_targets: + # - "//..." example_ffi_linux: platform: ubuntu2204 working_directory: examples/ffi diff --git a/examples/cross_compile/.bazelrc b/examples/cross_compile/.bazelrc new file mode 100644 index 0000000000..adc2d04be5 --- /dev/null +++ b/examples/cross_compile/.bazelrc @@ -0,0 +1,77 @@ +############################################################################### +## Bazel Configuration Flags +## +## `.bazelrc` is a Bazel configuration file. +## https://bazel.build/docs/best-practices#bazelrc-file +############################################################################### + +# https://bazel.build/reference/command-line-reference#flag--enable_platform_specific_config +common --enable_platform_specific_config + +# Enable the only currently supported report type +# https://bazel.build/reference/command-line-reference#flag--combined_report +coverage --combined_report=lcov + +# Avoid fully cached builds reporting no coverage and failing CI +# https://bazel.build/reference/command-line-reference#flag--experimental_fetch_all_coverage_outputs +coverage --experimental_fetch_all_coverage_outputs + +# Required for some of the tests +# https://bazel.build/reference/command-line-reference#flag--experimental_cc_shared_library +common --experimental_cc_shared_library + +############################################################################### +## Unique configuration groups +############################################################################### + +# Enable use of the nightly toolchains. +build:nightly --@rules_rust//rust/toolchain/channel=nightly + +# Enable rustfmt for all targets in the workspace +build:rustfmt --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect +build:rustfmt --output_groups=+rustfmt_checks + +# Enable clippy for all targets in the workspace +build:clippy --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect +build:clippy --output_groups=+clippy_checks + +# Enable unpretty for all targets in the workspace +build:unpretty --aspects=@rules_rust//rust:defs.bzl%rust_unpretty_aspect +build:unpretty --output_groups=+rust_unpretty + +# `unpretty` requires the nightly toolchain. See tracking issue: +# https://github.com/rust-lang/rust/issues/43364 +build:unpretty --config=nightly + +############################################################################### +## Incompatibility flags +############################################################################### + +# https://github.com/bazelbuild/bazel/issues/8195 +build --incompatible_disallow_empty_glob=true + +# https://github.com/bazelbuild/bazel/issues/12821 +build --nolegacy_external_runfiles + +# https://github.com/bazelbuild/bazel/issues/23043. +build --incompatible_autoload_externally= + +############################################################################### +## Bzlmod +############################################################################### + +# A configuration for disabling bzlmod. +common:no-bzlmod --noenable_bzlmod --enable_workspace + +# Disable the bzlmod lockfile, so we don't accidentally commit MODULE.bazel.lock +common --lockfile_mode=off + +############################################################################### +## Custom user flags +## +## This should always be the last thing in the `.bazelrc` file to ensure +## consistent behavior when setting flags in that file as `.bazelrc` files are +## evaluated top to bottom. +############################################################################### + +try-import %workspace%/user.bazelrc diff --git a/examples/cross_compile/.gitignore b/examples/cross_compile/.gitignore new file mode 100644 index 0000000000..2a3bb2d902 --- /dev/null +++ b/examples/cross_compile/.gitignore @@ -0,0 +1,2 @@ +/bazel-* +user.bazelrc diff --git a/examples/cross_compile/BUILD.bazel b/examples/cross_compile/BUILD.bazel new file mode 100644 index 0000000000..c4914a2ec7 --- /dev/null +++ b/examples/cross_compile/BUILD.bazel @@ -0,0 +1,54 @@ +load("@rules_rust//rust:rust_binary.bzl", "rust_binary") +load("@rules_rust//rust:rust_library.bzl", "rust_library") +load("//tools:infer_test.bzl", "infer_test") + +PLATFORMS = [ + "aarch64-apple-darwin", + "aarch64-pc-windows-msvc", + "aarch64-unknown-linux-musl", + "wasm32-wasip1", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-musl", +] + +rust_library( + name = "secrets", + srcs = ["src/lib.rs"], + edition = "2024", +) + +[ + rust_binary( + name = "cross_compile_{}".format(platform), + srcs = ["src/main.rs"], + edition = "2024", + platform = "//platforms:{}".format(platform), + # These targets are tagged as manual to allow the `target_compatible_with` + # attribute of `infer_test` to determine what should and shouldn't be built + # as not all platforms are buildable on all other platforms. + tags = ["manual"], + deps = [":secrets"], + ) + for platform in PLATFORMS +] + +[ + infer_test( + name = "cross_compile_{}_infer_test".format(platform), + file = ":cross_compile_{}".format(platform), + target_compatible_with = { + "x86_64-pc-windows-msvc": ["@platforms//os:windows"], + "aarch64-pc-windows-msvc": ["@platforms//os:windows"], + "aarch64-apple-darwin": ["@platforms//os:macos"], + }.get(platform, []), + type = { + "aarch64-apple-darwin": "application/x-mach-binary", + "aarch64-pc-windows-msvc": "application/vnd.microsoft.portable-executable", + "aarch64-unknown-linux-musl": "application/x-executable", + "wasm32-wasip1": "application/wasm", + "x86_64-pc-windows-msvc": "application/vnd.microsoft.portable-executable", + "x86_64-unknown-linux-musl": "application/x-executable", + }.get(platform, platform), + ) + for platform in PLATFORMS +] diff --git a/examples/cross_compile/Cargo.lock b/examples/cross_compile/Cargo.lock new file mode 100644 index 0000000000..4431d55580 --- /dev/null +++ b/examples/cross_compile/Cargo.lock @@ -0,0 +1,166 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cross_compile_example" +version = "0.1.0" +dependencies = [ + "infer", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] diff --git a/examples/cross_compile/Cargo.toml b/examples/cross_compile/Cargo.toml new file mode 100644 index 0000000000..3ec9151df0 --- /dev/null +++ b/examples/cross_compile/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross_compile_example" +version = "0.1.0" +edition = "2024" + +[dependencies] +infer = "0.19.0" diff --git a/examples/cross_compile/MODULE.bazel b/examples/cross_compile/MODULE.bazel new file mode 100644 index 0000000000..d895de55d8 --- /dev/null +++ b/examples/cross_compile/MODULE.bazel @@ -0,0 +1,50 @@ +module( + name = "cross_compile_example", + version = "0.0.0", +) + +bazel_dep(name = "rules_rust", version = "0.0.0") +local_path_override( + module_name = "rules_rust", + path = "../..", +) + +bazel_dep(name = "platforms", version = "1.0.0") +bazel_dep(name = "bazel_skylib", version = "1.8.2") + +RUST_EDITION = "2024" + +RUST_VERSION = "1.91.0" + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain( + edition = RUST_EDITION, + extra_target_triples = [ + "aarch64-apple-darwin", + "aarch64-pc-windows-msvc", + "aarch64-unknown-linux-musl", + "wasm32-unknown-unknown", + "wasm32-wasip1", + "wasm32-wasip2", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-musl", + ], + versions = [RUST_VERSION], +) +use_repo(rust, "rust_toolchains") + +register_toolchains("@rust_toolchains//:all") + +crate = use_extension( + "@rules_rust//crate_universe:extensions.bzl", + "crate", +) +crate.from_cargo( + name = "crates_io", + cargo_lockfile = "//:Cargo.lock", + manifests = [ + "//:Cargo.toml", + ], +) +use_repo(crate, "crates_io") diff --git a/examples/cross_compile/WORKSPACE.bazel b/examples/cross_compile/WORKSPACE.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/cross_compile/platforms/BUILD.bazel b/examples/cross_compile/platforms/BUILD.bazel new file mode 100644 index 0000000000..3ff637eb4d --- /dev/null +++ b/examples/cross_compile/platforms/BUILD.bazel @@ -0,0 +1,65 @@ +package(default_visibility = ["//visibility:public"]) + +platform( + name = "aarch64-unknown-linux-gnu", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) + +platform( + name = "x86_64-unknown-linux-gnu", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) + +platform( + name = "aarch64-unknown-linux-musl", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) + +platform( + name = "x86_64-unknown-linux-musl", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) + +platform( + name = "aarch64-apple-darwin", + constraint_values = [ + "@platforms//os:macos", + "@platforms//cpu:aarch64", + ], +) + +platform( + name = "x86_64-pc-windows-msvc", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], +) + +platform( + name = "aarch64-pc-windows-msvc", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:aarch64", + ], +) + +platform( + name = "wasm32-wasip1", + constraint_values = [ + "@platforms//os:none", + "@platforms//cpu:wasm32", + ], +) diff --git a/examples/cross_compile/src/lib.rs b/examples/cross_compile/src/lib.rs new file mode 100644 index 0000000000..3f60d9a1cf --- /dev/null +++ b/examples/cross_compile/src/lib.rs @@ -0,0 +1,3 @@ +pub fn secret_message() -> String { + "La-Li-Lu-Le-Lo".to_owned() +} diff --git a/examples/cross_compile/src/main.rs b/examples/cross_compile/src/main.rs new file mode 100644 index 0000000000..16deabb15a --- /dev/null +++ b/examples/cross_compile/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Secret Message: {}", secrets::secret_message()); +} diff --git a/examples/cross_compile/tools/BUILD.bazel b/examples/cross_compile/tools/BUILD.bazel new file mode 100644 index 0000000000..e56701a5c8 --- /dev/null +++ b/examples/cross_compile/tools/BUILD.bazel @@ -0,0 +1,11 @@ +load("@rules_rust//rust:rust_binary.bzl", "rust_binary") + +rust_binary( + name = "infer_tester", + srcs = ["infer_tester.rs"], + visibility = ["//visibility:public"], + deps = [ + "@crates_io//:infer", + "@rules_rust//rust/runfiles", + ], +) diff --git a/examples/cross_compile/tools/infer_test.bzl b/examples/cross_compile/tools/infer_test.bzl new file mode 100644 index 0000000000..d59188c9c3 --- /dev/null +++ b/examples/cross_compile/tools/infer_test.bzl @@ -0,0 +1,52 @@ +"""infer_test""" + +def _rlocationpath(file, workspace_name): + if file.short_path.startswith("../"): + return file.short_path[len("../"):] + + return "{}/{}".format(workspace_name, file.short_path) + +def _infer_test_impl(ctx): + runner = ctx.executable._test_runner + is_windows = runner.basename.endswith((".exe", ".bat", ".ps1")) + executable = ctx.actions.declare_file("{}{}".format(ctx.label.name, ".exe" if is_windows else "")) + ctx.actions.symlink( + target_file = runner, + output = executable, + is_executable = True, + ) + + return [ + DefaultInfo( + executable = executable, + runfiles = ctx.runfiles(files = [ctx.file.file]), + ), + RunEnvironmentInfo( + environment = { + "INFER_TEST_EXPECTED_TYPE": ctx.attr.type, + "INFER_TEST_FILE": _rlocationpath(ctx.file.file, ctx.workspace_name), + }, + ), + ] + +infer_test = rule( + doc = "A test that asserts on the file type of a given file.", + implementation = _infer_test_impl, + attrs = { + "file": attr.label( + doc = "The file to check.", + allow_single_file = True, + mandatory = True, + ), + "type": attr.string( + doc = "The expected type.", + mandatory = True, + ), + "_test_runner": attr.label( + cfg = "exec", + executable = True, + default = Label("//tools:infer_tester"), + ), + }, + test = True, +) diff --git a/examples/cross_compile/tools/infer_tester.rs b/examples/cross_compile/tools/infer_tester.rs new file mode 100644 index 0000000000..0f757f6800 --- /dev/null +++ b/examples/cross_compile/tools/infer_tester.rs @@ -0,0 +1,48 @@ +//! A test runner for `infer_test` which asserts on file types for a given file. + +use runfiles::{rlocation, Runfiles}; +use std::env; +use std::fs; +use std::process; + +fn main() { + // Locate the file via runfiles + let r = Runfiles::create().unwrap(); + let file_path = rlocation!( + r, + env::var("INFER_TEST_FILE") + .expect("Unable to access `INFER_TEST_FILE` environment variable.") + ) + .expect("Failed to locate runfile"); + + // Get the expected type from env + let expected_type = env::var("INFER_TEST_EXPECTED_TYPE") + .expect("Unable to access `INFER_TEST_EXPECTED_TYPE` environment variable."); + + // Read the file + let file_data = fs::read(&file_path) + .unwrap_or_else(|e| panic!("Failed to read file {:?}: {}", file_path, e)); + + // Infer the file type from the file + let inferred_type = infer::get(&file_data); + + // Assert on the results + match inferred_type { + Some(kind) => { + let mime_type = kind.mime_type(); + if mime_type == expected_type { + println!("✓ File type matches: {}", mime_type); + } else { + eprintln!("✗ File type mismatch!"); + eprintln!(" Expected: {}", expected_type); + eprintln!(" Got: {}", mime_type); + process::exit(1); + } + } + None => { + eprintln!("✗ Could not infer file type"); + eprintln!(" Expected: {}", expected_type); + process::exit(1); + } + } +} diff --git a/rust/settings/settings.bzl b/rust/settings/settings.bzl index 671a451c4f..105987b83d 100644 --- a/rust/settings/settings.bzl +++ b/rust/settings/settings.bzl @@ -122,6 +122,7 @@ def pipelined_compilation(): build_setting_default = False, ) +# buildifier: disable=unnamed-macro def experimental_use_cc_common_link(): """A flag to control whether to link rust_binary and rust_test targets using \ cc_common.link instead of rustc. @@ -131,6 +132,20 @@ def experimental_use_cc_common_link(): build_setting_default = False, ) + native.config_setting( + name = "experimental_use_cc_common_link_on", + flag_values = { + ":experimental_use_cc_common_link": "true", + }, + ) + + native.config_setting( + name = "experimental_use_cc_common_link_off", + flag_values = { + ":experimental_use_cc_common_link": "false", + }, + ) + def experimental_use_global_allocator(): """A flag to indicate that a global allocator is in use when using `--@rules_rust//rust/settings:experimental_use_cc_common_link` From 38b237330e78e398541fbcaa143ac942b614a5e8 Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Tue, 2 Dec 2025 08:38:34 -0800 Subject: [PATCH 5/6] Further isolate need for `cc_toolchain` --- examples/cross_compile/.bazelrc | 4 +++ examples/cross_compile/BUILD.bazel | 4 +-- ffi/cc/allocator_library/BUILD.bazel | 2 +- ffi/cc/empty/BUILD.bazel | 5 ++++ ffi/cc/global_allocator_library/BUILD.bazel | 2 +- rust/extensions.bzl | 2 +- rust/private/cc/BUILD.bazel | 24 ++++++++++++++++++ rust/private/cc/cc_utils.bzl | 12 +++++++++ rust/private/rust.bzl | 4 +-- rust/private/rust_allocator_libraries.bzl | 27 +++++++++++---------- rust/private/rustc.bzl | 9 +++---- rust/repositories.bzl | 6 ++--- rust/settings/BUILD.bazel | 3 +++ rust/settings/settings.bzl | 24 ++++++++++++++++++ rust/toolchain.bzl | 6 ++--- 15 files changed, 102 insertions(+), 32 deletions(-) create mode 100644 ffi/cc/empty/BUILD.bazel create mode 100644 rust/private/cc/BUILD.bazel create mode 100644 rust/private/cc/cc_utils.bzl diff --git a/examples/cross_compile/.bazelrc b/examples/cross_compile/.bazelrc index adc2d04be5..eabc75d21f 100644 --- a/examples/cross_compile/.bazelrc +++ b/examples/cross_compile/.bazelrc @@ -20,6 +20,10 @@ coverage --experimental_fetch_all_coverage_outputs # https://bazel.build/reference/command-line-reference#flag--experimental_cc_shared_library common --experimental_cc_shared_library +# Disable the allocator library by pointing to a unique target which satisfies the `CcInfo` +# contract but does not require a `cc_toolchain` to produce it. +common --@rules_rust//rust/settings:default_allocator_library=@rules_rust//ffi/cc/empty + ############################################################################### ## Unique configuration groups ############################################################################### diff --git a/examples/cross_compile/BUILD.bazel b/examples/cross_compile/BUILD.bazel index c4914a2ec7..55dcbfa98c 100644 --- a/examples/cross_compile/BUILD.bazel +++ b/examples/cross_compile/BUILD.bazel @@ -37,9 +37,9 @@ rust_library( name = "cross_compile_{}_infer_test".format(platform), file = ":cross_compile_{}".format(platform), target_compatible_with = { - "x86_64-pc-windows-msvc": ["@platforms//os:windows"], - "aarch64-pc-windows-msvc": ["@platforms//os:windows"], "aarch64-apple-darwin": ["@platforms//os:macos"], + "aarch64-pc-windows-msvc": ["@platforms//os:windows"], + "x86_64-pc-windows-msvc": ["@platforms//os:windows"], }.get(platform, []), type = { "aarch64-apple-darwin": "application/x-mach-binary", diff --git a/ffi/cc/allocator_library/BUILD.bazel b/ffi/cc/allocator_library/BUILD.bazel index 64b41a73dd..0e5f8845f7 100644 --- a/ffi/cc/allocator_library/BUILD.bazel +++ b/ffi/cc/allocator_library/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_cc//cc:cc_library.bzl", "cc_library") cc_library( name = "allocator_library", diff --git a/ffi/cc/empty/BUILD.bazel b/ffi/cc/empty/BUILD.bazel new file mode 100644 index 0000000000..9e7c1caa82 --- /dev/null +++ b/ffi/cc/empty/BUILD.bazel @@ -0,0 +1,5 @@ +alias( + name = "empty", + actual = "//rust/private/cc:empty", + visibility = ["//visibility:public"], +) diff --git a/ffi/cc/global_allocator_library/BUILD.bazel b/ffi/cc/global_allocator_library/BUILD.bazel index 16480416e5..b0febf8f6c 100644 --- a/ffi/cc/global_allocator_library/BUILD.bazel +++ b/ffi/cc/global_allocator_library/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_cc//cc:cc_library.bzl", "cc_library") cc_library( name = "global_allocator_library", diff --git a/rust/extensions.bzl b/rust/extensions.bzl index db151ff09d..749740032e 100644 --- a/rust/extensions.bzl +++ b/rust/extensions.bzl @@ -132,7 +132,7 @@ def _rust_impl(module_ctx): return module_ctx.extension_metadata(**metadata_kwargs) _COMMON_TAG_DEFAULTS = { - "allocator_library": "@rules_rust//ffi/cc/allocator_library", + "allocator_library": "", "rustfmt_version": DEFAULT_NIGHTLY_VERSION, "urls": DEFAULT_STATIC_RUST_URL_TEMPLATES, } diff --git a/rust/private/cc/BUILD.bazel b/rust/private/cc/BUILD.bazel new file mode 100644 index 0000000000..25beb854eb --- /dev/null +++ b/rust/private/cc/BUILD.bazel @@ -0,0 +1,24 @@ +load(":cc_utils.bzl", "cc_empty_library") + +cc_empty_library( + name = "empty", + visibility = ["//visibility:public"], +) + +alias( + name = "malloc", + actual = select({ + "//rust/settings:experimental_use_cc_common_link_on": "@bazel_tools//tools/cpp:malloc", + "//conditions:default": ":empty", + }), + visibility = ["//visibility:public"], +) + +alias( + name = "global_allocator_library", + actual = select({ + "//rust/settings:experimental_use_global_allocator_on": "//ffi/cc/global_allocator_library", + "//conditions:default": ":empty", + }), + visibility = ["//visibility:public"], +) diff --git a/rust/private/cc/cc_utils.bzl b/rust/private/cc/cc_utils.bzl new file mode 100644 index 0000000000..58d1046bc6 --- /dev/null +++ b/rust/private/cc/cc_utils.bzl @@ -0,0 +1,12 @@ +"""Rust CC utilities""" + +load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") + +def _cc_empty_library_impl(_ctx): + return [CcInfo()] + +cc_empty_library = rule( + doc = "A rule that provides an empty `CcInfo`.", + implementation = _cc_empty_library_impl, + provides = [CcInfo], +) diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index 7313c8f075..6ba4c361e1 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -868,11 +868,11 @@ _experimental_use_cc_common_link_attrs = { default = -1, ), "malloc": attr.label( - default = Label("@bazel_tools//tools/cpp:malloc"), + default = Label("//rust/private/cc:malloc"), doc = """Override the default dependency on `malloc`. By default, Rust binaries linked with cc_common.link are linked against -`@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use +`//rust/private/cc:malloc"`, which is an empty library and the resulting binary will use libc's `malloc`. This label must refer to a `cc_library` rule. """, mandatory = False, diff --git a/rust/private/rust_allocator_libraries.bzl b/rust/private/rust_allocator_libraries.bzl index ef344e1d47..4667faa090 100644 --- a/rust/private/rust_allocator_libraries.bzl +++ b/rust/private/rust_allocator_libraries.bzl @@ -89,21 +89,22 @@ def make_libstd_and_allocator_ccinfo( # 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() + if cc_toolchain: + if 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, - ) + 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, - )) + cc_infos.append(CcInfo( + linking_context = linking_context, + )) if rust_stdlib_info.std_rlibs: allocator_library_inputs = [] diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index d55d94b833..a28ad50b78 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -791,7 +791,9 @@ def collect_inputs( if linker_script: nolinkstamp_compile_direct_inputs.append(linker_script) - if crate_info.type in ["dylib", "cdylib"]: + if not cc_toolchain: + runtime_libs = depset() + elif crate_info.type in ["dylib", "cdylib"]: # For shared libraries we want to link C++ runtime library dynamically # (for example libstdc++.so or libc++.so). runtime_libs = cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration) @@ -1342,10 +1344,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 cc_toolchain or not _are_linkstamps_supported( - feature_configuration = feature_configuration, - has_grep_includes = hasattr(ctx.attr, "_use_grep_includes"), - ): + if not cc_toolchain or not _are_linkstamps_supported(feature_configuration = feature_configuration): linkstamps = depset([]) # Determine if the build is currently running with --stamp diff --git a/rust/repositories.bzl b/rust/repositories.bzl index 48930e5839..d3609c2d07 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -358,8 +358,7 @@ def rust_repositories(**kwargs): _RUST_TOOLCHAIN_REPOSITORY_ATTRS = { "allocator_library": attr.string( - doc = "Target that provides allocator functions when rust_library targets are embedded in a cc_binary.", - default = "@rules_rust//ffi/cc/allocator_library", + doc = "Target that provides allocator functions when `rust_library` targets are embedded in a `cc_binary`.", ), "auth": attr.string_dict( doc = ( @@ -391,8 +390,7 @@ _RUST_TOOLCHAIN_REPOSITORY_ATTRS = { doc = "Extra flags to pass to rustc in non-exec configuration", ), "global_allocator_library": attr.string( - doc = "Target that provides allocator functions when a global allocator is used with cc_common.link.", - default = "@rules_rust//ffi/cc/global_allocator_library", + doc = "Target that provides allocator functions when a global allocator is used with [`cc_common.link`](https://bazel.build/rules/lib/toplevel/cc_common#link).", ), "netrc": attr.string( doc = ".netrc file to use for authentication; mirrors the eponymous attribute from http_archive", diff --git a/rust/settings/BUILD.bazel b/rust/settings/BUILD.bazel index 55338dd452..1998763aab 100644 --- a/rust/settings/BUILD.bazel +++ b/rust/settings/BUILD.bazel @@ -10,6 +10,7 @@ load( "clippy_toml", "codegen_units", "collect_cfgs", + "default_allocator_library", "error_format", "experimental_link_std_dylib", "experimental_per_crate_rustc_flag", @@ -75,6 +76,8 @@ codegen_units() collect_cfgs() +default_allocator_library() + error_format() clippy_error_format() diff --git a/rust/settings/settings.bzl b/rust/settings/settings.bzl index 105987b83d..da44833b5e 100644 --- a/rust/settings/settings.bzl +++ b/rust/settings/settings.bzl @@ -146,6 +146,16 @@ def experimental_use_cc_common_link(): }, ) +# buildifier: disable=unnamed-macro +def default_allocator_library(): + """A flag that determines the default allocator library for `rust_toolchain` targets.""" + + native.label_flag( + name = "default_allocator_library", + build_setting_default = Label("//ffi/cc/allocator_library"), + ) + +# buildifier: disable=unnamed-macro def experimental_use_global_allocator(): """A flag to indicate that a global allocator is in use when using `--@rules_rust//rust/settings:experimental_use_cc_common_link` @@ -157,6 +167,20 @@ def experimental_use_global_allocator(): build_setting_default = False, ) + native.config_setting( + name = "experimental_use_global_allocator_on", + flag_values = { + ":experimental_use_global_allocator": "true", + }, + ) + + native.config_setting( + name = "experimental_use_global_allocator_off", + flag_values = { + ":experimental_use_global_allocator": "false", + }, + ) + def experimental_use_allocator_libraries_with_mangled_symbols(name): """A flag used to select allocator libraries implemented in rust that are compatible with the rustc allocator symbol mangling. diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index 5af30df398..91e3032166 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -635,8 +635,8 @@ rust_toolchain = rule( fragments = ["cpp"], attrs = { "allocator_library": attr.label( - doc = "Target that provides allocator functions when rust_library targets are embedded in a cc_binary.", - default = "@rules_rust//ffi/cc/allocator_library", + doc = "Target that provides allocator functions when `rust_library` targets are embedded in a `cc_binary`.", + default = Label("//rust/settings:default_allocator_library"), ), "binary_ext": attr.string( doc = "The extension for binaries created from rustc.", @@ -717,7 +717,7 @@ rust_toolchain = rule( ), "global_allocator_library": attr.label( doc = "Target that provides allocator functions for when a global allocator is present.", - default = "@rules_rust//ffi/cc/global_allocator_library", + default = Label("//rust/private/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.). Linker binaries must be runnable in the exec configuration, so cfg = \"exec\" is used. To choose a linker based on the target platform, use a select() when providing this attribute. The select() will be evaluated against the target platform before the exec transition is applied, allowing platform-specific linker selection while ensuring the selected linker is built for the exec platform.", From 210c3789ec39d9492fab99d267835393effa1efa Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Tue, 2 Dec 2025 10:06:04 -0800 Subject: [PATCH 6/6] Switch to busybox style `/bin/false` --- cargo/private/BUILD.bazel | 35 +++++++++++++++++------------------ cargo/private/no_binary.rs | 6 +++++- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/cargo/private/BUILD.bazel b/cargo/private/BUILD.bazel index d11e456cbc..fd60c4ff62 100644 --- a/cargo/private/BUILD.bazel +++ b/cargo/private/BUILD.bazel @@ -1,4 +1,5 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("@bazel_skylib//rules:copy_file.bzl", "copy_file") load("//rust:defs.bzl", "rust_binary") rust_binary( @@ -9,33 +10,31 @@ rust_binary( ) rust_binary( - name = "no_ar", + name = "no_binary", srcs = ["no_binary.rs"], edition = "2021", - rustc_env = { - "BINARY_ENV": "AR", - }, visibility = ["//visibility:public"], ) -rust_binary( +copy_file( + name = "no_ar", + src = ":no_binary", + out = "no_ar.exe", + is_executable = True, +) + +copy_file( name = "no_cc", - srcs = ["no_binary.rs"], - edition = "2021", - rustc_env = { - "BINARY_ENV": "CC", - }, - visibility = ["//visibility:public"], + src = ":no_binary", + out = "no_cc.exe", + is_executable = True, ) -rust_binary( +copy_file( name = "no_cxx", - srcs = ["no_binary.rs"], - edition = "2021", - rustc_env = { - "BINARY_ENV": "CXX", - }, - visibility = ["//visibility:public"], + src = ":no_binary", + out = "no_cxx.exe", + is_executable = True, ) bzl_library( diff --git a/cargo/private/no_binary.rs b/cargo/private/no_binary.rs index c3487b103a..56a43fd507 100644 --- a/cargo/private/no_binary.rs +++ b/cargo/private/no_binary.rs @@ -1,6 +1,10 @@ //! A cross platform implementation of `/bin/false` fn main() { - eprintln!(concat!("No binary provided for ", env!("BINARY_ENV"))); + let program_name = std::env::args() + .next() + .unwrap_or_else(|| "unknown".to_string()); + + eprintln!("No binary provided for {}", program_name); std::process::exit(1); }