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