1"""Utility macros for use in rules_rust repository rules""" 2 3load( 4 "@bazel_tools//tools/build_defs/repo:utils.bzl", 5 "read_netrc", 6 "read_user_netrc", 7 "use_netrc", 8) 9load("//rust:known_shas.bzl", "FILE_KEY_TO_SHA") 10load( 11 "//rust/platform:triple_mappings.bzl", 12 "system_to_binary_ext", 13 "system_to_dylib_ext", 14 "system_to_staticlib_ext", 15 "system_to_stdlib_linkflags", 16) 17load("//rust/private:common.bzl", "DEFAULT_NIGHTLY_ISO_DATE") 18 19DEFAULT_TOOLCHAIN_NAME_PREFIX = "toolchain_for" 20DEFAULT_STATIC_RUST_URL_TEMPLATES = ["https://static.rust-lang.org/dist/{}.tar.xz"] 21DEFAULT_NIGHTLY_VERSION = "nightly/{}".format(DEFAULT_NIGHTLY_ISO_DATE) 22DEFAULT_EXTRA_TARGET_TRIPLES = ["wasm32-unknown-unknown", "wasm32-wasi"] 23 24TINYJSON_KWARGS = dict( 25 name = "rules_rust_tinyjson", 26 sha256 = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a", 27 url = "https://static.crates.io/crates/tinyjson/tinyjson-2.5.1.crate", 28 strip_prefix = "tinyjson-2.5.1", 29 type = "tar.gz", 30 build_file = "@rules_rust//util/process_wrapper:BUILD.tinyjson.bazel", 31) 32 33_build_file_for_compiler_template = """\ 34filegroup( 35 name = "rustc", 36 srcs = ["bin/rustc{binary_ext}"], 37 visibility = ["//visibility:public"], 38) 39 40filegroup( 41 name = "rustc_lib", 42 srcs = glob( 43 [ 44 "bin/*{dylib_ext}", 45 "lib/*{dylib_ext}*", 46 "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}", 47 "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}", 48 "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", 49 ], 50 allow_empty = True, 51 ), 52 visibility = ["//visibility:public"], 53) 54 55filegroup( 56 name = "rustdoc", 57 srcs = ["bin/rustdoc{binary_ext}"], 58 visibility = ["//visibility:public"], 59) 60""" 61 62def BUILD_for_compiler(target_triple): 63 """Emits a BUILD file the compiler archive. 64 65 Args: 66 target_triple (str): The triple of the target platform 67 68 Returns: 69 str: The contents of a BUILD file 70 """ 71 return _build_file_for_compiler_template.format( 72 binary_ext = system_to_binary_ext(target_triple.system), 73 staticlib_ext = system_to_staticlib_ext(target_triple.system), 74 dylib_ext = system_to_dylib_ext(target_triple.system), 75 target_triple = target_triple.str, 76 ) 77 78_build_file_for_cargo_template = """\ 79filegroup( 80 name = "cargo", 81 srcs = ["bin/cargo{binary_ext}"], 82 visibility = ["//visibility:public"], 83)""" 84 85def BUILD_for_cargo(target_triple): 86 """Emits a BUILD file the cargo archive. 87 88 Args: 89 target_triple (str): The triple of the target platform 90 91 Returns: 92 str: The contents of a BUILD file 93 """ 94 return _build_file_for_cargo_template.format( 95 binary_ext = system_to_binary_ext(target_triple.system), 96 ) 97 98_build_file_for_rustfmt_template = """\ 99filegroup( 100 name = "rustfmt_bin", 101 srcs = ["bin/rustfmt{binary_ext}"], 102 visibility = ["//visibility:public"], 103) 104 105sh_binary( 106 name = "rustfmt", 107 srcs = [":rustfmt_bin"], 108 visibility = ["//visibility:public"], 109) 110""" 111 112def BUILD_for_rustfmt(target_triple): 113 """Emits a BUILD file the rustfmt archive. 114 115 Args: 116 target_triple (str): The triple of the target platform 117 118 Returns: 119 str: The contents of a BUILD file 120 """ 121 return _build_file_for_rustfmt_template.format( 122 binary_ext = system_to_binary_ext(target_triple.system), 123 ) 124 125_build_file_for_rust_analyzer_proc_macro_srv = """\ 126filegroup( 127 name = "rust_analyzer_proc_macro_srv", 128 srcs = ["libexec/rust-analyzer-proc-macro-srv{binary_ext}"], 129 visibility = ["//visibility:public"], 130) 131""" 132 133def BUILD_for_rust_analyzer_proc_macro_srv(exec_triple): 134 """Emits a BUILD file the rust_analyzer_proc_macro_srv archive. 135 136 Args: 137 exec_triple (str): The triple of the exec platform 138 Returns: 139 str: The contents of a BUILD file 140 """ 141 return _build_file_for_rust_analyzer_proc_macro_srv.format( 142 binary_ext = system_to_binary_ext(exec_triple.system), 143 ) 144 145_build_file_for_clippy_template = """\ 146filegroup( 147 name = "clippy_driver_bin", 148 srcs = ["bin/clippy-driver{binary_ext}"], 149 visibility = ["//visibility:public"], 150) 151filegroup( 152 name = "cargo_clippy_bin", 153 srcs = ["bin/cargo-clippy{binary_ext}"], 154 visibility = ["//visibility:public"], 155) 156""" 157 158def BUILD_for_clippy(target_triple): 159 """Emits a BUILD file the clippy archive. 160 161 Args: 162 target_triple (str): The triple of the target platform 163 164 Returns: 165 str: The contents of a BUILD file 166 """ 167 return _build_file_for_clippy_template.format( 168 binary_ext = system_to_binary_ext(target_triple.system), 169 ) 170 171_build_file_for_llvm_tools = """\ 172filegroup( 173 name = "llvm_cov_bin", 174 srcs = ["lib/rustlib/{target_triple}/bin/llvm-cov{binary_ext}"], 175 visibility = ["//visibility:public"], 176) 177 178filegroup( 179 name = "llvm_profdata_bin", 180 srcs = ["lib/rustlib/{target_triple}/bin/llvm-profdata{binary_ext}"], 181 visibility = ["//visibility:public"], 182) 183""" 184 185def BUILD_for_llvm_tools(target_triple): 186 """Emits a BUILD file the llvm-tools binaries. 187 188 Args: 189 target_triple (struct): The triple of the target platform 190 191 Returns: 192 str: The contents of a BUILD file 193 """ 194 return _build_file_for_llvm_tools.format( 195 binary_ext = system_to_binary_ext(target_triple.system), 196 target_triple = target_triple.str, 197 ) 198 199_build_file_for_stdlib_template = """\ 200load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup") 201 202rust_stdlib_filegroup( 203 name = "rust_std-{target_triple}", 204 srcs = glob( 205 [ 206 "lib/rustlib/{target_triple}/lib/*.rlib", 207 "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", 208 "lib/rustlib/{target_triple}/lib/*{staticlib_ext}", 209 "lib/rustlib/{target_triple}/lib/self-contained/**", 210 ], 211 # Some patterns (e.g. `lib/*.a`) don't match anything, see https://github.com/bazelbuild/rules_rust/pull/245 212 allow_empty = True, 213 ), 214 visibility = ["//visibility:public"], 215) 216 217# For legacy support 218alias( 219 name = "rust_lib-{target_triple}", 220 actual = "rust_std-{target_triple}", 221 visibility = ["//visibility:public"], 222) 223""" 224 225def BUILD_for_stdlib(target_triple): 226 """Emits a BUILD file the stdlib archive. 227 228 Args: 229 target_triple (triple): The triple of the target platform 230 231 Returns: 232 str: The contents of a BUILD file 233 """ 234 return _build_file_for_stdlib_template.format( 235 binary_ext = system_to_binary_ext(target_triple.system), 236 staticlib_ext = system_to_staticlib_ext(target_triple.system), 237 dylib_ext = system_to_dylib_ext(target_triple.system), 238 target_triple = target_triple.str, 239 ) 240 241_build_file_for_rust_toolchain_template = """\ 242load("@rules_rust//rust:toolchain.bzl", "rust_toolchain") 243 244rust_toolchain( 245 name = "{toolchain_name}", 246 rust_doc = "//:rustdoc", 247 rust_std = "//:rust_std-{target_triple}", 248 rustc = "//:rustc", 249 rustfmt = {rustfmt_label}, 250 cargo = "//:cargo", 251 clippy_driver = "//:clippy_driver_bin", 252 cargo_clippy = "//:cargo_clippy_bin", 253 llvm_cov = {llvm_cov_label}, 254 llvm_profdata = {llvm_profdata_label}, 255 rustc_lib = "//:rustc_lib", 256 allocator_library = {allocator_library}, 257 global_allocator_library = {global_allocator_library}, 258 binary_ext = "{binary_ext}", 259 staticlib_ext = "{staticlib_ext}", 260 dylib_ext = "{dylib_ext}", 261 stdlib_linkflags = [{stdlib_linkflags}], 262 default_edition = "{default_edition}", 263 exec_triple = "{exec_triple}", 264 target_triple = "{target_triple}", 265 visibility = ["//visibility:public"], 266 extra_rustc_flags = {extra_rustc_flags}, 267 extra_exec_rustc_flags = {extra_exec_rustc_flags}, 268 opt_level = {opt_level}, 269) 270""" 271 272def BUILD_for_rust_toolchain( 273 name, 274 exec_triple, 275 target_triple, 276 allocator_library, 277 global_allocator_library, 278 default_edition, 279 include_rustfmt, 280 include_llvm_tools, 281 stdlib_linkflags = None, 282 extra_rustc_flags = None, 283 extra_exec_rustc_flags = None, 284 opt_level = None): 285 """Emits a toolchain declaration to match an existing compiler and stdlib. 286 287 Args: 288 name (str): The name of the toolchain declaration 289 exec_triple (triple): The rust-style target that this compiler runs on 290 target_triple (triple): The rust-style target triple of the tool 291 allocator_library (str, optional): Target that provides allocator functions when rust_library targets are embedded in a cc_binary. 292 global_allocator_library (str, optional): Target that provides allocator functions when a global allocator is used with cc_common_link. 293 This target is only used in the target configuration; exec builds still use the symbols provided 294 by the `allocator_library` target. 295 default_edition (str): Default Rust edition. 296 include_rustfmt (bool): Whether rustfmt is present in the toolchain. 297 include_llvm_tools (bool): Whether llvm-tools are present in the toolchain. 298 stdlib_linkflags (list, optional): Overriden flags needed for linking to rust 299 stdlib, akin to BAZEL_LINKLIBS. Defaults to 300 None. 301 extra_rustc_flags (list, optional): Extra flags to pass to rustc in non-exec configuration. 302 extra_exec_rustc_flags (list, optional): Extra flags to pass to rustc in exec configuration. 303 opt_level (dict, optional): Optimization level config for this toolchain. 304 305 Returns: 306 str: A rendered template of a `rust_toolchain` declaration 307 """ 308 if stdlib_linkflags == None: 309 stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(target_triple.system)]) 310 311 rustfmt_label = "None" 312 if include_rustfmt: 313 rustfmt_label = "\"//:rustfmt_bin\"" 314 llvm_cov_label = "None" 315 llvm_profdata_label = "None" 316 if include_llvm_tools: 317 llvm_cov_label = "\"//:llvm_cov_bin\"" 318 llvm_profdata_label = "\"//:llvm_profdata_bin\"" 319 allocator_library_label = "None" 320 if allocator_library: 321 allocator_library_label = "\"{allocator_library}\"".format(allocator_library = allocator_library) 322 global_allocator_library_label = "None" 323 if global_allocator_library: 324 global_allocator_library_label = "\"{global_allocator_library}\"".format(global_allocator_library = global_allocator_library) 325 326 return _build_file_for_rust_toolchain_template.format( 327 toolchain_name = name, 328 binary_ext = system_to_binary_ext(target_triple.system), 329 staticlib_ext = system_to_staticlib_ext(target_triple.system), 330 dylib_ext = system_to_dylib_ext(target_triple.system), 331 allocator_library = allocator_library_label, 332 global_allocator_library = global_allocator_library_label, 333 stdlib_linkflags = stdlib_linkflags, 334 default_edition = default_edition, 335 exec_triple = exec_triple.str, 336 target_triple = target_triple.str, 337 rustfmt_label = rustfmt_label, 338 llvm_cov_label = llvm_cov_label, 339 llvm_profdata_label = llvm_profdata_label, 340 extra_rustc_flags = extra_rustc_flags, 341 extra_exec_rustc_flags = extra_exec_rustc_flags, 342 opt_level = opt_level, 343 ) 344 345_build_file_for_toolchain_template = """\ 346toolchain( 347 name = "{name}", 348 exec_compatible_with = {exec_constraint_sets_serialized}, 349 target_compatible_with = {target_constraint_sets_serialized}, 350 toolchain = "{toolchain}", 351 toolchain_type = "{toolchain_type}", 352 {target_settings} 353) 354""" 355 356def BUILD_for_toolchain( 357 name, 358 toolchain, 359 toolchain_type, 360 target_settings, 361 target_compatible_with, 362 exec_compatible_with): 363 target_settings_value = "target_settings = {},".format(json.encode(target_settings)) if target_settings else "# target_settings = []" 364 365 return _build_file_for_toolchain_template.format( 366 name = name, 367 exec_constraint_sets_serialized = json.encode(exec_compatible_with), 368 target_constraint_sets_serialized = json.encode(target_compatible_with), 369 toolchain = toolchain, 370 toolchain_type = toolchain_type, 371 target_settings = target_settings_value, 372 ) 373 374def load_rustfmt(ctx, target_triple, version, iso_date): 375 """Loads a rustfmt binary and yields corresponding BUILD for it 376 377 Args: 378 ctx (repository_ctx): The repository rule's context object. 379 target_triple (struct): The platform triple to download rustfmt for. 380 version (str): The version or channel of rustfmt. 381 iso_date (str): The date of the tool (or None, if the version is a specific version). 382 383 Returns: 384 Tuple[str, Dict[str, str]]: The BUILD file contents for this rustfmt binary and sha256 of it's artifact. 385 """ 386 387 sha256 = load_arbitrary_tool( 388 ctx, 389 iso_date = iso_date, 390 target_triple = target_triple, 391 tool_name = "rustfmt", 392 tool_subdirectories = ["rustfmt-preview"], 393 version = version, 394 ) 395 396 return BUILD_for_rustfmt(target_triple), sha256 397 398def load_rust_compiler(ctx, iso_date, target_triple, version): 399 """Loads a rust compiler and yields corresponding BUILD for it 400 401 Args: 402 ctx (repository_ctx): A repository_ctx. 403 iso_date (str): The date of the tool (or None, if the version is a specific version). 404 target_triple (struct): The Rust-style target that this compiler runs on. 405 version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. 406 407 Returns: 408 Tuple[str, Dict[str, str]]: The BUILD file contents for this compiler and compiler library 409 and sha256 of it's artifact. 410 """ 411 412 sha256 = load_arbitrary_tool( 413 ctx, 414 iso_date = iso_date, 415 target_triple = target_triple, 416 tool_name = "rustc", 417 tool_subdirectories = ["rustc"], 418 version = version, 419 ) 420 421 return BUILD_for_compiler(target_triple), sha256 422 423def load_clippy(ctx, iso_date, target_triple, version): 424 """Loads Clippy and yields corresponding BUILD for it 425 426 Args: 427 ctx (repository_ctx): A repository_ctx. 428 iso_date (str): The date of the tool (or None, if the version is a specific version). 429 target_triple (struct): The Rust-style target that this compiler runs on. 430 version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. 431 432 Returns: 433 Tuple[str, str]: The BUILD file contents for Clippy and the sha256 of it's artifact 434 """ 435 sha256 = load_arbitrary_tool( 436 ctx, 437 iso_date = iso_date, 438 target_triple = target_triple, 439 tool_name = "clippy", 440 tool_subdirectories = ["clippy-preview"], 441 version = version, 442 ) 443 444 return BUILD_for_clippy(target_triple), sha256 445 446def load_cargo(ctx, iso_date, target_triple, version): 447 """Loads Cargo and yields corresponding BUILD for it 448 449 Args: 450 ctx (repository_ctx): A repository_ctx. 451 iso_date (str): The date of the tool (or None, if the version is a specific version). 452 target_triple (struct): The Rust-style target that this compiler runs on. 453 version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. 454 455 Returns: 456 Tuple[str, Dict[str, str]]: The BUILD file contents for Cargo and the sha256 of its artifact. 457 """ 458 459 sha256 = load_arbitrary_tool( 460 ctx, 461 iso_date = iso_date, 462 target_triple = target_triple, 463 tool_name = "cargo", 464 tool_subdirectories = ["cargo"], 465 version = version, 466 ) 467 468 return BUILD_for_cargo(target_triple), sha256 469 470def includes_rust_analyzer_proc_macro_srv(version, iso_date): 471 """Determine whether or not the rust_analyzer_proc_macro_srv binary in available in the given version of Rust. 472 473 Args: 474 version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. 475 iso_date (str): The date of the tool (or None, if the version is a specific version). 476 477 Returns: 478 bool: Whether or not the binary is expected to be included 479 """ 480 481 if version == "nightly": 482 return iso_date >= "2022-09-21" 483 elif version == "beta": 484 return False 485 elif version >= "1.64.0": 486 return True 487 488 return False 489 490def load_rust_src(ctx, iso_date, version, sha256 = None): 491 """Loads the rust source code. Used by the rust-analyzer rust-project.json generator. 492 493 Args: 494 ctx (ctx): A repository_ctx. 495 version (str): The version of the tool among "nightly", "beta', or an exact version. 496 iso_date (str): The date of the tool (or None, if the version is a specific version). 497 sha256 (str): The sha256 value for the `rust-src` artifact 498 499 Returns: 500 Dict[str, str]: A mapping of the artifact name to sha256 501 """ 502 tool_suburl = produce_tool_suburl("rust-src", None, version, iso_date) 503 url = ctx.attr.urls[0].format(tool_suburl) 504 505 tool_path = produce_tool_path("rust-src", version, None) 506 archive_path = tool_path + _get_tool_extension(getattr(ctx.attr, "urls", None)) 507 508 is_reproducible = True 509 if sha256 == None: 510 sha256s = getattr(ctx.attr, "sha256s", {}) 511 sha256 = sha256s.get(archive_path, None) or FILE_KEY_TO_SHA.get(archive_path, None) 512 if not sha256: 513 sha256 = "" 514 is_reproducible = False 515 516 result = ctx.download_and_extract( 517 url, 518 output = "lib/rustlib/src", 519 sha256 = sha256, 520 auth = _make_auth_dict(ctx, [url]), 521 stripPrefix = "{}/rust-src/lib/rustlib/src/rust".format(tool_path), 522 ) 523 ctx.file( 524 "lib/rustlib/src/BUILD.bazel", 525 """\ 526filegroup( 527 name = "rustc_srcs", 528 srcs = glob(["**/*"]), 529 visibility = ["//visibility:public"], 530)""", 531 ) 532 533 if is_reproducible: 534 return {} 535 536 return {archive_path: result.sha256} 537 538_build_file_for_rust_analyzer_toolchain_template = """\ 539load("@rules_rust//rust:toolchain.bzl", "rust_analyzer_toolchain") 540 541rust_analyzer_toolchain( 542 name = "{name}", 543 proc_macro_srv = {proc_macro_srv}, 544 rustc = "{rustc}", 545 rustc_srcs = "//lib/rustlib/src:rustc_srcs", 546 visibility = ["//visibility:public"], 547) 548""" 549 550def BUILD_for_rust_analyzer_toolchain(name, rustc, proc_macro_srv): 551 return _build_file_for_rust_analyzer_toolchain_template.format( 552 name = name, 553 rustc = rustc, 554 proc_macro_srv = repr(proc_macro_srv), 555 ) 556 557_build_file_for_rustfmt_toolchain_template = """\ 558load("@rules_rust//rust:toolchain.bzl", "rustfmt_toolchain") 559 560rustfmt_toolchain( 561 name = "{name}", 562 rustfmt = "{rustfmt}", 563 rustc = "{rustc}", 564 rustc_lib = "{rustc_lib}", 565 visibility = ["//visibility:public"], 566) 567""" 568 569def BUILD_for_rustfmt_toolchain(name, rustfmt, rustc, rustc_lib): 570 return _build_file_for_rustfmt_toolchain_template.format( 571 name = name, 572 rustfmt = rustfmt, 573 rustc = rustc, 574 rustc_lib = rustc_lib, 575 ) 576 577def load_rust_stdlib(ctx, target_triple): 578 """Loads a rust standard library and yields corresponding BUILD for it 579 580 Args: 581 ctx (repository_ctx): A repository_ctx. 582 target_triple (struct): The rust-style target triple of the tool 583 584 Returns: 585 Tuple[str, Dict[str, str]]: The BUILD file contents for this stdlib and the sha256 of the artifact. 586 """ 587 588 sha256 = load_arbitrary_tool( 589 ctx, 590 iso_date = ctx.attr.iso_date, 591 target_triple = target_triple, 592 tool_name = "rust-std", 593 tool_subdirectories = ["rust-std-{}".format(target_triple.str)], 594 version = ctx.attr.version, 595 ) 596 597 return BUILD_for_stdlib(target_triple), sha256 598 599def load_rustc_dev_nightly(ctx, target_triple): 600 """Loads the nightly rustc dev component 601 602 Args: 603 ctx: A repository_ctx. 604 target_triple: The rust-style target triple of the tool 605 606 Returns: 607 Dict[str, str]: The sha256 value of the rustc-dev artifact. 608 """ 609 610 subdir_name = "rustc-dev" 611 if ctx.attr.iso_date < "2020-12-24": 612 subdir_name = "rustc-dev-{}".format(target_triple) 613 614 sha256 = load_arbitrary_tool( 615 ctx, 616 iso_date = ctx.attr.iso_date, 617 target_triple = target_triple, 618 tool_name = "rustc-dev", 619 tool_subdirectories = [subdir_name], 620 version = ctx.attr.version, 621 ) 622 623 return sha256 624 625def load_llvm_tools(ctx, target_triple): 626 """Loads the llvm tools 627 628 Args: 629 ctx (repository_ctx): A repository_ctx. 630 target_triple (struct): The rust-style target triple of the tool 631 632 Returns: 633 Tuple[str, Dict[str, str]]: The BUILD.bazel content and sha256 value of the llvm tools artifact. 634 """ 635 sha256 = load_arbitrary_tool( 636 ctx, 637 iso_date = ctx.attr.iso_date, 638 target_triple = target_triple, 639 tool_name = "llvm-tools", 640 tool_subdirectories = ["llvm-tools-preview"], 641 version = ctx.attr.version, 642 ) 643 644 return BUILD_for_llvm_tools(target_triple), sha256 645 646def check_version_valid(version, iso_date, param_prefix = ""): 647 """Verifies that the provided rust version and iso_date make sense. 648 649 Args: 650 version (str): The rustc version 651 iso_date (str): The rustc nightly version's iso date 652 param_prefix (str, optional): The name of the tool who's version is being checked. 653 """ 654 655 if not version and iso_date: 656 fail("{param_prefix}iso_date must be paired with a {param_prefix}version".format(param_prefix = param_prefix)) 657 658 if version in ("beta", "nightly") and not iso_date: 659 fail("{param_prefix}iso_date must be specified if version is 'beta' or 'nightly'".format(param_prefix = param_prefix)) 660 661def produce_tool_suburl(tool_name, target_triple, version, iso_date = None): 662 """Produces a fully qualified Rust tool name for URL 663 664 Args: 665 tool_name (str): The name of the tool per `static.rust-lang.org`. 666 target_triple (struct): The rust-style target triple of the tool. 667 version (str): The version of the tool among "nightly", "beta', or an exact version. 668 iso_date (str): The date of the tool (or None, if the version is a specific version). 669 670 Returns: 671 str: The fully qualified url path for the specified tool. 672 """ 673 path = produce_tool_path(tool_name, version, target_triple) 674 return iso_date + "/" + path if (iso_date and version in ("beta", "nightly")) else path 675 676def produce_tool_path(tool_name, version, target_triple = None): 677 """Produces a qualified Rust tool name 678 679 Args: 680 tool_name (str): The name of the tool per static.rust-lang.org 681 version (str): The version of the tool among "nightly", "beta', or an exact version. 682 target_triple (struct, optional): The rust-style target triple of the tool 683 684 Returns: 685 str: The qualified path for the specified tool. 686 """ 687 if not tool_name: 688 fail("No tool name was provided") 689 if not version: 690 fail("No tool version was provided") 691 692 # Not all tools require a triple. E.g. `rustc_src` (Rust source files for rust-analyzer). 693 platform_triple = None 694 if target_triple: 695 platform_triple = target_triple.str 696 697 return "-".join([e for e in [tool_name, version, platform_triple] if e]) 698 699def lookup_tool_sha256( 700 repository_ctx, 701 tool_name, 702 target_triple, 703 version, 704 iso_date): 705 """Looks up the sha256 hash of a specific tool archive. 706 707 The lookup order is: 708 709 1. The sha256s dict in the context attributes; 710 2. The list of sha256 hashes populated in `//rust:known_shas.bzl`; 711 712 Args: 713 repository_ctx (repository_ctx): A repository_ctx (no attrs required). 714 tool_name (str): The name of the given tool per the archive naming. 715 target_triple (struct): The rust-style target triple of the tool. 716 version (str): The version of the tool among "nightly", "beta', or an exact version. 717 iso_date (str): The date of the tool (ignored if the version is a specific version). 718 719 Returns: 720 str: The sha256 of the tool archive, or an empty string if the hash could not be found. 721 """ 722 tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date) 723 urls = getattr(repository_ctx.attr, "urls", None) 724 archive_path = tool_suburl + _get_tool_extension(urls) 725 sha256s = getattr(repository_ctx.attr, "sha256s", dict()) 726 727 from_attr = sha256s.get(archive_path, None) 728 if from_attr: 729 return archive_path, from_attr 730 731 from_builtin = FILE_KEY_TO_SHA.get(archive_path, None) 732 if from_builtin: 733 return archive_path, from_builtin 734 735 return archive_path, "" 736 737def load_arbitrary_tool( 738 ctx, 739 tool_name, 740 tool_subdirectories, 741 version, 742 iso_date, 743 target_triple, 744 sha256 = None): 745 """Loads a Rust tool, downloads, and extracts into the common workspace. 746 747 This function sources the tool from the Rust-lang static file server. The index is available at: 748 - https://static.rust-lang.org/dist/channel-rust-stable.toml 749 - https://static.rust-lang.org/dist/channel-rust-beta.toml 750 - https://static.rust-lang.org/dist/channel-rust-nightly.toml 751 752 Args: 753 ctx (repository_ctx): A repository_ctx (no attrs required). 754 tool_name (str): The name of the given tool per the archive naming. 755 tool_subdirectories (str): The subdirectories of the tool files (at a level below the root directory of 756 the archive). The root directory of the archive is expected to match 757 $TOOL_NAME-$VERSION-$TARGET_TRIPLE. 758 Example: 759 tool_name 760 | version 761 | | target_triple 762 v v v 763 rust-1.39.0-x86_64-unknown-linux-gnu/clippy-preview 764 .../rustc 765 .../etc 766 tool_subdirectories = ["clippy-preview", "rustc"] 767 version (str): The version of the tool among "nightly", "beta', or an exact version. 768 iso_date (str): The date of the tool (ignored if the version is a specific version). 769 target_triple (struct): The rust-style target triple of the tool. 770 sha256 (str, optional): The expected hash of hash of the Rust tool. Defaults to "". 771 772 Returns: 773 Dict[str, str]: A mapping of the tool name to it's sha256 value if the requested tool does not have 774 enough information in the repository_ctx to be reproducible. 775 """ 776 check_version_valid(version, iso_date, param_prefix = tool_name + "_") 777 778 # View the indices mentioned in the docstring to find the tool_suburl for a given 779 # tool. 780 tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date) 781 urls = [] 782 783 for url in getattr(ctx.attr, "urls", DEFAULT_STATIC_RUST_URL_TEMPLATES): 784 new_url = url.format(tool_suburl) 785 if new_url not in urls: 786 urls.append(new_url) 787 788 tool_path = produce_tool_path(tool_name, version, target_triple) 789 790 archive_path, ctx_sha256 = lookup_tool_sha256(ctx, tool_name, target_triple, version, iso_date) 791 792 is_reproducible = True 793 if sha256 == None: 794 sha256 = ctx_sha256 795 is_reproducible = bool(ctx_sha256) 796 797 for subdirectory in tool_subdirectories: 798 # As long as the sha256 value is consistent accross calls here the 799 # cost of downloading an artifact is negated as by Bazel's caching. 800 result = ctx.download_and_extract( 801 urls, 802 sha256 = sha256, 803 auth = _make_auth_dict(ctx, urls), 804 stripPrefix = "{}/{}".format(tool_path, subdirectory), 805 ) 806 807 # In the event no sha256 was provided, set it to the value of the first 808 # downloaded item so subsequent downloads use a cached artifact. 809 if not sha256: 810 sha256 = result.sha256 811 812 # If the artifact is reproducibly downloadable then return an 813 # empty dict to inform consumers no attributes require updating. 814 if is_reproducible: 815 return {} 816 817 return {archive_path: sha256} 818 819# The function is copied from the main branch of bazel_tools. 820# It should become available there from version 7.1.0, 821# We should remove this function when we upgrade minimum supported 822# version to 7.1.0. 823# https://github.com/bazelbuild/bazel/blob/d37762b494a4e122d46a5a71e3a8cc77fa15aa25/tools/build_defs/repo/utils.bzl#L424-L446 824def _get_auth(ctx, urls): 825 """Utility function to obtain the correct auth dict for a list of urls from .netrc file. 826 827 Support optional netrc and auth_patterns attributes if available. 828 829 Args: 830 ctx: The repository context of the repository rule calling this utility 831 function. 832 urls: the list of urls to read 833 834 Returns: 835 the auth dict which can be passed to repository_ctx.download 836 """ 837 if hasattr(ctx.attr, "netrc") and ctx.attr.netrc: 838 netrc = read_netrc(ctx, ctx.attr.netrc) 839 elif "NETRC" in ctx.os.environ: 840 netrc = read_netrc(ctx, ctx.os.environ["NETRC"]) 841 else: 842 netrc = read_user_netrc(ctx) 843 auth_patterns = {} 844 if hasattr(ctx.attr, "auth_patterns") and ctx.attr.auth_patterns: 845 auth_patterns = ctx.attr.auth_patterns 846 return use_netrc(netrc, urls, auth_patterns) 847 848def _make_auth_dict(ctx, urls): 849 auth = getattr(ctx.attr, "auth", {}) 850 if not auth: 851 return _get_auth(ctx, urls) 852 ret = {} 853 for url in urls: 854 ret[url] = auth 855 return ret 856 857def _get_tool_extension(urls = None): 858 if urls == None: 859 urls = DEFAULT_STATIC_RUST_URL_TEMPLATES 860 if urls[0][-7:] == ".tar.gz": 861 return ".tar.gz" 862 elif urls[0][-7:] == ".tar.xz": 863 return ".tar.xz" 864 else: 865 return "" 866 867def select_rust_version(versions): 868 """Select the highest priorty version for a list of Rust versions 869 870 Priority order: `stable > nightly > beta` 871 872 Note that duplicate channels are unexpected in `versions`. 873 874 Args: 875 versions (list): A list of Rust versions. E.g. [`1.66.0`, `nightly/2022-12-15`] 876 877 Returns: 878 str: The highest ranking value from `versions` 879 """ 880 if not versions: 881 fail("No versions were provided") 882 883 current = versions[0] 884 885 for ver in versions: 886 if ver.startswith("beta"): 887 if current[0].isdigit() or current.startswith("nightly"): 888 continue 889 if current.startswith("beta") and ver > current: 890 current = ver 891 continue 892 893 current = ver 894 elif ver.startswith("nightly"): 895 if current[0].isdigit(): 896 continue 897 if current.startswith("nightly") and ver > current: 898 current = ver 899 continue 900 901 current = ver 902 903 else: 904 current = ver 905 906 return current 907 908_build_file_for_toolchain_hub_template = """ 909toolchain( 910 name = "{name}", 911 exec_compatible_with = {exec_constraint_sets_serialized}, 912 target_compatible_with = {target_constraint_sets_serialized}, 913 toolchain = "{toolchain}", 914 toolchain_type = "{toolchain_type}", 915 visibility = ["//visibility:public"], 916) 917""" 918 919def BUILD_for_toolchain_hub( 920 toolchain_names, 921 toolchain_labels, 922 toolchain_types, 923 target_compatible_with, 924 exec_compatible_with): 925 return "\n".join([_build_file_for_toolchain_hub_template.format( 926 name = toolchain_name, 927 exec_constraint_sets_serialized = json.encode(exec_compatible_with[toolchain_name]), 928 target_constraint_sets_serialized = json.encode(target_compatible_with[toolchain_name]), 929 toolchain = toolchain_labels[toolchain_name], 930 toolchain_type = toolchain_types[toolchain_name], 931 ) for toolchain_name in toolchain_names]) 932 933def _toolchain_repository_hub_impl(repository_ctx): 934 repository_ctx.file("WORKSPACE.bazel", """workspace(name = "{}")""".format( 935 repository_ctx.name, 936 )) 937 938 repository_ctx.file("BUILD.bazel", BUILD_for_toolchain_hub( 939 toolchain_names = repository_ctx.attr.toolchain_names, 940 toolchain_labels = repository_ctx.attr.toolchain_labels, 941 toolchain_types = repository_ctx.attr.toolchain_types, 942 target_compatible_with = repository_ctx.attr.target_compatible_with, 943 exec_compatible_with = repository_ctx.attr.exec_compatible_with, 944 )) 945 946toolchain_repository_hub = repository_rule( 947 doc = ( 948 "Generates a toolchain-bearing repository that declares a set of other toolchains from other " + 949 "repositories. This exists to allow registering a set of toolchains in one go with the `:all` target." 950 ), 951 attrs = { 952 "exec_compatible_with": attr.string_list_dict( 953 doc = "A list of constraints for the execution platform for this toolchain, keyed by toolchain name.", 954 mandatory = True, 955 ), 956 "target_compatible_with": attr.string_list_dict( 957 doc = "A list of constraints for the target platform for this toolchain, keyed by toolchain name.", 958 mandatory = True, 959 ), 960 "toolchain_labels": attr.string_dict( 961 doc = "The name of the toolchain implementation target, keyed by toolchain name.", 962 mandatory = True, 963 ), 964 "toolchain_names": attr.string_list( 965 mandatory = True, 966 ), 967 "toolchain_types": attr.string_dict( 968 doc = "The toolchain type of the toolchain to declare, keyed by toolchain name.", 969 mandatory = True, 970 ), 971 }, 972 implementation = _toolchain_repository_hub_impl, 973) 974