1# Copyright 2015 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"""Rust rule implementations""" 16 17load("@bazel_skylib//lib:paths.bzl", "paths") 18load("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common") 19load("//rust/private:providers.bzl", "BuildInfo") 20load("//rust/private:rustc.bzl", "rustc_compile_action") 21load( 22 "//rust/private:utils.bzl", 23 "can_build_metadata", 24 "compute_crate_name", 25 "crate_root_src", 26 "dedent", 27 "determine_lib_name", 28 "determine_output_hash", 29 "expand_dict_value_locations", 30 "find_toolchain", 31 "generate_output_diagnostics", 32 "get_edition", 33 "get_import_macro_deps", 34 "transform_deps", 35 "transform_sources", 36) 37 38# TODO(marco): Separate each rule into its own file. 39 40def _assert_no_deprecated_attributes(_ctx): 41 """Forces a failure if any deprecated attributes were specified 42 43 Args: 44 _ctx (ctx): The current rule's context object 45 """ 46 pass 47 48def _assert_correct_dep_mapping(ctx): 49 """Forces a failure if proc_macro_deps and deps are mixed inappropriately 50 51 Args: 52 ctx (ctx): The current rule's context object 53 """ 54 for dep in ctx.attr.deps: 55 if rust_common.crate_info in dep: 56 if dep[rust_common.crate_info].type == "proc-macro": 57 fail( 58 "{} listed {} in its deps, but it is a proc-macro. It should instead be in the bazel property proc_macro_deps.".format( 59 ctx.label, 60 dep.label, 61 ), 62 ) 63 for dep in ctx.attr.proc_macro_deps: 64 type = dep[rust_common.crate_info].type 65 if type != "proc-macro": 66 fail( 67 "{} listed {} in its proc_macro_deps, but it is not proc-macro, it is a {}. It should probably instead be listed in deps.".format( 68 ctx.label, 69 dep.label, 70 type, 71 ), 72 ) 73 74def _rust_library_impl(ctx): 75 """The implementation of the `rust_library` rule. 76 77 This rule provides CcInfo, so it can be used everywhere Bazel 78 expects rules_cc, but care must be taken to have the correct 79 dependencies on an allocator and std implemetation as needed. 80 81 Args: 82 ctx (ctx): The rule's context object 83 84 Returns: 85 list: A list of providers. 86 """ 87 return _rust_library_common(ctx, "rlib") 88 89def _rust_static_library_impl(ctx): 90 """The implementation of the `rust_static_library` rule. 91 92 This rule provides CcInfo, so it can be used everywhere Bazel 93 expects rules_cc. 94 95 Args: 96 ctx (ctx): The rule's context object 97 98 Returns: 99 list: A list of providers. 100 """ 101 return _rust_library_common(ctx, "staticlib") 102 103def _rust_shared_library_impl(ctx): 104 """The implementation of the `rust_shared_library` rule. 105 106 This rule provides CcInfo, so it can be used everywhere Bazel 107 expects rules_cc. 108 109 On Windows, a PDB file containing debugging information is available under 110 the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder 111 is available under the key `dsym_folder` in `OutputGroupInfo`. 112 113 Args: 114 ctx (ctx): The rule's context object 115 116 Returns: 117 list: A list of providers. 118 """ 119 return _rust_library_common(ctx, "cdylib") 120 121def _rust_proc_macro_impl(ctx): 122 """The implementation of the `rust_proc_macro` rule. 123 124 Args: 125 ctx (ctx): The rule's context object 126 127 Returns: 128 list: A list of providers. 129 """ 130 return _rust_library_common(ctx, "proc-macro") 131 132def _rust_library_common(ctx, crate_type): 133 """The common implementation of the library-like rules. 134 135 Args: 136 ctx (ctx): The rule's context object 137 crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro 138 139 Returns: 140 list: A list of providers. See `rustc_compile_action` 141 """ 142 _assert_no_deprecated_attributes(ctx) 143 _assert_correct_dep_mapping(ctx) 144 145 toolchain = find_toolchain(ctx) 146 147 crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name) 148 149 crate_root = getattr(ctx.file, "crate_root", None) 150 if not crate_root: 151 crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_type) 152 srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root) 153 154 # Determine unique hash for this rlib. 155 # Note that we don't include a hash for `cdylib` and `staticlib` since they are meant to be consumed externally 156 # and having a deterministic name is important since it ends up embedded in the executable. This is problematic 157 # when one needs to include the library with a specific filename into a larger application. 158 # (see https://github.com/bazelbuild/rules_rust/issues/405#issuecomment-993089889 for more details) 159 if crate_type in ["cdylib", "staticlib"]: 160 output_hash = None 161 else: 162 output_hash = determine_output_hash(crate_root, ctx.label) 163 164 rust_lib_name = determine_lib_name( 165 crate_name, 166 crate_type, 167 toolchain, 168 output_hash, 169 ) 170 rust_lib = ctx.actions.declare_file(rust_lib_name) 171 rust_metadata = None 172 rustc_rmeta_output = None 173 if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining: 174 rust_metadata = ctx.actions.declare_file( 175 paths.replace_extension(rust_lib_name, ".rmeta"), 176 sibling = rust_lib, 177 ) 178 rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata) 179 180 deps = transform_deps(ctx.attr.deps) 181 proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx)) 182 183 return rustc_compile_action( 184 ctx = ctx, 185 attr = ctx.attr, 186 toolchain = toolchain, 187 output_hash = output_hash, 188 crate_info_dict = dict( 189 name = crate_name, 190 type = crate_type, 191 root = crate_root, 192 srcs = depset(srcs), 193 deps = depset(deps), 194 proc_macro_deps = depset(proc_macro_deps), 195 aliases = ctx.attr.aliases, 196 output = rust_lib, 197 rustc_output = generate_output_diagnostics(ctx, rust_lib), 198 metadata = rust_metadata, 199 rustc_rmeta_output = rustc_rmeta_output, 200 edition = get_edition(ctx.attr, toolchain, ctx.label), 201 rustc_env = ctx.attr.rustc_env, 202 rustc_env_files = ctx.files.rustc_env_files, 203 is_test = False, 204 data = depset(ctx.files.data), 205 compile_data = depset(ctx.files.compile_data), 206 compile_data_targets = depset(ctx.attr.compile_data), 207 owner = ctx.label, 208 ), 209 ) 210 211def _rust_binary_impl(ctx): 212 """The implementation of the `rust_binary` rule 213 214 Args: 215 ctx (ctx): The rule's context object 216 217 Returns: 218 list: A list of providers. See `rustc_compile_action` 219 """ 220 toolchain = find_toolchain(ctx) 221 crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name) 222 _assert_correct_dep_mapping(ctx) 223 224 output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext) 225 226 deps = transform_deps(ctx.attr.deps) 227 proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx)) 228 229 crate_root = getattr(ctx.file, "crate_root", None) 230 if not crate_root: 231 crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, ctx.attr.crate_type) 232 srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root) 233 234 providers = rustc_compile_action( 235 ctx = ctx, 236 attr = ctx.attr, 237 toolchain = toolchain, 238 crate_info_dict = dict( 239 name = crate_name, 240 type = ctx.attr.crate_type, 241 root = crate_root, 242 srcs = depset(srcs), 243 deps = depset(deps), 244 proc_macro_deps = depset(proc_macro_deps), 245 aliases = ctx.attr.aliases, 246 output = output, 247 rustc_output = generate_output_diagnostics(ctx, output), 248 edition = get_edition(ctx.attr, toolchain, ctx.label), 249 rustc_env = ctx.attr.rustc_env, 250 rustc_env_files = ctx.files.rustc_env_files, 251 is_test = False, 252 data = depset(ctx.files.data), 253 compile_data = depset(ctx.files.compile_data), 254 compile_data_targets = depset(ctx.attr.compile_data), 255 owner = ctx.label, 256 ), 257 ) 258 259 providers.append(RunEnvironmentInfo( 260 environment = expand_dict_value_locations( 261 ctx, 262 ctx.attr.env, 263 ctx.attr.data, 264 ), 265 )) 266 267 return providers 268 269def get_rust_test_flags(attr): 270 """Determine the desired rustc flags for test targets. 271 272 Args: 273 attr (dict): Attributes of a rule 274 275 Returns: 276 List: A list of test flags 277 """ 278 if getattr(attr, "use_libtest_harness", True): 279 rust_flags = ["--test"] 280 else: 281 rust_flags = ["--cfg", "test"] 282 283 return rust_flags 284 285def _rust_test_impl(ctx): 286 """The implementation of the `rust_test` rule. 287 288 Args: 289 ctx (ctx): The ctx object for the current target. 290 291 Returns: 292 list: The list of providers. See `rustc_compile_action` 293 """ 294 _assert_no_deprecated_attributes(ctx) 295 _assert_correct_dep_mapping(ctx) 296 297 toolchain = find_toolchain(ctx) 298 299 crate_type = "bin" 300 deps = transform_deps(ctx.attr.deps) 301 proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx)) 302 303 if toolchain._incompatible_test_attr_crate_and_srcs_mutually_exclusive: 304 if ctx.attr.crate and ctx.attr.srcs: 305 fail("rust_test.crate and rust_test.srcs are mutually exclusive. Update {} to use only one of these attributes".format( 306 ctx.label, 307 )) 308 309 if ctx.attr.crate: 310 # Target is building the crate in `test` config 311 crate = ctx.attr.crate[rust_common.crate_info] if rust_common.crate_info in ctx.attr.crate else ctx.attr.crate[rust_common.test_crate_info].crate 312 313 output_hash = determine_output_hash(crate.root, ctx.label) 314 output = ctx.actions.declare_file( 315 "test-%s/%s%s" % ( 316 output_hash, 317 ctx.label.name, 318 toolchain.binary_ext, 319 ), 320 ) 321 322 srcs, crate_root = transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None)) 323 324 # Optionally join compile data 325 if crate.compile_data: 326 compile_data = depset(ctx.files.compile_data, transitive = [crate.compile_data]) 327 else: 328 compile_data = depset(ctx.files.compile_data) 329 if crate.compile_data_targets: 330 compile_data_targets = depset(ctx.attr.compile_data, transitive = [crate.compile_data_targets]) 331 else: 332 compile_data_targets = depset(ctx.attr.compile_data) 333 rustc_env_files = ctx.files.rustc_env_files + crate.rustc_env_files 334 335 # crate.rustc_env is already expanded upstream in rust_library rule implementation 336 rustc_env = dict(crate.rustc_env) 337 data_paths = depset(direct = getattr(ctx.attr, "data", [])).to_list() 338 rustc_env.update(expand_dict_value_locations( 339 ctx, 340 ctx.attr.rustc_env, 341 data_paths, 342 )) 343 344 # Build the test binary using the dependency's srcs. 345 crate_info_dict = dict( 346 name = crate.name, 347 type = crate_type, 348 root = crate.root, 349 srcs = depset(srcs, transitive = [crate.srcs]), 350 deps = depset(deps, transitive = [crate.deps]), 351 proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]), 352 aliases = ctx.attr.aliases, 353 output = output, 354 rustc_output = generate_output_diagnostics(ctx, output), 355 edition = crate.edition, 356 rustc_env = rustc_env, 357 rustc_env_files = rustc_env_files, 358 is_test = True, 359 compile_data = compile_data, 360 compile_data_targets = compile_data_targets, 361 wrapped_crate_type = crate.type, 362 owner = ctx.label, 363 ) 364 else: 365 crate_root = getattr(ctx.file, "crate_root", None) 366 367 if not crate_root: 368 crate_root_type = "lib" if ctx.attr.use_libtest_harness else "bin" 369 crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_root_type) 370 srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root) 371 372 output_hash = determine_output_hash(crate_root, ctx.label) 373 output = ctx.actions.declare_file( 374 "test-%s/%s%s" % ( 375 output_hash, 376 ctx.label.name, 377 toolchain.binary_ext, 378 ), 379 ) 380 381 data_paths = depset(direct = getattr(ctx.attr, "data", [])).to_list() 382 rustc_env = expand_dict_value_locations( 383 ctx, 384 ctx.attr.rustc_env, 385 data_paths, 386 ) 387 388 # Target is a standalone crate. Build the test binary as its own crate. 389 crate_info_dict = dict( 390 name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name), 391 type = crate_type, 392 root = crate_root, 393 srcs = depset(srcs), 394 deps = depset(deps), 395 proc_macro_deps = depset(proc_macro_deps), 396 aliases = ctx.attr.aliases, 397 output = output, 398 rustc_output = generate_output_diagnostics(ctx, output), 399 edition = get_edition(ctx.attr, toolchain, ctx.label), 400 rustc_env = rustc_env, 401 rustc_env_files = ctx.files.rustc_env_files, 402 is_test = True, 403 compile_data = depset(ctx.files.compile_data), 404 compile_data_targets = depset(ctx.attr.compile_data), 405 owner = ctx.label, 406 ) 407 408 providers = rustc_compile_action( 409 ctx = ctx, 410 attr = ctx.attr, 411 toolchain = toolchain, 412 crate_info_dict = crate_info_dict, 413 rust_flags = get_rust_test_flags(ctx.attr), 414 skip_expanding_rustc_env = True, 415 ) 416 data = getattr(ctx.attr, "data", []) 417 418 env = expand_dict_value_locations( 419 ctx, 420 getattr(ctx.attr, "env", {}), 421 data, 422 ) 423 if toolchain.llvm_cov and ctx.configuration.coverage_enabled: 424 if not toolchain.llvm_profdata: 425 fail("toolchain.llvm_profdata is required if toolchain.llvm_cov is set.") 426 427 if toolchain._experimental_use_coverage_metadata_files: 428 llvm_cov_path = toolchain.llvm_cov.path 429 llvm_profdata_path = toolchain.llvm_profdata.path 430 else: 431 llvm_cov_path = toolchain.llvm_cov.short_path 432 if llvm_cov_path.startswith("../"): 433 llvm_cov_path = llvm_cov_path[len("../"):] 434 435 llvm_profdata_path = toolchain.llvm_profdata.short_path 436 if llvm_profdata_path.startswith("../"): 437 llvm_profdata_path = llvm_profdata_path[len("../"):] 438 439 env["RUST_LLVM_COV"] = llvm_cov_path 440 env["RUST_LLVM_PROFDATA"] = llvm_profdata_path 441 components = "{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/") 442 env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c]) 443 providers.append(testing.TestEnvironment(env)) 444 445 return providers 446 447def _rust_library_group_impl(ctx): 448 dep_variant_infos = [] 449 dep_variant_transitive_infos = [] 450 runfiles = [] 451 452 for dep in ctx.attr.deps: 453 if rust_common.crate_info in dep: 454 dep_variant_infos.append(rust_common.dep_variant_info( 455 crate_info = dep[rust_common.crate_info] if rust_common.crate_info in dep else None, 456 dep_info = dep[rust_common.dep_info] if rust_common.crate_info in dep else None, 457 build_info = dep[BuildInfo] if BuildInfo in dep else None, 458 cc_info = dep[CcInfo] if CcInfo in dep else None, 459 crate_group_info = None, 460 )) 461 elif rust_common.crate_group_info in dep: 462 dep_variant_transitive_infos.append(dep[rust_common.crate_group_info].dep_variant_infos) 463 else: 464 fail("crate_group_info targets can only depend on rust_library or rust_library_group targets.") 465 466 if dep[DefaultInfo].default_runfiles != None: 467 runfiles.append(dep[DefaultInfo].default_runfiles) 468 469 return [ 470 rust_common.crate_group_info( 471 dep_variant_infos = depset(dep_variant_infos, transitive = dep_variant_transitive_infos), 472 ), 473 DefaultInfo(runfiles = ctx.runfiles().merge_all(runfiles)), 474 coverage_common.instrumented_files_info( 475 ctx, 476 dependency_attributes = ["deps"], 477 ), 478 ] 479 480def _stamp_attribute(default_value): 481 return attr.int( 482 doc = dedent("""\ 483 Whether to encode build information into the `Rustc` action. Possible values: 484 485 - `stamp = 1`: Always stamp the build information into the `Rustc` action, even in \ 486 [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \ 487 This setting should be avoided, since it potentially kills remote caching for the target and \ 488 any downstream actions that depend on it. 489 490 - `stamp = 0`: Always replace build information by constant values. This gives good build result caching. 491 492 - `stamp = -1`: Embedding of build information is controlled by the \ 493 [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. 494 495 Stamped targets are not rebuilt unless their dependencies change. 496 497 For example if a `rust_library` is stamped, and a `rust_binary` depends on that library, the stamped 498 library won't be rebuilt when we change sources of the `rust_binary`. This is different from how 499 [`cc_library.linkstamps`](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) 500 behaves. 501 """), 502 default = default_value, 503 values = [1, 0, -1], 504 ) 505 506# Internal attributes core to Rustc actions. 507RUSTC_ATTRS = { 508 "_cc_toolchain": attr.label( 509 doc = ( 510 "In order to use find_cc_toolchain, your rule has to depend " + 511 "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " + 512 "docs for details." 513 ), 514 default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), 515 ), 516 "_error_format": attr.label( 517 default = Label("//:error_format"), 518 ), 519 "_extra_exec_rustc_flag": attr.label( 520 default = Label("//:extra_exec_rustc_flag"), 521 ), 522 "_extra_exec_rustc_flags": attr.label( 523 default = Label("//:extra_exec_rustc_flags"), 524 ), 525 "_extra_rustc_flag": attr.label( 526 default = Label("//:extra_rustc_flag"), 527 ), 528 "_extra_rustc_flags": attr.label( 529 default = Label("//:extra_rustc_flags"), 530 ), 531 "_import_macro_dep": attr.label( 532 default = Label("//util/import"), 533 cfg = "exec", 534 ), 535 "_is_proc_macro_dep": attr.label( 536 default = Label("//rust/private:is_proc_macro_dep"), 537 ), 538 "_is_proc_macro_dep_enabled": attr.label( 539 default = Label("//rust/private:is_proc_macro_dep_enabled"), 540 ), 541 "_per_crate_rustc_flag": attr.label( 542 default = Label("//:experimental_per_crate_rustc_flag"), 543 ), 544 "_process_wrapper": attr.label( 545 doc = "A process wrapper for running rustc on all platforms.", 546 default = Label("//util/process_wrapper"), 547 executable = True, 548 allow_single_file = True, 549 cfg = "exec", 550 ), 551 "_rustc_output_diagnostics": attr.label( 552 default = Label("//:rustc_output_diagnostics"), 553 ), 554} 555 556_common_attrs = { 557 "aliases": attr.label_keyed_string_dict( 558 doc = dedent("""\ 559 Remap crates to a new name or moniker for linkage to this target 560 561 These are other `rust_library` targets and will be presented as the new name given. 562 """), 563 ), 564 "compile_data": attr.label_list( 565 doc = dedent("""\ 566 List of files used by this rule at compile time. 567 568 This attribute can be used to specify any data files that are embedded into 569 the library, such as via the 570 [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html) 571 macro. 572 """), 573 allow_files = True, 574 ), 575 "crate_features": attr.string_list( 576 doc = dedent("""\ 577 List of features to enable for this crate. 578 579 Features are defined in the code using the `#[cfg(feature = "foo")]` 580 configuration option. The features listed here will be passed to `rustc` 581 with `--cfg feature="${feature_name}"` flags. 582 """), 583 ), 584 "crate_name": attr.string( 585 doc = dedent("""\ 586 Crate name to use for this target. 587 588 This must be a valid Rust identifier, i.e. it may contain only alphanumeric characters and underscores. 589 Defaults to the target name, with any hyphens replaced by underscores. 590 """), 591 ), 592 "crate_root": attr.label( 593 doc = dedent("""\ 594 The file that will be passed to `rustc` to be used for building this crate. 595 596 If `crate_root` is not set, then this rule will look for a `lib.rs` file (or `main.rs` for rust_binary) 597 or the single file in `srcs` if `srcs` contains only one file. 598 """), 599 allow_single_file = [".rs"], 600 ), 601 "data": attr.label_list( 602 doc = dedent("""\ 603 List of files used by this rule at compile time and runtime. 604 605 If including data at compile time with include_str!() and similar, 606 prefer `compile_data` over `data`, to prevent the data also being included 607 in the runfiles. 608 """), 609 allow_files = True, 610 ), 611 "deps": attr.label_list( 612 doc = dedent("""\ 613 List of other libraries to be linked to this library target. 614 615 These can be either other `rust_library` targets or `cc_library` targets if 616 linking a native library. 617 """), 618 ), 619 "edition": attr.string( 620 doc = "The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain.", 621 ), 622 # Previously `proc_macro_deps` were a part of `deps`, and then proc_macro_host_transition was 623 # used into cfg="host" using `@local_config_platform//:host`. 624 # This fails for remote execution, which needs cfg="exec", and there isn't anything like 625 # `@local_config_platform//:exec` exposed. 626 "proc_macro_deps": attr.label_list( 627 doc = dedent("""\ 628 List of `rust_proc_macro` targets used to help build this library target. 629 """), 630 cfg = "exec", 631 providers = [rust_common.crate_info], 632 ), 633 "rustc_env": attr.string_dict( 634 doc = dedent("""\ 635 Dictionary of additional `"key": "value"` environment variables to set for rustc. 636 637 rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the 638 location of a generated file or external tool. Cargo build scripts that wish to 639 expand locations should use cargo_build_script()'s build_script_env argument instead, 640 as build scripts are run in a different environment - see cargo_build_script()'s 641 documentation for more. 642 """), 643 ), 644 "rustc_env_files": attr.label_list( 645 doc = dedent("""\ 646 Files containing additional environment variables to set for rustc. 647 648 These files should contain a single variable per line, of format 649 `NAME=value`, and newlines may be included in a value by ending a 650 line with a trailing back-slash (`\\\\`). 651 652 The order that these files will be processed is unspecified, so 653 multiple definitions of a particular variable are discouraged. 654 655 Note that the variables here are subject to 656 [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) 657 stamping should the `stamp` attribute be enabled. Stamp variables 658 should be wrapped in brackets in order to be resolved. E.g. 659 `NAME={WORKSPACE_STATUS_VARIABLE}`. 660 """), 661 allow_files = True, 662 ), 663 "rustc_flags": attr.string_list( 664 doc = dedent("""\ 665 List of compiler flags passed to `rustc`. 666 667 These strings are subject to Make variable expansion for predefined 668 source/output path variables like `$location`, `$execpath`, and 669 `$rootpath`. This expansion is useful if you wish to pass a generated 670 file of arguments to rustc: `@$(location //package:target)`. 671 """), 672 ), 673 # TODO(stardoc): How do we provide additional documentation to an inherited attribute? 674 # "name": attr.string( 675 # doc = "This name will also be used as the name of the crate built by this rule.", 676 # `), 677 "srcs": attr.label_list( 678 doc = dedent("""\ 679 List of Rust `.rs` source files used to build the library. 680 681 If `srcs` contains more than one file, then there must be a file either 682 named `lib.rs`. Otherwise, `crate_root` must be set to the source file that 683 is the root of the crate to be passed to rustc to build this crate. 684 """), 685 allow_files = [".rs"], 686 ), 687 "stamp": _stamp_attribute( 688 default_value = 0, 689 ), 690 "version": attr.string( 691 doc = "A version to inject in the cargo environment variable.", 692 default = "0.0.0", 693 ), 694 "_stamp_flag": attr.label( 695 doc = "A setting used to determine whether or not the `--stamp` flag is enabled", 696 default = Label("//rust/private:stamp"), 697 ), 698} | RUSTC_ATTRS 699 700_coverage_attrs = { 701 "_collect_cc_coverage": attr.label( 702 default = Label("//util/collect_coverage"), 703 executable = True, 704 cfg = "exec", 705 ), 706 # Bazel’s coverage runner 707 # (https://github.com/bazelbuild/bazel/blob/6.0.0/tools/test/collect_coverage.sh) 708 # needs a binary called “lcov_merge.” Its location is passed in the 709 # LCOV_MERGER environmental variable. For builtin rules, this variable 710 # is set automatically based on a magic “$lcov_merger” or 711 # “:lcov_merger” attribute, but it’s not possible to create such 712 # attributes in Starlark. Therefore we specify the variable ourselves. 713 # Note that the coverage runner runs in the runfiles root instead of 714 # the execution root, therefore we use “path” instead of “short_path.” 715 "_lcov_merger": attr.label( 716 default = configuration_field(fragment = "coverage", name = "output_generator"), 717 executable = True, 718 cfg = "exec", 719 ), 720} 721 722_experimental_use_cc_common_link_attrs = { 723 "experimental_use_cc_common_link": attr.int( 724 doc = ( 725 "Whether to use cc_common.link to link rust binaries. " + 726 "Possible values: [-1, 0, 1]. " + 727 "-1 means use the value of the toolchain.experimental_use_cc_common_link " + 728 "boolean build setting to determine. " + 729 "0 means do not use cc_common.link (use rustc instead). " + 730 "1 means use cc_common.link." 731 ), 732 values = [-1, 0, 1], 733 default = -1, 734 ), 735 "malloc": attr.label( 736 default = Label("@bazel_tools//tools/cpp:malloc"), 737 doc = """Override the default dependency on `malloc`. 738 739By default, Rust binaries linked with cc_common.link are linked against 740`@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use 741libc's `malloc`. This label must refer to a `cc_library` rule. 742""", 743 mandatory = False, 744 providers = [[CcInfo]], 745 ), # A late-bound attribute denoting the value of the `--custom_malloc` 746 # command line flag (or None if the flag is not provided). 747 "_custom_malloc": attr.label( 748 default = configuration_field( 749 fragment = "cpp", 750 name = "custom_malloc", 751 ), 752 providers = [[CcInfo]], 753 ), 754} 755 756_rust_test_attrs = dict({ 757 "crate": attr.label( 758 mandatory = False, 759 doc = dedent("""\ 760 Target inline tests declared in the given crate 761 762 These tests are typically those that would be held out under 763 `#[cfg(test)]` declarations. 764 """), 765 ), 766 "env": attr.string_dict( 767 mandatory = False, 768 doc = dedent("""\ 769 Specifies additional environment variables to set when the test is executed by bazel test. 770 Values are subject to `$(rootpath)`, `$(execpath)`, location, and 771 ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution. 772 """), 773 ), 774 "use_libtest_harness": attr.bool( 775 mandatory = False, 776 default = True, 777 doc = dedent("""\ 778 Whether to use `libtest`. For targets using this flag, individual tests can be run by using the 779 [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag. 780 E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`. 781 """), 782 ), 783 "_use_grep_includes": attr.bool(default = True), 784}.items() + _coverage_attrs.items() + _experimental_use_cc_common_link_attrs.items()) 785 786rust_library = rule( 787 implementation = _rust_library_impl, 788 provides = COMMON_PROVIDERS, 789 attrs = dict(_common_attrs.items() + { 790 "disable_pipelining": attr.bool( 791 default = False, 792 doc = dedent("""\ 793 Disables pipelining for this rule if it is globally enabled. 794 This will cause this rule to not produce a `.rmeta` file and all the dependent 795 crates will instead use the `.rlib` file. 796 """), 797 ), 798 }.items()), 799 fragments = ["cpp"], 800 toolchains = [ 801 str(Label("//rust:toolchain_type")), 802 "@bazel_tools//tools/cpp:toolchain_type", 803 ], 804 doc = dedent("""\ 805 Builds a Rust library crate. 806 807 Example: 808 809 Suppose you have the following directory structure for a simple Rust library crate: 810 811 ```output 812 [workspace]/ 813 WORKSPACE 814 hello_lib/ 815 BUILD 816 src/ 817 greeter.rs 818 lib.rs 819 ``` 820 821 `hello_lib/src/greeter.rs`: 822 ```rust 823 pub struct Greeter { 824 greeting: String, 825 } 826 827 impl Greeter { 828 pub fn new(greeting: &str) -> Greeter { 829 Greeter { greeting: greeting.to_string(), } 830 } 831 832 pub fn greet(&self, thing: &str) { 833 println!("{} {}", &self.greeting, thing); 834 } 835 } 836 ``` 837 838 `hello_lib/src/lib.rs`: 839 840 ```rust 841 pub mod greeter; 842 ``` 843 844 `hello_lib/BUILD`: 845 ```python 846 package(default_visibility = ["//visibility:public"]) 847 848 load("@rules_rust//rust:defs.bzl", "rust_library") 849 850 rust_library( 851 name = "hello_lib", 852 srcs = [ 853 "src/greeter.rs", 854 "src/lib.rs", 855 ], 856 ) 857 ``` 858 859 Build the library: 860 ```output 861 $ bazel build //hello_lib 862 INFO: Found 1 target... 863 Target //examples/rust/hello_lib:hello_lib up-to-date: 864 bazel-bin/examples/rust/hello_lib/libhello_lib.rlib 865 INFO: Elapsed time: 1.245s, Critical Path: 1.01s 866 ``` 867 """), 868) 869 870def _rust_static_library_transition_impl(settings, attr): 871 return { 872 "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"], 873 } 874 875_rust_static_library_transition = transition( 876 implementation = _rust_static_library_transition_impl, 877 inputs = [ 878 "//command_line_option:platforms", 879 ], 880 outputs = [ 881 "//command_line_option:platforms", 882 ], 883) 884 885rust_static_library = rule( 886 implementation = _rust_static_library_impl, 887 attrs = dict(_common_attrs.items() + { 888 "platform": attr.label( 889 doc = "Optional platform to transition the static library to.", 890 default = None, 891 ), 892 "_allowlist_function_transition": attr.label( 893 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 894 ), 895 }.items()), 896 fragments = ["cpp"], 897 cfg = _rust_static_library_transition, 898 toolchains = [ 899 str(Label("//rust:toolchain_type")), 900 "@bazel_tools//tools/cpp:toolchain_type", 901 ], 902 provides = [CcInfo], 903 doc = dedent("""\ 904 Builds a Rust static library. 905 906 This static library will contain all transitively reachable crates and native objects. 907 It is meant to be used when producing an artifact that is then consumed by some other build system 908 (for example to produce an archive that Python program links against). 909 910 This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`. 911 912 When building the whole binary in Bazel, use `rust_library` instead. 913 """), 914) 915 916def _rust_shared_library_transition_impl(settings, attr): 917 return { 918 "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"], 919 } 920 921_rust_shared_library_transition = transition( 922 implementation = _rust_shared_library_transition_impl, 923 inputs = [ 924 "//command_line_option:platforms", 925 ], 926 outputs = [ 927 "//command_line_option:platforms", 928 ], 929) 930 931rust_shared_library = rule( 932 implementation = _rust_shared_library_impl, 933 attrs = dict(_common_attrs.items() + _experimental_use_cc_common_link_attrs.items() + { 934 "platform": attr.label( 935 doc = "Optional platform to transition the shared library to.", 936 default = None, 937 ), 938 "_allowlist_function_transition": attr.label( 939 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 940 ), 941 "_use_grep_includes": attr.bool(default = True), 942 }.items()), 943 fragments = ["cpp"], 944 cfg = _rust_shared_library_transition, 945 toolchains = [ 946 str(Label("//rust:toolchain_type")), 947 "@bazel_tools//tools/cpp:toolchain_type", 948 ], 949 provides = [CcInfo], 950 doc = dedent("""\ 951 Builds a Rust shared library. 952 953 This shared library will contain all transitively reachable crates and native objects. 954 It is meant to be used when producing an artifact that is then consumed by some other build system 955 (for example to produce a shared library that Python program links against). 956 957 This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`. 958 959 When building the whole binary in Bazel, use `rust_library` instead. 960 """), 961) 962 963def _proc_macro_dep_transition_impl(settings, _attr): 964 if settings["//rust/private:is_proc_macro_dep_enabled"]: 965 return {"//rust/private:is_proc_macro_dep": True} 966 else: 967 return [] 968 969_proc_macro_dep_transition = transition( 970 inputs = ["//rust/private:is_proc_macro_dep_enabled"], 971 outputs = ["//rust/private:is_proc_macro_dep"], 972 implementation = _proc_macro_dep_transition_impl, 973) 974 975rust_proc_macro = rule( 976 implementation = _rust_proc_macro_impl, 977 provides = COMMON_PROVIDERS, 978 # Start by copying the common attributes, then override the `deps` attribute 979 # to apply `_proc_macro_dep_transition`. To add this transition we additionally 980 # need to declare `_allowlist_function_transition`, see 981 # https://docs.bazel.build/versions/main/skylark/config.html#user-defined-transitions. 982 attrs = dict( 983 _common_attrs.items(), 984 _allowlist_function_transition = attr.label( 985 default = Label("//tools/allowlists/function_transition_allowlist"), 986 ), 987 deps = attr.label_list( 988 doc = dedent("""\ 989 List of other libraries to be linked to this library target. 990 991 These can be either other `rust_library` targets or `cc_library` targets if 992 linking a native library. 993 """), 994 cfg = _proc_macro_dep_transition, 995 ), 996 ), 997 fragments = ["cpp"], 998 toolchains = [ 999 str(Label("//rust:toolchain_type")), 1000 "@bazel_tools//tools/cpp:toolchain_type", 1001 ], 1002 doc = dedent("""\ 1003 Builds a Rust proc-macro crate. 1004 """), 1005) 1006 1007_rust_binary_attrs = dict({ 1008 "crate_type": attr.string( 1009 doc = dedent("""\ 1010 Crate type that will be passed to `rustc` to be used for building this crate. 1011 1012 This option is a temporary workaround and should be used only when building 1013 for WebAssembly targets (//rust/platform:wasi and //rust/platform:wasm). 1014 """), 1015 default = "bin", 1016 ), 1017 "env": attr.string_dict( 1018 mandatory = False, 1019 doc = dedent("""\ 1020 Specifies additional environment variables to set when the target is executed by bazel run. 1021 Values are subject to `$(rootpath)`, `$(execpath)`, location, and 1022 ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution. 1023 1024 Execpath returns absolute path, and in order to be able to construct the absolute path we 1025 need to wrap the test binary in a launcher. Using a launcher comes with complications, such as 1026 more complicated debugger attachment. 1027 """), 1028 ), 1029 "linker_script": attr.label( 1030 doc = dedent("""\ 1031 Link script to forward into linker via rustc options. 1032 """), 1033 allow_single_file = True, 1034 ), 1035 "out_binary": attr.bool( 1036 doc = ( 1037 "Force a target, regardless of it's `crate_type`, to always mark the " + 1038 "file as executable. This attribute is only used to support wasm targets but is " + 1039 "expected to be removed following a resolution to https://github.com/bazelbuild/rules_rust/issues/771." 1040 ), 1041 default = False, 1042 ), 1043 "stamp": _stamp_attribute(default_value = -1), 1044 "_use_grep_includes": attr.bool(default = True), 1045}.items() + _experimental_use_cc_common_link_attrs.items()) 1046 1047def _rust_binary_transition_impl(settings, attr): 1048 return { 1049 "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"], 1050 } 1051 1052_rust_binary_transition = transition( 1053 implementation = _rust_binary_transition_impl, 1054 inputs = [ 1055 "//command_line_option:platforms", 1056 ], 1057 outputs = [ 1058 "//command_line_option:platforms", 1059 ], 1060) 1061 1062rust_binary = rule( 1063 implementation = _rust_binary_impl, 1064 provides = COMMON_PROVIDERS, 1065 attrs = dict(_common_attrs.items() + _rust_binary_attrs.items() + { 1066 "platform": attr.label( 1067 doc = "Optional platform to transition the binary to.", 1068 default = None, 1069 ), 1070 "_allowlist_function_transition": attr.label( 1071 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 1072 ), 1073 }.items()), 1074 executable = True, 1075 fragments = ["cpp"], 1076 cfg = _rust_binary_transition, 1077 toolchains = [ 1078 str(Label("//rust:toolchain_type")), 1079 "@bazel_tools//tools/cpp:toolchain_type", 1080 ], 1081 doc = dedent("""\ 1082 Builds a Rust binary crate. 1083 1084 Example: 1085 1086 Suppose you have the following directory structure for a Rust project with a 1087 library crate, `hello_lib`, and a binary crate, `hello_world` that uses the 1088 `hello_lib` library: 1089 1090 ```output 1091 [workspace]/ 1092 WORKSPACE 1093 hello_lib/ 1094 BUILD 1095 src/ 1096 lib.rs 1097 hello_world/ 1098 BUILD 1099 src/ 1100 main.rs 1101 ``` 1102 1103 `hello_lib/src/lib.rs`: 1104 ```rust 1105 pub struct Greeter { 1106 greeting: String, 1107 } 1108 1109 impl Greeter { 1110 pub fn new(greeting: &str) -> Greeter { 1111 Greeter { greeting: greeting.to_string(), } 1112 } 1113 1114 pub fn greet(&self, thing: &str) { 1115 println!("{} {}", &self.greeting, thing); 1116 } 1117 } 1118 ``` 1119 1120 `hello_lib/BUILD`: 1121 ```python 1122 package(default_visibility = ["//visibility:public"]) 1123 1124 load("@rules_rust//rust:defs.bzl", "rust_library") 1125 1126 rust_library( 1127 name = "hello_lib", 1128 srcs = ["src/lib.rs"], 1129 ) 1130 ``` 1131 1132 `hello_world/src/main.rs`: 1133 ```rust 1134 extern crate hello_lib; 1135 1136 fn main() { 1137 let hello = hello_lib::Greeter::new("Hello"); 1138 hello.greet("world"); 1139 } 1140 ``` 1141 1142 `hello_world/BUILD`: 1143 ```python 1144 load("@rules_rust//rust:defs.bzl", "rust_binary") 1145 1146 rust_binary( 1147 name = "hello_world", 1148 srcs = ["src/main.rs"], 1149 deps = ["//hello_lib"], 1150 ) 1151 ``` 1152 1153 Build and run `hello_world`: 1154 ``` 1155 $ bazel run //hello_world 1156 INFO: Found 1 target... 1157 Target //examples/rust/hello_world:hello_world up-to-date: 1158 bazel-bin/examples/rust/hello_world/hello_world 1159 INFO: Elapsed time: 1.308s, Critical Path: 1.22s 1160 1161 INFO: Running command line: bazel-bin/examples/rust/hello_world/hello_world 1162 Hello world 1163 ``` 1164 1165 On Windows, a PDB file containing debugging information is available under 1166 the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder 1167 is available under the key `dsym_folder` in `OutputGroupInfo`. 1168"""), 1169) 1170 1171def _common_attrs_for_binary_without_process_wrapper(attrs): 1172 new_attr = dict(attrs) 1173 1174 # use a fake process wrapper 1175 new_attr["_process_wrapper"] = attr.label( 1176 default = None, 1177 executable = True, 1178 allow_single_file = True, 1179 cfg = "exec", 1180 ) 1181 1182 new_attr["_bootstrap_process_wrapper"] = attr.label( 1183 default = Label("//util/process_wrapper:bootstrap_process_wrapper"), 1184 executable = True, 1185 allow_single_file = True, 1186 cfg = "exec", 1187 ) 1188 1189 # fix stamp = 0 1190 new_attr["stamp"] = attr.int( 1191 doc = dedent("""\ 1192 Fix `stamp = 0` as stamping is not supported when building without process_wrapper: 1193 https://github.com/bazelbuild/rules_rust/blob/8df4517d370b0c543a01ba38b63e1d5a4104b035/rust/private/rustc.bzl#L955 1194 """), 1195 default = 0, 1196 values = [0], 1197 ) 1198 1199 return new_attr 1200 1201# Provides an internal rust_{binary,library} to use that we can use to build the process 1202# wrapper, this breaks the dependency of rust_* on the process wrapper by 1203# setting it to None, which the functions in rustc detect and build accordingly. 1204rust_binary_without_process_wrapper = rule( 1205 implementation = _rust_binary_impl, 1206 provides = COMMON_PROVIDERS, 1207 attrs = _common_attrs_for_binary_without_process_wrapper(_common_attrs.items() + _rust_binary_attrs.items() + { 1208 "platform": attr.label( 1209 doc = "Optional platform to transition the binary to.", 1210 default = None, 1211 ), 1212 "_allowlist_function_transition": attr.label( 1213 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 1214 ), 1215 }.items()), 1216 executable = True, 1217 fragments = ["cpp"], 1218 cfg = _rust_binary_transition, 1219 toolchains = [ 1220 str(Label("//rust:toolchain_type")), 1221 "@bazel_tools//tools/cpp:toolchain_type", 1222 ], 1223) 1224 1225rust_library_without_process_wrapper = rule( 1226 implementation = _rust_library_impl, 1227 provides = COMMON_PROVIDERS, 1228 attrs = dict(_common_attrs_for_binary_without_process_wrapper(_common_attrs).items()), 1229 fragments = ["cpp"], 1230 toolchains = [ 1231 str(Label("//rust:toolchain_type")), 1232 "@bazel_tools//tools/cpp:toolchain_type", 1233 ], 1234) 1235 1236def _rust_test_transition_impl(settings, attr): 1237 return { 1238 "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"], 1239 } 1240 1241_rust_test_transition = transition( 1242 implementation = _rust_test_transition_impl, 1243 inputs = [ 1244 "//command_line_option:platforms", 1245 ], 1246 outputs = [ 1247 "//command_line_option:platforms", 1248 ], 1249) 1250 1251rust_test = rule( 1252 implementation = _rust_test_impl, 1253 provides = COMMON_PROVIDERS, 1254 attrs = dict(_common_attrs.items() + _rust_test_attrs.items() + { 1255 "platform": attr.label( 1256 doc = "Optional platform to transition the test to.", 1257 default = None, 1258 ), 1259 "_allowlist_function_transition": attr.label( 1260 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 1261 ), 1262 }.items()), 1263 executable = True, 1264 fragments = ["cpp"], 1265 cfg = _rust_test_transition, 1266 test = True, 1267 toolchains = [ 1268 str(Label("//rust:toolchain_type")), 1269 "@bazel_tools//tools/cpp:toolchain_type", 1270 ], 1271 doc = dedent("""\ 1272 Builds a Rust test crate. 1273 1274 Examples: 1275 1276 Suppose you have the following directory structure for a Rust library crate \ 1277 with unit test code in the library sources: 1278 1279 ```output 1280 [workspace]/ 1281 WORKSPACE 1282 hello_lib/ 1283 BUILD 1284 src/ 1285 lib.rs 1286 ``` 1287 1288 `hello_lib/src/lib.rs`: 1289 ```rust 1290 pub struct Greeter { 1291 greeting: String, 1292 } 1293 1294 impl Greeter { 1295 pub fn new(greeting: &str) -> Greeter { 1296 Greeter { greeting: greeting.to_string(), } 1297 } 1298 1299 pub fn greet(&self, thing: &str) -> String { 1300 format!("{} {}", &self.greeting, thing) 1301 } 1302 } 1303 1304 #[cfg(test)] 1305 mod test { 1306 use super::Greeter; 1307 1308 #[test] 1309 fn test_greeting() { 1310 let hello = Greeter::new("Hi"); 1311 assert_eq!("Hi Rust", hello.greet("Rust")); 1312 } 1313 } 1314 ``` 1315 1316 To build and run the tests, simply add a `rust_test` rule with no `srcs` 1317 and only depends on the `hello_lib` `rust_library` target via the 1318 `crate` attribute: 1319 1320 `hello_lib/BUILD`: 1321 ```python 1322 load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") 1323 1324 rust_library( 1325 name = "hello_lib", 1326 srcs = ["src/lib.rs"], 1327 ) 1328 1329 rust_test( 1330 name = "hello_lib_test", 1331 crate = ":hello_lib", 1332 # You may add other deps that are specific to the test configuration 1333 deps = ["//some/dev/dep"], 1334 ) 1335 ``` 1336 1337 Run the test with `bazel test //hello_lib:hello_lib_test`. The crate 1338 will be built using the same crate name as the underlying ":hello_lib" 1339 crate. 1340 1341 ### Example: `test` directory 1342 1343 Integration tests that live in the [`tests` directory][int-tests], they are \ 1344 essentially built as separate crates. Suppose you have the following directory \ 1345 structure where `greeting.rs` is an integration test for the `hello_lib` \ 1346 library crate: 1347 1348 [int-tests]: http://doc.rust-lang.org/book/testing.html#the-tests-directory 1349 1350 ```output 1351 [workspace]/ 1352 WORKSPACE 1353 hello_lib/ 1354 BUILD 1355 src/ 1356 lib.rs 1357 tests/ 1358 greeting.rs 1359 ``` 1360 1361 `hello_lib/tests/greeting.rs`: 1362 ```rust 1363 extern crate hello_lib; 1364 1365 use hello_lib; 1366 1367 #[test] 1368 fn test_greeting() { 1369 let hello = greeter::Greeter::new("Hello"); 1370 assert_eq!("Hello world", hello.greeting("world")); 1371 } 1372 ``` 1373 1374 To build the `greeting.rs` integration test, simply add a `rust_test` target 1375 with `greeting.rs` in `srcs` and a dependency on the `hello_lib` target: 1376 1377 `hello_lib/BUILD`: 1378 ```python 1379 load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") 1380 1381 rust_library( 1382 name = "hello_lib", 1383 srcs = ["src/lib.rs"], 1384 ) 1385 1386 rust_test( 1387 name = "greeting_test", 1388 srcs = ["tests/greeting.rs"], 1389 deps = [":hello_lib"], 1390 ) 1391 ``` 1392 1393 Run the test with `bazel test //hello_lib:greeting_test`. 1394"""), 1395) 1396 1397def rust_test_suite(name, srcs, **kwargs): 1398 """A rule for creating a test suite for a set of `rust_test` targets. 1399 1400 This rule can be used for setting up typical rust [integration tests][it]. Given the following 1401 directory structure: 1402 1403 ```text 1404 [crate]/ 1405 BUILD.bazel 1406 src/ 1407 lib.rs 1408 main.rs 1409 tests/ 1410 integrated_test_a.rs 1411 integrated_test_b.rs 1412 integrated_test_c.rs 1413 patterns/ 1414 fibonacci_test.rs 1415 ``` 1416 1417 The rule can be used to generate [rust_test](#rust_test) targets for each source file under `tests` 1418 and a [test_suite][ts] which encapsulates all tests. 1419 1420 ```python 1421 load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test_suite") 1422 1423 rust_library( 1424 name = "math_lib", 1425 srcs = ["src/lib.rs"], 1426 ) 1427 1428 rust_binary( 1429 name = "math_bin", 1430 srcs = ["src/main.rs"], 1431 ) 1432 1433 rust_test_suite( 1434 name = "integrated_tests_suite", 1435 srcs = glob(["tests/**"]), 1436 deps = [":math_lib"], 1437 ) 1438 ``` 1439 1440 [it]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html 1441 [ts]: https://docs.bazel.build/versions/master/be/general.html#test_suite 1442 1443 Args: 1444 name (str): The name of the `test_suite`. 1445 srcs (list): All test sources, typically `glob(["tests/**/*.rs"])`. 1446 **kwargs (dict): Additional keyword arguments for the underyling [rust_test](#rust_test) targets. The 1447 `tags` argument is also passed to the generated `test_suite` target. 1448 """ 1449 tests = [] 1450 1451 for src in srcs: 1452 if not src.endswith(".rs"): 1453 fail("srcs should have `.rs` extensions") 1454 1455 # Prefixed with `name` to allow parameterization with macros 1456 # The test name should not end with `.rs` 1457 test_name = name + "_" + src[:-3] 1458 rust_test( 1459 name = test_name, 1460 srcs = [src], 1461 **kwargs 1462 ) 1463 tests.append(test_name) 1464 1465 native.test_suite( 1466 name = name, 1467 tests = tests, 1468 tags = kwargs.get("tags", None), 1469 ) 1470 1471rust_library_group = rule( 1472 implementation = _rust_library_group_impl, 1473 provides = [rust_common.crate_group_info], 1474 attrs = { 1475 "deps": attr.label_list( 1476 doc = "Other dependencies to forward through this crate group.", 1477 providers = [[rust_common.crate_group_info], [rust_common.crate_info]], 1478 ), 1479 }, 1480 doc = dedent("""\ 1481 Functions as an alias for a set of dependencies. 1482 1483 Specifically, the following are equivalent: 1484 1485 ```starlark 1486 rust_library_group( 1487 name = "crate_group", 1488 deps = [ 1489 ":crate1", 1490 ":crate2", 1491 ], 1492 ) 1493 1494 rust_library( 1495 name = "foobar", 1496 deps = [":crate_group"], 1497 ... 1498 ) 1499 ``` 1500 1501 and 1502 1503 ```starlark 1504 rust_library( 1505 name = "foobar", 1506 deps = [ 1507 ":crate1", 1508 ":crate2", 1509 ], 1510 ... 1511 ) 1512 ``` 1513 """), 1514) 1515