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