diff --git a/MODULE.bazel b/MODULE.bazel index f7b48cdd83..dc27123b03 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -8,10 +8,32 @@ print("WARNING: The rules_rust Bazel module is still highly experimental and sub bazel_dep(name = "platforms", version = "0.0.7") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "bazel_skylib", version = "1.2.0") -bazel_dep(name = "apple_support", version = "1.3.1") +bazel_dep( + name = "apple_support", + version = "1.3.1", + repo_name = "build_bazel_apple_support", +) internal_deps = use_extension("//rust/private:extensions.bzl", "internal_deps") use_repo( internal_deps, "rules_rust_tinyjson", ) + +rust = use_extension("//rust:extensions.bzl", "rust") + +# Allow us to run, for example, "bazel build //tools/runfiles" with bzlmod. +# Register it as a dev dependency so that we don't force this toolchain on +# downstream users. +rust.toolchain(edition = "2021") +use_repo(rust, "rust_toolchains") + +register_toolchains( + "@rust_toolchains//:all", + dev_dependency = True, +) + +use_repo(rust, "rust_host_tools") + +cargo_bazel_bootstrap = use_extension("//crate_universe/private/module_extensions:cargo_bazel_bootstrap.bzl", "cargo_bazel_bootstrap") +use_repo(cargo_bazel_bootstrap, "cargo_bazel_bootstrap") diff --git a/crate_universe/deps_bootstrap.bzl b/crate_universe/deps_bootstrap.bzl index 28f6d14b0d..e4fda6de97 100644 --- a/crate_universe/deps_bootstrap.bzl +++ b/crate_universe/deps_bootstrap.bzl @@ -6,12 +6,13 @@ load("//crate_universe/private:srcs.bzl", "CARGO_BAZEL_SRCS") # buildifier: disable=bzl-visibility load("//rust/private:common.bzl", "rust_common") -def cargo_bazel_bootstrap(name = "cargo_bazel_bootstrap", rust_version = rust_common.default_version): +def cargo_bazel_bootstrap(name = "cargo_bazel_bootstrap", rust_version = rust_common.default_version, **kwargs): """An optional repository which bootstraps `cargo-bazel` for use with `crates_repository` Args: name (str, optional): The name of the `cargo_bootstrap_repository`. rust_version (str, optional): The rust version to use. Defaults to the default of `cargo_bootstrap_repository`. + **kwargs: kwargs to pass through to cargo_bootstrap_repository. """ cargo_bootstrap_repository( name = name, @@ -22,4 +23,5 @@ def cargo_bazel_bootstrap(name = "cargo_bazel_bootstrap", rust_version = rust_co version = rust_version, # The increased timeout helps avoid flakes in CI timeout = 900, + **kwargs ) diff --git a/crate_universe/private/module_extensions/BUILD.bazel b/crate_universe/private/module_extensions/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crate_universe/private/module_extensions/cargo_bazel_bootstrap.bzl b/crate_universe/private/module_extensions/cargo_bazel_bootstrap.bzl new file mode 100644 index 0000000000..18a46eff25 --- /dev/null +++ b/crate_universe/private/module_extensions/cargo_bazel_bootstrap.bzl @@ -0,0 +1,14 @@ +"""Module extension for bootstrapping cargo-bazel.""" + +load("//crate_universe:deps_bootstrap.bzl", _cargo_bazel_bootstrap_repo_rule = "cargo_bazel_bootstrap") + +def _cargo_bazel_bootstrap_impl(_): + _cargo_bazel_bootstrap_repo_rule( + rust_toolchain_cargo_template = "@rust_host_tools//:bin/{tool}", + rust_toolchain_rustc_template = "@rust_host_tools//:bin/{tool}", + ) + +cargo_bazel_bootstrap = module_extension( + implementation = _cargo_bazel_bootstrap_impl, + doc = """Module extension to generate the cargo_bazel binary.""", +) diff --git a/rust/extensions.bzl b/rust/extensions.bzl index 883946c9ea..f45a557162 100644 --- a/rust/extensions.bzl +++ b/rust/extensions.bzl @@ -1,16 +1,99 @@ "Module extensions for using rules_rust with bzlmod" +load("//rust:defs.bzl", "rust_common") +load("//rust:repositories.bzl", "rust_register_toolchains", "rust_toolchain_tools_repository") +load("//rust/platform:triple.bzl", "get_host_triple") load( "//rust/private:repository_utils.bzl", "DEFAULT_EXTRA_TARGET_TRIPLES", "DEFAULT_NIGHTLY_VERSION", "DEFAULT_STATIC_RUST_URL_TEMPLATES", ) -load(":repositories.bzl", "rust_register_toolchains") -def _rust_impl(ctx): - mod = ctx.modules[0] - for toolchain in mod.tags.toolchain: +_HOST_TOOL_ERR = """When %s, host tools must be explicitly defined. For example: + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.host_tools( + edition = "2021", + version = "1.70.2", +) +""" + +_EXAMPLE_TOOLCHAIN = """ +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain( + edition = "2021", + versions = ["1.70.2"], +) +use_repo(rust, "rust_toolchains") +register_toolchains("@rust_toolchains//:all")""" + +_TRANSITIVE_DEP_ERR = """ +Your transitive dependency %s is using rules_rust, so you need to define a rust toolchain. +To do so, you will need to add the following to your root MODULE.bazel. For example: + +bazel_dep(name = "rules_rust", version = "") +""" + _EXAMPLE_TOOLCHAIN + +_TOOLCHAIN_ERR = """ +Please add at least one toolchain to your root MODULE.bazel. For example: +""" + _EXAMPLE_TOOLCHAIN + +def _rust_impl(module_ctx): + # Toolchain configuration is only allowed in the root module. + # It would be very confusing (and a security concern) if I was using the + # default rust toolchains, then when I added a module built on rust, I was + # suddenly using a custom rustc. + root = None + for mod in module_ctx.modules: + if mod.is_root: + root = mod + if not root: + fail(_TRANSITIVE_DEP_ERR % module_ctx.modules[0].name) + + toolchains = root.tags.toolchain + if not toolchains: + fail(_TOOLCHAIN_ERR) + + if len(root.tags.host_tools) == 1: + host_tools = root.tags.host_tools[0] + elif not root.tags.host_tools: + if len(toolchains) != 1: + fail(_HOST_TOOL_ERR % "multiple toolchains are provided") + toolchain = toolchains[0] + if len(toolchain.versions) == 1: + version = toolchain.versions[0] + elif not toolchain.versions: + version = None + else: + fail(_HOST_TOOL_ERR % "multiple toolchain versions are provided") + host_tools = struct( + allocator_library = toolchain.allocator_library, + dev_components = toolchain.dev_components, + edition = toolchain.edition, + rustfmt_version = toolchain.rustfmt_version, + sha256s = toolchain.sha256s, + urls = toolchain.urls, + version = version, + ) + else: + fail("Multiple host_tools were defined in your root MODULE.bazel") + + host_triple = get_host_triple(module_ctx) + rust_toolchain_tools_repository( + name = "rust_host_tools", + exec_triple = host_triple.str, + target_triple = host_triple.str, + allocator_library = host_tools.allocator_library, + dev_components = host_tools.dev_components, + edition = host_tools.edition, + rustfmt_version = host_tools.rustfmt_version, + sha256s = host_tools.sha256s, + urls = host_tools.urls, + version = host_tools.version or rust_common.default_version, + ) + + for toolchain in toolchains: rust_register_toolchains( dev_components = toolchain.dev_components, edition = toolchain.edition, @@ -24,19 +107,31 @@ def _rust_impl(ctx): register_toolchains = False, ) -rust_toolchain = tag_class(attrs = { - "allocator_library": attr.string(default = "@rules_rust//ffi/cc/allocator_library"), - "dev_components": attr.bool(default = False), - "edition": attr.string(), - "extra_target_triples": attr.string_list(default = DEFAULT_EXTRA_TARGET_TRIPLES), - "rust_analyzer_version": attr.string(), - "rustfmt_version": attr.string(default = DEFAULT_NIGHTLY_VERSION), - "sha256s": attr.string_dict(), - "urls": attr.string_list(default = DEFAULT_STATIC_RUST_URL_TEMPLATES), - "versions": attr.string_list(default = []), -}) +_COMMON_TAG_KWARGS = dict( + allocator_library = attr.string(default = "@rules_rust//ffi/cc/allocator_library"), + dev_components = attr.bool(default = False), + edition = attr.string(), + rustfmt_version = attr.string(default = DEFAULT_NIGHTLY_VERSION), + sha256s = attr.string_dict(), + urls = attr.string_list(default = DEFAULT_STATIC_RUST_URL_TEMPLATES), +) + +_RUST_TOOLCHAIN_TAG = tag_class(attrs = dict( + extra_target_triples = attr.string_list(default = DEFAULT_EXTRA_TARGET_TRIPLES), + rust_analyzer_version = attr.string(), + versions = attr.string_list(default = []), + **_COMMON_TAG_KWARGS +)) + +_RUST_HOST_TOOLS_TAG = tag_class(attrs = dict( + version = attr.string(), + **_COMMON_TAG_KWARGS +)) rust = module_extension( implementation = _rust_impl, - tag_classes = {"toolchain": rust_toolchain}, + tag_classes = { + "host_tools": _RUST_HOST_TOOLS_TAG, + "toolchain": _RUST_TOOLCHAIN_TAG, + }, )