| """Utility macros for use in rules_rust repository rules""" |
| |
| load( |
| "@bazel_tools//tools/build_defs/repo:utils.bzl", |
| "read_netrc", |
| "read_user_netrc", |
| "use_netrc", |
| ) |
| load("//rust:known_shas.bzl", "FILE_KEY_TO_SHA") |
| load( |
| "//rust/platform:triple_mappings.bzl", |
| "system_to_binary_ext", |
| "system_to_dylib_ext", |
| "system_to_staticlib_ext", |
| "system_to_stdlib_linkflags", |
| ) |
| load("//rust/private:common.bzl", "DEFAULT_NIGHTLY_ISO_DATE") |
| |
| DEFAULT_TOOLCHAIN_NAME_PREFIX = "toolchain_for" |
| DEFAULT_STATIC_RUST_URL_TEMPLATES = ["https://static.rust-lang.org/dist/{}.tar.xz"] |
| DEFAULT_NIGHTLY_VERSION = "nightly/{}".format(DEFAULT_NIGHTLY_ISO_DATE) |
| DEFAULT_EXTRA_TARGET_TRIPLES = ["wasm32-unknown-unknown", "wasm32-wasi"] |
| |
| TINYJSON_KWARGS = dict( |
| name = "rules_rust_tinyjson", |
| sha256 = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a", |
| url = "https://crates.io/api/v1/crates/tinyjson/2.5.1/download", |
| strip_prefix = "tinyjson-2.5.1", |
| type = "tar.gz", |
| build_file = "@rules_rust//util/process_wrapper:BUILD.tinyjson.bazel", |
| ) |
| |
| _build_file_for_compiler_template = """\ |
| filegroup( |
| name = "rustc", |
| srcs = ["bin/rustc{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| filegroup( |
| name = "rustc_lib", |
| srcs = glob( |
| [ |
| "bin/*{dylib_ext}", |
| "lib/*{dylib_ext}*", |
| "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}", |
| "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}", |
| "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", |
| ], |
| allow_empty = True, |
| ), |
| visibility = ["//visibility:public"], |
| ) |
| |
| filegroup( |
| name = "rustdoc", |
| srcs = ["bin/rustdoc{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_compiler(target_triple): |
| """Emits a BUILD file the compiler archive. |
| |
| Args: |
| target_triple (str): The triple of the target platform |
| |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_compiler_template.format( |
| binary_ext = system_to_binary_ext(target_triple.system), |
| staticlib_ext = system_to_staticlib_ext(target_triple.system), |
| dylib_ext = system_to_dylib_ext(target_triple.system), |
| target_triple = target_triple.str, |
| ) |
| |
| _build_file_for_cargo_template = """\ |
| filegroup( |
| name = "cargo", |
| srcs = ["bin/cargo{binary_ext}"], |
| visibility = ["//visibility:public"], |
| )""" |
| |
| def BUILD_for_cargo(target_triple): |
| """Emits a BUILD file the cargo archive. |
| |
| Args: |
| target_triple (str): The triple of the target platform |
| |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_cargo_template.format( |
| binary_ext = system_to_binary_ext(target_triple.system), |
| ) |
| |
| _build_file_for_rustfmt_template = """\ |
| filegroup( |
| name = "rustfmt_bin", |
| srcs = ["bin/rustfmt{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| sh_binary( |
| name = "rustfmt", |
| srcs = [":rustfmt_bin"], |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_rustfmt(target_triple): |
| """Emits a BUILD file the rustfmt archive. |
| |
| Args: |
| target_triple (str): The triple of the target platform |
| |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_rustfmt_template.format( |
| binary_ext = system_to_binary_ext(target_triple.system), |
| ) |
| |
| _build_file_for_clippy_template = """\ |
| filegroup( |
| name = "clippy_driver_bin", |
| srcs = ["bin/clippy-driver{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| _build_file_for_rust_analyzer_proc_macro_srv = """\ |
| filegroup( |
| name = "rust_analyzer_proc_macro_srv", |
| srcs = ["libexec/rust-analyzer-proc-macro-srv{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_rust_analyzer_proc_macro_srv(exec_triple): |
| """Emits a BUILD file the rust_analyzer_proc_macro_srv archive. |
| |
| Args: |
| exec_triple (str): The triple of the exec platform |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_rust_analyzer_proc_macro_srv.format( |
| binary_ext = system_to_binary_ext(exec_triple.system), |
| ) |
| |
| def BUILD_for_clippy(target_triple): |
| """Emits a BUILD file the clippy archive. |
| |
| Args: |
| target_triple (str): The triple of the target platform |
| |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_clippy_template.format( |
| binary_ext = system_to_binary_ext(target_triple.system), |
| ) |
| |
| _build_file_for_llvm_tools = """\ |
| filegroup( |
| name = "llvm_cov_bin", |
| srcs = ["lib/rustlib/{target_triple}/bin/llvm-cov{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| filegroup( |
| name = "llvm_profdata_bin", |
| srcs = ["lib/rustlib/{target_triple}/bin/llvm-profdata{binary_ext}"], |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_llvm_tools(target_triple): |
| """Emits a BUILD file the llvm-tools binaries. |
| |
| Args: |
| target_triple (struct): The triple of the target platform |
| |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_llvm_tools.format( |
| binary_ext = system_to_binary_ext(target_triple.system), |
| target_triple = target_triple.str, |
| ) |
| |
| _build_file_for_stdlib_template = """\ |
| load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup") |
| |
| rust_stdlib_filegroup( |
| name = "rust_std-{target_triple}", |
| srcs = glob( |
| [ |
| "lib/rustlib/{target_triple}/lib/*.rlib", |
| "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", |
| "lib/rustlib/{target_triple}/lib/*{staticlib_ext}", |
| "lib/rustlib/{target_triple}/lib/self-contained/**", |
| ], |
| # Some patterns (e.g. `lib/*.a`) don't match anything, see https://github.com/bazelbuild/rules_rust/pull/245 |
| allow_empty = True, |
| ), |
| visibility = ["//visibility:public"], |
| ) |
| |
| # For legacy support |
| alias( |
| name = "rust_lib-{target_triple}", |
| actual = "rust_std-{target_triple}", |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_stdlib(target_triple): |
| """Emits a BUILD file the stdlib archive. |
| |
| Args: |
| target_triple (triple): The triple of the target platform |
| |
| Returns: |
| str: The contents of a BUILD file |
| """ |
| return _build_file_for_stdlib_template.format( |
| binary_ext = system_to_binary_ext(target_triple.system), |
| staticlib_ext = system_to_staticlib_ext(target_triple.system), |
| dylib_ext = system_to_dylib_ext(target_triple.system), |
| target_triple = target_triple.str, |
| ) |
| |
| _build_file_for_rust_toolchain_template = """\ |
| load("@rules_rust//rust:toolchain.bzl", "rust_toolchain") |
| |
| rust_toolchain( |
| name = "{toolchain_name}", |
| rust_doc = "//:rustdoc", |
| rust_std = "//:rust_std-{target_triple}", |
| rustc = "//:rustc", |
| rustfmt = {rustfmt_label}, |
| cargo = "//:cargo", |
| clippy_driver = "//:clippy_driver_bin", |
| llvm_cov = {llvm_cov_label}, |
| llvm_profdata = {llvm_profdata_label}, |
| rustc_lib = "//:rustc_lib", |
| allocator_library = {allocator_library}, |
| global_allocator_library = {global_allocator_library}, |
| binary_ext = "{binary_ext}", |
| staticlib_ext = "{staticlib_ext}", |
| dylib_ext = "{dylib_ext}", |
| stdlib_linkflags = [{stdlib_linkflags}], |
| default_edition = "{default_edition}", |
| exec_triple = "{exec_triple}", |
| target_triple = "{target_triple}", |
| visibility = ["//visibility:public"], |
| extra_rustc_flags = {extra_rustc_flags}, |
| extra_exec_rustc_flags = {extra_exec_rustc_flags}, |
| opt_level = {opt_level}, |
| ) |
| """ |
| |
| def BUILD_for_rust_toolchain( |
| name, |
| exec_triple, |
| target_triple, |
| allocator_library, |
| global_allocator_library, |
| default_edition, |
| include_rustfmt, |
| include_llvm_tools, |
| stdlib_linkflags = None, |
| extra_rustc_flags = None, |
| extra_exec_rustc_flags = None, |
| opt_level = None): |
| """Emits a toolchain declaration to match an existing compiler and stdlib. |
| |
| Args: |
| name (str): The name of the toolchain declaration |
| exec_triple (triple): The rust-style target that this compiler runs on |
| target_triple (triple): The rust-style target triple of the tool |
| allocator_library (str, optional): Target that provides allocator functions when rust_library targets are embedded in a cc_binary. |
| global_allocator_library (str, optional): Target that provides allocator functions when a global allocator is used with cc_common_link. |
| This target is only used in the target configuration; exec builds still use the symbols provided |
| by the `allocator_library` target. |
| default_edition (str): Default Rust edition. |
| include_rustfmt (bool): Whether rustfmt is present in the toolchain. |
| include_llvm_tools (bool): Whether llvm-tools are present in the toolchain. |
| stdlib_linkflags (list, optional): Overriden flags needed for linking to rust |
| stdlib, akin to BAZEL_LINKLIBS. Defaults to |
| None. |
| extra_rustc_flags (list, optional): Extra flags to pass to rustc in non-exec configuration. |
| extra_exec_rustc_flags (list, optional): Extra flags to pass to rustc in exec configuration. |
| opt_level (dict, optional): Optimization level config for this toolchain. |
| |
| Returns: |
| str: A rendered template of a `rust_toolchain` declaration |
| """ |
| if stdlib_linkflags == None: |
| stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(target_triple.system)]) |
| |
| rustfmt_label = "None" |
| if include_rustfmt: |
| rustfmt_label = "\"//:rustfmt_bin\"" |
| llvm_cov_label = "None" |
| llvm_profdata_label = "None" |
| if include_llvm_tools: |
| llvm_cov_label = "\"//:llvm_cov_bin\"" |
| llvm_profdata_label = "\"//:llvm_profdata_bin\"" |
| allocator_library_label = "None" |
| if allocator_library: |
| allocator_library_label = "\"{allocator_library}\"".format(allocator_library = allocator_library) |
| global_allocator_library_label = "None" |
| if global_allocator_library: |
| global_allocator_library_label = "\"{global_allocator_library}\"".format(global_allocator_library = global_allocator_library) |
| |
| return _build_file_for_rust_toolchain_template.format( |
| toolchain_name = name, |
| binary_ext = system_to_binary_ext(target_triple.system), |
| staticlib_ext = system_to_staticlib_ext(target_triple.system), |
| dylib_ext = system_to_dylib_ext(target_triple.system), |
| allocator_library = allocator_library_label, |
| global_allocator_library = global_allocator_library_label, |
| stdlib_linkflags = stdlib_linkflags, |
| default_edition = default_edition, |
| exec_triple = exec_triple.str, |
| target_triple = target_triple.str, |
| rustfmt_label = rustfmt_label, |
| llvm_cov_label = llvm_cov_label, |
| llvm_profdata_label = llvm_profdata_label, |
| extra_rustc_flags = extra_rustc_flags, |
| extra_exec_rustc_flags = extra_exec_rustc_flags, |
| opt_level = opt_level, |
| ) |
| |
| _build_file_for_toolchain_template = """\ |
| toolchain( |
| name = "{name}", |
| exec_compatible_with = {exec_constraint_sets_serialized}, |
| target_compatible_with = {target_constraint_sets_serialized}, |
| toolchain = "{toolchain}", |
| toolchain_type = "{toolchain_type}", |
| {target_settings} |
| ) |
| """ |
| |
| def BUILD_for_toolchain( |
| name, |
| toolchain, |
| toolchain_type, |
| target_settings, |
| target_compatible_with, |
| exec_compatible_with): |
| target_settings_value = "target_settings = {},".format(json.encode(target_settings)) if target_settings else "# target_settings = []" |
| |
| return _build_file_for_toolchain_template.format( |
| name = name, |
| exec_constraint_sets_serialized = json.encode(exec_compatible_with), |
| target_constraint_sets_serialized = json.encode(target_compatible_with), |
| toolchain = toolchain, |
| toolchain_type = toolchain_type, |
| target_settings = target_settings_value, |
| ) |
| |
| def load_rustfmt(ctx, target_triple, version, iso_date): |
| """Loads a rustfmt binary and yields corresponding BUILD for it |
| |
| Args: |
| ctx (repository_ctx): The repository rule's context object. |
| target_triple (struct): The platform triple to download rustfmt for. |
| version (str): The version or channel of rustfmt. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| |
| Returns: |
| Tuple[str, Dict[str, str]]: The BUILD file contents for this rustfmt binary and sha256 of it's artifact. |
| """ |
| |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = iso_date, |
| target_triple = target_triple, |
| tool_name = "rustfmt", |
| tool_subdirectories = ["rustfmt-preview"], |
| version = version, |
| ) |
| |
| return BUILD_for_rustfmt(target_triple), sha256 |
| |
| def load_rust_compiler(ctx, iso_date, target_triple, version): |
| """Loads a rust compiler and yields corresponding BUILD for it |
| |
| Args: |
| ctx (repository_ctx): A repository_ctx. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| target_triple (struct): The Rust-style target that this compiler runs on. |
| version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. |
| |
| Returns: |
| Tuple[str, Dict[str, str]]: The BUILD file contents for this compiler and compiler library |
| and sha256 of it's artifact. |
| """ |
| |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = iso_date, |
| target_triple = target_triple, |
| tool_name = "rustc", |
| tool_subdirectories = ["rustc"], |
| version = version, |
| ) |
| |
| return BUILD_for_compiler(target_triple), sha256 |
| |
| def load_clippy(ctx, iso_date, target_triple, version): |
| """Loads Clippy and yields corresponding BUILD for it |
| |
| Args: |
| ctx (repository_ctx): A repository_ctx. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| target_triple (struct): The Rust-style target that this compiler runs on. |
| version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. |
| |
| Returns: |
| Tuple[str, str]: The BUILD file contents for Clippy and the sha256 of it's artifact |
| """ |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = iso_date, |
| target_triple = target_triple, |
| tool_name = "clippy", |
| tool_subdirectories = ["clippy-preview"], |
| version = version, |
| ) |
| |
| return BUILD_for_clippy(target_triple), sha256 |
| |
| def load_cargo(ctx, iso_date, target_triple, version): |
| """Loads Cargo and yields corresponding BUILD for it |
| |
| Args: |
| ctx (repository_ctx): A repository_ctx. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| target_triple (struct): The Rust-style target that this compiler runs on. |
| version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. |
| |
| Returns: |
| Tuple[str, Dict[str, str]]: The BUILD file contents for Cargo and the sha256 of its artifact. |
| """ |
| |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = iso_date, |
| target_triple = target_triple, |
| tool_name = "cargo", |
| tool_subdirectories = ["cargo"], |
| version = version, |
| ) |
| |
| return BUILD_for_cargo(target_triple), sha256 |
| |
| def includes_rust_analyzer_proc_macro_srv(version, iso_date): |
| """Determine whether or not the rust_analyzer_proc_macro_srv binary in available in the given version of Rust. |
| |
| Args: |
| version (str): The version of the tool among \"nightly\", \"beta\", or an exact version. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| |
| Returns: |
| bool: Whether or not the binary is expected to be included |
| """ |
| |
| if version == "nightly": |
| return iso_date >= "2022-09-21" |
| elif version == "beta": |
| return False |
| elif version >= "1.64.0": |
| return True |
| |
| return False |
| |
| def load_rust_src(ctx, iso_date, version, sha256 = None): |
| """Loads the rust source code. Used by the rust-analyzer rust-project.json generator. |
| |
| Args: |
| ctx (ctx): A repository_ctx. |
| version (str): The version of the tool among "nightly", "beta', or an exact version. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| sha256 (str): The sha256 value for the `rust-src` artifact |
| |
| Returns: |
| Dict[str, str]: A mapping of the artifact name to sha256 |
| """ |
| tool_suburl = produce_tool_suburl("rust-src", None, version, iso_date) |
| url = ctx.attr.urls[0].format(tool_suburl) |
| |
| tool_path = produce_tool_path("rust-src", version, None) |
| archive_path = tool_path + _get_tool_extension(getattr(ctx.attr, "urls", None)) |
| |
| is_reproducible = True |
| if sha256 == None: |
| sha256s = getattr(ctx.attr, "sha256s", {}) |
| sha256 = sha256s.get(archive_path, None) or FILE_KEY_TO_SHA.get(archive_path, None) |
| if not sha256: |
| sha256 = "" |
| is_reproducible = False |
| |
| result = ctx.download_and_extract( |
| url, |
| output = "lib/rustlib/src", |
| sha256 = sha256, |
| auth = _make_auth_dict(ctx, [url]), |
| stripPrefix = "{}/rust-src/lib/rustlib/src/rust".format(tool_path), |
| ) |
| ctx.file( |
| "lib/rustlib/src/BUILD.bazel", |
| """\ |
| filegroup( |
| name = "rustc_srcs", |
| srcs = glob(["**/*"]), |
| visibility = ["//visibility:public"], |
| )""", |
| ) |
| |
| if is_reproducible: |
| return {} |
| |
| return {archive_path: result.sha256} |
| |
| _build_file_for_rust_analyzer_toolchain_template = """\ |
| load("@rules_rust//rust:toolchain.bzl", "rust_analyzer_toolchain") |
| |
| rust_analyzer_toolchain( |
| name = "{name}", |
| proc_macro_srv = {proc_macro_srv}, |
| rustc = "{rustc}", |
| rustc_srcs = "//lib/rustlib/src:rustc_srcs", |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_rust_analyzer_toolchain(name, rustc, proc_macro_srv): |
| return _build_file_for_rust_analyzer_toolchain_template.format( |
| name = name, |
| rustc = rustc, |
| proc_macro_srv = repr(proc_macro_srv), |
| ) |
| |
| _build_file_for_rustfmt_toolchain_template = """\ |
| load("@rules_rust//rust:toolchain.bzl", "rustfmt_toolchain") |
| |
| rustfmt_toolchain( |
| name = "{name}", |
| rustfmt = "{rustfmt}", |
| rustc = "{rustc}", |
| rustc_lib = "{rustc_lib}", |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_rustfmt_toolchain(name, rustfmt, rustc, rustc_lib): |
| return _build_file_for_rustfmt_toolchain_template.format( |
| name = name, |
| rustfmt = rustfmt, |
| rustc = rustc, |
| rustc_lib = rustc_lib, |
| ) |
| |
| def load_rust_stdlib(ctx, target_triple): |
| """Loads a rust standard library and yields corresponding BUILD for it |
| |
| Args: |
| ctx (repository_ctx): A repository_ctx. |
| target_triple (struct): The rust-style target triple of the tool |
| |
| Returns: |
| Tuple[str, Dict[str, str]]: The BUILD file contents for this stdlib and the sha256 of the artifact. |
| """ |
| |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = ctx.attr.iso_date, |
| target_triple = target_triple, |
| tool_name = "rust-std", |
| tool_subdirectories = ["rust-std-{}".format(target_triple.str)], |
| version = ctx.attr.version, |
| ) |
| |
| return BUILD_for_stdlib(target_triple), sha256 |
| |
| def load_rustc_dev_nightly(ctx, target_triple): |
| """Loads the nightly rustc dev component |
| |
| Args: |
| ctx: A repository_ctx. |
| target_triple: The rust-style target triple of the tool |
| |
| Returns: |
| Dict[str, str]: The sha256 value of the rustc-dev artifact. |
| """ |
| |
| subdir_name = "rustc-dev" |
| if ctx.attr.iso_date < "2020-12-24": |
| subdir_name = "rustc-dev-{}".format(target_triple) |
| |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = ctx.attr.iso_date, |
| target_triple = target_triple, |
| tool_name = "rustc-dev", |
| tool_subdirectories = [subdir_name], |
| version = ctx.attr.version, |
| ) |
| |
| return sha256 |
| |
| def load_llvm_tools(ctx, target_triple): |
| """Loads the llvm tools |
| |
| Args: |
| ctx (repository_ctx): A repository_ctx. |
| target_triple (struct): The rust-style target triple of the tool |
| |
| Returns: |
| Tuple[str, Dict[str, str]]: The BUILD.bazel content and sha256 value of the llvm tools artifact. |
| """ |
| sha256 = load_arbitrary_tool( |
| ctx, |
| iso_date = ctx.attr.iso_date, |
| target_triple = target_triple, |
| tool_name = "llvm-tools", |
| tool_subdirectories = ["llvm-tools-preview"], |
| version = ctx.attr.version, |
| ) |
| |
| return BUILD_for_llvm_tools(target_triple), sha256 |
| |
| def check_version_valid(version, iso_date, param_prefix = ""): |
| """Verifies that the provided rust version and iso_date make sense. |
| |
| Args: |
| version (str): The rustc version |
| iso_date (str): The rustc nightly version's iso date |
| param_prefix (str, optional): The name of the tool who's version is being checked. |
| """ |
| |
| if not version and iso_date: |
| fail("{param_prefix}iso_date must be paired with a {param_prefix}version".format(param_prefix = param_prefix)) |
| |
| if version in ("beta", "nightly") and not iso_date: |
| fail("{param_prefix}iso_date must be specified if version is 'beta' or 'nightly'".format(param_prefix = param_prefix)) |
| |
| def produce_tool_suburl(tool_name, target_triple, version, iso_date = None): |
| """Produces a fully qualified Rust tool name for URL |
| |
| Args: |
| tool_name (str): The name of the tool per `static.rust-lang.org`. |
| target_triple (struct): The rust-style target triple of the tool. |
| version (str): The version of the tool among "nightly", "beta', or an exact version. |
| iso_date (str): The date of the tool (or None, if the version is a specific version). |
| |
| Returns: |
| str: The fully qualified url path for the specified tool. |
| """ |
| path = produce_tool_path(tool_name, version, target_triple) |
| return iso_date + "/" + path if (iso_date and version in ("beta", "nightly")) else path |
| |
| def produce_tool_path(tool_name, version, target_triple = None): |
| """Produces a qualified Rust tool name |
| |
| Args: |
| tool_name (str): The name of the tool per static.rust-lang.org |
| version (str): The version of the tool among "nightly", "beta', or an exact version. |
| target_triple (struct, optional): The rust-style target triple of the tool |
| |
| Returns: |
| str: The qualified path for the specified tool. |
| """ |
| if not tool_name: |
| fail("No tool name was provided") |
| if not version: |
| fail("No tool version was provided") |
| |
| # Not all tools require a triple. E.g. `rustc_src` (Rust source files for rust-analyzer). |
| platform_triple = None |
| if target_triple: |
| platform_triple = target_triple.str |
| |
| return "-".join([e for e in [tool_name, version, platform_triple] if e]) |
| |
| def lookup_tool_sha256( |
| repository_ctx, |
| tool_name, |
| target_triple, |
| version, |
| iso_date): |
| """Looks up the sha256 hash of a specific tool archive. |
| |
| The lookup order is: |
| |
| 1. The sha256s dict in the context attributes; |
| 2. The list of sha256 hashes populated in `//rust:known_shas.bzl`; |
| |
| Args: |
| repository_ctx (repository_ctx): A repository_ctx (no attrs required). |
| tool_name (str): The name of the given tool per the archive naming. |
| target_triple (struct): The rust-style target triple of the tool. |
| version (str): The version of the tool among "nightly", "beta', or an exact version. |
| iso_date (str): The date of the tool (ignored if the version is a specific version). |
| |
| Returns: |
| str: The sha256 of the tool archive, or an empty string if the hash could not be found. |
| """ |
| tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date) |
| urls = getattr(repository_ctx.attr, "urls", None) |
| archive_path = tool_suburl + _get_tool_extension(urls) |
| sha256s = getattr(repository_ctx.attr, "sha256s", dict()) |
| |
| from_attr = sha256s.get(archive_path, None) |
| if from_attr: |
| return archive_path, from_attr |
| |
| from_builtin = FILE_KEY_TO_SHA.get(archive_path, None) |
| if from_builtin: |
| return archive_path, from_builtin |
| |
| return archive_path, "" |
| |
| def load_arbitrary_tool( |
| ctx, |
| tool_name, |
| tool_subdirectories, |
| version, |
| iso_date, |
| target_triple, |
| sha256 = None): |
| """Loads a Rust tool, downloads, and extracts into the common workspace. |
| |
| This function sources the tool from the Rust-lang static file server. The index is available at: |
| - https://static.rust-lang.org/dist/channel-rust-stable.toml |
| - https://static.rust-lang.org/dist/channel-rust-beta.toml |
| - https://static.rust-lang.org/dist/channel-rust-nightly.toml |
| |
| Args: |
| ctx (repository_ctx): A repository_ctx (no attrs required). |
| tool_name (str): The name of the given tool per the archive naming. |
| tool_subdirectories (str): The subdirectories of the tool files (at a level below the root directory of |
| the archive). The root directory of the archive is expected to match |
| $TOOL_NAME-$VERSION-$TARGET_TRIPLE. |
| Example: |
| tool_name |
| | version |
| | | target_triple |
| v v v |
| rust-1.39.0-x86_64-unknown-linux-gnu/clippy-preview |
| .../rustc |
| .../etc |
| tool_subdirectories = ["clippy-preview", "rustc"] |
| version (str): The version of the tool among "nightly", "beta', or an exact version. |
| iso_date (str): The date of the tool (ignored if the version is a specific version). |
| target_triple (struct): The rust-style target triple of the tool. |
| sha256 (str, optional): The expected hash of hash of the Rust tool. Defaults to "". |
| |
| Returns: |
| Dict[str, str]: A mapping of the tool name to it's sha256 value if the requested tool does not have |
| enough information in the repository_ctx to be reproducible. |
| """ |
| check_version_valid(version, iso_date, param_prefix = tool_name + "_") |
| |
| # View the indices mentioned in the docstring to find the tool_suburl for a given |
| # tool. |
| tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date) |
| urls = [] |
| |
| for url in getattr(ctx.attr, "urls", DEFAULT_STATIC_RUST_URL_TEMPLATES): |
| new_url = url.format(tool_suburl) |
| if new_url not in urls: |
| urls.append(new_url) |
| |
| tool_path = produce_tool_path(tool_name, version, target_triple) |
| |
| archive_path, ctx_sha256 = lookup_tool_sha256(ctx, tool_name, target_triple, version, iso_date) |
| |
| is_reproducible = True |
| if sha256 == None: |
| sha256 = ctx_sha256 |
| is_reproducible = bool(ctx_sha256) |
| |
| for subdirectory in tool_subdirectories: |
| # As long as the sha256 value is consistent accross calls here the |
| # cost of downloading an artifact is negated as by Bazel's caching. |
| result = ctx.download_and_extract( |
| urls, |
| sha256 = sha256, |
| auth = _make_auth_dict(ctx, urls), |
| stripPrefix = "{}/{}".format(tool_path, subdirectory), |
| ) |
| |
| # In the event no sha256 was provided, set it to the value of the first |
| # downloaded item so subsequent downloads use a cached artifact. |
| if not sha256: |
| sha256 = result.sha256 |
| |
| # If the artifact is reproducibly downloadable then return an |
| # empty dict to inform consumers no attributes require updating. |
| if is_reproducible: |
| return {} |
| |
| return {archive_path: sha256} |
| |
| # The function is copied from the main branch of bazel_tools. |
| # It should become available there from version 7.1.0, |
| # We should remove this function when we upgrade minimum supported |
| # version to 7.1.0. |
| # https://github.com/bazelbuild/bazel/blob/d37762b494a4e122d46a5a71e3a8cc77fa15aa25/tools/build_defs/repo/utils.bzl#L424-L446 |
| def _get_auth(ctx, urls): |
| """Utility function to obtain the correct auth dict for a list of urls from .netrc file. |
| |
| Support optional netrc and auth_patterns attributes if available. |
| |
| Args: |
| ctx: The repository context of the repository rule calling this utility |
| function. |
| urls: the list of urls to read |
| |
| Returns: |
| the auth dict which can be passed to repository_ctx.download |
| """ |
| if hasattr(ctx.attr, "netrc") and ctx.attr.netrc: |
| netrc = read_netrc(ctx, ctx.attr.netrc) |
| elif "NETRC" in ctx.os.environ: |
| netrc = read_netrc(ctx, ctx.os.environ["NETRC"]) |
| else: |
| netrc = read_user_netrc(ctx) |
| auth_patterns = {} |
| if hasattr(ctx.attr, "auth_patterns") and ctx.attr.auth_patterns: |
| auth_patterns = ctx.attr.auth_patterns |
| return use_netrc(netrc, urls, auth_patterns) |
| |
| def _make_auth_dict(ctx, urls): |
| auth = getattr(ctx.attr, "auth", {}) |
| if not auth: |
| return _get_auth(ctx, urls) |
| ret = {} |
| for url in urls: |
| ret[url] = auth |
| return ret |
| |
| def _get_tool_extension(urls = None): |
| if urls == None: |
| urls = DEFAULT_STATIC_RUST_URL_TEMPLATES |
| if urls[0][-7:] == ".tar.gz": |
| return ".tar.gz" |
| elif urls[0][-7:] == ".tar.xz": |
| return ".tar.xz" |
| else: |
| return "" |
| |
| def select_rust_version(versions): |
| """Select the highest priorty version for a list of Rust versions |
| |
| Priority order: `stable > nightly > beta` |
| |
| Note that duplicate channels are unexpected in `versions`. |
| |
| Args: |
| versions (list): A list of Rust versions. E.g. [`1.66.0`, `nightly/2022-12-15`] |
| |
| Returns: |
| str: The highest ranking value from `versions` |
| """ |
| if not versions: |
| fail("No versions were provided") |
| |
| current = versions[0] |
| |
| for ver in versions: |
| if ver.startswith("beta"): |
| if current[0].isdigit() or current.startswith("nightly"): |
| continue |
| if current.startswith("beta") and ver > current: |
| current = ver |
| continue |
| |
| current = ver |
| elif ver.startswith("nightly"): |
| if current[0].isdigit(): |
| continue |
| if current.startswith("nightly") and ver > current: |
| current = ver |
| continue |
| |
| current = ver |
| |
| else: |
| current = ver |
| |
| return current |
| |
| _build_file_for_toolchain_hub_template = """ |
| toolchain( |
| name = "{name}", |
| exec_compatible_with = {exec_constraint_sets_serialized}, |
| target_compatible_with = {target_constraint_sets_serialized}, |
| toolchain = "{toolchain}", |
| toolchain_type = "{toolchain_type}", |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def BUILD_for_toolchain_hub( |
| toolchain_names, |
| toolchain_labels, |
| toolchain_types, |
| target_compatible_with, |
| exec_compatible_with): |
| return "\n".join([_build_file_for_toolchain_hub_template.format( |
| name = toolchain_name, |
| exec_constraint_sets_serialized = json.encode(exec_compatible_with[toolchain_name]), |
| target_constraint_sets_serialized = json.encode(target_compatible_with[toolchain_name]), |
| toolchain = toolchain_labels[toolchain_name], |
| toolchain_type = toolchain_types[toolchain_name], |
| ) for toolchain_name in toolchain_names]) |
| |
| def _toolchain_repository_hub_impl(repository_ctx): |
| repository_ctx.file("WORKSPACE.bazel", """workspace(name = "{}")""".format( |
| repository_ctx.name, |
| )) |
| |
| repository_ctx.file("BUILD.bazel", BUILD_for_toolchain_hub( |
| toolchain_names = repository_ctx.attr.toolchain_names, |
| toolchain_labels = repository_ctx.attr.toolchain_labels, |
| toolchain_types = repository_ctx.attr.toolchain_types, |
| target_compatible_with = repository_ctx.attr.target_compatible_with, |
| exec_compatible_with = repository_ctx.attr.exec_compatible_with, |
| )) |
| |
| toolchain_repository_hub = repository_rule( |
| doc = ( |
| "Generates a toolchain-bearing repository that declares a set of other toolchains from other " + |
| "repositories. This exists to allow registering a set of toolchains in one go with the `:all` target." |
| ), |
| attrs = { |
| "exec_compatible_with": attr.string_list_dict( |
| doc = "A list of constraints for the execution platform for this toolchain, keyed by toolchain name.", |
| mandatory = True, |
| ), |
| "target_compatible_with": attr.string_list_dict( |
| doc = "A list of constraints for the target platform for this toolchain, keyed by toolchain name.", |
| mandatory = True, |
| ), |
| "toolchain_labels": attr.string_dict( |
| doc = "The name of the toolchain implementation target, keyed by toolchain name.", |
| mandatory = True, |
| ), |
| "toolchain_names": attr.string_list( |
| mandatory = True, |
| ), |
| "toolchain_types": attr.string_dict( |
| doc = "The toolchain type of the toolchain to declare, keyed by toolchain name.", |
| mandatory = True, |
| ), |
| }, |
| implementation = _toolchain_repository_hub_impl, |
| ) |