diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index c4425d446e..a620a054d4 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -693,6 +693,10 @@ def establish_cc_info(ctx, crate_info, toolchain, cc_toolchain, feature_configur if CcInfo in dep: cc_infos.append(dep[CcInfo]) + if crate_info.type in ("rlib", "lib") and toolchain.libstd_and_allocator_ccinfo: + # TODO: if we already have an rlib in our deps, we could skip this + cc_infos.append(toolchain.libstd_and_allocator_ccinfo) + return [cc_common.merge_cc_infos(cc_infos = cc_infos)] def add_edition_flags(args, crate): diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index 359d0fb74c..fdbb96ac43 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -1,5 +1,75 @@ """The rust_toolchain rule definition and implementation.""" +load( + "//rust/private:utils.bzl", + "find_cc_toolchain", +) + +def _make_dota(ctx, f): + """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. + f (File): The file to symlink. + + Returns: + The symlink's File. + """ + dot_a = ctx.actions.declare_file(f.basename + ".a", sibling = f) + ctx.actions.symlink(output = dot_a, target_file = f) + return dot_a + +def _make_libstd_and_allocator_ccinfo(ctx, rust_lib, allocator_library): + """Make the CcInfo (if possible) for libstd and allocator libraries. + + Args: + ctx (ctx): The rule's context object. + rust_lib: The rust standard library. + allocator_library: The target to use for providing allocator functions. + + + Returns: + A CcInfo object for the required libraries, or None if no such libraries are available. + """ + cc_toolchain, feature_configuration = find_cc_toolchain(ctx) + link_inputs = [] + std_rlibs = [f for f in rust_lib.files.to_list() if f.basename.endswith(".rlib")] + if std_rlibs: + dot_a_files = [_make_dota(ctx, f) for f in std_rlibs] + link_inputs.append(cc_common.create_linker_input( + owner = rust_lib.label, + libraries = depset( + [ + cc_common.create_library_to_link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + static_library = f, + pic_static_library = f, + ) + for f in dot_a_files + ], + ), + )) + if allocator_library: + link_inputs.append(cc_common.create_linker_input( + owner = allocator_library.allocator_library.label, + libraries = depset([ + cc_common.create_library_to_link( + # TODO(augie): wat? this needs no actions + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + static_library = f, + ) + for f in allocator_library.files.to_list() + ]), + )) + libstd_and_allocator_ccinfo = None + if link_inputs: + return CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = depset(link_inputs))) + return None + def _rust_toolchain_impl(ctx): """The rust_toolchain implementation @@ -38,11 +108,13 @@ def _rust_toolchain_impl(ctx): default_edition = ctx.attr.default_edition, compilation_mode_opts = compilation_mode_opts, crosstool_files = ctx.files._crosstool, + libstd_and_allocator_ccinfo = _make_libstd_and_allocator_ccinfo(ctx, ctx.attr.rust_lib, ctx.attr.allocator_library), ) return [toolchain] rust_toolchain = rule( implementation = _rust_toolchain_impl, + fragments = ["cpp"], attrs = { "binary_ext": attr.string( doc = "The extension for binaries created from rustc.", @@ -131,6 +203,12 @@ rust_toolchain = rule( "_crosstool": attr.label( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), ), + "_cc_toolchain": attr.label( + default = "@bazel_tools//tools/cpp:current_cc_toolchain", + ), + "allocator_library": attr.label( + doc = "Target that provides allocator functions when rust_library targets are embedded in a cc_binary.", + ), }, doc = """Declares a Rust toolchain for use. diff --git a/test/unit/cc_info/cc_info_test.bzl b/test/unit/cc_info/cc_info_test.bzl index f655486099..588924d6cc 100644 --- a/test/unit/cc_info/cc_info_test.bzl +++ b/test/unit/cc_info/cc_info_test.bzl @@ -6,11 +6,11 @@ load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_ def _is_dylib_on_windows(ctx): return ctx.target_platform_has_constraint(ctx.attr._windows[platform_common.ConstraintValueInfo]) -def _assert_cc_info_has_library_to_link(env, tut, type): +def _assert_cc_info_has_library_to_link(env, tut, type, ccinfo_count): asserts.true(env, CcInfo in tut, "rust_library should provide CcInfo") cc_info = tut[CcInfo] linker_inputs = cc_info.linking_context.linker_inputs.to_list() - asserts.equals(env, len(linker_inputs), 1) + asserts.equals(env, len(linker_inputs), ccinfo_count) library_to_link = linker_inputs[0].libraries[0] asserts.equals(env, False, library_to_link.alwayslink) @@ -42,7 +42,7 @@ def _assert_cc_info_has_library_to_link(env, tut, type): def _rlib_provides_cc_info_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) - _assert_cc_info_has_library_to_link(env, tut, "rlib") + _assert_cc_info_has_library_to_link(env, tut, "rlib", 2) return analysistest.end(env) def _bin_does_not_provide_cc_info_test_impl(ctx): @@ -60,13 +60,13 @@ def _proc_macro_does_not_provide_cc_info_test_impl(ctx): def _cdylib_provides_cc_info_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) - _assert_cc_info_has_library_to_link(env, tut, "cdylib") + _assert_cc_info_has_library_to_link(env, tut, "cdylib", 1) return analysistest.end(env) def _staticlib_provides_cc_info_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) - _assert_cc_info_has_library_to_link(env, tut, "staticlib") + _assert_cc_info_has_library_to_link(env, tut, "staticlib", 1) return analysistest.end(env) rlib_provides_cc_info_test = analysistest.make(_rlib_provides_cc_info_test_impl)