1*d4726bddSHONG Yifan# Copyright 2018 The Bazel Authors. All rights reserved. 2*d4726bddSHONG Yifan# 3*d4726bddSHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License"); 4*d4726bddSHONG Yifan# you may not use this file except in compliance with the License. 5*d4726bddSHONG Yifan# You may obtain a copy of the License at 6*d4726bddSHONG Yifan# 7*d4726bddSHONG Yifan# http://www.apache.org/licenses/LICENSE-2.0 8*d4726bddSHONG Yifan# 9*d4726bddSHONG Yifan# Unless required by applicable law or agreed to in writing, software 10*d4726bddSHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS, 11*d4726bddSHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*d4726bddSHONG Yifan# See the License for the specific language governing permissions and 13*d4726bddSHONG Yifan# limitations under the License. 14*d4726bddSHONG Yifan 15*d4726bddSHONG Yifan"""Functionality for constructing actions that invoke the Rust compiler""" 16*d4726bddSHONG Yifan 17*d4726bddSHONG Yifanload("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") 18*d4726bddSHONG Yifanload( 19*d4726bddSHONG Yifan "@bazel_tools//tools/build_defs/cc:action_names.bzl", 20*d4726bddSHONG Yifan "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME", 21*d4726bddSHONG Yifan "CPP_LINK_EXECUTABLE_ACTION_NAME", 22*d4726bddSHONG Yifan "CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME", 23*d4726bddSHONG Yifan "CPP_LINK_STATIC_LIBRARY_ACTION_NAME", 24*d4726bddSHONG Yifan) 25*d4726bddSHONG Yifanload("//rust/private:common.bzl", "rust_common") 26*d4726bddSHONG Yifanload("//rust/private:providers.bzl", "RustcOutputDiagnosticsInfo", _BuildInfo = "BuildInfo") 27*d4726bddSHONG Yifanload("//rust/private:stamp.bzl", "is_stamping_enabled") 28*d4726bddSHONG Yifanload( 29*d4726bddSHONG Yifan "//rust/private:utils.bzl", 30*d4726bddSHONG Yifan "abs", 31*d4726bddSHONG Yifan "expand_dict_value_locations", 32*d4726bddSHONG Yifan "expand_list_element_locations", 33*d4726bddSHONG Yifan "find_cc_toolchain", 34*d4726bddSHONG Yifan "get_lib_name_default", 35*d4726bddSHONG Yifan "get_lib_name_for_windows", 36*d4726bddSHONG Yifan "get_preferred_artifact", 37*d4726bddSHONG Yifan "is_exec_configuration", 38*d4726bddSHONG Yifan "make_static_lib_symlink", 39*d4726bddSHONG Yifan "relativize", 40*d4726bddSHONG Yifan) 41*d4726bddSHONG Yifanload(":utils.bzl", "is_std_dylib") 42*d4726bddSHONG Yifan 43*d4726bddSHONG Yifan# This feature is disabled unless one of the dependencies is a cc_library. 44*d4726bddSHONG Yifan# Authors of C++ toolchains can place linker flags that should only be applied 45*d4726bddSHONG Yifan# when linking with C objects in a feature with this name, or require this 46*d4726bddSHONG Yifan# feature from other features which needs to be disabled together. 47*d4726bddSHONG YifanRUST_LINK_CC_FEATURE = "rules_rust_link_cc" 48*d4726bddSHONG Yifan 49*d4726bddSHONG YifanBuildInfo = _BuildInfo 50*d4726bddSHONG Yifan 51*d4726bddSHONG YifanAliasableDepInfo = provider( 52*d4726bddSHONG Yifan doc = "A provider mapping an alias name to a Crate's information.", 53*d4726bddSHONG Yifan fields = { 54*d4726bddSHONG Yifan "dep": "CrateInfo", 55*d4726bddSHONG Yifan "name": "str", 56*d4726bddSHONG Yifan }, 57*d4726bddSHONG Yifan) 58*d4726bddSHONG Yifan 59*d4726bddSHONG Yifan_error_format_values = ["human", "json", "short"] 60*d4726bddSHONG Yifan 61*d4726bddSHONG YifanErrorFormatInfo = provider( 62*d4726bddSHONG Yifan doc = "Set the --error-format flag for all rustc invocations", 63*d4726bddSHONG Yifan fields = {"error_format": "(string) [" + ", ".join(_error_format_values) + "]"}, 64*d4726bddSHONG Yifan) 65*d4726bddSHONG Yifan 66*d4726bddSHONG YifanExtraRustcFlagsInfo = provider( 67*d4726bddSHONG Yifan doc = "Pass each value as an additional flag to non-exec rustc invocations", 68*d4726bddSHONG Yifan fields = {"extra_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"}, 69*d4726bddSHONG Yifan) 70*d4726bddSHONG Yifan 71*d4726bddSHONG YifanExtraExecRustcFlagsInfo = provider( 72*d4726bddSHONG Yifan doc = "Pass each value as an additional flag to exec rustc invocations", 73*d4726bddSHONG Yifan fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"}, 74*d4726bddSHONG Yifan) 75*d4726bddSHONG Yifan 76*d4726bddSHONG YifanPerCrateRustcFlagsInfo = provider( 77*d4726bddSHONG Yifan doc = "Pass each value as an additional flag to non-exec rustc invocations for crates matching the provided filter", 78*d4726bddSHONG Yifan fields = {"per_crate_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"}, 79*d4726bddSHONG Yifan) 80*d4726bddSHONG Yifan 81*d4726bddSHONG YifanIsProcMacroDepInfo = provider( 82*d4726bddSHONG Yifan doc = "Records if this is a transitive dependency of a proc-macro.", 83*d4726bddSHONG Yifan fields = {"is_proc_macro_dep": "Boolean"}, 84*d4726bddSHONG Yifan) 85*d4726bddSHONG Yifan 86*d4726bddSHONG Yifandef _is_proc_macro_dep_impl(ctx): 87*d4726bddSHONG Yifan return IsProcMacroDepInfo(is_proc_macro_dep = ctx.build_setting_value) 88*d4726bddSHONG Yifan 89*d4726bddSHONG Yifanis_proc_macro_dep = rule( 90*d4726bddSHONG Yifan doc = "Records if this is a transitive dependency of a proc-macro.", 91*d4726bddSHONG Yifan implementation = _is_proc_macro_dep_impl, 92*d4726bddSHONG Yifan build_setting = config.bool(flag = True), 93*d4726bddSHONG Yifan) 94*d4726bddSHONG Yifan 95*d4726bddSHONG YifanIsProcMacroDepEnabledInfo = provider( 96*d4726bddSHONG Yifan doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.", 97*d4726bddSHONG Yifan fields = {"enabled": "Boolean"}, 98*d4726bddSHONG Yifan) 99*d4726bddSHONG Yifan 100*d4726bddSHONG Yifandef _is_proc_macro_dep_enabled_impl(ctx): 101*d4726bddSHONG Yifan return IsProcMacroDepEnabledInfo(enabled = ctx.build_setting_value) 102*d4726bddSHONG Yifan 103*d4726bddSHONG Yifanis_proc_macro_dep_enabled = rule( 104*d4726bddSHONG Yifan doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.", 105*d4726bddSHONG Yifan implementation = _is_proc_macro_dep_enabled_impl, 106*d4726bddSHONG Yifan build_setting = config.bool(flag = True), 107*d4726bddSHONG Yifan) 108*d4726bddSHONG Yifan 109*d4726bddSHONG Yifandef _get_rustc_env(attr, toolchain, crate_name): 110*d4726bddSHONG Yifan """Gathers rustc environment variables 111*d4726bddSHONG Yifan 112*d4726bddSHONG Yifan Args: 113*d4726bddSHONG Yifan attr (struct): The current target's attributes 114*d4726bddSHONG Yifan toolchain (rust_toolchain): The current target's rust toolchain context 115*d4726bddSHONG Yifan crate_name (str): The name of the crate to be compiled 116*d4726bddSHONG Yifan 117*d4726bddSHONG Yifan Returns: 118*d4726bddSHONG Yifan dict: Rustc environment variables 119*d4726bddSHONG Yifan """ 120*d4726bddSHONG Yifan version = attr.version if hasattr(attr, "version") else "0.0.0" 121*d4726bddSHONG Yifan major, minor, patch = version.split(".", 2) 122*d4726bddSHONG Yifan if "-" in patch: 123*d4726bddSHONG Yifan patch, pre = patch.split("-", 1) 124*d4726bddSHONG Yifan else: 125*d4726bddSHONG Yifan pre = "" 126*d4726bddSHONG Yifan 127*d4726bddSHONG Yifan result = { 128*d4726bddSHONG Yifan "CARGO_CFG_TARGET_ARCH": "" if toolchain.target_arch == None else toolchain.target_arch, 129*d4726bddSHONG Yifan "CARGO_CFG_TARGET_OS": "" if toolchain.target_os == None else toolchain.target_os, 130*d4726bddSHONG Yifan "CARGO_CRATE_NAME": crate_name, 131*d4726bddSHONG Yifan "CARGO_PKG_AUTHORS": "", 132*d4726bddSHONG Yifan "CARGO_PKG_DESCRIPTION": "", 133*d4726bddSHONG Yifan "CARGO_PKG_HOMEPAGE": "", 134*d4726bddSHONG Yifan "CARGO_PKG_NAME": attr.name, 135*d4726bddSHONG Yifan "CARGO_PKG_VERSION": version, 136*d4726bddSHONG Yifan "CARGO_PKG_VERSION_MAJOR": major, 137*d4726bddSHONG Yifan "CARGO_PKG_VERSION_MINOR": minor, 138*d4726bddSHONG Yifan "CARGO_PKG_VERSION_PATCH": patch, 139*d4726bddSHONG Yifan "CARGO_PKG_VERSION_PRE": pre, 140*d4726bddSHONG Yifan } 141*d4726bddSHONG Yifan if hasattr(attr, "_is_proc_macro_dep_enabled") and attr._is_proc_macro_dep_enabled[IsProcMacroDepEnabledInfo].enabled: 142*d4726bddSHONG Yifan is_proc_macro_dep = "0" 143*d4726bddSHONG Yifan if hasattr(attr, "_is_proc_macro_dep") and attr._is_proc_macro_dep[IsProcMacroDepInfo].is_proc_macro_dep: 144*d4726bddSHONG Yifan is_proc_macro_dep = "1" 145*d4726bddSHONG Yifan result["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"] = is_proc_macro_dep 146*d4726bddSHONG Yifan return result 147*d4726bddSHONG Yifan 148*d4726bddSHONG Yifandef get_compilation_mode_opts(ctx, toolchain): 149*d4726bddSHONG Yifan """Gathers rustc flags for the current compilation mode (opt/debug) 150*d4726bddSHONG Yifan 151*d4726bddSHONG Yifan Args: 152*d4726bddSHONG Yifan ctx (ctx): The current rule's context object 153*d4726bddSHONG Yifan toolchain (rust_toolchain): The current rule's `rust_toolchain` 154*d4726bddSHONG Yifan 155*d4726bddSHONG Yifan Returns: 156*d4726bddSHONG Yifan struct: See `_rust_toolchain_impl` for more details 157*d4726bddSHONG Yifan """ 158*d4726bddSHONG Yifan comp_mode = ctx.var["COMPILATION_MODE"] 159*d4726bddSHONG Yifan if not comp_mode in toolchain.compilation_mode_opts: 160*d4726bddSHONG Yifan fail("Unrecognized compilation mode {} for toolchain.".format(comp_mode)) 161*d4726bddSHONG Yifan 162*d4726bddSHONG Yifan return toolchain.compilation_mode_opts[comp_mode] 163*d4726bddSHONG Yifan 164*d4726bddSHONG Yifandef _are_linkstamps_supported(feature_configuration, has_grep_includes): 165*d4726bddSHONG Yifan # Are linkstamps supported by the C++ toolchain? 166*d4726bddSHONG Yifan return (cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "linkstamps") and 167*d4726bddSHONG Yifan # Is Bazel recent enough to support Starlark linkstamps? 168*d4726bddSHONG Yifan hasattr(cc_common, "register_linkstamp_compile_action") and 169*d4726bddSHONG Yifan # The current rule doesn't define _grep_includes attribute; this 170*d4726bddSHONG Yifan # attribute is required for compiling linkstamps. 171*d4726bddSHONG Yifan has_grep_includes) 172*d4726bddSHONG Yifan 173*d4726bddSHONG Yifandef _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode): 174*d4726bddSHONG Yifan """Whether or not [PIC][pic] should be enabled 175*d4726bddSHONG Yifan 176*d4726bddSHONG Yifan [pic]: https://en.wikipedia.org/wiki/Position-independent_code 177*d4726bddSHONG Yifan 178*d4726bddSHONG Yifan Args: 179*d4726bddSHONG Yifan cc_toolchain (CcToolchainInfo): The current `cc_toolchain`. 180*d4726bddSHONG Yifan feature_configuration (FeatureConfiguration): Feature configuration to be queried. 181*d4726bddSHONG Yifan crate_type (str): A Rust target's crate type. 182*d4726bddSHONG Yifan compilation_mode: The compilation mode. 183*d4726bddSHONG Yifan 184*d4726bddSHONG Yifan Returns: 185*d4726bddSHONG Yifan bool: Whether or not [PIC][pic] should be enabled. 186*d4726bddSHONG Yifan """ 187*d4726bddSHONG Yifan 188*d4726bddSHONG Yifan # We use the same logic to select between `pic` and `nopic` outputs as the C++ rules: 189*d4726bddSHONG Yifan # - For shared libraries - we use `pic`. This covers `dylib`, `cdylib` and `proc-macro` crate types. 190*d4726bddSHONG Yifan # - In `fastbuild` and `dbg` mode we use `pic` by default. 191*d4726bddSHONG Yifan # - In `opt` mode we use `nopic` outputs to build binaries. 192*d4726bddSHONG Yifan if crate_type in ("cdylib", "dylib", "proc-macro"): 193*d4726bddSHONG Yifan return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) 194*d4726bddSHONG Yifan elif compilation_mode in ("fastbuild", "dbg"): 195*d4726bddSHONG Yifan return True 196*d4726bddSHONG Yifan return False 197*d4726bddSHONG Yifan 198*d4726bddSHONG Yifandef _is_proc_macro(crate_info): 199*d4726bddSHONG Yifan return "proc-macro" in (crate_info.type, crate_info.wrapped_crate_type) 200*d4726bddSHONG Yifan 201*d4726bddSHONG Yifandef collect_deps( 202*d4726bddSHONG Yifan deps, 203*d4726bddSHONG Yifan proc_macro_deps, 204*d4726bddSHONG Yifan aliases): 205*d4726bddSHONG Yifan """Walks through dependencies and collects the transitive dependencies. 206*d4726bddSHONG Yifan 207*d4726bddSHONG Yifan Args: 208*d4726bddSHONG Yifan deps (list): The deps from ctx.attr.deps. 209*d4726bddSHONG Yifan proc_macro_deps (list): The proc_macro deps from ctx.attr.proc_macro_deps. 210*d4726bddSHONG Yifan aliases (dict): A dict mapping aliased targets to their actual Crate information. 211*d4726bddSHONG Yifan 212*d4726bddSHONG Yifan Returns: 213*d4726bddSHONG Yifan tuple: Returns a tuple of: 214*d4726bddSHONG Yifan DepInfo, 215*d4726bddSHONG Yifan BuildInfo, 216*d4726bddSHONG Yifan linkstamps (depset[CcLinkstamp]): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries when applicable. 217*d4726bddSHONG Yifan 218*d4726bddSHONG Yifan """ 219*d4726bddSHONG Yifan direct_crates = [] 220*d4726bddSHONG Yifan transitive_crates = [] 221*d4726bddSHONG Yifan transitive_data = [] 222*d4726bddSHONG Yifan transitive_proc_macro_data = [] 223*d4726bddSHONG Yifan transitive_noncrates = [] 224*d4726bddSHONG Yifan transitive_build_infos = [] 225*d4726bddSHONG Yifan transitive_link_search_paths = [] 226*d4726bddSHONG Yifan build_info = None 227*d4726bddSHONG Yifan linkstamps = [] 228*d4726bddSHONG Yifan transitive_crate_outputs = [] 229*d4726bddSHONG Yifan transitive_metadata_outputs = [] 230*d4726bddSHONG Yifan 231*d4726bddSHONG Yifan crate_deps = [] 232*d4726bddSHONG Yifan for dep in depset(transitive = [deps, proc_macro_deps]).to_list(): 233*d4726bddSHONG Yifan crate_group = None 234*d4726bddSHONG Yifan 235*d4726bddSHONG Yifan if type(dep) == "Target" and rust_common.crate_group_info in dep: 236*d4726bddSHONG Yifan crate_group = dep[rust_common.crate_group_info] 237*d4726bddSHONG Yifan elif type(dep) == "struct" and hasattr(dep, "crate_group_info") and dep.crate_group_info != None: 238*d4726bddSHONG Yifan crate_group = dep.crate_group_info 239*d4726bddSHONG Yifan else: 240*d4726bddSHONG Yifan crate_deps.append(dep) 241*d4726bddSHONG Yifan 242*d4726bddSHONG Yifan if crate_group: 243*d4726bddSHONG Yifan for dep_variant_info in crate_group.dep_variant_infos.to_list(): 244*d4726bddSHONG Yifan crate_deps.append(struct( 245*d4726bddSHONG Yifan crate_info = dep_variant_info.crate_info, 246*d4726bddSHONG Yifan dep_info = dep_variant_info.dep_info, 247*d4726bddSHONG Yifan cc_info = dep_variant_info.cc_info, 248*d4726bddSHONG Yifan )) 249*d4726bddSHONG Yifan 250*d4726bddSHONG Yifan aliases = {k.label: v for k, v in aliases.items()} 251*d4726bddSHONG Yifan for dep in crate_deps: 252*d4726bddSHONG Yifan (crate_info, dep_info) = _get_crate_and_dep_info(dep) 253*d4726bddSHONG Yifan cc_info = _get_cc_info(dep) 254*d4726bddSHONG Yifan dep_build_info = _get_build_info(dep) 255*d4726bddSHONG Yifan 256*d4726bddSHONG Yifan if cc_info: 257*d4726bddSHONG Yifan linkstamps.append(cc_info.linking_context.linkstamps()) 258*d4726bddSHONG Yifan 259*d4726bddSHONG Yifan if crate_info: 260*d4726bddSHONG Yifan # This dependency is a rust_library 261*d4726bddSHONG Yifan 262*d4726bddSHONG Yifan # When crate_info.owner is set, we use it. When the dep type is Target we get the 263*d4726bddSHONG Yifan # label from dep.label 264*d4726bddSHONG Yifan owner = getattr(crate_info, "owner", dep.label if type(dep) == "Target" else None) 265*d4726bddSHONG Yifan 266*d4726bddSHONG Yifan direct_crates.append(AliasableDepInfo( 267*d4726bddSHONG Yifan name = aliases.get(owner, crate_info.name), 268*d4726bddSHONG Yifan dep = crate_info, 269*d4726bddSHONG Yifan )) 270*d4726bddSHONG Yifan 271*d4726bddSHONG Yifan transitive_crates.append( 272*d4726bddSHONG Yifan depset( 273*d4726bddSHONG Yifan [crate_info], 274*d4726bddSHONG Yifan transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crates], 275*d4726bddSHONG Yifan ), 276*d4726bddSHONG Yifan ) 277*d4726bddSHONG Yifan 278*d4726bddSHONG Yifan if _is_proc_macro(crate_info): 279*d4726bddSHONG Yifan # This crate's data and its non-macro dependencies' data are proc macro data. 280*d4726bddSHONG Yifan transitive_proc_macro_data.append(crate_info.data) 281*d4726bddSHONG Yifan transitive_proc_macro_data.append(dep_info.transitive_data) 282*d4726bddSHONG Yifan else: 283*d4726bddSHONG Yifan # This crate's proc macro dependencies' data are proc macro data. 284*d4726bddSHONG Yifan transitive_proc_macro_data.append(dep_info.transitive_proc_macro_data) 285*d4726bddSHONG Yifan 286*d4726bddSHONG Yifan # Track transitive non-macro data in case a proc macro depends on this crate. 287*d4726bddSHONG Yifan transitive_data.append(crate_info.data) 288*d4726bddSHONG Yifan transitive_data.append(dep_info.transitive_data) 289*d4726bddSHONG Yifan 290*d4726bddSHONG Yifan # If this dependency produces metadata, add it to the metadata outputs. 291*d4726bddSHONG Yifan # If it doesn't (for example a custom library that exports crate_info), 292*d4726bddSHONG Yifan # we depend on crate_info.output. 293*d4726bddSHONG Yifan depend_on = crate_info.metadata 294*d4726bddSHONG Yifan if not crate_info.metadata: 295*d4726bddSHONG Yifan depend_on = crate_info.output 296*d4726bddSHONG Yifan 297*d4726bddSHONG Yifan # If this dependency is a proc_macro, it still can be used for lib crates 298*d4726bddSHONG Yifan # that produce metadata. 299*d4726bddSHONG Yifan # In that case, we don't depend on its metadata dependencies. 300*d4726bddSHONG Yifan transitive_metadata_outputs.append( 301*d4726bddSHONG Yifan depset( 302*d4726bddSHONG Yifan [depend_on], 303*d4726bddSHONG Yifan transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_metadata_outputs], 304*d4726bddSHONG Yifan ), 305*d4726bddSHONG Yifan ) 306*d4726bddSHONG Yifan 307*d4726bddSHONG Yifan transitive_crate_outputs.append( 308*d4726bddSHONG Yifan depset( 309*d4726bddSHONG Yifan [crate_info.output], 310*d4726bddSHONG Yifan transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crate_outputs], 311*d4726bddSHONG Yifan ), 312*d4726bddSHONG Yifan ) 313*d4726bddSHONG Yifan 314*d4726bddSHONG Yifan if "proc-macro" not in [crate_info.type, crate_info.wrapped_crate_type]: 315*d4726bddSHONG Yifan transitive_noncrates.append(dep_info.transitive_noncrates) 316*d4726bddSHONG Yifan transitive_link_search_paths.append(dep_info.link_search_path_files) 317*d4726bddSHONG Yifan 318*d4726bddSHONG Yifan transitive_build_infos.append(dep_info.transitive_build_infos) 319*d4726bddSHONG Yifan elif cc_info or dep_build_info: 320*d4726bddSHONG Yifan if cc_info: 321*d4726bddSHONG Yifan # This dependency is a cc_library 322*d4726bddSHONG Yifan transitive_noncrates.append(cc_info.linking_context.linker_inputs) 323*d4726bddSHONG Yifan 324*d4726bddSHONG Yifan if dep_build_info: 325*d4726bddSHONG Yifan if build_info: 326*d4726bddSHONG Yifan fail("Several deps are providing build information, " + 327*d4726bddSHONG Yifan "only one is allowed in the dependencies") 328*d4726bddSHONG Yifan build_info = dep_build_info 329*d4726bddSHONG Yifan transitive_build_infos.append(depset([build_info])) 330*d4726bddSHONG Yifan if build_info.link_search_paths: 331*d4726bddSHONG Yifan transitive_link_search_paths.append(depset([build_info.link_search_paths])) 332*d4726bddSHONG Yifan else: 333*d4726bddSHONG Yifan fail("rust targets can only depend on rust_library, rust_*_library or cc_library " + 334*d4726bddSHONG Yifan "targets.") 335*d4726bddSHONG Yifan 336*d4726bddSHONG Yifan transitive_crates_depset = depset(transitive = transitive_crates) 337*d4726bddSHONG Yifan transitive_data_depset = depset(transitive = transitive_data) 338*d4726bddSHONG Yifan transitive_proc_macro_data_depset = depset(transitive = transitive_proc_macro_data) 339*d4726bddSHONG Yifan 340*d4726bddSHONG Yifan return ( 341*d4726bddSHONG Yifan rust_common.dep_info( 342*d4726bddSHONG Yifan direct_crates = depset(direct_crates), 343*d4726bddSHONG Yifan transitive_crates = transitive_crates_depset, 344*d4726bddSHONG Yifan transitive_data = transitive_data_depset, 345*d4726bddSHONG Yifan transitive_proc_macro_data = transitive_proc_macro_data_depset, 346*d4726bddSHONG Yifan transitive_noncrates = depset( 347*d4726bddSHONG Yifan transitive = transitive_noncrates, 348*d4726bddSHONG Yifan order = "topological", # dylib link flag ordering matters. 349*d4726bddSHONG Yifan ), 350*d4726bddSHONG Yifan transitive_crate_outputs = depset(transitive = transitive_crate_outputs), 351*d4726bddSHONG Yifan transitive_metadata_outputs = depset(transitive = transitive_metadata_outputs), 352*d4726bddSHONG Yifan transitive_build_infos = depset(transitive = transitive_build_infos), 353*d4726bddSHONG Yifan link_search_path_files = depset(transitive = transitive_link_search_paths), 354*d4726bddSHONG Yifan dep_env = build_info.dep_env if build_info else None, 355*d4726bddSHONG Yifan ), 356*d4726bddSHONG Yifan build_info, 357*d4726bddSHONG Yifan depset(transitive = linkstamps), 358*d4726bddSHONG Yifan ) 359*d4726bddSHONG Yifan 360*d4726bddSHONG Yifandef _collect_libs_from_linker_inputs(linker_inputs, use_pic): 361*d4726bddSHONG Yifan # TODO: We could let the user choose how to link, instead of always preferring to link static libraries. 362*d4726bddSHONG Yifan return [ 363*d4726bddSHONG Yifan get_preferred_artifact(lib, use_pic) 364*d4726bddSHONG Yifan for li in linker_inputs 365*d4726bddSHONG Yifan for lib in li.libraries 366*d4726bddSHONG Yifan ] 367*d4726bddSHONG Yifan 368*d4726bddSHONG Yifandef _get_crate_and_dep_info(dep): 369*d4726bddSHONG Yifan if type(dep) == "Target" and rust_common.crate_info in dep: 370*d4726bddSHONG Yifan return (dep[rust_common.crate_info], dep[rust_common.dep_info]) 371*d4726bddSHONG Yifan elif type(dep) == "struct" and hasattr(dep, "crate_info"): 372*d4726bddSHONG Yifan return (dep.crate_info, dep.dep_info) 373*d4726bddSHONG Yifan return (None, None) 374*d4726bddSHONG Yifan 375*d4726bddSHONG Yifandef _get_cc_info(dep): 376*d4726bddSHONG Yifan if type(dep) == "Target" and CcInfo in dep: 377*d4726bddSHONG Yifan return dep[CcInfo] 378*d4726bddSHONG Yifan elif type(dep) == "struct" and hasattr(dep, "cc_info"): 379*d4726bddSHONG Yifan return dep.cc_info 380*d4726bddSHONG Yifan return None 381*d4726bddSHONG Yifan 382*d4726bddSHONG Yifandef _get_build_info(dep): 383*d4726bddSHONG Yifan if type(dep) == "Target" and BuildInfo in dep: 384*d4726bddSHONG Yifan return dep[BuildInfo] 385*d4726bddSHONG Yifan elif type(dep) == "struct" and hasattr(dep, "build_info"): 386*d4726bddSHONG Yifan return dep.build_info 387*d4726bddSHONG Yifan return None 388*d4726bddSHONG Yifan 389*d4726bddSHONG Yifandef get_cc_user_link_flags(ctx): 390*d4726bddSHONG Yifan """Get the current target's linkopt flags 391*d4726bddSHONG Yifan 392*d4726bddSHONG Yifan Args: 393*d4726bddSHONG Yifan ctx (ctx): The current rule's context object 394*d4726bddSHONG Yifan 395*d4726bddSHONG Yifan Returns: 396*d4726bddSHONG Yifan depset: The flags passed to Bazel by --linkopt option. 397*d4726bddSHONG Yifan """ 398*d4726bddSHONG Yifan return ctx.fragments.cpp.linkopts 399*d4726bddSHONG Yifan 400*d4726bddSHONG Yifandef get_linker_and_args(ctx, attr, crate_type, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = False): 401*d4726bddSHONG Yifan """Gathers cc_common linker information 402*d4726bddSHONG Yifan 403*d4726bddSHONG Yifan Args: 404*d4726bddSHONG Yifan ctx (ctx): The current target's context object 405*d4726bddSHONG Yifan attr (struct): Attributes to use in gathering linker args 406*d4726bddSHONG Yifan crate_type (str): The target crate's type (i.e. "bin", "proc-macro", etc.). 407*d4726bddSHONG Yifan cc_toolchain (CcToolchain): cc_toolchain for which we are creating build variables. 408*d4726bddSHONG Yifan feature_configuration (FeatureConfiguration): Feature configuration to be queried. 409*d4726bddSHONG Yifan rpaths (depset): Depset of directories where loader will look for libraries at runtime. 410*d4726bddSHONG Yifan add_flags_for_binary (bool, optional): Whether to add "bin" link flags to the command regardless of `crate_type`. 411*d4726bddSHONG Yifan 412*d4726bddSHONG Yifan 413*d4726bddSHONG Yifan Returns: 414*d4726bddSHONG Yifan tuple: A tuple of the following items: 415*d4726bddSHONG Yifan - (str): The tool path for given action. 416*d4726bddSHONG Yifan - (sequence): A flattened command line flags for given action. 417*d4726bddSHONG Yifan - (dict): Environment variables to be set for given action. 418*d4726bddSHONG Yifan """ 419*d4726bddSHONG Yifan user_link_flags = get_cc_user_link_flags(ctx) 420*d4726bddSHONG Yifan 421*d4726bddSHONG Yifan if crate_type in ("bin") or add_flags_for_binary: 422*d4726bddSHONG Yifan is_linking_dynamic_library = False 423*d4726bddSHONG Yifan action_name = CPP_LINK_EXECUTABLE_ACTION_NAME 424*d4726bddSHONG Yifan elif crate_type in ("dylib"): 425*d4726bddSHONG Yifan is_linking_dynamic_library = True 426*d4726bddSHONG Yifan action_name = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME 427*d4726bddSHONG Yifan elif crate_type in ("staticlib"): 428*d4726bddSHONG Yifan is_linking_dynamic_library = False 429*d4726bddSHONG Yifan action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME 430*d4726bddSHONG Yifan elif crate_type in ("cdylib", "proc-macro"): 431*d4726bddSHONG Yifan # Proc macros get compiled as shared libraries to be loaded by the compiler. 432*d4726bddSHONG Yifan is_linking_dynamic_library = True 433*d4726bddSHONG Yifan action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME 434*d4726bddSHONG Yifan elif crate_type in ("lib", "rlib"): 435*d4726bddSHONG Yifan fail("Invalid `crate_type` for linking action: {}".format(crate_type)) 436*d4726bddSHONG Yifan else: 437*d4726bddSHONG Yifan fail("Unknown `crate_type`: {}".format(crate_type)) 438*d4726bddSHONG Yifan 439*d4726bddSHONG Yifan # Add linkopts from dependencies. This includes linkopts from transitive 440*d4726bddSHONG Yifan # dependencies since they get merged up. 441*d4726bddSHONG Yifan for dep in getattr(attr, "deps", []): 442*d4726bddSHONG Yifan if CcInfo in dep and dep[CcInfo].linking_context: 443*d4726bddSHONG Yifan for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list(): 444*d4726bddSHONG Yifan for flag in linker_input.user_link_flags: 445*d4726bddSHONG Yifan user_link_flags.append(flag) 446*d4726bddSHONG Yifan link_variables = cc_common.create_link_variables( 447*d4726bddSHONG Yifan feature_configuration = feature_configuration, 448*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 449*d4726bddSHONG Yifan is_linking_dynamic_library = is_linking_dynamic_library, 450*d4726bddSHONG Yifan runtime_library_search_directories = rpaths, 451*d4726bddSHONG Yifan user_link_flags = user_link_flags, 452*d4726bddSHONG Yifan ) 453*d4726bddSHONG Yifan link_args = cc_common.get_memory_inefficient_command_line( 454*d4726bddSHONG Yifan feature_configuration = feature_configuration, 455*d4726bddSHONG Yifan action_name = action_name, 456*d4726bddSHONG Yifan variables = link_variables, 457*d4726bddSHONG Yifan ) 458*d4726bddSHONG Yifan link_env = cc_common.get_environment_variables( 459*d4726bddSHONG Yifan feature_configuration = feature_configuration, 460*d4726bddSHONG Yifan action_name = action_name, 461*d4726bddSHONG Yifan variables = link_variables, 462*d4726bddSHONG Yifan ) 463*d4726bddSHONG Yifan ld = cc_common.get_tool_for_action( 464*d4726bddSHONG Yifan feature_configuration = feature_configuration, 465*d4726bddSHONG Yifan action_name = action_name, 466*d4726bddSHONG Yifan ) 467*d4726bddSHONG Yifan 468*d4726bddSHONG Yifan return ld, link_args, link_env 469*d4726bddSHONG Yifan 470*d4726bddSHONG Yifandef _process_build_scripts( 471*d4726bddSHONG Yifan build_info, 472*d4726bddSHONG Yifan dep_info, 473*d4726bddSHONG Yifan compile_inputs, 474*d4726bddSHONG Yifan include_link_flags = True): 475*d4726bddSHONG Yifan """Gathers the outputs from a target's `cargo_build_script` action. 476*d4726bddSHONG Yifan 477*d4726bddSHONG Yifan Args: 478*d4726bddSHONG Yifan build_info (BuildInfo): The target Build's dependency info. 479*d4726bddSHONG Yifan dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs. 480*d4726bddSHONG Yifan compile_inputs (depset): A set of all files that will participate in the build. 481*d4726bddSHONG Yifan include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library. 482*d4726bddSHONG Yifan 483*d4726bddSHONG Yifan Returns: 484*d4726bddSHONG Yifan tuple: A tuple: A tuple of the following items: 485*d4726bddSHONG Yifan - (depset[File]): A list of all build info `OUT_DIR` File objects 486*d4726bddSHONG Yifan - (str): The `OUT_DIR` of the current build info 487*d4726bddSHONG Yifan - (File): An optional path to a generated environment file from a `cargo_build_script` target 488*d4726bddSHONG Yifan - (depset[File]): All direct and transitive build flags from the current build info. 489*d4726bddSHONG Yifan """ 490*d4726bddSHONG Yifan extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(build_info, dep_info, include_link_flags = include_link_flags) 491*d4726bddSHONG Yifan compile_inputs = depset(transitive = [extra_inputs, compile_inputs]) 492*d4726bddSHONG Yifan return compile_inputs, out_dir, build_env_file, build_flags_files 493*d4726bddSHONG Yifan 494*d4726bddSHONG Yifandef _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib): 495*d4726bddSHONG Yifan """Constructs a disambiguating symlink for a library dependency. 496*d4726bddSHONG Yifan 497*d4726bddSHONG Yifan Args: 498*d4726bddSHONG Yifan actions (Actions): The rule's context actions object. 499*d4726bddSHONG Yifan toolchain: The Rust toolchain object. 500*d4726bddSHONG Yifan crate_info (CrateInfo): The target crate's info. 501*d4726bddSHONG Yifan lib (File): The library to symlink to. 502*d4726bddSHONG Yifan 503*d4726bddSHONG Yifan Returns: 504*d4726bddSHONG Yifan (File): The disambiguating symlink for the library. 505*d4726bddSHONG Yifan """ 506*d4726bddSHONG Yifan # FIXME: Once the relative order part of the native-link-modifiers rustc 507*d4726bddSHONG Yifan # feature is stable, we should be able to eliminate the need to construct 508*d4726bddSHONG Yifan # symlinks by passing the full paths to the libraries. 509*d4726bddSHONG Yifan # https://github.com/rust-lang/rust/issues/81490. 510*d4726bddSHONG Yifan 511*d4726bddSHONG Yifan # Take the absolute value of hash() since it could be negative. 512*d4726bddSHONG Yifan path_hash = abs(hash(lib.path)) 513*d4726bddSHONG Yifan lib_name = get_lib_name_for_windows(lib) if toolchain.target_os.startswith("windows") else get_lib_name_default(lib) 514*d4726bddSHONG Yifan 515*d4726bddSHONG Yifan if toolchain.target_os.startswith("windows"): 516*d4726bddSHONG Yifan prefix = "" 517*d4726bddSHONG Yifan extension = ".lib" 518*d4726bddSHONG Yifan elif lib_name.endswith(".pic"): 519*d4726bddSHONG Yifan # Strip the .pic suffix 520*d4726bddSHONG Yifan lib_name = lib_name[:-4] 521*d4726bddSHONG Yifan prefix = "lib" 522*d4726bddSHONG Yifan extension = ".pic.a" 523*d4726bddSHONG Yifan else: 524*d4726bddSHONG Yifan prefix = "lib" 525*d4726bddSHONG Yifan extension = ".a" 526*d4726bddSHONG Yifan 527*d4726bddSHONG Yifan # Ensure the symlink follows the lib<name>.a pattern on Unix-like platforms 528*d4726bddSHONG Yifan # or <name>.lib on Windows. 529*d4726bddSHONG Yifan # Add a hash of the original library path to disambiguate libraries with the same basename. 530*d4726bddSHONG Yifan symlink_name = "{}{}-{}{}".format(prefix, lib_name, path_hash, extension) 531*d4726bddSHONG Yifan 532*d4726bddSHONG Yifan # Add the symlink to a target crate-specific _ambiguous_libs/ subfolder, 533*d4726bddSHONG Yifan # to avoid possible collisions with sibling crates that may depend on the 534*d4726bddSHONG Yifan # same ambiguous libraries. 535*d4726bddSHONG Yifan symlink = actions.declare_file("_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name) 536*d4726bddSHONG Yifan actions.symlink( 537*d4726bddSHONG Yifan output = symlink, 538*d4726bddSHONG Yifan target_file = lib, 539*d4726bddSHONG Yifan progress_message = "Creating symlink to ambiguous lib: {}".format(lib.path), 540*d4726bddSHONG Yifan ) 541*d4726bddSHONG Yifan return symlink 542*d4726bddSHONG Yifan 543*d4726bddSHONG Yifandef _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic): 544*d4726bddSHONG Yifan """Constructs disambiguating symlinks for ambiguous library dependencies. 545*d4726bddSHONG Yifan 546*d4726bddSHONG Yifan The symlinks are all created in a _ambiguous_libs/ subfolder specific to 547*d4726bddSHONG Yifan the target crate to avoid possible collisions with sibling crates that may 548*d4726bddSHONG Yifan depend on the same ambiguous libraries. 549*d4726bddSHONG Yifan 550*d4726bddSHONG Yifan Args: 551*d4726bddSHONG Yifan actions (Actions): The rule's context actions object. 552*d4726bddSHONG Yifan toolchain: The Rust toolchain object. 553*d4726bddSHONG Yifan crate_info (CrateInfo): The target crate's info. 554*d4726bddSHONG Yifan dep_info: (DepInfo): The target crate's dependency info. 555*d4726bddSHONG Yifan use_pic: (boolean): Whether the build should use PIC. 556*d4726bddSHONG Yifan 557*d4726bddSHONG Yifan Returns: 558*d4726bddSHONG Yifan dict[String, File]: A mapping from ambiguous library paths to their 559*d4726bddSHONG Yifan disambiguating symlink. 560*d4726bddSHONG Yifan """ 561*d4726bddSHONG Yifan # FIXME: Once the relative order part of the native-link-modifiers rustc 562*d4726bddSHONG Yifan # feature is stable, we should be able to eliminate the need to construct 563*d4726bddSHONG Yifan # symlinks by passing the full paths to the libraries. 564*d4726bddSHONG Yifan # https://github.com/rust-lang/rust/issues/81490. 565*d4726bddSHONG Yifan 566*d4726bddSHONG Yifan # A dictionary from file paths of ambiguous libraries to the corresponding 567*d4726bddSHONG Yifan # symlink. 568*d4726bddSHONG Yifan ambiguous_libs = {} 569*d4726bddSHONG Yifan 570*d4726bddSHONG Yifan # A dictionary maintaining a mapping from preferred library name to the 571*d4726bddSHONG Yifan # last visited artifact with that name. 572*d4726bddSHONG Yifan visited_libs = {} 573*d4726bddSHONG Yifan for link_input in dep_info.transitive_noncrates.to_list(): 574*d4726bddSHONG Yifan for lib in link_input.libraries: 575*d4726bddSHONG Yifan # FIXME: Dynamic libs are not disambiguated right now, there are 576*d4726bddSHONG Yifan # cases where those have a non-standard name with version (e.g., 577*d4726bddSHONG Yifan # //test/unit/versioned_libs). We hope that the link modifiers 578*d4726bddSHONG Yifan # stabilization will come before we need to make this work. 579*d4726bddSHONG Yifan if _is_dylib(lib): 580*d4726bddSHONG Yifan continue 581*d4726bddSHONG Yifan artifact = get_preferred_artifact(lib, use_pic) 582*d4726bddSHONG Yifan name = get_lib_name_for_windows(artifact) if toolchain.target_os.startswith("windows") else get_lib_name_default(artifact) 583*d4726bddSHONG Yifan 584*d4726bddSHONG Yifan # On Linux-like platforms, normally library base names start with 585*d4726bddSHONG Yifan # `lib`, following the pattern `lib[name].(a|lo)` and we pass 586*d4726bddSHONG Yifan # -lstatic=name. 587*d4726bddSHONG Yifan # On Windows, the base name looks like `name.lib` and we pass 588*d4726bddSHONG Yifan # -lstatic=name. 589*d4726bddSHONG Yifan # FIXME: Under the native-link-modifiers unstable rustc feature, 590*d4726bddSHONG Yifan # we could use -lstatic:+verbatim instead. 591*d4726bddSHONG Yifan needs_symlink_to_standardize_name = ( 592*d4726bddSHONG Yifan toolchain.target_os.startswith(("linux", "mac", "darwin")) and 593*d4726bddSHONG Yifan artifact.basename.endswith(".a") and not artifact.basename.startswith("lib") 594*d4726bddSHONG Yifan ) or ( 595*d4726bddSHONG Yifan toolchain.target_os.startswith("windows") and not artifact.basename.endswith(".lib") 596*d4726bddSHONG Yifan ) 597*d4726bddSHONG Yifan 598*d4726bddSHONG Yifan # Detect cases where we need to disambiguate library dependencies 599*d4726bddSHONG Yifan # by constructing symlinks. 600*d4726bddSHONG Yifan if ( 601*d4726bddSHONG Yifan needs_symlink_to_standardize_name or 602*d4726bddSHONG Yifan # We have multiple libraries with the same name. 603*d4726bddSHONG Yifan (name in visited_libs and visited_libs[name].path != artifact.path) 604*d4726bddSHONG Yifan ): 605*d4726bddSHONG Yifan # Disambiguate the previously visited library (if we just detected 606*d4726bddSHONG Yifan # that it is ambiguous) and the current library. 607*d4726bddSHONG Yifan if name in visited_libs: 608*d4726bddSHONG Yifan old_path = visited_libs[name].path 609*d4726bddSHONG Yifan if old_path not in ambiguous_libs: 610*d4726bddSHONG Yifan ambiguous_libs[old_path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, visited_libs[name]) 611*d4726bddSHONG Yifan ambiguous_libs[artifact.path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, artifact) 612*d4726bddSHONG Yifan 613*d4726bddSHONG Yifan visited_libs[name] = artifact 614*d4726bddSHONG Yifan return ambiguous_libs 615*d4726bddSHONG Yifan 616*d4726bddSHONG Yifandef _depend_on_metadata(crate_info, force_depend_on_objects): 617*d4726bddSHONG Yifan """Determines if we can depend on metadata for this crate. 618*d4726bddSHONG Yifan 619*d4726bddSHONG Yifan By default (when pipelining is disabled or when the crate type needs to link against 620*d4726bddSHONG Yifan objects) we depend on the set of object files (.rlib). 621*d4726bddSHONG Yifan When pipelining is enabled and the crate type supports depending on metadata, 622*d4726bddSHONG Yifan we depend on metadata files only (.rmeta). 623*d4726bddSHONG Yifan In some rare cases, even if both of those conditions are true, we still want to 624*d4726bddSHONG Yifan depend on objects. This is what force_depend_on_objects is. 625*d4726bddSHONG Yifan 626*d4726bddSHONG Yifan Args: 627*d4726bddSHONG Yifan crate_info (CrateInfo): The Crate to determine this for. 628*d4726bddSHONG Yifan force_depend_on_objects (bool): if set we will not depend on metadata. 629*d4726bddSHONG Yifan 630*d4726bddSHONG Yifan Returns: 631*d4726bddSHONG Yifan Whether we can depend on metadata for this crate. 632*d4726bddSHONG Yifan """ 633*d4726bddSHONG Yifan if force_depend_on_objects: 634*d4726bddSHONG Yifan return False 635*d4726bddSHONG Yifan 636*d4726bddSHONG Yifan return crate_info.type in ("rlib", "lib") 637*d4726bddSHONG Yifan 638*d4726bddSHONG Yifandef collect_inputs( 639*d4726bddSHONG Yifan ctx, 640*d4726bddSHONG Yifan file, 641*d4726bddSHONG Yifan files, 642*d4726bddSHONG Yifan linkstamps, 643*d4726bddSHONG Yifan toolchain, 644*d4726bddSHONG Yifan cc_toolchain, 645*d4726bddSHONG Yifan feature_configuration, 646*d4726bddSHONG Yifan crate_info, 647*d4726bddSHONG Yifan dep_info, 648*d4726bddSHONG Yifan build_info, 649*d4726bddSHONG Yifan stamp = False, 650*d4726bddSHONG Yifan force_depend_on_objects = False, 651*d4726bddSHONG Yifan experimental_use_cc_common_link = False, 652*d4726bddSHONG Yifan include_link_flags = True): 653*d4726bddSHONG Yifan """Gather's the inputs and required input information for a rustc action 654*d4726bddSHONG Yifan 655*d4726bddSHONG Yifan Args: 656*d4726bddSHONG Yifan ctx (ctx): The rule's context object. 657*d4726bddSHONG Yifan file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`. 658*d4726bddSHONG Yifan files (list): A list of all inputs (`ctx.files`). 659*d4726bddSHONG Yifan linkstamps (depset): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries. 660*d4726bddSHONG Yifan toolchain (rust_toolchain): The current `rust_toolchain`. 661*d4726bddSHONG Yifan cc_toolchain (CcToolchainInfo): The current `cc_toolchain`. 662*d4726bddSHONG Yifan feature_configuration (FeatureConfiguration): Feature configuration to be queried. 663*d4726bddSHONG Yifan crate_info (CrateInfo): The Crate information of the crate to process build scripts for. 664*d4726bddSHONG Yifan dep_info (DepInfo): The target Crate's dependency information. 665*d4726bddSHONG Yifan build_info (BuildInfo): The target Crate's build settings. 666*d4726bddSHONG Yifan stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see 667*d4726bddSHONG Yifan https://docs.bazel.build/versions/main/user-manual.html#flag--stamp 668*d4726bddSHONG Yifan force_depend_on_objects (bool, optional): Forces dependencies of this rule to be objects rather than 669*d4726bddSHONG Yifan metadata, even for libraries. This is used in rustdoc tests. 670*d4726bddSHONG Yifan experimental_use_cc_common_link (bool, optional): Whether rules_rust uses cc_common.link to link 671*d4726bddSHONG Yifan rust binaries. 672*d4726bddSHONG Yifan include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library. 673*d4726bddSHONG Yifan 674*d4726bddSHONG Yifan Returns: 675*d4726bddSHONG Yifan tuple: A tuple: A tuple of the following items: 676*d4726bddSHONG Yifan - (list): A list of all build info `OUT_DIR` File objects 677*d4726bddSHONG Yifan - (str): The `OUT_DIR` of the current build info 678*d4726bddSHONG Yifan - (File): An optional path to a generated environment file from a `cargo_build_script` target 679*d4726bddSHONG Yifan - (depset[File]): All direct and transitive build flag files from the current build info 680*d4726bddSHONG Yifan - (list[File]): Linkstamp outputs 681*d4726bddSHONG Yifan - (dict[String, File]): Ambiguous libs, see `_disambiguate_libs`. 682*d4726bddSHONG Yifan """ 683*d4726bddSHONG Yifan linker_script = getattr(file, "linker_script") if hasattr(file, "linker_script") else None 684*d4726bddSHONG Yifan 685*d4726bddSHONG Yifan # TODO: As of writing this comment Bazel used Java CcToolchainInfo. 686*d4726bddSHONG Yifan # However there is ongoing work to rewrite provider in Starlark. 687*d4726bddSHONG Yifan # rules_rust is not coupled with Bazel release. Remove conditional and change to 688*d4726bddSHONG Yifan # _linker_files once Starlark CcToolchainInfo is visible to Bazel. 689*d4726bddSHONG Yifan # https://github.com/bazelbuild/rules_rust/issues/2425 690*d4726bddSHONG Yifan if hasattr(cc_toolchain, "_linker_files"): 691*d4726bddSHONG Yifan linker_depset = cc_toolchain._linker_files 692*d4726bddSHONG Yifan else: 693*d4726bddSHONG Yifan linker_depset = cc_toolchain.linker_files() 694*d4726bddSHONG Yifan compilation_mode = ctx.var["COMPILATION_MODE"] 695*d4726bddSHONG Yifan 696*d4726bddSHONG Yifan use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type, compilation_mode) 697*d4726bddSHONG Yifan 698*d4726bddSHONG Yifan # Pass linker inputs only for linking-like actions, not for example where 699*d4726bddSHONG Yifan # the output is rlib. This avoids quadratic behavior where transitive noncrates are 700*d4726bddSHONG Yifan # flattened on each transitive rust_library dependency. 701*d4726bddSHONG Yifan additional_transitive_inputs = [] 702*d4726bddSHONG Yifan ambiguous_libs = {} 703*d4726bddSHONG Yifan if crate_info.type not in ("lib", "rlib"): 704*d4726bddSHONG Yifan linker_inputs = dep_info.transitive_noncrates.to_list() 705*d4726bddSHONG Yifan ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic) 706*d4726bddSHONG Yifan additional_transitive_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [ 707*d4726bddSHONG Yifan additional_input 708*d4726bddSHONG Yifan for linker_input in linker_inputs 709*d4726bddSHONG Yifan for additional_input in linker_input.additional_inputs 710*d4726bddSHONG Yifan ] + ambiguous_libs.values() 711*d4726bddSHONG Yifan 712*d4726bddSHONG Yifan # Compute linkstamps. Use the inputs of the binary as inputs to the 713*d4726bddSHONG Yifan # linkstamp action to ensure linkstamps are rebuilt whenever binary inputs 714*d4726bddSHONG Yifan # change. 715*d4726bddSHONG Yifan linkstamp_outs = [] 716*d4726bddSHONG Yifan 717*d4726bddSHONG Yifan transitive_crate_outputs = dep_info.transitive_crate_outputs 718*d4726bddSHONG Yifan if _depend_on_metadata(crate_info, force_depend_on_objects): 719*d4726bddSHONG Yifan transitive_crate_outputs = dep_info.transitive_metadata_outputs 720*d4726bddSHONG Yifan 721*d4726bddSHONG Yifan build_info_inputs = [] 722*d4726bddSHONG Yifan if build_info: 723*d4726bddSHONG Yifan if build_info.rustc_env: 724*d4726bddSHONG Yifan build_info_inputs.append(build_info.rustc_env) 725*d4726bddSHONG Yifan if build_info.flags: 726*d4726bddSHONG Yifan build_info_inputs.append(build_info.flags) 727*d4726bddSHONG Yifan 728*d4726bddSHONG Yifan nolinkstamp_compile_inputs = depset( 729*d4726bddSHONG Yifan getattr(files, "data", []) + 730*d4726bddSHONG Yifan build_info_inputs + 731*d4726bddSHONG Yifan ([toolchain.target_json] if toolchain.target_json else []) + 732*d4726bddSHONG Yifan ([] if linker_script == None else [linker_script]), 733*d4726bddSHONG Yifan transitive = [ 734*d4726bddSHONG Yifan linker_depset, 735*d4726bddSHONG Yifan crate_info.srcs, 736*d4726bddSHONG Yifan transitive_crate_outputs, 737*d4726bddSHONG Yifan depset(additional_transitive_inputs), 738*d4726bddSHONG Yifan crate_info.compile_data, 739*d4726bddSHONG Yifan dep_info.transitive_proc_macro_data, 740*d4726bddSHONG Yifan toolchain.all_files, 741*d4726bddSHONG Yifan ], 742*d4726bddSHONG Yifan ) 743*d4726bddSHONG Yifan 744*d4726bddSHONG Yifan # Register linkstamps when linking with rustc (when linking with 745*d4726bddSHONG Yifan # cc_common.link linkstamps are handled by cc_common.link itself). 746*d4726bddSHONG Yifan if not experimental_use_cc_common_link and crate_info.type in ("bin", "cdylib"): 747*d4726bddSHONG Yifan # There is no other way to register an action for each member of a depset than 748*d4726bddSHONG Yifan # flattening the depset as of 2021-10-12. Luckily, usually there is only one linkstamp 749*d4726bddSHONG Yifan # in a build, and we only flatten the list on binary targets that perform transitive linking, 750*d4726bddSHONG Yifan # so it's extremely unlikely that this call to `to_list()` will ever be a performance 751*d4726bddSHONG Yifan # problem. 752*d4726bddSHONG Yifan for linkstamp in linkstamps.to_list(): 753*d4726bddSHONG Yifan # The linkstamp output path is based on the binary crate 754*d4726bddSHONG Yifan # name and the input linkstamp path. This is to disambiguate 755*d4726bddSHONG Yifan # the linkstamp outputs produced by multiple binary crates 756*d4726bddSHONG Yifan # that depend on the same linkstamp. We use the same pattern 757*d4726bddSHONG Yifan # for the output name as the one used by native cc rules. 758*d4726bddSHONG Yifan out_name = "_objs/" + crate_info.output.basename + "/" + linkstamp.file().path[:-len(linkstamp.file().extension)] + "o" 759*d4726bddSHONG Yifan linkstamp_out = ctx.actions.declare_file(out_name) 760*d4726bddSHONG Yifan linkstamp_outs.append(linkstamp_out) 761*d4726bddSHONG Yifan cc_common.register_linkstamp_compile_action( 762*d4726bddSHONG Yifan actions = ctx.actions, 763*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 764*d4726bddSHONG Yifan feature_configuration = feature_configuration, 765*d4726bddSHONG Yifan source_file = linkstamp.file(), 766*d4726bddSHONG Yifan output_file = linkstamp_out, 767*d4726bddSHONG Yifan compilation_inputs = linkstamp.hdrs(), 768*d4726bddSHONG Yifan inputs_for_validation = nolinkstamp_compile_inputs, 769*d4726bddSHONG Yifan label_replacement = str(ctx.label), 770*d4726bddSHONG Yifan output_replacement = crate_info.output.path, 771*d4726bddSHONG Yifan ) 772*d4726bddSHONG Yifan 773*d4726bddSHONG Yifan # If stamping is enabled include the volatile and stable status info file 774*d4726bddSHONG Yifan stamp_info = [ctx.version_file, ctx.info_file] if stamp else [] 775*d4726bddSHONG Yifan 776*d4726bddSHONG Yifan compile_inputs = depset( 777*d4726bddSHONG Yifan linkstamp_outs + stamp_info, 778*d4726bddSHONG Yifan transitive = [ 779*d4726bddSHONG Yifan nolinkstamp_compile_inputs, 780*d4726bddSHONG Yifan ], 781*d4726bddSHONG Yifan ) 782*d4726bddSHONG Yifan 783*d4726bddSHONG Yifan # For backwards compatibility, we also check the value of the `rustc_env_files` attribute when 784*d4726bddSHONG Yifan # `crate_info.rustc_env_files` is not populated. 785*d4726bddSHONG Yifan build_env_files = crate_info.rustc_env_files if crate_info.rustc_env_files else getattr(files, "rustc_env_files", []) 786*d4726bddSHONG Yifan compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(build_info, dep_info, compile_inputs, include_link_flags = include_link_flags) 787*d4726bddSHONG Yifan if build_env_file: 788*d4726bddSHONG Yifan build_env_files = [f for f in build_env_files] + [build_env_file] 789*d4726bddSHONG Yifan compile_inputs = depset(build_env_files, transitive = [compile_inputs]) 790*d4726bddSHONG Yifan return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs 791*d4726bddSHONG Yifan 792*d4726bddSHONG Yifandef construct_arguments( 793*d4726bddSHONG Yifan ctx, 794*d4726bddSHONG Yifan attr, 795*d4726bddSHONG Yifan file, 796*d4726bddSHONG Yifan toolchain, 797*d4726bddSHONG Yifan tool_path, 798*d4726bddSHONG Yifan cc_toolchain, 799*d4726bddSHONG Yifan feature_configuration, 800*d4726bddSHONG Yifan crate_info, 801*d4726bddSHONG Yifan dep_info, 802*d4726bddSHONG Yifan linkstamp_outs, 803*d4726bddSHONG Yifan ambiguous_libs, 804*d4726bddSHONG Yifan output_hash, 805*d4726bddSHONG Yifan rust_flags, 806*d4726bddSHONG Yifan out_dir, 807*d4726bddSHONG Yifan build_env_files, 808*d4726bddSHONG Yifan build_flags_files, 809*d4726bddSHONG Yifan emit = ["dep-info", "link"], 810*d4726bddSHONG Yifan force_all_deps_direct = False, 811*d4726bddSHONG Yifan add_flags_for_binary = False, 812*d4726bddSHONG Yifan include_link_flags = True, 813*d4726bddSHONG Yifan stamp = False, 814*d4726bddSHONG Yifan remap_path_prefix = "", 815*d4726bddSHONG Yifan use_json_output = False, 816*d4726bddSHONG Yifan build_metadata = False, 817*d4726bddSHONG Yifan force_depend_on_objects = False, 818*d4726bddSHONG Yifan skip_expanding_rustc_env = False): 819*d4726bddSHONG Yifan """Builds an Args object containing common rustc flags 820*d4726bddSHONG Yifan 821*d4726bddSHONG Yifan Args: 822*d4726bddSHONG Yifan ctx (ctx): The rule's context object 823*d4726bddSHONG Yifan attr (struct): The attributes for the target. These may be different from ctx.attr in an aspect context. 824*d4726bddSHONG Yifan file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`. 825*d4726bddSHONG Yifan toolchain (rust_toolchain): The current target's `rust_toolchain` 826*d4726bddSHONG Yifan tool_path (str): Path to rustc 827*d4726bddSHONG Yifan cc_toolchain (CcToolchain): The CcToolchain for the current target. 828*d4726bddSHONG Yifan feature_configuration (FeatureConfiguration): Class used to construct command lines from CROSSTOOL features. 829*d4726bddSHONG Yifan crate_info (CrateInfo): The CrateInfo provider of the target crate 830*d4726bddSHONG Yifan dep_info (DepInfo): The DepInfo provider of the target crate 831*d4726bddSHONG Yifan linkstamp_outs (list): Linkstamp outputs of native dependencies 832*d4726bddSHONG Yifan ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs` 833*d4726bddSHONG Yifan output_hash (str): The hashed path of the crate root 834*d4726bddSHONG Yifan rust_flags (list): Additional flags to pass to rustc 835*d4726bddSHONG Yifan out_dir (str): The path to the output directory for the target Crate. 836*d4726bddSHONG Yifan build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions. 837*d4726bddSHONG Yifan build_flags_files (depset): The output files of a `cargo_build_script` actions containing rustc build flags 838*d4726bddSHONG Yifan emit (list): Values for the --emit flag to rustc. 839*d4726bddSHONG Yifan force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern 840*d4726bddSHONG Yifan to the commandline as opposed to -L. 841*d4726bddSHONG Yifan add_flags_for_binary (bool, optional): Whether to add "bin" link flags to the command regardless of `emit` and `crate_type`. 842*d4726bddSHONG Yifan include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library. 843*d4726bddSHONG Yifan stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see 844*d4726bddSHONG Yifan https://docs.bazel.build/versions/main/user-manual.html#flag--stamp 845*d4726bddSHONG Yifan remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to None, no prefix will be set. 846*d4726bddSHONG Yifan use_json_output (bool): Have rustc emit json and process_wrapper parse json messages to output rendered output. 847*d4726bddSHONG Yifan build_metadata (bool): Generate CLI arguments for building *only* .rmeta files. This requires use_json_output. 848*d4726bddSHONG Yifan force_depend_on_objects (bool): Force using `.rlib` object files instead of metadata (`.rmeta`) files even if they are available. 849*d4726bddSHONG Yifan skip_expanding_rustc_env (bool): Whether to skip expanding CrateInfo.rustc_env_attr 850*d4726bddSHONG Yifan 851*d4726bddSHONG Yifan Returns: 852*d4726bddSHONG Yifan tuple: A tuple of the following items 853*d4726bddSHONG Yifan - (struct): A struct of arguments used to run the `Rustc` action 854*d4726bddSHONG Yifan - process_wrapper_flags (Args): Arguments for the process wrapper 855*d4726bddSHONG Yifan - rustc_path (Args): Arguments for invoking rustc via the process wrapper 856*d4726bddSHONG Yifan - rustc_flags (Args): Rust flags for the Rust compiler 857*d4726bddSHONG Yifan - all (list): A list of all `Args` objects in the order listed above. 858*d4726bddSHONG Yifan This is to be passed to the `arguments` parameter of actions 859*d4726bddSHONG Yifan - (dict): Common rustc environment variables 860*d4726bddSHONG Yifan """ 861*d4726bddSHONG Yifan if build_metadata and not use_json_output: 862*d4726bddSHONG Yifan fail("build_metadata requires parse_json_output") 863*d4726bddSHONG Yifan 864*d4726bddSHONG Yifan output_dir = getattr(crate_info.output, "dirname", None) 865*d4726bddSHONG Yifan linker_script = getattr(file, "linker_script", None) 866*d4726bddSHONG Yifan 867*d4726bddSHONG Yifan env = _get_rustc_env(attr, toolchain, crate_info.name) 868*d4726bddSHONG Yifan 869*d4726bddSHONG Yifan # Wrapper args first 870*d4726bddSHONG Yifan process_wrapper_flags = ctx.actions.args() 871*d4726bddSHONG Yifan 872*d4726bddSHONG Yifan for build_env_file in build_env_files: 873*d4726bddSHONG Yifan process_wrapper_flags.add("--env-file", build_env_file) 874*d4726bddSHONG Yifan 875*d4726bddSHONG Yifan process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file") 876*d4726bddSHONG Yifan 877*d4726bddSHONG Yifan # Certain rust build processes expect to find files from the environment 878*d4726bddSHONG Yifan # variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera, 879*d4726bddSHONG Yifan # asakuma. 880*d4726bddSHONG Yifan # 881*d4726bddSHONG Yifan # The compiler and by extension proc-macros see the current working 882*d4726bddSHONG Yifan # directory as the Bazel exec root. This is what `$CARGO_MANIFEST_DIR` 883*d4726bddSHONG Yifan # would default to but is often the wrong value (e.g. if the source is in a 884*d4726bddSHONG Yifan # sub-package or if we are building something in an external repository). 885*d4726bddSHONG Yifan # Hence, we need to set `CARGO_MANIFEST_DIR` explicitly. 886*d4726bddSHONG Yifan # 887*d4726bddSHONG Yifan # Since we cannot get the `exec_root` from starlark, we cheat a little and 888*d4726bddSHONG Yifan # use `${pwd}` which resolves the `exec_root` at action execution time. 889*d4726bddSHONG Yifan process_wrapper_flags.add("--subst", "pwd=${pwd}") 890*d4726bddSHONG Yifan 891*d4726bddSHONG Yifan # If stamping is enabled, enable the functionality in the process wrapper 892*d4726bddSHONG Yifan if stamp: 893*d4726bddSHONG Yifan process_wrapper_flags.add("--volatile-status-file", ctx.version_file) 894*d4726bddSHONG Yifan process_wrapper_flags.add("--stable-status-file", ctx.info_file) 895*d4726bddSHONG Yifan 896*d4726bddSHONG Yifan # Both ctx.label.workspace_root and ctx.label.package are relative paths 897*d4726bddSHONG Yifan # and either can be empty strings. Avoid trailing/double slashes in the path. 898*d4726bddSHONG Yifan components = "${{pwd}}/{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/") 899*d4726bddSHONG Yifan env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c]) 900*d4726bddSHONG Yifan 901*d4726bddSHONG Yifan if out_dir != None: 902*d4726bddSHONG Yifan env["OUT_DIR"] = "${pwd}/" + out_dir 903*d4726bddSHONG Yifan 904*d4726bddSHONG Yifan # Arguments for launching rustc from the process wrapper 905*d4726bddSHONG Yifan rustc_path = ctx.actions.args() 906*d4726bddSHONG Yifan rustc_path.add("--") 907*d4726bddSHONG Yifan rustc_path.add(tool_path) 908*d4726bddSHONG Yifan 909*d4726bddSHONG Yifan # Rustc arguments 910*d4726bddSHONG Yifan rustc_flags = ctx.actions.args() 911*d4726bddSHONG Yifan rustc_flags.set_param_file_format("multiline") 912*d4726bddSHONG Yifan rustc_flags.use_param_file("@%s", use_always = False) 913*d4726bddSHONG Yifan rustc_flags.add(crate_info.root) 914*d4726bddSHONG Yifan rustc_flags.add(crate_info.name, format = "--crate-name=%s") 915*d4726bddSHONG Yifan rustc_flags.add(crate_info.type, format = "--crate-type=%s") 916*d4726bddSHONG Yifan 917*d4726bddSHONG Yifan error_format = "human" 918*d4726bddSHONG Yifan if hasattr(attr, "_error_format"): 919*d4726bddSHONG Yifan error_format = attr._error_format[ErrorFormatInfo].error_format 920*d4726bddSHONG Yifan 921*d4726bddSHONG Yifan if use_json_output: 922*d4726bddSHONG Yifan # If --error-format was set to json, we just pass the output through 923*d4726bddSHONG Yifan # Otherwise process_wrapper uses the "rendered" field. 924*d4726bddSHONG Yifan process_wrapper_flags.add("--rustc-output-format", "json" if error_format == "json" else "rendered") 925*d4726bddSHONG Yifan 926*d4726bddSHONG Yifan # Configure rustc json output by adding artifact notifications. 927*d4726bddSHONG Yifan # These will always be filtered out by process_wrapper and will be use to terminate 928*d4726bddSHONG Yifan # rustc when appropriate. 929*d4726bddSHONG Yifan json = ["artifacts"] 930*d4726bddSHONG Yifan if error_format == "short": 931*d4726bddSHONG Yifan json.append("diagnostic-short") 932*d4726bddSHONG Yifan elif error_format == "human" and toolchain.target_os != "windows": 933*d4726bddSHONG Yifan # If the os is not windows, we can get colorized output. 934*d4726bddSHONG Yifan json.append("diagnostic-rendered-ansi") 935*d4726bddSHONG Yifan 936*d4726bddSHONG Yifan rustc_flags.add_joined(json, format_joined = "--json=%s", join_with = ",") 937*d4726bddSHONG Yifan 938*d4726bddSHONG Yifan error_format = "json" 939*d4726bddSHONG Yifan 940*d4726bddSHONG Yifan if build_metadata: 941*d4726bddSHONG Yifan # Configure process_wrapper to terminate rustc when metadata are emitted 942*d4726bddSHONG Yifan process_wrapper_flags.add("--rustc-quit-on-rmeta", "true") 943*d4726bddSHONG Yifan if crate_info.rustc_rmeta_output: 944*d4726bddSHONG Yifan process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output.path) 945*d4726bddSHONG Yifan elif crate_info.rustc_output: 946*d4726bddSHONG Yifan process_wrapper_flags.add("--output-file", crate_info.rustc_output.path) 947*d4726bddSHONG Yifan 948*d4726bddSHONG Yifan rustc_flags.add(error_format, format = "--error-format=%s") 949*d4726bddSHONG Yifan 950*d4726bddSHONG Yifan # Mangle symbols to disambiguate crates with the same name. This could 951*d4726bddSHONG Yifan # happen only for non-final artifacts where we compute an output_hash, 952*d4726bddSHONG Yifan # e.g., rust_library. 953*d4726bddSHONG Yifan # 954*d4726bddSHONG Yifan # For "final" artifacts and ones intended for distribution outside of 955*d4726bddSHONG Yifan # Bazel, such as rust_binary, rust_static_library and rust_shared_library, 956*d4726bddSHONG Yifan # where output_hash is None we don't need to add these flags. 957*d4726bddSHONG Yifan if output_hash: 958*d4726bddSHONG Yifan rustc_flags.add(output_hash, format = "--codegen=metadata=-%s") 959*d4726bddSHONG Yifan rustc_flags.add(output_hash, format = "--codegen=extra-filename=-%s") 960*d4726bddSHONG Yifan 961*d4726bddSHONG Yifan if output_dir: 962*d4726bddSHONG Yifan rustc_flags.add(output_dir, format = "--out-dir=%s") 963*d4726bddSHONG Yifan 964*d4726bddSHONG Yifan compilation_mode = get_compilation_mode_opts(ctx, toolchain) 965*d4726bddSHONG Yifan rustc_flags.add(compilation_mode.opt_level, format = "--codegen=opt-level=%s") 966*d4726bddSHONG Yifan rustc_flags.add(compilation_mode.debug_info, format = "--codegen=debuginfo=%s") 967*d4726bddSHONG Yifan rustc_flags.add(compilation_mode.strip_level, format = "--codegen=strip=%s") 968*d4726bddSHONG Yifan 969*d4726bddSHONG Yifan # For determinism to help with build distribution and such 970*d4726bddSHONG Yifan if remap_path_prefix != None: 971*d4726bddSHONG Yifan rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix)) 972*d4726bddSHONG Yifan 973*d4726bddSHONG Yifan emit_without_paths = [] 974*d4726bddSHONG Yifan for kind in emit: 975*d4726bddSHONG Yifan if kind == "link" and crate_info.type == "bin" and crate_info.output != None: 976*d4726bddSHONG Yifan rustc_flags.add(crate_info.output, format = "--emit=link=%s") 977*d4726bddSHONG Yifan else: 978*d4726bddSHONG Yifan emit_without_paths.append(kind) 979*d4726bddSHONG Yifan 980*d4726bddSHONG Yifan if emit_without_paths: 981*d4726bddSHONG Yifan rustc_flags.add_joined(emit_without_paths, format_joined = "--emit=%s", join_with = ",") 982*d4726bddSHONG Yifan if error_format != "json": 983*d4726bddSHONG Yifan # Color is not compatible with json output. 984*d4726bddSHONG Yifan rustc_flags.add("--color=always") 985*d4726bddSHONG Yifan rustc_flags.add(toolchain.target_flag_value, format = "--target=%s") 986*d4726bddSHONG Yifan if hasattr(attr, "crate_features"): 987*d4726bddSHONG Yifan rustc_flags.add_all(getattr(attr, "crate_features"), before_each = "--cfg", format_each = 'feature="%s"') 988*d4726bddSHONG Yifan if linker_script: 989*d4726bddSHONG Yifan rustc_flags.add(linker_script, format = "--codegen=link-arg=-T%s") 990*d4726bddSHONG Yifan 991*d4726bddSHONG Yifan # Tell Rustc where to find the standard library (or libcore) 992*d4726bddSHONG Yifan rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s") 993*d4726bddSHONG Yifan rustc_flags.add_all(rust_flags) 994*d4726bddSHONG Yifan 995*d4726bddSHONG Yifan # Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test 996*d4726bddSHONG Yifan # Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681 997*d4726bddSHONG Yifan data_paths = depset(direct = getattr(attr, "data", []), transitive = [crate_info.compile_data_targets]).to_list() 998*d4726bddSHONG Yifan 999*d4726bddSHONG Yifan rustc_flags.add_all( 1000*d4726bddSHONG Yifan expand_list_element_locations( 1001*d4726bddSHONG Yifan ctx, 1002*d4726bddSHONG Yifan getattr(attr, "rustc_flags", []), 1003*d4726bddSHONG Yifan data_paths, 1004*d4726bddSHONG Yifan ), 1005*d4726bddSHONG Yifan ) 1006*d4726bddSHONG Yifan add_edition_flags(rustc_flags, crate_info) 1007*d4726bddSHONG Yifan 1008*d4726bddSHONG Yifan # Link! 1009*d4726bddSHONG Yifan if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary: 1010*d4726bddSHONG Yifan # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc 1011*d4726bddSHONG Yifan # linker since it won't understand. 1012*d4726bddSHONG Yifan compilation_mode = ctx.var["COMPILATION_MODE"] 1013*d4726bddSHONG Yifan if toolchain.target_arch != "wasm32": 1014*d4726bddSHONG Yifan if output_dir: 1015*d4726bddSHONG Yifan use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type, compilation_mode) 1016*d4726bddSHONG Yifan rpaths = _compute_rpaths(toolchain, output_dir, dep_info, use_pic) 1017*d4726bddSHONG Yifan else: 1018*d4726bddSHONG Yifan rpaths = depset() 1019*d4726bddSHONG Yifan 1020*d4726bddSHONG Yifan ld, link_args, link_env = get_linker_and_args(ctx, attr, crate_info.type, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = add_flags_for_binary) 1021*d4726bddSHONG Yifan 1022*d4726bddSHONG Yifan env.update(link_env) 1023*d4726bddSHONG Yifan rustc_flags.add(ld, format = "--codegen=linker=%s") 1024*d4726bddSHONG Yifan 1025*d4726bddSHONG Yifan # Split link args into individual "--codegen=link-arg=" flags to handle nested spaces. 1026*d4726bddSHONG Yifan # Additional context: https://github.com/rust-lang/rust/pull/36574 1027*d4726bddSHONG Yifan rustc_flags.add_all(link_args, format_each = "--codegen=link-arg=%s") 1028*d4726bddSHONG Yifan 1029*d4726bddSHONG Yifan _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) 1030*d4726bddSHONG Yifan 1031*d4726bddSHONG Yifan use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects) 1032*d4726bddSHONG Yifan 1033*d4726bddSHONG Yifan # These always need to be added, even if not linking this crate. 1034*d4726bddSHONG Yifan add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct, use_metadata) 1035*d4726bddSHONG Yifan 1036*d4726bddSHONG Yifan needs_extern_proc_macro_flag = _is_proc_macro(crate_info) and crate_info.edition != "2015" 1037*d4726bddSHONG Yifan if needs_extern_proc_macro_flag: 1038*d4726bddSHONG Yifan rustc_flags.add("--extern") 1039*d4726bddSHONG Yifan rustc_flags.add("proc_macro") 1040*d4726bddSHONG Yifan 1041*d4726bddSHONG Yifan if toolchain.llvm_cov and ctx.configuration.coverage_enabled: 1042*d4726bddSHONG Yifan # https://doc.rust-lang.org/rustc/instrument-coverage.html 1043*d4726bddSHONG Yifan rustc_flags.add("--codegen=instrument-coverage") 1044*d4726bddSHONG Yifan 1045*d4726bddSHONG Yifan if toolchain._experimental_link_std_dylib: 1046*d4726bddSHONG Yifan rustc_flags.add("--codegen=prefer-dynamic") 1047*d4726bddSHONG Yifan 1048*d4726bddSHONG Yifan # Make bin crate data deps available to tests. 1049*d4726bddSHONG Yifan for data in getattr(attr, "data", []): 1050*d4726bddSHONG Yifan if rust_common.crate_info in data: 1051*d4726bddSHONG Yifan dep_crate_info = data[rust_common.crate_info] 1052*d4726bddSHONG Yifan if dep_crate_info.type == "bin": 1053*d4726bddSHONG Yifan # Trying to make CARGO_BIN_EXE_{} canonical across platform by strip out extension if exists 1054*d4726bddSHONG Yifan env_basename = dep_crate_info.output.basename[:-(1 + len(dep_crate_info.output.extension))] if len(dep_crate_info.output.extension) > 0 else dep_crate_info.output.basename 1055*d4726bddSHONG Yifan env["CARGO_BIN_EXE_" + env_basename] = dep_crate_info.output.short_path 1056*d4726bddSHONG Yifan 1057*d4726bddSHONG Yifan # Add environment variables from the Rust toolchain. 1058*d4726bddSHONG Yifan env.update(toolchain.env) 1059*d4726bddSHONG Yifan 1060*d4726bddSHONG Yifan # Update environment with user provided variables. 1061*d4726bddSHONG Yifan if skip_expanding_rustc_env: 1062*d4726bddSHONG Yifan env.update(crate_info.rustc_env) 1063*d4726bddSHONG Yifan else: 1064*d4726bddSHONG Yifan env.update(expand_dict_value_locations( 1065*d4726bddSHONG Yifan ctx, 1066*d4726bddSHONG Yifan crate_info.rustc_env, 1067*d4726bddSHONG Yifan data_paths, 1068*d4726bddSHONG Yifan )) 1069*d4726bddSHONG Yifan 1070*d4726bddSHONG Yifan # Ensure the sysroot is set for the target platform 1071*d4726bddSHONG Yifan if not toolchain._incompatible_no_rustc_sysroot_env: 1072*d4726bddSHONG Yifan env["SYSROOT"] = toolchain.sysroot 1073*d4726bddSHONG Yifan if toolchain._experimental_toolchain_generated_sysroot: 1074*d4726bddSHONG Yifan rustc_flags.add(toolchain.sysroot, format = "--sysroot=%s") 1075*d4726bddSHONG Yifan 1076*d4726bddSHONG Yifan if toolchain._rename_first_party_crates: 1077*d4726bddSHONG Yifan env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir 1078*d4726bddSHONG Yifan 1079*d4726bddSHONG Yifan if crate_info.type in toolchain.extra_rustc_flags_for_crate_types.keys(): 1080*d4726bddSHONG Yifan rustc_flags.add_all(toolchain.extra_rustc_flags_for_crate_types[crate_info.type]) 1081*d4726bddSHONG Yifan 1082*d4726bddSHONG Yifan if is_exec_configuration(ctx): 1083*d4726bddSHONG Yifan rustc_flags.add_all(toolchain.extra_exec_rustc_flags) 1084*d4726bddSHONG Yifan else: 1085*d4726bddSHONG Yifan rustc_flags.add_all(toolchain.extra_rustc_flags) 1086*d4726bddSHONG Yifan 1087*d4726bddSHONG Yifan # extra_rustc_flags apply to the target configuration, not the exec configuration. 1088*d4726bddSHONG Yifan if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx): 1089*d4726bddSHONG Yifan rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags) 1090*d4726bddSHONG Yifan 1091*d4726bddSHONG Yifan if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec_configuration(ctx): 1092*d4726bddSHONG Yifan rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags) 1093*d4726bddSHONG Yifan 1094*d4726bddSHONG Yifan if hasattr(ctx.attr, "_per_crate_rustc_flag") and not is_exec_configuration(ctx): 1095*d4726bddSHONG Yifan per_crate_rustc_flags = ctx.attr._per_crate_rustc_flag[PerCrateRustcFlagsInfo].per_crate_rustc_flags 1096*d4726bddSHONG Yifan _add_per_crate_rustc_flags(ctx, rustc_flags, crate_info, per_crate_rustc_flags) 1097*d4726bddSHONG Yifan 1098*d4726bddSHONG Yifan if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx): 1099*d4726bddSHONG Yifan rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags) 1100*d4726bddSHONG Yifan 1101*d4726bddSHONG Yifan if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec_configuration(ctx): 1102*d4726bddSHONG Yifan rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags) 1103*d4726bddSHONG Yifan 1104*d4726bddSHONG Yifan if _is_no_std(ctx, toolchain, crate_info): 1105*d4726bddSHONG Yifan rustc_flags.add('--cfg=feature="no_std"') 1106*d4726bddSHONG Yifan 1107*d4726bddSHONG Yifan # Needed for bzlmod-aware runfiles resolution. 1108*d4726bddSHONG Yifan env["REPOSITORY_NAME"] = ctx.label.workspace_name 1109*d4726bddSHONG Yifan 1110*d4726bddSHONG Yifan # Create a struct which keeps the arguments separate so each may be tuned or 1111*d4726bddSHONG Yifan # replaced where necessary 1112*d4726bddSHONG Yifan args = struct( 1113*d4726bddSHONG Yifan process_wrapper_flags = process_wrapper_flags, 1114*d4726bddSHONG Yifan rustc_path = rustc_path, 1115*d4726bddSHONG Yifan rustc_flags = rustc_flags, 1116*d4726bddSHONG Yifan all = [process_wrapper_flags, rustc_path, rustc_flags], 1117*d4726bddSHONG Yifan ) 1118*d4726bddSHONG Yifan 1119*d4726bddSHONG Yifan return args, env 1120*d4726bddSHONG Yifan 1121*d4726bddSHONG Yifandef rustc_compile_action( 1122*d4726bddSHONG Yifan ctx, 1123*d4726bddSHONG Yifan attr, 1124*d4726bddSHONG Yifan toolchain, 1125*d4726bddSHONG Yifan rust_flags = [], 1126*d4726bddSHONG Yifan output_hash = None, 1127*d4726bddSHONG Yifan force_all_deps_direct = False, 1128*d4726bddSHONG Yifan crate_info_dict = None, 1129*d4726bddSHONG Yifan skip_expanding_rustc_env = False, 1130*d4726bddSHONG Yifan include_coverage = True): 1131*d4726bddSHONG Yifan """Create and run a rustc compile action based on the current rule's attributes 1132*d4726bddSHONG Yifan 1133*d4726bddSHONG Yifan Args: 1134*d4726bddSHONG Yifan ctx (ctx): The rule's context object 1135*d4726bddSHONG Yifan attr (struct): Attributes to use for the rust compile action 1136*d4726bddSHONG Yifan toolchain (rust_toolchain): The current `rust_toolchain` 1137*d4726bddSHONG Yifan output_hash (str, optional): The hashed path of the crate root. Defaults to None. 1138*d4726bddSHONG Yifan rust_flags (list, optional): Additional flags to pass to rustc. Defaults to []. 1139*d4726bddSHONG Yifan force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern 1140*d4726bddSHONG Yifan to the commandline as opposed to -L. 1141*d4726bddSHONG Yifan crate_info_dict: A mutable dict used to create CrateInfo provider 1142*d4726bddSHONG Yifan skip_expanding_rustc_env (bool, optional): Whether to expand CrateInfo.rustc_env 1143*d4726bddSHONG Yifan include_coverage (bool, optional): Whether to generate coverage information or not. 1144*d4726bddSHONG Yifan 1145*d4726bddSHONG Yifan Returns: 1146*d4726bddSHONG Yifan list: A list of the following providers: 1147*d4726bddSHONG Yifan - (CrateInfo): info for the crate we just built; same as `crate_info` parameter. 1148*d4726bddSHONG Yifan - (DepInfo): The transitive dependencies of this crate. 1149*d4726bddSHONG Yifan - (DefaultInfo): The output file for this crate, and its runfiles. 1150*d4726bddSHONG Yifan """ 1151*d4726bddSHONG Yifan crate_info = rust_common.create_crate_info(**crate_info_dict) 1152*d4726bddSHONG Yifan 1153*d4726bddSHONG Yifan build_metadata = crate_info_dict.get("metadata", None) 1154*d4726bddSHONG Yifan rustc_output = crate_info_dict.get("rustc_output", None) 1155*d4726bddSHONG Yifan rustc_rmeta_output = crate_info_dict.get("rustc_rmeta_output", None) 1156*d4726bddSHONG Yifan 1157*d4726bddSHONG Yifan # Determine whether to use cc_common.link: 1158*d4726bddSHONG Yifan # * either if experimental_use_cc_common_link is 1, 1159*d4726bddSHONG Yifan # * or if experimental_use_cc_common_link is -1 and 1160*d4726bddSHONG Yifan # the toolchain experimental_use_cc_common_link is true. 1161*d4726bddSHONG Yifan experimental_use_cc_common_link = False 1162*d4726bddSHONG Yifan if hasattr(ctx.attr, "experimental_use_cc_common_link"): 1163*d4726bddSHONG Yifan if ctx.attr.experimental_use_cc_common_link == 0: 1164*d4726bddSHONG Yifan experimental_use_cc_common_link = False 1165*d4726bddSHONG Yifan elif ctx.attr.experimental_use_cc_common_link == 1: 1166*d4726bddSHONG Yifan experimental_use_cc_common_link = True 1167*d4726bddSHONG Yifan elif ctx.attr.experimental_use_cc_common_link == -1: 1168*d4726bddSHONG Yifan experimental_use_cc_common_link = toolchain._experimental_use_cc_common_link 1169*d4726bddSHONG Yifan 1170*d4726bddSHONG Yifan dep_info, build_info, linkstamps = collect_deps( 1171*d4726bddSHONG Yifan deps = crate_info_dict["deps"], 1172*d4726bddSHONG Yifan proc_macro_deps = crate_info_dict["proc_macro_deps"], 1173*d4726bddSHONG Yifan aliases = crate_info_dict["aliases"], 1174*d4726bddSHONG Yifan ) 1175*d4726bddSHONG Yifan extra_disabled_features = [RUST_LINK_CC_FEATURE] 1176*d4726bddSHONG Yifan if crate_info.type in ["bin", "cdylib"] and dep_info.transitive_noncrates.to_list(): 1177*d4726bddSHONG Yifan # One or more of the transitive deps is a cc_library / cc_import 1178*d4726bddSHONG Yifan extra_disabled_features = [] 1179*d4726bddSHONG Yifan cc_toolchain, feature_configuration = find_cc_toolchain(ctx, extra_disabled_features) 1180*d4726bddSHONG Yifan if not _are_linkstamps_supported( 1181*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1182*d4726bddSHONG Yifan has_grep_includes = hasattr(ctx.attr, "_use_grep_includes"), 1183*d4726bddSHONG Yifan ): 1184*d4726bddSHONG Yifan linkstamps = depset([]) 1185*d4726bddSHONG Yifan 1186*d4726bddSHONG Yifan # Determine if the build is currently running with --stamp 1187*d4726bddSHONG Yifan stamp = is_stamping_enabled(attr) 1188*d4726bddSHONG Yifan 1189*d4726bddSHONG Yifan compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( 1190*d4726bddSHONG Yifan ctx = ctx, 1191*d4726bddSHONG Yifan file = ctx.file, 1192*d4726bddSHONG Yifan files = ctx.files, 1193*d4726bddSHONG Yifan linkstamps = linkstamps, 1194*d4726bddSHONG Yifan toolchain = toolchain, 1195*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1196*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1197*d4726bddSHONG Yifan crate_info = crate_info, 1198*d4726bddSHONG Yifan dep_info = dep_info, 1199*d4726bddSHONG Yifan build_info = build_info, 1200*d4726bddSHONG Yifan stamp = stamp, 1201*d4726bddSHONG Yifan experimental_use_cc_common_link = experimental_use_cc_common_link, 1202*d4726bddSHONG Yifan ) 1203*d4726bddSHONG Yifan 1204*d4726bddSHONG Yifan # The types of rustc outputs to emit. 1205*d4726bddSHONG Yifan # If we build metadata, we need to keep the command line of the two invocations 1206*d4726bddSHONG Yifan # (rlib and rmeta) as similar as possible, otherwise rustc rejects the rmeta as 1207*d4726bddSHONG Yifan # a candidate. 1208*d4726bddSHONG Yifan # Because of that we need to add emit=metadata to both the rlib and rmeta invocation. 1209*d4726bddSHONG Yifan # 1210*d4726bddSHONG Yifan # When cc_common linking is enabled, emit a `.o` file, which is later 1211*d4726bddSHONG Yifan # passed to the cc_common.link action. 1212*d4726bddSHONG Yifan emit = ["dep-info", "link"] 1213*d4726bddSHONG Yifan if build_metadata: 1214*d4726bddSHONG Yifan emit.append("metadata") 1215*d4726bddSHONG Yifan if experimental_use_cc_common_link: 1216*d4726bddSHONG Yifan emit = ["obj"] 1217*d4726bddSHONG Yifan 1218*d4726bddSHONG Yifan args, env_from_args = construct_arguments( 1219*d4726bddSHONG Yifan ctx = ctx, 1220*d4726bddSHONG Yifan attr = attr, 1221*d4726bddSHONG Yifan file = ctx.file, 1222*d4726bddSHONG Yifan toolchain = toolchain, 1223*d4726bddSHONG Yifan tool_path = toolchain.rustc.path, 1224*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1225*d4726bddSHONG Yifan emit = emit, 1226*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1227*d4726bddSHONG Yifan crate_info = crate_info, 1228*d4726bddSHONG Yifan dep_info = dep_info, 1229*d4726bddSHONG Yifan linkstamp_outs = linkstamp_outs, 1230*d4726bddSHONG Yifan ambiguous_libs = ambiguous_libs, 1231*d4726bddSHONG Yifan output_hash = output_hash, 1232*d4726bddSHONG Yifan rust_flags = rust_flags, 1233*d4726bddSHONG Yifan out_dir = out_dir, 1234*d4726bddSHONG Yifan build_env_files = build_env_files, 1235*d4726bddSHONG Yifan build_flags_files = build_flags_files, 1236*d4726bddSHONG Yifan force_all_deps_direct = force_all_deps_direct, 1237*d4726bddSHONG Yifan stamp = stamp, 1238*d4726bddSHONG Yifan use_json_output = bool(build_metadata) or bool(rustc_output) or bool(rustc_rmeta_output), 1239*d4726bddSHONG Yifan skip_expanding_rustc_env = skip_expanding_rustc_env, 1240*d4726bddSHONG Yifan ) 1241*d4726bddSHONG Yifan 1242*d4726bddSHONG Yifan args_metadata = None 1243*d4726bddSHONG Yifan if build_metadata: 1244*d4726bddSHONG Yifan args_metadata, _ = construct_arguments( 1245*d4726bddSHONG Yifan ctx = ctx, 1246*d4726bddSHONG Yifan attr = attr, 1247*d4726bddSHONG Yifan file = ctx.file, 1248*d4726bddSHONG Yifan toolchain = toolchain, 1249*d4726bddSHONG Yifan tool_path = toolchain.rustc.path, 1250*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1251*d4726bddSHONG Yifan emit = emit, 1252*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1253*d4726bddSHONG Yifan crate_info = crate_info, 1254*d4726bddSHONG Yifan dep_info = dep_info, 1255*d4726bddSHONG Yifan linkstamp_outs = linkstamp_outs, 1256*d4726bddSHONG Yifan ambiguous_libs = ambiguous_libs, 1257*d4726bddSHONG Yifan output_hash = output_hash, 1258*d4726bddSHONG Yifan rust_flags = rust_flags, 1259*d4726bddSHONG Yifan out_dir = out_dir, 1260*d4726bddSHONG Yifan build_env_files = build_env_files, 1261*d4726bddSHONG Yifan build_flags_files = build_flags_files, 1262*d4726bddSHONG Yifan force_all_deps_direct = force_all_deps_direct, 1263*d4726bddSHONG Yifan stamp = stamp, 1264*d4726bddSHONG Yifan use_json_output = True, 1265*d4726bddSHONG Yifan build_metadata = True, 1266*d4726bddSHONG Yifan ) 1267*d4726bddSHONG Yifan 1268*d4726bddSHONG Yifan env = dict(ctx.configuration.default_shell_env) 1269*d4726bddSHONG Yifan 1270*d4726bddSHONG Yifan # this is the final list of env vars 1271*d4726bddSHONG Yifan env.update(env_from_args) 1272*d4726bddSHONG Yifan 1273*d4726bddSHONG Yifan if hasattr(attr, "version") and attr.version != "0.0.0": 1274*d4726bddSHONG Yifan formatted_version = " v{}".format(attr.version) 1275*d4726bddSHONG Yifan else: 1276*d4726bddSHONG Yifan formatted_version = "" 1277*d4726bddSHONG Yifan 1278*d4726bddSHONG Yifan # Declares the outputs of the rustc compile action. 1279*d4726bddSHONG Yifan # By default this is the binary output; if cc_common.link is used, this is 1280*d4726bddSHONG Yifan # the main `.o` file (`output_o` below). 1281*d4726bddSHONG Yifan outputs = [crate_info.output] 1282*d4726bddSHONG Yifan 1283*d4726bddSHONG Yifan # The `.o` output file, only used for linking via cc_common.link. 1284*d4726bddSHONG Yifan output_o = None 1285*d4726bddSHONG Yifan if experimental_use_cc_common_link: 1286*d4726bddSHONG Yifan obj_ext = ".o" 1287*d4726bddSHONG Yifan output_o = ctx.actions.declare_file(crate_info.name + obj_ext, sibling = crate_info.output) 1288*d4726bddSHONG Yifan outputs = [output_o] 1289*d4726bddSHONG Yifan 1290*d4726bddSHONG Yifan # For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the 1291*d4726bddSHONG Yifan # interface library that rustc generates in the output files. 1292*d4726bddSHONG Yifan interface_library = None 1293*d4726bddSHONG Yifan if toolchain.target_os == "windows" and crate_info.type == "cdylib": 1294*d4726bddSHONG Yifan # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc 1295*d4726bddSHONG Yifan # expects (see https://github.com/rust-lang/rust/pull/29520 for more context). 1296*d4726bddSHONG Yifan interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib", sibling = crate_info.output) 1297*d4726bddSHONG Yifan outputs.append(interface_library) 1298*d4726bddSHONG Yifan 1299*d4726bddSHONG Yifan # The action might generate extra output that we don't want to include in the `DefaultInfo` files. 1300*d4726bddSHONG Yifan action_outputs = list(outputs) 1301*d4726bddSHONG Yifan if rustc_output: 1302*d4726bddSHONG Yifan action_outputs.append(rustc_output) 1303*d4726bddSHONG Yifan 1304*d4726bddSHONG Yifan # Get the compilation mode for the current target. 1305*d4726bddSHONG Yifan compilation_mode = get_compilation_mode_opts(ctx, toolchain) 1306*d4726bddSHONG Yifan 1307*d4726bddSHONG Yifan # Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate 1308*d4726bddSHONG Yifan # types that benefit from having debug information in a separate file. 1309*d4726bddSHONG Yifan pdb_file = None 1310*d4726bddSHONG Yifan dsym_folder = None 1311*d4726bddSHONG Yifan if crate_info.type in ("cdylib", "bin"): 1312*d4726bddSHONG Yifan if toolchain.target_os == "windows" and compilation_mode.strip_level == "none": 1313*d4726bddSHONG Yifan pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output) 1314*d4726bddSHONG Yifan action_outputs.append(pdb_file) 1315*d4726bddSHONG Yifan elif toolchain.target_os == "darwin": 1316*d4726bddSHONG Yifan dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM", sibling = crate_info.output) 1317*d4726bddSHONG Yifan action_outputs.append(dsym_folder) 1318*d4726bddSHONG Yifan 1319*d4726bddSHONG Yifan if ctx.executable._process_wrapper: 1320*d4726bddSHONG Yifan # Run as normal 1321*d4726bddSHONG Yifan ctx.actions.run( 1322*d4726bddSHONG Yifan executable = ctx.executable._process_wrapper, 1323*d4726bddSHONG Yifan inputs = compile_inputs, 1324*d4726bddSHONG Yifan outputs = action_outputs, 1325*d4726bddSHONG Yifan env = env, 1326*d4726bddSHONG Yifan arguments = args.all, 1327*d4726bddSHONG Yifan mnemonic = "Rustc", 1328*d4726bddSHONG Yifan progress_message = "Compiling Rust {} {}{} ({} files)".format( 1329*d4726bddSHONG Yifan crate_info.type, 1330*d4726bddSHONG Yifan ctx.label.name, 1331*d4726bddSHONG Yifan formatted_version, 1332*d4726bddSHONG Yifan len(crate_info.srcs.to_list()), 1333*d4726bddSHONG Yifan ), 1334*d4726bddSHONG Yifan toolchain = "@rules_rust//rust:toolchain_type", 1335*d4726bddSHONG Yifan ) 1336*d4726bddSHONG Yifan if args_metadata: 1337*d4726bddSHONG Yifan ctx.actions.run( 1338*d4726bddSHONG Yifan executable = ctx.executable._process_wrapper, 1339*d4726bddSHONG Yifan inputs = compile_inputs, 1340*d4726bddSHONG Yifan outputs = [build_metadata] + [x for x in [rustc_rmeta_output] if x], 1341*d4726bddSHONG Yifan env = env, 1342*d4726bddSHONG Yifan arguments = args_metadata.all, 1343*d4726bddSHONG Yifan mnemonic = "RustcMetadata", 1344*d4726bddSHONG Yifan progress_message = "Compiling Rust metadata {} {}{} ({} files)".format( 1345*d4726bddSHONG Yifan crate_info.type, 1346*d4726bddSHONG Yifan ctx.label.name, 1347*d4726bddSHONG Yifan formatted_version, 1348*d4726bddSHONG Yifan len(crate_info.srcs.to_list()), 1349*d4726bddSHONG Yifan ), 1350*d4726bddSHONG Yifan toolchain = "@rules_rust//rust:toolchain_type", 1351*d4726bddSHONG Yifan ) 1352*d4726bddSHONG Yifan elif hasattr(ctx.executable, "_bootstrap_process_wrapper"): 1353*d4726bddSHONG Yifan # Run without process_wrapper 1354*d4726bddSHONG Yifan if build_env_files or build_flags_files or stamp or build_metadata: 1355*d4726bddSHONG Yifan fail("build_env_files, build_flags_files, stamp, build_metadata are not supported when building without process_wrapper") 1356*d4726bddSHONG Yifan ctx.actions.run( 1357*d4726bddSHONG Yifan executable = ctx.executable._bootstrap_process_wrapper, 1358*d4726bddSHONG Yifan inputs = compile_inputs, 1359*d4726bddSHONG Yifan outputs = action_outputs, 1360*d4726bddSHONG Yifan env = env, 1361*d4726bddSHONG Yifan arguments = [args.rustc_path, args.rustc_flags], 1362*d4726bddSHONG Yifan mnemonic = "Rustc", 1363*d4726bddSHONG Yifan progress_message = "Compiling Rust (without process_wrapper) {} {}{} ({} files)".format( 1364*d4726bddSHONG Yifan crate_info.type, 1365*d4726bddSHONG Yifan ctx.label.name, 1366*d4726bddSHONG Yifan formatted_version, 1367*d4726bddSHONG Yifan len(crate_info.srcs.to_list()), 1368*d4726bddSHONG Yifan ), 1369*d4726bddSHONG Yifan toolchain = "@rules_rust//rust:toolchain_type", 1370*d4726bddSHONG Yifan ) 1371*d4726bddSHONG Yifan else: 1372*d4726bddSHONG Yifan fail("No process wrapper was defined for {}".format(ctx.label)) 1373*d4726bddSHONG Yifan 1374*d4726bddSHONG Yifan if experimental_use_cc_common_link: 1375*d4726bddSHONG Yifan # Wrap the main `.o` file into a compilation output suitable for 1376*d4726bddSHONG Yifan # cc_common.link. The main `.o` file is useful in both PIC and non-PIC 1377*d4726bddSHONG Yifan # modes. 1378*d4726bddSHONG Yifan compilation_outputs = cc_common.create_compilation_outputs( 1379*d4726bddSHONG Yifan objects = depset([output_o]), 1380*d4726bddSHONG Yifan pic_objects = depset([output_o]), 1381*d4726bddSHONG Yifan ) 1382*d4726bddSHONG Yifan 1383*d4726bddSHONG Yifan malloc_library = ctx.attr._custom_malloc or ctx.attr.malloc 1384*d4726bddSHONG Yifan 1385*d4726bddSHONG Yifan # Collect the linking contexts of the standard library and dependencies. 1386*d4726bddSHONG Yifan linking_contexts = [ 1387*d4726bddSHONG Yifan malloc_library[CcInfo].linking_context, 1388*d4726bddSHONG Yifan _get_std_and_alloc_info(ctx, toolchain, crate_info).linking_context, 1389*d4726bddSHONG Yifan toolchain.stdlib_linkflags.linking_context, 1390*d4726bddSHONG Yifan ] 1391*d4726bddSHONG Yifan 1392*d4726bddSHONG Yifan for dep in crate_info.deps.to_list(): 1393*d4726bddSHONG Yifan if dep.cc_info: 1394*d4726bddSHONG Yifan linking_contexts.append(dep.cc_info.linking_context) 1395*d4726bddSHONG Yifan 1396*d4726bddSHONG Yifan # In the cc_common.link action we need to pass the name of the final 1397*d4726bddSHONG Yifan # binary (output) relative to the package of this target. 1398*d4726bddSHONG Yifan # We compute it by stripping the path to the package directory, 1399*d4726bddSHONG Yifan # which is a prefix of the path of `crate_info.output`. 1400*d4726bddSHONG Yifan 1401*d4726bddSHONG Yifan # The path to the package dir, including a trailing "/". 1402*d4726bddSHONG Yifan package_dir = ctx.bin_dir.path + "/" 1403*d4726bddSHONG Yifan 1404*d4726bddSHONG Yifan # For external repositories, workspace root is not part of the output 1405*d4726bddSHONG Yifan # path when sibling repository layout is used (the repository name is 1406*d4726bddSHONG Yifan # part of the bin_dir). This scenario happens when the workspace root 1407*d4726bddSHONG Yifan # starts with "../" 1408*d4726bddSHONG Yifan if ctx.label.workspace_root and not ctx.label.workspace_root.startswith("../"): 1409*d4726bddSHONG Yifan package_dir = package_dir + ctx.label.workspace_root + "/" 1410*d4726bddSHONG Yifan if ctx.label.package: 1411*d4726bddSHONG Yifan package_dir = package_dir + ctx.label.package + "/" 1412*d4726bddSHONG Yifan 1413*d4726bddSHONG Yifan if not crate_info.output.path.startswith(package_dir): 1414*d4726bddSHONG Yifan fail("The package dir path", package_dir, "should be a prefix of the crate_info.output.path", crate_info.output.path) 1415*d4726bddSHONG Yifan 1416*d4726bddSHONG Yifan output_relative_to_package = crate_info.output.path[len(package_dir):] 1417*d4726bddSHONG Yifan 1418*d4726bddSHONG Yifan # Compile actions that produce shared libraries create output of the form "libfoo.so" for linux and macos; 1419*d4726bddSHONG Yifan # cc_common.link expects us to pass "foo" to the name parameter. We cannot simply use crate_info.name because 1420*d4726bddSHONG Yifan # the name of the crate does not always match the name of output file, e.g a crate named foo-bar will produce 1421*d4726bddSHONG Yifan # a (lib)foo_bar output file. 1422*d4726bddSHONG Yifan if crate_info.type == "cdylib": 1423*d4726bddSHONG Yifan output_lib = crate_info.output.basename 1424*d4726bddSHONG Yifan if toolchain.target_os != "windows": 1425*d4726bddSHONG Yifan # Strip the leading "lib" prefix 1426*d4726bddSHONG Yifan output_lib = output_lib[3:] 1427*d4726bddSHONG Yifan 1428*d4726bddSHONG Yifan # Strip the file extension 1429*d4726bddSHONG Yifan output_lib = output_lib[:-(1 + len(crate_info.output.extension))] 1430*d4726bddSHONG Yifan 1431*d4726bddSHONG Yifan # Remove the basename (which contains the undesired 'lib' prefix and the file extension) 1432*d4726bddSHONG Yifan output_relative_to_package = output_relative_to_package[:-len(crate_info.output.basename)] 1433*d4726bddSHONG Yifan 1434*d4726bddSHONG Yifan # Append the name of the library 1435*d4726bddSHONG Yifan output_relative_to_package = output_relative_to_package + output_lib 1436*d4726bddSHONG Yifan 1437*d4726bddSHONG Yifan cc_common.link( 1438*d4726bddSHONG Yifan actions = ctx.actions, 1439*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1440*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1441*d4726bddSHONG Yifan linking_contexts = linking_contexts, 1442*d4726bddSHONG Yifan compilation_outputs = compilation_outputs, 1443*d4726bddSHONG Yifan name = output_relative_to_package, 1444*d4726bddSHONG Yifan stamp = ctx.attr.stamp, 1445*d4726bddSHONG Yifan output_type = "executable" if crate_info.type == "bin" else "dynamic_library", 1446*d4726bddSHONG Yifan ) 1447*d4726bddSHONG Yifan 1448*d4726bddSHONG Yifan outputs = [crate_info.output] 1449*d4726bddSHONG Yifan 1450*d4726bddSHONG Yifan coverage_runfiles = [] 1451*d4726bddSHONG Yifan if toolchain.llvm_cov and ctx.configuration.coverage_enabled and crate_info.is_test: 1452*d4726bddSHONG Yifan coverage_runfiles = [toolchain.llvm_cov, toolchain.llvm_profdata] 1453*d4726bddSHONG Yifan 1454*d4726bddSHONG Yifan experimental_use_coverage_metadata_files = toolchain._experimental_use_coverage_metadata_files 1455*d4726bddSHONG Yifan 1456*d4726bddSHONG Yifan dynamic_libraries = [ 1457*d4726bddSHONG Yifan library_to_link.dynamic_library 1458*d4726bddSHONG Yifan for dep in getattr(ctx.attr, "deps", []) 1459*d4726bddSHONG Yifan if CcInfo in dep 1460*d4726bddSHONG Yifan for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list() 1461*d4726bddSHONG Yifan for library_to_link in linker_input.libraries 1462*d4726bddSHONG Yifan if _is_dylib(library_to_link) 1463*d4726bddSHONG Yifan ] 1464*d4726bddSHONG Yifan runfiles = ctx.runfiles( 1465*d4726bddSHONG Yifan files = getattr(ctx.files, "data", []) + 1466*d4726bddSHONG Yifan ([] if experimental_use_coverage_metadata_files else coverage_runfiles) + 1467*d4726bddSHONG Yifan dynamic_libraries, 1468*d4726bddSHONG Yifan collect_data = True, 1469*d4726bddSHONG Yifan ) 1470*d4726bddSHONG Yifan if getattr(ctx.attr, "crate", None): 1471*d4726bddSHONG Yifan runfiles = runfiles.merge(ctx.attr.crate[DefaultInfo].default_runfiles) 1472*d4726bddSHONG Yifan runfiles = runfiles.merge(ctx.attr.crate[DefaultInfo].data_runfiles) 1473*d4726bddSHONG Yifan 1474*d4726bddSHONG Yifan # TODO: Remove after some resolution to 1475*d4726bddSHONG Yifan # https://github.com/bazelbuild/rules_rust/issues/771 1476*d4726bddSHONG Yifan out_binary = getattr(attr, "out_binary", False) 1477*d4726bddSHONG Yifan 1478*d4726bddSHONG Yifan executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None 1479*d4726bddSHONG Yifan 1480*d4726bddSHONG Yifan instrumented_files_kwargs = { 1481*d4726bddSHONG Yifan "dependency_attributes": ["deps", "crate"], 1482*d4726bddSHONG Yifan "extensions": ["rs"], 1483*d4726bddSHONG Yifan "source_attributes": ["srcs"], 1484*d4726bddSHONG Yifan } 1485*d4726bddSHONG Yifan 1486*d4726bddSHONG Yifan if experimental_use_coverage_metadata_files: 1487*d4726bddSHONG Yifan instrumented_files_kwargs.update({ 1488*d4726bddSHONG Yifan "metadata_files": coverage_runfiles + [executable] if executable else [], 1489*d4726bddSHONG Yifan }) 1490*d4726bddSHONG Yifan 1491*d4726bddSHONG Yifan providers = [ 1492*d4726bddSHONG Yifan DefaultInfo( 1493*d4726bddSHONG Yifan # nb. This field is required for cc_library to depend on our output. 1494*d4726bddSHONG Yifan files = depset(outputs), 1495*d4726bddSHONG Yifan runfiles = runfiles, 1496*d4726bddSHONG Yifan executable = executable, 1497*d4726bddSHONG Yifan ), 1498*d4726bddSHONG Yifan ] 1499*d4726bddSHONG Yifan 1500*d4726bddSHONG Yifan # When invoked by aspects (and when running `bazel coverage`), the 1501*d4726bddSHONG Yifan # baseline_coverage.dat created here will conflict with the baseline_coverage.dat of the 1502*d4726bddSHONG Yifan # underlying target, which is a build failure. So we add an option to disable it so that this 1503*d4726bddSHONG Yifan # function can be invoked from aspects for rules that have its own InstrumentedFilesInfo. 1504*d4726bddSHONG Yifan if include_coverage: 1505*d4726bddSHONG Yifan providers.append( 1506*d4726bddSHONG Yifan coverage_common.instrumented_files_info( 1507*d4726bddSHONG Yifan ctx, 1508*d4726bddSHONG Yifan **instrumented_files_kwargs 1509*d4726bddSHONG Yifan ), 1510*d4726bddSHONG Yifan ) 1511*d4726bddSHONG Yifan 1512*d4726bddSHONG Yifan if crate_info_dict != None: 1513*d4726bddSHONG Yifan crate_info_dict.update({ 1514*d4726bddSHONG Yifan "rustc_env": env, 1515*d4726bddSHONG Yifan }) 1516*d4726bddSHONG Yifan crate_info = rust_common.create_crate_info(**crate_info_dict) 1517*d4726bddSHONG Yifan 1518*d4726bddSHONG Yifan if crate_info.type in ["staticlib", "cdylib"]: 1519*d4726bddSHONG Yifan # These rules are not supposed to be depended on by other rust targets, and 1520*d4726bddSHONG Yifan # as such they shouldn't provide a CrateInfo. However, one may still want to 1521*d4726bddSHONG Yifan # write a rust_test for them, so we provide the CrateInfo wrapped in a provider 1522*d4726bddSHONG Yifan # that rust_test understands. 1523*d4726bddSHONG Yifan providers.extend([rust_common.test_crate_info(crate = crate_info), dep_info]) 1524*d4726bddSHONG Yifan else: 1525*d4726bddSHONG Yifan providers.extend([crate_info, dep_info]) 1526*d4726bddSHONG Yifan 1527*d4726bddSHONG Yifan providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library) 1528*d4726bddSHONG Yifan 1529*d4726bddSHONG Yifan output_group_info = {} 1530*d4726bddSHONG Yifan 1531*d4726bddSHONG Yifan if pdb_file: 1532*d4726bddSHONG Yifan output_group_info["pdb_file"] = depset([pdb_file]) 1533*d4726bddSHONG Yifan if dsym_folder: 1534*d4726bddSHONG Yifan output_group_info["dsym_folder"] = depset([dsym_folder]) 1535*d4726bddSHONG Yifan if build_metadata: 1536*d4726bddSHONG Yifan output_group_info["build_metadata"] = depset([build_metadata]) 1537*d4726bddSHONG Yifan if build_metadata: 1538*d4726bddSHONG Yifan output_group_info["build_metadata"] = depset([build_metadata]) 1539*d4726bddSHONG Yifan if rustc_rmeta_output: 1540*d4726bddSHONG Yifan output_group_info["rustc_rmeta_output"] = depset([rustc_rmeta_output]) 1541*d4726bddSHONG Yifan if rustc_output: 1542*d4726bddSHONG Yifan output_group_info["rustc_output"] = depset([rustc_output]) 1543*d4726bddSHONG Yifan 1544*d4726bddSHONG Yifan if output_group_info: 1545*d4726bddSHONG Yifan providers.append(OutputGroupInfo(**output_group_info)) 1546*d4726bddSHONG Yifan 1547*d4726bddSHONG Yifan return providers 1548*d4726bddSHONG Yifan 1549*d4726bddSHONG Yifandef _is_no_std(ctx, toolchain, crate_info): 1550*d4726bddSHONG Yifan if is_exec_configuration(ctx) or crate_info.is_test: 1551*d4726bddSHONG Yifan return False 1552*d4726bddSHONG Yifan if toolchain._no_std == "off": 1553*d4726bddSHONG Yifan return False 1554*d4726bddSHONG Yifan return True 1555*d4726bddSHONG Yifan 1556*d4726bddSHONG Yifandef _get_std_and_alloc_info(ctx, toolchain, crate_info): 1557*d4726bddSHONG Yifan if is_exec_configuration(ctx): 1558*d4726bddSHONG Yifan return toolchain.libstd_and_allocator_ccinfo 1559*d4726bddSHONG Yifan if toolchain._experimental_use_global_allocator: 1560*d4726bddSHONG Yifan if _is_no_std(ctx, toolchain, crate_info): 1561*d4726bddSHONG Yifan return toolchain.nostd_and_global_allocator_cc_info 1562*d4726bddSHONG Yifan else: 1563*d4726bddSHONG Yifan return toolchain.libstd_and_global_allocator_ccinfo 1564*d4726bddSHONG Yifan else: 1565*d4726bddSHONG Yifan return toolchain.libstd_and_allocator_ccinfo 1566*d4726bddSHONG Yifan 1567*d4726bddSHONG Yifandef _is_dylib(dep): 1568*d4726bddSHONG Yifan return not bool(dep.static_library or dep.pic_static_library) 1569*d4726bddSHONG Yifan 1570*d4726bddSHONG Yifandef _collect_nonstatic_linker_inputs(cc_info): 1571*d4726bddSHONG Yifan shared_linker_inputs = [] 1572*d4726bddSHONG Yifan for linker_input in cc_info.linking_context.linker_inputs.to_list(): 1573*d4726bddSHONG Yifan dylibs = [ 1574*d4726bddSHONG Yifan lib 1575*d4726bddSHONG Yifan for lib in linker_input.libraries 1576*d4726bddSHONG Yifan if _is_dylib(lib) 1577*d4726bddSHONG Yifan ] 1578*d4726bddSHONG Yifan if dylibs: 1579*d4726bddSHONG Yifan shared_linker_inputs.append(cc_common.create_linker_input( 1580*d4726bddSHONG Yifan owner = linker_input.owner, 1581*d4726bddSHONG Yifan libraries = depset(dylibs), 1582*d4726bddSHONG Yifan )) 1583*d4726bddSHONG Yifan return shared_linker_inputs 1584*d4726bddSHONG Yifan 1585*d4726bddSHONG Yifandef establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library): 1586*d4726bddSHONG Yifan """If the produced crate is suitable yield a CcInfo to allow for interop with cc rules 1587*d4726bddSHONG Yifan 1588*d4726bddSHONG Yifan Args: 1589*d4726bddSHONG Yifan ctx (ctx): The rule's context object 1590*d4726bddSHONG Yifan attr (struct): Attributes to use in gathering CcInfo 1591*d4726bddSHONG Yifan crate_info (CrateInfo): The CrateInfo provider of the target crate 1592*d4726bddSHONG Yifan toolchain (rust_toolchain): The current `rust_toolchain` 1593*d4726bddSHONG Yifan cc_toolchain (CcToolchainInfo): The current `CcToolchainInfo` 1594*d4726bddSHONG Yifan feature_configuration (FeatureConfiguration): Feature configuration to be queried. 1595*d4726bddSHONG Yifan interface_library (File): Optional interface library for cdylib crates on Windows. 1596*d4726bddSHONG Yifan 1597*d4726bddSHONG Yifan Returns: 1598*d4726bddSHONG Yifan list: A list containing the CcInfo provider 1599*d4726bddSHONG Yifan """ 1600*d4726bddSHONG Yifan 1601*d4726bddSHONG Yifan # A test will not need to produce CcInfo as nothing can depend on test targets 1602*d4726bddSHONG Yifan if crate_info.is_test: 1603*d4726bddSHONG Yifan return [] 1604*d4726bddSHONG Yifan 1605*d4726bddSHONG Yifan # Only generate CcInfo for particular crate types 1606*d4726bddSHONG Yifan if crate_info.type not in ("staticlib", "cdylib", "rlib", "lib"): 1607*d4726bddSHONG Yifan return [] 1608*d4726bddSHONG Yifan 1609*d4726bddSHONG Yifan # TODO: Remove after some resolution to 1610*d4726bddSHONG Yifan # https://github.com/bazelbuild/rules_rust/issues/771 1611*d4726bddSHONG Yifan if getattr(attr, "out_binary", False): 1612*d4726bddSHONG Yifan return [] 1613*d4726bddSHONG Yifan 1614*d4726bddSHONG Yifan if crate_info.type == "staticlib": 1615*d4726bddSHONG Yifan library_to_link = cc_common.create_library_to_link( 1616*d4726bddSHONG Yifan actions = ctx.actions, 1617*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1618*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1619*d4726bddSHONG Yifan static_library = crate_info.output, 1620*d4726bddSHONG Yifan # TODO(hlopko): handle PIC/NOPIC correctly 1621*d4726bddSHONG Yifan pic_static_library = crate_info.output, 1622*d4726bddSHONG Yifan alwayslink = getattr(attr, "alwayslink", False), 1623*d4726bddSHONG Yifan ) 1624*d4726bddSHONG Yifan elif crate_info.type in ("rlib", "lib"): 1625*d4726bddSHONG Yifan # bazel hard-codes a check for endswith((".a", ".pic.a", 1626*d4726bddSHONG Yifan # ".lib")) in create_library_to_link, so we work around that 1627*d4726bddSHONG Yifan # by creating a symlink to the .rlib with a .a extension. 1628*d4726bddSHONG Yifan dot_a = make_static_lib_symlink(ctx.label.package, ctx.actions, crate_info.output) 1629*d4726bddSHONG Yifan 1630*d4726bddSHONG Yifan # TODO(hlopko): handle PIC/NOPIC correctly 1631*d4726bddSHONG Yifan library_to_link = cc_common.create_library_to_link( 1632*d4726bddSHONG Yifan actions = ctx.actions, 1633*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1634*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1635*d4726bddSHONG Yifan static_library = dot_a, 1636*d4726bddSHONG Yifan # TODO(hlopko): handle PIC/NOPIC correctly 1637*d4726bddSHONG Yifan pic_static_library = dot_a, 1638*d4726bddSHONG Yifan alwayslink = getattr(attr, "alwayslink", False), 1639*d4726bddSHONG Yifan ) 1640*d4726bddSHONG Yifan elif crate_info.type == "cdylib": 1641*d4726bddSHONG Yifan library_to_link = cc_common.create_library_to_link( 1642*d4726bddSHONG Yifan actions = ctx.actions, 1643*d4726bddSHONG Yifan feature_configuration = feature_configuration, 1644*d4726bddSHONG Yifan cc_toolchain = cc_toolchain, 1645*d4726bddSHONG Yifan dynamic_library = crate_info.output, 1646*d4726bddSHONG Yifan interface_library = interface_library, 1647*d4726bddSHONG Yifan ) 1648*d4726bddSHONG Yifan else: 1649*d4726bddSHONG Yifan fail("Unexpected case") 1650*d4726bddSHONG Yifan 1651*d4726bddSHONG Yifan link_input = cc_common.create_linker_input( 1652*d4726bddSHONG Yifan owner = ctx.label, 1653*d4726bddSHONG Yifan libraries = depset([library_to_link]), 1654*d4726bddSHONG Yifan ) 1655*d4726bddSHONG Yifan 1656*d4726bddSHONG Yifan linking_context = cc_common.create_linking_context( 1657*d4726bddSHONG Yifan # TODO - What to do for no_std? 1658*d4726bddSHONG Yifan linker_inputs = depset([link_input]), 1659*d4726bddSHONG Yifan ) 1660*d4726bddSHONG Yifan 1661*d4726bddSHONG Yifan cc_infos = [ 1662*d4726bddSHONG Yifan CcInfo(linking_context = linking_context), 1663*d4726bddSHONG Yifan toolchain.stdlib_linkflags, 1664*d4726bddSHONG Yifan ] 1665*d4726bddSHONG Yifan 1666*d4726bddSHONG Yifan # Flattening is okay since crate_info.deps only records direct deps. 1667*d4726bddSHONG Yifan for dep in crate_info.deps.to_list(): 1668*d4726bddSHONG Yifan if dep.cc_info: 1669*d4726bddSHONG Yifan # A Rust staticlib or shared library doesn't need to propagate linker inputs 1670*d4726bddSHONG Yifan # of its dependencies, except for shared libraries. 1671*d4726bddSHONG Yifan if crate_info.type in ["cdylib", "staticlib"]: 1672*d4726bddSHONG Yifan shared_linker_inputs = _collect_nonstatic_linker_inputs(dep.cc_info) 1673*d4726bddSHONG Yifan if shared_linker_inputs: 1674*d4726bddSHONG Yifan linking_context = cc_common.create_linking_context( 1675*d4726bddSHONG Yifan linker_inputs = depset(shared_linker_inputs), 1676*d4726bddSHONG Yifan ) 1677*d4726bddSHONG Yifan cc_infos.append(CcInfo(linking_context = linking_context)) 1678*d4726bddSHONG Yifan else: 1679*d4726bddSHONG Yifan cc_infos.append(dep.cc_info) 1680*d4726bddSHONG Yifan 1681*d4726bddSHONG Yifan if crate_info.type in ("rlib", "lib"): 1682*d4726bddSHONG Yifan libstd_and_allocator_cc_info = _get_std_and_alloc_info(ctx, toolchain, crate_info) 1683*d4726bddSHONG Yifan if libstd_and_allocator_cc_info: 1684*d4726bddSHONG Yifan # TODO: if we already have an rlib in our deps, we could skip this 1685*d4726bddSHONG Yifan cc_infos.append(libstd_and_allocator_cc_info) 1686*d4726bddSHONG Yifan 1687*d4726bddSHONG Yifan return [cc_common.merge_cc_infos(cc_infos = cc_infos)] 1688*d4726bddSHONG Yifan 1689*d4726bddSHONG Yifandef add_edition_flags(args, crate): 1690*d4726bddSHONG Yifan """Adds the Rust edition flag to an arguments object reference 1691*d4726bddSHONG Yifan 1692*d4726bddSHONG Yifan Args: 1693*d4726bddSHONG Yifan args (Args): A reference to an Args object 1694*d4726bddSHONG Yifan crate (CrateInfo): A CrateInfo provider 1695*d4726bddSHONG Yifan """ 1696*d4726bddSHONG Yifan if crate.edition != "2015": 1697*d4726bddSHONG Yifan args.add(crate.edition, format = "--edition=%s") 1698*d4726bddSHONG Yifan 1699*d4726bddSHONG Yifandef _create_extra_input_args(build_info, dep_info, include_link_flags = True): 1700*d4726bddSHONG Yifan """Gather additional input arguments from transitive dependencies 1701*d4726bddSHONG Yifan 1702*d4726bddSHONG Yifan Args: 1703*d4726bddSHONG Yifan build_info (BuildInfo): The BuildInfo provider from the target Crate's set of inputs. 1704*d4726bddSHONG Yifan dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs. 1705*d4726bddSHONG Yifan include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library. 1706*d4726bddSHONG Yifan 1707*d4726bddSHONG Yifan Returns: 1708*d4726bddSHONG Yifan tuple: A tuple of the following items: 1709*d4726bddSHONG Yifan - (depset[File]): A list of all build info `OUT_DIR` File objects 1710*d4726bddSHONG Yifan - (str): The `OUT_DIR` of the current build info 1711*d4726bddSHONG Yifan - (File): An optional generated environment file from a `cargo_build_script` target 1712*d4726bddSHONG Yifan - (depset[File]): All direct and transitive build flag files from the current build info to be passed to rustc. 1713*d4726bddSHONG Yifan """ 1714*d4726bddSHONG Yifan input_files = [] 1715*d4726bddSHONG Yifan input_depsets = [] 1716*d4726bddSHONG Yifan 1717*d4726bddSHONG Yifan # Arguments to the commandline line wrapper that are going to be used 1718*d4726bddSHONG Yifan # to create the final command line 1719*d4726bddSHONG Yifan out_dir = None 1720*d4726bddSHONG Yifan build_env_file = None 1721*d4726bddSHONG Yifan build_flags_files = [] 1722*d4726bddSHONG Yifan 1723*d4726bddSHONG Yifan if build_info: 1724*d4726bddSHONG Yifan if build_info.out_dir: 1725*d4726bddSHONG Yifan out_dir = build_info.out_dir.path 1726*d4726bddSHONG Yifan input_files.append(build_info.out_dir) 1727*d4726bddSHONG Yifan build_env_file = build_info.rustc_env 1728*d4726bddSHONG Yifan if build_info.flags: 1729*d4726bddSHONG Yifan build_flags_files.append(build_info.flags) 1730*d4726bddSHONG Yifan if build_info.linker_flags and include_link_flags: 1731*d4726bddSHONG Yifan build_flags_files.append(build_info.linker_flags) 1732*d4726bddSHONG Yifan input_files.append(build_info.linker_flags) 1733*d4726bddSHONG Yifan 1734*d4726bddSHONG Yifan input_depsets.append(build_info.compile_data) 1735*d4726bddSHONG Yifan 1736*d4726bddSHONG Yifan return ( 1737*d4726bddSHONG Yifan depset(input_files, transitive = [dep_info.link_search_path_files] + input_depsets), 1738*d4726bddSHONG Yifan out_dir, 1739*d4726bddSHONG Yifan build_env_file, 1740*d4726bddSHONG Yifan depset(build_flags_files, transitive = [dep_info.link_search_path_files]), 1741*d4726bddSHONG Yifan ) 1742*d4726bddSHONG Yifan 1743*d4726bddSHONG Yifandef _compute_rpaths(toolchain, output_dir, dep_info, use_pic): 1744*d4726bddSHONG Yifan """Determine the artifact's rpaths relative to the bazel root for runtime linking of shared libraries. 1745*d4726bddSHONG Yifan 1746*d4726bddSHONG Yifan Args: 1747*d4726bddSHONG Yifan toolchain (rust_toolchain): The current `rust_toolchain` 1748*d4726bddSHONG Yifan output_dir (str): The output directory of the current target 1749*d4726bddSHONG Yifan dep_info (DepInfo): The current target's dependency info 1750*d4726bddSHONG Yifan use_pic: If set, prefers pic_static_library over static_library. 1751*d4726bddSHONG Yifan 1752*d4726bddSHONG Yifan Returns: 1753*d4726bddSHONG Yifan depset: A set of relative paths from the output directory to each dependency 1754*d4726bddSHONG Yifan """ 1755*d4726bddSHONG Yifan 1756*d4726bddSHONG Yifan # Windows has no rpath equivalent, so always return an empty depset. 1757*d4726bddSHONG Yifan # Fuchsia assembles shared libraries during packaging. 1758*d4726bddSHONG Yifan if toolchain.target_os == "windows" or toolchain.target_os == "fuchsia": 1759*d4726bddSHONG Yifan return depset([]) 1760*d4726bddSHONG Yifan 1761*d4726bddSHONG Yifan dylibs = [ 1762*d4726bddSHONG Yifan get_preferred_artifact(lib, use_pic) 1763*d4726bddSHONG Yifan for linker_input in dep_info.transitive_noncrates.to_list() 1764*d4726bddSHONG Yifan for lib in linker_input.libraries 1765*d4726bddSHONG Yifan if _is_dylib(lib) 1766*d4726bddSHONG Yifan ] 1767*d4726bddSHONG Yifan 1768*d4726bddSHONG Yifan # Include std dylib if dylib linkage is enabled 1769*d4726bddSHONG Yifan if toolchain._experimental_link_std_dylib: 1770*d4726bddSHONG Yifan # TODO: Make toolchain.rust_std to only include libstd.so 1771*d4726bddSHONG Yifan # When dylib linkage is enabled, toolchain.rust_std should only need to 1772*d4726bddSHONG Yifan # include libstd.so. Hence, no filtering needed. 1773*d4726bddSHONG Yifan for file in toolchain.rust_std.to_list(): 1774*d4726bddSHONG Yifan if is_std_dylib(file): 1775*d4726bddSHONG Yifan dylibs.append(file) 1776*d4726bddSHONG Yifan 1777*d4726bddSHONG Yifan if not dylibs: 1778*d4726bddSHONG Yifan return depset([]) 1779*d4726bddSHONG Yifan 1780*d4726bddSHONG Yifan # For darwin, dylibs compiled by Bazel will fail to be resolved at runtime 1781*d4726bddSHONG Yifan # without a version of Bazel that includes 1782*d4726bddSHONG Yifan # https://github.com/bazelbuild/bazel/pull/13427. This is known to not be 1783*d4726bddSHONG Yifan # included in Bazel 4.1 and below. 1784*d4726bddSHONG Yifan if toolchain.target_os not in ["linux", "darwin", "android"]: 1785*d4726bddSHONG Yifan fail("Runtime linking is not supported on {}, but found {}".format( 1786*d4726bddSHONG Yifan toolchain.target_os, 1787*d4726bddSHONG Yifan dep_info.transitive_noncrates, 1788*d4726bddSHONG Yifan )) 1789*d4726bddSHONG Yifan 1790*d4726bddSHONG Yifan # Multiple dylibs can be present in the same directory, so deduplicate them. 1791*d4726bddSHONG Yifan return depset([ 1792*d4726bddSHONG Yifan relativize(lib_dir, output_dir) 1793*d4726bddSHONG Yifan for lib_dir in _get_dir_names(dylibs) 1794*d4726bddSHONG Yifan ]) 1795*d4726bddSHONG Yifan 1796*d4726bddSHONG Yifandef _get_dir_names(files): 1797*d4726bddSHONG Yifan """Returns a list of directory names from the given list of File objects 1798*d4726bddSHONG Yifan 1799*d4726bddSHONG Yifan Args: 1800*d4726bddSHONG Yifan files (list): A list of File objects 1801*d4726bddSHONG Yifan 1802*d4726bddSHONG Yifan Returns: 1803*d4726bddSHONG Yifan list: A list of directory names for all files 1804*d4726bddSHONG Yifan """ 1805*d4726bddSHONG Yifan dirs = {} 1806*d4726bddSHONG Yifan for f in files: 1807*d4726bddSHONG Yifan dirs[f.dirname] = None 1808*d4726bddSHONG Yifan return dirs.keys() 1809*d4726bddSHONG Yifan 1810*d4726bddSHONG Yifandef add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_metadata = False): 1811*d4726bddSHONG Yifan """Adds link flags to an Args object reference 1812*d4726bddSHONG Yifan 1813*d4726bddSHONG Yifan Args: 1814*d4726bddSHONG Yifan args (Args): An arguments object reference 1815*d4726bddSHONG Yifan dep_info (DepInfo): The current target's dependency info 1816*d4726bddSHONG Yifan force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern 1817*d4726bddSHONG Yifan to the commandline as opposed to -L. 1818*d4726bddSHONG Yifan use_metadata (bool, optional): Build command line arugments using metadata for crates that provide it. 1819*d4726bddSHONG Yifan """ 1820*d4726bddSHONG Yifan 1821*d4726bddSHONG Yifan direct_crates = depset( 1822*d4726bddSHONG Yifan transitive = [ 1823*d4726bddSHONG Yifan dep_info.direct_crates, 1824*d4726bddSHONG Yifan dep_info.transitive_crates, 1825*d4726bddSHONG Yifan ], 1826*d4726bddSHONG Yifan ) if force_all_deps_direct else dep_info.direct_crates 1827*d4726bddSHONG Yifan 1828*d4726bddSHONG Yifan crate_to_link_flags = _crate_to_link_flag_metadata if use_metadata else _crate_to_link_flag 1829*d4726bddSHONG Yifan args.add_all(direct_crates, uniquify = True, map_each = crate_to_link_flags) 1830*d4726bddSHONG Yifan 1831*d4726bddSHONG Yifan args.add_all( 1832*d4726bddSHONG Yifan dep_info.transitive_crates, 1833*d4726bddSHONG Yifan map_each = _get_crate_dirname, 1834*d4726bddSHONG Yifan uniquify = True, 1835*d4726bddSHONG Yifan format_each = "-Ldependency=%s", 1836*d4726bddSHONG Yifan ) 1837*d4726bddSHONG Yifan 1838*d4726bddSHONG Yifandef _crate_to_link_flag_metadata(crate): 1839*d4726bddSHONG Yifan """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object 1840*d4726bddSHONG Yifan 1841*d4726bddSHONG Yifan Args: 1842*d4726bddSHONG Yifan crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider 1843*d4726bddSHONG Yifan 1844*d4726bddSHONG Yifan Returns: 1845*d4726bddSHONG Yifan list: Link flags for the given provider 1846*d4726bddSHONG Yifan """ 1847*d4726bddSHONG Yifan 1848*d4726bddSHONG Yifan # This is AliasableDepInfo, we should use the alias as a crate name 1849*d4726bddSHONG Yifan if hasattr(crate, "dep"): 1850*d4726bddSHONG Yifan name = crate.name 1851*d4726bddSHONG Yifan crate_info = crate.dep 1852*d4726bddSHONG Yifan else: 1853*d4726bddSHONG Yifan name = crate.name 1854*d4726bddSHONG Yifan crate_info = crate 1855*d4726bddSHONG Yifan 1856*d4726bddSHONG Yifan lib_or_meta = crate_info.metadata 1857*d4726bddSHONG Yifan if not crate_info.metadata: 1858*d4726bddSHONG Yifan lib_or_meta = crate_info.output 1859*d4726bddSHONG Yifan return ["--extern={}={}".format(name, lib_or_meta.path)] 1860*d4726bddSHONG Yifan 1861*d4726bddSHONG Yifandef _crate_to_link_flag(crate): 1862*d4726bddSHONG Yifan """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object 1863*d4726bddSHONG Yifan 1864*d4726bddSHONG Yifan Args: 1865*d4726bddSHONG Yifan crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider 1866*d4726bddSHONG Yifan 1867*d4726bddSHONG Yifan Returns: 1868*d4726bddSHONG Yifan list: Link flags for the given provider 1869*d4726bddSHONG Yifan """ 1870*d4726bddSHONG Yifan 1871*d4726bddSHONG Yifan # This is AliasableDepInfo, we should use the alias as a crate name 1872*d4726bddSHONG Yifan if hasattr(crate, "dep"): 1873*d4726bddSHONG Yifan name = crate.name 1874*d4726bddSHONG Yifan crate_info = crate.dep 1875*d4726bddSHONG Yifan else: 1876*d4726bddSHONG Yifan name = crate.name 1877*d4726bddSHONG Yifan crate_info = crate 1878*d4726bddSHONG Yifan return ["--extern={}={}".format(name, crate_info.output.path)] 1879*d4726bddSHONG Yifan 1880*d4726bddSHONG Yifandef _get_crate_dirname(crate): 1881*d4726bddSHONG Yifan """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path 1882*d4726bddSHONG Yifan 1883*d4726bddSHONG Yifan Args: 1884*d4726bddSHONG Yifan crate (CrateInfo): A CrateInfo provider from the current rule 1885*d4726bddSHONG Yifan 1886*d4726bddSHONG Yifan Returns: 1887*d4726bddSHONG Yifan str: The directory name of the the output File that will be produced. 1888*d4726bddSHONG Yifan """ 1889*d4726bddSHONG Yifan return crate.output.dirname 1890*d4726bddSHONG Yifan 1891*d4726bddSHONG Yifandef _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = False, for_darwin = False, flavor_msvc = False): 1892*d4726bddSHONG Yifan artifact = get_preferred_artifact(lib, use_pic) 1893*d4726bddSHONG Yifan if ambiguous_libs and artifact.path in ambiguous_libs: 1894*d4726bddSHONG Yifan artifact = ambiguous_libs[artifact.path] 1895*d4726bddSHONG Yifan if lib.static_library or lib.pic_static_library: 1896*d4726bddSHONG Yifan # To ensure appropriate linker library argument order, in the presence 1897*d4726bddSHONG Yifan # of both native libraries that depend on rlibs and rlibs that depend 1898*d4726bddSHONG Yifan # on native libraries, we use an approach where we "sandwich" the 1899*d4726bddSHONG Yifan # rust libraries between two similar sections of all of native 1900*d4726bddSHONG Yifan # libraries: 1901*d4726bddSHONG Yifan # n1 n2 ... r1 r2 ... n1 n2 ... 1902*d4726bddSHONG Yifan # A B C 1903*d4726bddSHONG Yifan # This way any dependency from a native library to a rust library 1904*d4726bddSHONG Yifan # is resolved from A to B, and any dependency from a rust library to 1905*d4726bddSHONG Yifan # a native one is resolved from B to C. 1906*d4726bddSHONG Yifan # The question of resolving dependencies from a native library from A 1907*d4726bddSHONG Yifan # to any rust library is addressed in a different place, where we 1908*d4726bddSHONG Yifan # create symlinks to the rlibs, pretending they are native libraries, 1909*d4726bddSHONG Yifan # and adding references to these symlinks in the native section A. 1910*d4726bddSHONG Yifan # We rely in the behavior of -Clink-arg to put the linker args 1911*d4726bddSHONG Yifan # at the end of the linker invocation constructed by rustc. 1912*d4726bddSHONG Yifan 1913*d4726bddSHONG Yifan # We skip adding `-Clink-arg=-l` for libstd and libtest from the standard library, as 1914*d4726bddSHONG Yifan # these two libraries are present both as an `.rlib` and a `.so` format. 1915*d4726bddSHONG Yifan # On linux, Rustc adds a -Bdynamic to the linker command line before the libraries specified 1916*d4726bddSHONG Yifan # with `-Clink-arg`, which leads to us linking against the `.so`s but not putting the 1917*d4726bddSHONG Yifan # corresponding value to the runtime library search paths, which results in a 1918*d4726bddSHONG Yifan # "cannot open shared object file: No such file or directory" error at exectuion time. 1919*d4726bddSHONG Yifan # We can fix this by adding a `-Clink-arg=-Bstatic` on linux, but we don't have that option for 1920*d4726bddSHONG Yifan # macos. The proper solution for this issue would be to remove `libtest-{hash}.so` and `libstd-{hash}.so` 1921*d4726bddSHONG Yifan # from the toolchain. However, it is not enough to change the toolchain's `rust_std_{...}` filegroups 1922*d4726bddSHONG Yifan # here: https://github.com/bazelbuild/rules_rust/blob/a9d5d894ad801002d007b858efd154e503796b9f/rust/private/repository_utils.bzl#L144 1923*d4726bddSHONG Yifan # because rustc manages to escape the sandbox and still finds them at linking time. 1924*d4726bddSHONG Yifan # We need to modify the repository rules to erase those files completely. 1925*d4726bddSHONG Yifan if "lib/rustlib" in artifact.path and ( 1926*d4726bddSHONG Yifan artifact.basename.startswith("libtest-") or artifact.basename.startswith("libstd-") or 1927*d4726bddSHONG Yifan artifact.basename.startswith("test-") or artifact.basename.startswith("std-") 1928*d4726bddSHONG Yifan ): 1929*d4726bddSHONG Yifan return [] if for_darwin else ["-lstatic=%s" % get_lib_name(artifact)] 1930*d4726bddSHONG Yifan 1931*d4726bddSHONG Yifan if for_windows: 1932*d4726bddSHONG Yifan if flavor_msvc: 1933*d4726bddSHONG Yifan return [ 1934*d4726bddSHONG Yifan "-lstatic=%s" % get_lib_name(artifact), 1935*d4726bddSHONG Yifan "-Clink-arg={}".format(artifact.basename), 1936*d4726bddSHONG Yifan ] 1937*d4726bddSHONG Yifan else: 1938*d4726bddSHONG Yifan return [ 1939*d4726bddSHONG Yifan "-lstatic=%s" % get_lib_name(artifact), 1940*d4726bddSHONG Yifan "-Clink-arg=-l{}".format(artifact.basename), 1941*d4726bddSHONG Yifan ] 1942*d4726bddSHONG Yifan else: 1943*d4726bddSHONG Yifan return [ 1944*d4726bddSHONG Yifan "-lstatic=%s" % get_lib_name(artifact), 1945*d4726bddSHONG Yifan "-Clink-arg=-l{}".format(get_lib_name(artifact)), 1946*d4726bddSHONG Yifan ] 1947*d4726bddSHONG Yifan elif _is_dylib(lib): 1948*d4726bddSHONG Yifan return [ 1949*d4726bddSHONG Yifan "-ldylib=%s" % get_lib_name(artifact), 1950*d4726bddSHONG Yifan ] 1951*d4726bddSHONG Yifan 1952*d4726bddSHONG Yifan return [] 1953*d4726bddSHONG Yifan 1954*d4726bddSHONG Yifandef _make_link_flags_windows(make_link_flags_args, flavor_msvc): 1955*d4726bddSHONG Yifan linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args 1956*d4726bddSHONG Yifan ret = [] 1957*d4726bddSHONG Yifan for lib in linker_input.libraries: 1958*d4726bddSHONG Yifan if lib.alwayslink: 1959*d4726bddSHONG Yifan if flavor_msvc: 1960*d4726bddSHONG Yifan ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path]) 1961*d4726bddSHONG Yifan else: 1962*d4726bddSHONG Yifan ret.extend([ 1963*d4726bddSHONG Yifan "-C", 1964*d4726bddSHONG Yifan "link-arg=-Wl,--whole-archive", 1965*d4726bddSHONG Yifan "-C", 1966*d4726bddSHONG Yifan ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path), 1967*d4726bddSHONG Yifan "-C", 1968*d4726bddSHONG Yifan "link-arg=-Wl,--no-whole-archive", 1969*d4726bddSHONG Yifan ]) 1970*d4726bddSHONG Yifan elif include_link_flags: 1971*d4726bddSHONG Yifan ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc)) 1972*d4726bddSHONG Yifan return ret 1973*d4726bddSHONG Yifan 1974*d4726bddSHONG Yifandef _make_link_flags_windows_msvc(make_link_flags_args): 1975*d4726bddSHONG Yifan return _make_link_flags_windows(make_link_flags_args, flavor_msvc = True) 1976*d4726bddSHONG Yifan 1977*d4726bddSHONG Yifandef _make_link_flags_windows_gnu(make_link_flags_args): 1978*d4726bddSHONG Yifan return _make_link_flags_windows(make_link_flags_args, flavor_msvc = False) 1979*d4726bddSHONG Yifan 1980*d4726bddSHONG Yifandef _make_link_flags_darwin(make_link_flags_args): 1981*d4726bddSHONG Yifan linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args 1982*d4726bddSHONG Yifan ret = [] 1983*d4726bddSHONG Yifan for lib in linker_input.libraries: 1984*d4726bddSHONG Yifan if lib.alwayslink: 1985*d4726bddSHONG Yifan ret.extend([ 1986*d4726bddSHONG Yifan "-C", 1987*d4726bddSHONG Yifan ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path), 1988*d4726bddSHONG Yifan ]) 1989*d4726bddSHONG Yifan elif include_link_flags: 1990*d4726bddSHONG Yifan ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_darwin = True)) 1991*d4726bddSHONG Yifan return ret 1992*d4726bddSHONG Yifan 1993*d4726bddSHONG Yifandef _make_link_flags_default(make_link_flags_args): 1994*d4726bddSHONG Yifan linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args 1995*d4726bddSHONG Yifan ret = [] 1996*d4726bddSHONG Yifan for lib in linker_input.libraries: 1997*d4726bddSHONG Yifan if lib.alwayslink: 1998*d4726bddSHONG Yifan ret.extend([ 1999*d4726bddSHONG Yifan "-C", 2000*d4726bddSHONG Yifan "link-arg=-Wl,--whole-archive", 2001*d4726bddSHONG Yifan "-C", 2002*d4726bddSHONG Yifan ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path), 2003*d4726bddSHONG Yifan "-C", 2004*d4726bddSHONG Yifan "link-arg=-Wl,--no-whole-archive", 2005*d4726bddSHONG Yifan ]) 2006*d4726bddSHONG Yifan elif include_link_flags: 2007*d4726bddSHONG Yifan ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default)) 2008*d4726bddSHONG Yifan return ret 2009*d4726bddSHONG Yifan 2010*d4726bddSHONG Yifandef _libraries_dirnames(make_link_flags_args): 2011*d4726bddSHONG Yifan link_input, use_pic, _, _ = make_link_flags_args 2012*d4726bddSHONG Yifan 2013*d4726bddSHONG Yifan # De-duplicate names. 2014*d4726bddSHONG Yifan return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list() 2015*d4726bddSHONG Yifan 2016*d4726bddSHONG Yifandef _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = True): 2017*d4726bddSHONG Yifan """Adds linker flags for all dependencies of the current target. 2018*d4726bddSHONG Yifan 2019*d4726bddSHONG Yifan Args: 2020*d4726bddSHONG Yifan args (Args): The Args struct for a ctx.action 2021*d4726bddSHONG Yifan dep_info (DepInfo): Dependency Info provider 2022*d4726bddSHONG Yifan linkstamp_outs (list): Linkstamp outputs of native dependencies 2023*d4726bddSHONG Yifan ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs` 2024*d4726bddSHONG Yifan crate_type: Crate type of the current target 2025*d4726bddSHONG Yifan toolchain (rust_toolchain): The current `rust_toolchain` 2026*d4726bddSHONG Yifan cc_toolchain (CcToolchainInfo): The current `cc_toolchain` 2027*d4726bddSHONG Yifan feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain 2028*d4726bddSHONG Yifan compilation_mode (bool): The compilation mode for this build. 2029*d4726bddSHONG Yifan include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library. 2030*d4726bddSHONG Yifan """ 2031*d4726bddSHONG Yifan if crate_type in ["lib", "rlib"]: 2032*d4726bddSHONG Yifan return 2033*d4726bddSHONG Yifan 2034*d4726bddSHONG Yifan use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode) 2035*d4726bddSHONG Yifan 2036*d4726bddSHONG Yifan if toolchain.target_os == "windows": 2037*d4726bddSHONG Yifan make_link_flags = _make_link_flags_windows_msvc if toolchain.target_triple.abi == "msvc" else _make_link_flags_windows_gnu 2038*d4726bddSHONG Yifan get_lib_name = get_lib_name_for_windows 2039*d4726bddSHONG Yifan elif toolchain.target_os.startswith(("mac", "darwin", "ios")): 2040*d4726bddSHONG Yifan make_link_flags = _make_link_flags_darwin 2041*d4726bddSHONG Yifan get_lib_name = get_lib_name_default 2042*d4726bddSHONG Yifan else: 2043*d4726bddSHONG Yifan make_link_flags = _make_link_flags_default 2044*d4726bddSHONG Yifan get_lib_name = get_lib_name_default 2045*d4726bddSHONG Yifan 2046*d4726bddSHONG Yifan # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0 2047*d4726bddSHONG Yifan make_link_flags_args = [(arg, use_pic, ambiguous_libs, include_link_flags) for arg in dep_info.transitive_noncrates.to_list()] 2048*d4726bddSHONG Yifan args.add_all(make_link_flags_args, map_each = _libraries_dirnames, uniquify = True, format_each = "-Lnative=%s") 2049*d4726bddSHONG Yifan if ambiguous_libs: 2050*d4726bddSHONG Yifan # If there are ambiguous libs, the disambiguation symlinks to them are 2051*d4726bddSHONG Yifan # all created in the same directory. Add it to the library search path. 2052*d4726bddSHONG Yifan ambiguous_libs_dirname = ambiguous_libs.values()[0].dirname 2053*d4726bddSHONG Yifan args.add(ambiguous_libs_dirname, format = "-Lnative=%s") 2054*d4726bddSHONG Yifan 2055*d4726bddSHONG Yifan args.add_all(make_link_flags_args, map_each = make_link_flags) 2056*d4726bddSHONG Yifan 2057*d4726bddSHONG Yifan args.add_all(linkstamp_outs, before_each = "-C", format_each = "link-args=%s") 2058*d4726bddSHONG Yifan 2059*d4726bddSHONG Yifan if crate_type in ["dylib", "cdylib"]: 2060*d4726bddSHONG Yifan # For shared libraries we want to link C++ runtime library dynamically 2061*d4726bddSHONG Yifan # (for example libstdc++.so or libc++.so). 2062*d4726bddSHONG Yifan args.add_all( 2063*d4726bddSHONG Yifan cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration), 2064*d4726bddSHONG Yifan map_each = _get_dirname, 2065*d4726bddSHONG Yifan format_each = "-Lnative=%s", 2066*d4726bddSHONG Yifan ) 2067*d4726bddSHONG Yifan if include_link_flags: 2068*d4726bddSHONG Yifan args.add_all( 2069*d4726bddSHONG Yifan cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration), 2070*d4726bddSHONG Yifan map_each = get_lib_name, 2071*d4726bddSHONG Yifan format_each = "-ldylib=%s", 2072*d4726bddSHONG Yifan ) 2073*d4726bddSHONG Yifan else: 2074*d4726bddSHONG Yifan # For all other crate types we want to link C++ runtime library statically 2075*d4726bddSHONG Yifan # (for example libstdc++.a or libc++.a). 2076*d4726bddSHONG Yifan args.add_all( 2077*d4726bddSHONG Yifan cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration), 2078*d4726bddSHONG Yifan map_each = _get_dirname, 2079*d4726bddSHONG Yifan format_each = "-Lnative=%s", 2080*d4726bddSHONG Yifan ) 2081*d4726bddSHONG Yifan if include_link_flags: 2082*d4726bddSHONG Yifan args.add_all( 2083*d4726bddSHONG Yifan cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration), 2084*d4726bddSHONG Yifan map_each = get_lib_name, 2085*d4726bddSHONG Yifan format_each = "-lstatic=%s", 2086*d4726bddSHONG Yifan ) 2087*d4726bddSHONG Yifan 2088*d4726bddSHONG Yifandef _get_dirname(file): 2089*d4726bddSHONG Yifan """A helper function for `_add_native_link_flags`. 2090*d4726bddSHONG Yifan 2091*d4726bddSHONG Yifan Args: 2092*d4726bddSHONG Yifan file (File): The target file 2093*d4726bddSHONG Yifan 2094*d4726bddSHONG Yifan Returns: 2095*d4726bddSHONG Yifan str: Directory name of `file` 2096*d4726bddSHONG Yifan """ 2097*d4726bddSHONG Yifan return file.dirname 2098*d4726bddSHONG Yifan 2099*d4726bddSHONG Yifandef _add_per_crate_rustc_flags(ctx, args, crate_info, per_crate_rustc_flags): 2100*d4726bddSHONG Yifan """Adds matching per-crate rustc flags to an arguments object reference 2101*d4726bddSHONG Yifan 2102*d4726bddSHONG Yifan Args: 2103*d4726bddSHONG Yifan ctx (ctx): The source rule's context object 2104*d4726bddSHONG Yifan args (Args): A reference to an Args object 2105*d4726bddSHONG Yifan crate_info (CrateInfo): A CrateInfo provider 2106*d4726bddSHONG Yifan per_crate_rustc_flags (list): A list of per_crate_rustc_flag values 2107*d4726bddSHONG Yifan """ 2108*d4726bddSHONG Yifan for per_crate_rustc_flag in per_crate_rustc_flags: 2109*d4726bddSHONG Yifan at_index = per_crate_rustc_flag.find("@") 2110*d4726bddSHONG Yifan if at_index == -1: 2111*d4726bddSHONG Yifan fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag)) 2112*d4726bddSHONG Yifan 2113*d4726bddSHONG Yifan prefix_filter = per_crate_rustc_flag[:at_index] 2114*d4726bddSHONG Yifan flag = per_crate_rustc_flag[at_index + 1:] 2115*d4726bddSHONG Yifan if not flag: 2116*d4726bddSHONG Yifan fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag)) 2117*d4726bddSHONG Yifan 2118*d4726bddSHONG Yifan label_string = str(ctx.label) 2119*d4726bddSHONG Yifan label = label_string[1:] if label_string.startswith("@//") else label_string 2120*d4726bddSHONG Yifan execution_path = crate_info.root.path 2121*d4726bddSHONG Yifan 2122*d4726bddSHONG Yifan if label.startswith(prefix_filter) or execution_path.startswith(prefix_filter): 2123*d4726bddSHONG Yifan args.add(flag) 2124*d4726bddSHONG Yifan 2125*d4726bddSHONG Yifandef _error_format_impl(ctx): 2126*d4726bddSHONG Yifan """Implementation of the `error_format` rule 2127*d4726bddSHONG Yifan 2128*d4726bddSHONG Yifan Args: 2129*d4726bddSHONG Yifan ctx (ctx): The rule's context object 2130*d4726bddSHONG Yifan 2131*d4726bddSHONG Yifan Returns: 2132*d4726bddSHONG Yifan list: A list containing the ErrorFormatInfo provider 2133*d4726bddSHONG Yifan """ 2134*d4726bddSHONG Yifan raw = ctx.build_setting_value 2135*d4726bddSHONG Yifan if raw not in _error_format_values: 2136*d4726bddSHONG Yifan fail("{} expected a value in `{}` but got `{}`".format( 2137*d4726bddSHONG Yifan ctx.label, 2138*d4726bddSHONG Yifan _error_format_values, 2139*d4726bddSHONG Yifan raw, 2140*d4726bddSHONG Yifan )) 2141*d4726bddSHONG Yifan return [ErrorFormatInfo(error_format = raw)] 2142*d4726bddSHONG Yifan 2143*d4726bddSHONG Yifanerror_format = rule( 2144*d4726bddSHONG Yifan doc = ( 2145*d4726bddSHONG Yifan "Change the [--error-format](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-error-format) " + 2146*d4726bddSHONG Yifan "flag from the command line with `--@rules_rust//:error_format`. See rustc documentation for valid values." 2147*d4726bddSHONG Yifan ), 2148*d4726bddSHONG Yifan implementation = _error_format_impl, 2149*d4726bddSHONG Yifan build_setting = config.string(flag = True), 2150*d4726bddSHONG Yifan) 2151*d4726bddSHONG Yifan 2152*d4726bddSHONG Yifandef _rustc_output_diagnostics_impl(ctx): 2153*d4726bddSHONG Yifan """Implementation of the `rustc_output_diagnostics` rule 2154*d4726bddSHONG Yifan 2155*d4726bddSHONG Yifan Args: 2156*d4726bddSHONG Yifan ctx (ctx): The rule's context object 2157*d4726bddSHONG Yifan 2158*d4726bddSHONG Yifan Returns: 2159*d4726bddSHONG Yifan list: A list containing the RustcOutputDiagnosticsInfo provider 2160*d4726bddSHONG Yifan """ 2161*d4726bddSHONG Yifan return [RustcOutputDiagnosticsInfo( 2162*d4726bddSHONG Yifan rustc_output_diagnostics = ctx.build_setting_value, 2163*d4726bddSHONG Yifan )] 2164*d4726bddSHONG Yifan 2165*d4726bddSHONG Yifanrustc_output_diagnostics = rule( 2166*d4726bddSHONG Yifan doc = ( 2167*d4726bddSHONG Yifan "Setting this flag from the command line with `--@rules_rust//:rustc_output_diagnostics` " + 2168*d4726bddSHONG Yifan "makes rules_rust save rustc json output(suitable for consumption by rust-analyzer) in a file. " + 2169*d4726bddSHONG Yifan "These are accessible via the " + 2170*d4726bddSHONG Yifan "`rustc_rmeta_output`(for pipelined compilation) and `rustc_output` output groups. " + 2171*d4726bddSHONG Yifan "You can find these using `bazel cquery`" 2172*d4726bddSHONG Yifan ), 2173*d4726bddSHONG Yifan implementation = _rustc_output_diagnostics_impl, 2174*d4726bddSHONG Yifan build_setting = config.bool(flag = True), 2175*d4726bddSHONG Yifan) 2176*d4726bddSHONG Yifan 2177*d4726bddSHONG Yifandef _extra_rustc_flags_impl(ctx): 2178*d4726bddSHONG Yifan return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value) 2179*d4726bddSHONG Yifan 2180*d4726bddSHONG Yifanextra_rustc_flags = rule( 2181*d4726bddSHONG Yifan doc = ( 2182*d4726bddSHONG Yifan "Add additional rustc_flags from the command line with `--@rules_rust//:extra_rustc_flags`. " + 2183*d4726bddSHONG Yifan "This flag should only be used for flags that need to be applied across the entire build. For options that " + 2184*d4726bddSHONG Yifan "apply to individual crates, use the rustc_flags attribute on the individual crate's rule instead. NOTE: " + 2185*d4726bddSHONG Yifan "These flags not applied to the exec configuration (proc-macros, cargo_build_script, etc); " + 2186*d4726bddSHONG Yifan "use `--@rules_rust//:extra_exec_rustc_flags` to apply flags to the exec configuration." 2187*d4726bddSHONG Yifan ), 2188*d4726bddSHONG Yifan implementation = _extra_rustc_flags_impl, 2189*d4726bddSHONG Yifan build_setting = config.string_list(flag = True), 2190*d4726bddSHONG Yifan) 2191*d4726bddSHONG Yifan 2192*d4726bddSHONG Yifandef _extra_rustc_flag_impl(ctx): 2193*d4726bddSHONG Yifan return ExtraRustcFlagsInfo(extra_rustc_flags = [f for f in ctx.build_setting_value if f != ""]) 2194*d4726bddSHONG Yifan 2195*d4726bddSHONG Yifanextra_rustc_flag = rule( 2196*d4726bddSHONG Yifan doc = ( 2197*d4726bddSHONG Yifan "Add additional rustc_flag from the command line with `--@rules_rust//:extra_rustc_flag`. " + 2198*d4726bddSHONG Yifan "Multiple uses are accumulated and appended after the extra_rustc_flags." 2199*d4726bddSHONG Yifan ), 2200*d4726bddSHONG Yifan implementation = _extra_rustc_flag_impl, 2201*d4726bddSHONG Yifan build_setting = config.string(flag = True, allow_multiple = True), 2202*d4726bddSHONG Yifan) 2203*d4726bddSHONG Yifan 2204*d4726bddSHONG Yifandef _extra_exec_rustc_flags_impl(ctx): 2205*d4726bddSHONG Yifan return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = ctx.build_setting_value) 2206*d4726bddSHONG Yifan 2207*d4726bddSHONG Yifanextra_exec_rustc_flags = rule( 2208*d4726bddSHONG Yifan doc = ( 2209*d4726bddSHONG Yifan "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flags`. " + 2210*d4726bddSHONG Yifan "This flag should only be used for flags that need to be applied across the entire build. " + 2211*d4726bddSHONG Yifan "These flags only apply to the exec configuration (proc-macros, cargo_build_script, etc)." 2212*d4726bddSHONG Yifan ), 2213*d4726bddSHONG Yifan implementation = _extra_exec_rustc_flags_impl, 2214*d4726bddSHONG Yifan build_setting = config.string_list(flag = True), 2215*d4726bddSHONG Yifan) 2216*d4726bddSHONG Yifan 2217*d4726bddSHONG Yifandef _extra_exec_rustc_flag_impl(ctx): 2218*d4726bddSHONG Yifan return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = [f for f in ctx.build_setting_value if f != ""]) 2219*d4726bddSHONG Yifan 2220*d4726bddSHONG Yifanextra_exec_rustc_flag = rule( 2221*d4726bddSHONG Yifan doc = ( 2222*d4726bddSHONG Yifan "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flag`. " + 2223*d4726bddSHONG Yifan "Multiple uses are accumulated and appended after the extra_exec_rustc_flags." 2224*d4726bddSHONG Yifan ), 2225*d4726bddSHONG Yifan implementation = _extra_exec_rustc_flag_impl, 2226*d4726bddSHONG Yifan build_setting = config.string(flag = True, allow_multiple = True), 2227*d4726bddSHONG Yifan) 2228*d4726bddSHONG Yifan 2229*d4726bddSHONG Yifandef _per_crate_rustc_flag_impl(ctx): 2230*d4726bddSHONG Yifan return PerCrateRustcFlagsInfo(per_crate_rustc_flags = [f for f in ctx.build_setting_value if f != ""]) 2231*d4726bddSHONG Yifan 2232*d4726bddSHONG Yifanper_crate_rustc_flag = rule( 2233*d4726bddSHONG Yifan doc = ( 2234*d4726bddSHONG Yifan "Add additional rustc_flag to matching crates from the command line with `--@rules_rust//:experimental_per_crate_rustc_flag`. " + 2235*d4726bddSHONG Yifan "The expected flag format is prefix_filter@flag, where any crate with a label or execution path starting with the prefix filter will be built with the given flag." + 2236*d4726bddSHONG Yifan "The label matching uses the canonical form of the label (i.e //package:label_name)." + 2237*d4726bddSHONG Yifan "The execution path is the relative path to your workspace directory including the base name (including extension) of the crate root." + 2238*d4726bddSHONG Yifan "This flag is only applied to the exec configuration (proc-macros, cargo_build_script, etc)." + 2239*d4726bddSHONG Yifan "Multiple uses are accumulated." 2240*d4726bddSHONG Yifan ), 2241*d4726bddSHONG Yifan implementation = _per_crate_rustc_flag_impl, 2242*d4726bddSHONG Yifan build_setting = config.string(flag = True, allow_multiple = True), 2243*d4726bddSHONG Yifan) 2244*d4726bddSHONG Yifan 2245*d4726bddSHONG Yifandef _no_std_impl(ctx): 2246*d4726bddSHONG Yifan value = str(ctx.attr._no_std[BuildSettingInfo].value) 2247*d4726bddSHONG Yifan if is_exec_configuration(ctx): 2248*d4726bddSHONG Yifan return [config_common.FeatureFlagInfo(value = "off")] 2249*d4726bddSHONG Yifan return [config_common.FeatureFlagInfo(value = value)] 2250*d4726bddSHONG Yifan 2251*d4726bddSHONG Yifanno_std = rule( 2252*d4726bddSHONG Yifan doc = ( 2253*d4726bddSHONG Yifan "No std; we need this so that we can distinguish between host and exec" 2254*d4726bddSHONG Yifan ), 2255*d4726bddSHONG Yifan attrs = { 2256*d4726bddSHONG Yifan "_no_std": attr.label(default = "//:no_std"), 2257*d4726bddSHONG Yifan }, 2258*d4726bddSHONG Yifan implementation = _no_std_impl, 2259*d4726bddSHONG Yifan) 2260