diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index ea5ba025b0..abbd20f7ad 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -27,6 +27,7 @@ load( "find_cc_toolchain", "get_lib_name", "get_preferred_artifact", + "make_static_lib_symlink", "relativize", ) @@ -635,8 +636,7 @@ def establish_cc_info(ctx, crate_info, toolchain, cc_toolchain, feature_configur # 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 = ctx.actions.declare_file(crate_info.name + ".a", sibling = crate_info.output) - ctx.actions.symlink(output = dot_a, target_file = crate_info.output) + dot_a = make_static_lib_symlink(ctx.actions, crate_info.output) # TODO(hlopko): handle PIC/NOPIC correctly library_to_link = cc_common.create_library_to_link( diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl index 30b8aee1c2..e0591d3af1 100644 --- a/rust/private/utils.bzl +++ b/rust/private/utils.bzl @@ -296,3 +296,30 @@ def dedent(doc_string): # Remove the leading block of spaces from the current line block = " " * space_count return "\n".join([line.replace(block, "", 1).rstrip() for line in lines]) + +def make_static_lib_symlink(actions, rlib_file): + """Add a .a symlink to an .rlib file. + + The name of the symlink is derived from the of the .rlib file as follows: + * `.a`, if starts with `lib` + * `lib.a`, otherwise. + + For example, the name of the symlink for + * `libcratea.rlib` is `libcratea.a` + * `crateb.rlib` is `libcrateb.a`. + + Args: + actions (actions): The rule's context actions object. + rlib_file (File): The file to symlink, which must end in .rlib. + + Returns: + The symlink's File. + """ + if not rlib_file.basename.endswith(".rlib"): + fail("file is not an .rlib: ", rlib_file.basename) + basename = rlib_file.basename[:-5] + if not basename.startswith("lib"): + basename = "lib" + basename + dot_a = actions.declare_file(basename + ".a", sibling = rlib_file) + actions.symlink(output = dot_a, target_file = rlib_file) + return dot_a diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index 95c42591bb..6ba4400c74 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -1,21 +1,7 @@ """The rust_toolchain rule definition and implementation.""" load("//rust/private:common.bzl", "rust_common") -load("//rust/private:utils.bzl", "dedent", "find_cc_toolchain") - -def _make_dota(ctx, file): - """Add a symlink for a file that ends in .a, so it can be used as a staticlib. - - Args: - ctx (ctx): The rule's context object. - file (File): The file to symlink. - - Returns: - The symlink's File. - """ - dot_a = ctx.actions.declare_file(file.basename + ".a", sibling = file) - ctx.actions.symlink(output = dot_a, target_file = file) - return dot_a +load("//rust/private:utils.bzl", "dedent", "find_cc_toolchain", "make_static_lib_symlink") def _rust_stdlib_filegroup_impl(ctx): rust_lib = ctx.files.srcs @@ -35,7 +21,7 @@ def _rust_stdlib_filegroup_impl(ctx): # # alloc depends on the allocator_library if it's configured, but we # do that later. - dot_a_files = [_make_dota(ctx, f) for f in std_rlibs] + dot_a_files = [make_static_lib_symlink(ctx.actions, f) for f in std_rlibs] alloc_files = [f for f in dot_a_files if "alloc" in f.basename and "std" not in f.basename] between_alloc_and_core_files = [f for f in dot_a_files if "compiler_builtins" in f.basename] @@ -140,13 +126,36 @@ def _make_libstd_and_allocator_ccinfo(ctx, rust_lib, allocator_library): 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 "panic_abort" not in f.basename + ] between_core_and_std_inputs = depset( - [_ltl(f, ctx, cc_toolchain, feature_configuration) for f in rust_stdlib_info.between_core_and_std_files], + [ + _ltl(f, ctx, cc_toolchain, feature_configuration) + for f in filtered_between_core_and_std_files + ], transitive = [core_inputs], order = "topological", ) std_inputs = depset( - [_ltl(f, ctx, cc_toolchain, feature_configuration) for f in rust_stdlib_info.std_files], + [ + _ltl(f, ctx, cc_toolchain, feature_configuration) + for f in rust_stdlib_info.std_files + ], transitive = [between_core_and_std_inputs], order = "topological", ) diff --git a/test/unit/cc_info/cc_info_test.bzl b/test/unit/cc_info/cc_info_test.bzl index 9a947a595f..32f2385cde 100644 --- a/test/unit/cc_info/cc_info_test.bzl +++ b/test/unit/cc_info/cc_info_test.bzl @@ -36,6 +36,8 @@ def _assert_cc_info_has_library_to_link(env, tut, type, ccinfo_count): asserts.equals(env, None, library_to_link.resolved_symlink_dynamic_library) asserts.equals(env, None, library_to_link.resolved_symlink_interface_library) asserts.true(env, library_to_link.static_library != None) + if type in ("rlib", "lib"): + asserts.true(env, library_to_link.static_library.basename.startswith("lib" + tut.label.name)) asserts.true(env, library_to_link.pic_static_library != None) asserts.equals(env, library_to_link.static_library, library_to_link.pic_static_library) diff --git a/test/unit/stdlib/BUILD.bazel b/test/unit/stdlib/BUILD.bazel new file mode 100644 index 0000000000..3907b2edbf --- /dev/null +++ b/test/unit/stdlib/BUILD.bazel @@ -0,0 +1,4 @@ +load(":stdlib.bzl", "stdlib_suite") + +############################ UNIT TESTS ############################# +stdlib_suite(name = "stdlib_suite") diff --git a/test/unit/stdlib_ordering/some_rlib.rs b/test/unit/stdlib/some_rlib.rs similarity index 100% rename from test/unit/stdlib_ordering/some_rlib.rs rename to test/unit/stdlib/some_rlib.rs diff --git a/test/unit/stdlib_ordering/stdlib_ordering.bzl b/test/unit/stdlib/stdlib.bzl similarity index 52% rename from test/unit/stdlib_ordering/stdlib_ordering.bzl rename to test/unit/stdlib/stdlib.bzl index 645943f39e..9d83190450 100644 --- a/test/unit/stdlib_ordering/stdlib_ordering.bzl +++ b/test/unit/stdlib/stdlib.bzl @@ -1,4 +1,4 @@ -"""Unittest to verify ordering of rust stdlib in rust_library() CcInfo""" +"""Unittest to verify contents and ordering of rust stdlib in rust_library() CcInfo""" load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") load("//rust:defs.bzl", "rust_library") @@ -26,17 +26,41 @@ def _dedup_preserving_order(list): r.append(e) return r +def _stdlibs(tut): + """Given a target, return the list of its standard rust libraries.""" + libs = [ + lib.static_library + for li in tut[CcInfo].linking_context.linker_inputs.to_list() + for lib in li.libraries + ] + stdlibs = [lib for lib in libs if (tut.label.name not in lib.basename)] + return stdlibs + def _libstd_ordering_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) - libs = [lib.static_library for li in tut[CcInfo].linking_context.linker_inputs.to_list() for lib in li.libraries] - rlibs = [_categorize_library(lib.basename) for lib in libs if ".rlib" in lib.basename] - set_to_check = _dedup_preserving_order([lib for lib in rlibs if lib != "other"]) + stdlib_categories = [_categorize_library(lib.basename) for lib in _stdlibs(tut)] + set_to_check = _dedup_preserving_order([lib for lib in stdlib_categories if lib != "other"]) asserts.equals(env, ["std", "core", "compiler_builtins", "alloc"], set_to_check) return analysistest.end(env) libstd_ordering_test = analysistest.make(_libstd_ordering_test_impl) +def _libstd_panic_test_impl(ctx): + # The libraries panic_unwind and panic_abort are alternatives. + # Check that they don't occur together. + env = analysistest.begin(ctx) + tut = analysistest.target_under_test(env) + stdlibs = _stdlibs(tut) + has_panic_unwind = [lib for lib in stdlibs if "panic_unwind" in lib.basename] + if has_panic_unwind: + has_panic_abort = [lib for lib in stdlibs if "panic_abort" in lib.basename] + asserts.false(env, has_panic_abort) + + return analysistest.end(env) + +libstd_panic_test = analysistest.make(_libstd_panic_test_impl) + def _native_dep_test(): rust_library( name = "some_rlib", @@ -48,7 +72,12 @@ def _native_dep_test(): target_under_test = ":some_rlib", ) -def stdlib_ordering_suite(name): + libstd_panic_test( + name = "libstd_panic_test", + target_under_test = ":some_rlib", + ) + +def stdlib_suite(name): """Entry-point macro called from the BUILD file. Args: diff --git a/test/unit/stdlib_ordering/BUILD.bazel b/test/unit/stdlib_ordering/BUILD.bazel deleted file mode 100644 index fb860b0214..0000000000 --- a/test/unit/stdlib_ordering/BUILD.bazel +++ /dev/null @@ -1,4 +0,0 @@ -load(":stdlib_ordering.bzl", "stdlib_ordering_suite") - -############################ UNIT TESTS ############################# -stdlib_ordering_suite(name = "stdlib_ordering_suite")