diff --git a/bindgen/bindgen.bzl b/bindgen/bindgen.bzl index 7644f78bb8..03079a000f 100644 --- a/bindgen/bindgen.bzl +++ b/bindgen/bindgen.bzl @@ -16,7 +16,7 @@ load("//rust:rust.bzl", "rust_library") # buildifier: disable=bzl-visibility -load("//rust/private:utils.bzl", "find_toolchain", "get_libs_for_static_executable") +load("//rust/private:utils.bzl", "find_toolchain", "get_preferred_artifact") def rust_bindgen_library( name, @@ -84,7 +84,8 @@ def _rust_bindgen_impl(ctx): output = ctx.outputs.out # libclang should only have 1 output file - libclang_dir = get_libs_for_static_executable(libclang).to_list()[0].dirname + linker_inputs = dep[CcInfo].linking_context.linker_inputs.to_list() + libclang_dir = depset([get_preferred_artifact(lib) for li in linker_inputs for lib in li.libraries]).to_list()[0].dirname include_directories = cc_lib[CcInfo].compilation_context.includes.to_list() quote_include_directories = cc_lib[CcInfo].compilation_context.quote_includes.to_list() system_include_directories = cc_lib[CcInfo].compilation_context.system_includes.to_list() diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 65d605f75b..5fa9feae53 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -23,7 +23,7 @@ load( "expand_locations", "find_cc_toolchain", "get_lib_name", - "get_libs_for_static_executable", + "get_preferred_artifact", "relativize", "rule_attrs", ) @@ -57,6 +57,7 @@ DepInfo = provider( "transitive_dylibs": "depset[File]", "transitive_libs": "List[File]: All transitive dependencies, not filtered by type.", "transitive_staticlibs": "depset[File]", + "transitive_staticlibs_alwayslink": "depset[File]", }, ) @@ -151,6 +152,7 @@ def collect_deps(label, deps, proc_macro_deps, aliases, toolchain): transitive_crates = [] transitive_dylibs = [] transitive_staticlibs = [] + transitive_staticlibs_alwayslink = [] transitive_build_infos = [] build_info = None @@ -167,24 +169,29 @@ def collect_deps(label, deps, proc_macro_deps, aliases, toolchain): transitive_crates.append(depset([dep[rust_common.crate_info]], transitive = [dep[DepInfo].transitive_crates])) transitive_dylibs.append(dep[DepInfo].transitive_dylibs) transitive_staticlibs.append(dep[DepInfo].transitive_staticlibs) + transitive_staticlibs_alwayslink.append(dep[DepInfo].transitive_staticlibs_alwayslink) transitive_build_infos.append(dep[DepInfo].transitive_build_infos) elif CcInfo in dep: # This dependency is a cc_library # TODO: We could let the user choose how to link, instead of always preferring to link static libraries. - libs = get_libs_for_static_executable(dep) - - transitive_dylibs.append(depset([ - lib - for lib in libs.to_list() - # Dynamic libraries may have a version number nowhere, or before (macos) or after (linux) the extension. - if lib.basename.endswith(toolchain.dylib_ext) or lib.basename.split(".", 2)[1] == toolchain.dylib_ext[1:] - ])) - transitive_staticlibs.append(depset([ - lib - for lib in libs.to_list() - if lib.basename.endswith(toolchain.staticlib_ext) - ])) + linker_inputs = dep[CcInfo].linking_context.linker_inputs.to_list() + for li in linker_inputs: + always = [] + static = [] + dylib = [] + for lib in li.libraries: + art = get_preferred_artifact(lib) + if lib.alwayslink: + always.append(lib.static_library or lib.pic_static_library) + # Dynamic libraries may have a version number nowhere, or before (macos) or after (linux) the extension. + elif art.basename.endswith(toolchain.dylib_ext) or art.basename.split(".", 2)[1] == toolchain.dylib_ext[1:]: + dylib.append(art) + elif art.basename.endswith(toolchain.staticlib_ext): + static.append(art) + transitive_staticlibs_alwayslink.append(depset(always)) + transitive_dylibs.append(depset(dylib)) + transitive_staticlibs.append(depset(static)) elif BuildInfo in dep: if build_info: fail("Several deps are providing build information, only one is allowed in the dependencies", "deps") @@ -196,7 +203,7 @@ def collect_deps(label, deps, proc_macro_deps, aliases, toolchain): transitive_crates_depset = depset(transitive = transitive_crates) transitive_libs = depset( [c.output for c in transitive_crates_depset.to_list()], - transitive = transitive_staticlibs + transitive_dylibs, + transitive = transitive_staticlibs + transitive_dylibs + transitive_staticlibs_alwayslink, ) return ( @@ -208,6 +215,7 @@ def collect_deps(label, deps, proc_macro_deps, aliases, toolchain): order = "topological", # dylib link flag ordering matters. ), transitive_staticlibs = depset(transitive = transitive_staticlibs), + transitive_staticlibs_alwayslink = depset(transitive = transitive_staticlibs_alwayslink), transitive_libs = transitive_libs.to_list(), transitive_build_infos = depset(transitive = transitive_build_infos), dep_env = build_info.dep_env if build_info else None, @@ -822,6 +830,20 @@ def _add_native_link_flags(args, dep_info, crate_type, cc_toolchain, feature_con args.add_all(dep_info.transitive_dylibs, map_each = get_lib_name, format_each = "-ldylib=%s") args.add_all(dep_info.transitive_staticlibs, map_each = get_lib_name, format_each = "-lstatic=%s") + # alwayslink=True libraries need special handling so the alwayslink args + # make it to the linker. Eventually we might be able to use a first-class + # rustc flag for this (https://github.com/rust-lang/cargo/issues/7586), + # but until then we have to do it this way. + for lib in dep_info.transitive_staticlibs_alwayslink.to_list(): + args.add_all([ + "-C", + "link-arg=-Wl,--whole-archive", + "-C", + ("link-arg=-l%s" % get_lib_name(lib)), + "-C", + "link-arg=-Wl,--no-whole-archive", + ]) + if crate_type in ["dylib", "cdylib"]: # For shared libraries we want to link C++ runtime library dynamically # (for example libstdc++.so or libc++.so). diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl index 5bd6259c3a..06ed842833 100644 --- a/rust/private/utils.bzl +++ b/rust/private/utils.bzl @@ -118,19 +118,7 @@ def determine_output_hash(crate_root): """ return repr(hash(crate_root.path)) -def get_libs_for_static_executable(dep): - """find the libraries used for linking a static executable. - - Args: - dep (Target): A cc_library target. - - Returns: - depset: A depset[File] - """ - linker_inputs = dep[CcInfo].linking_context.linker_inputs.to_list() - return depset([_get_preferred_artifact(lib) for li in linker_inputs for lib in li.libraries]) - -def _get_preferred_artifact(library_to_link): +def get_preferred_artifact(library_to_link): """Get the first available library to link from a LibraryToLink object. Args: