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