diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000000..007d911847 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,23 @@ +module( + name = "rules_rust", + version = "0.9.0", +) + +bazel_dep(name = "platforms", version = "0.0.5") +bazel_dep(name = "rules_cc", version = "0.0.1") +bazel_dep(name = "bazel_skylib", version = "1.2.0") +bazel_dep(name = "apple_support", version = "0.13.0") + +# Load tinyjson without using the process wrapper +raw_cargo = use_extension("//:extensions.bzl", "raw_cargo") + +raw_cargo.crate( + name = "tinyjson", + sha256 = "1a8304da9f9370f6a6f9020b7903b044aa9ce3470f300a1fba5bc77c78145a16", + version = "2.3.0", +) + +use_repo( + raw_cargo, + rules_rust_tinyjson = "raw_cargo_tinyjson_2_3_0", +) diff --git a/examples/bzlmod/hello_world/.bazelrc b/examples/bzlmod/hello_world/.bazelrc new file mode 100644 index 0000000000..c8428d9570 --- /dev/null +++ b/examples/bzlmod/hello_world/.bazelrc @@ -0,0 +1 @@ +build --experimental_enable_bzlmod \ No newline at end of file diff --git a/examples/bzlmod/hello_world/.gitignore b/examples/bzlmod/hello_world/.gitignore new file mode 100644 index 0000000000..65e8edca79 --- /dev/null +++ b/examples/bzlmod/hello_world/.gitignore @@ -0,0 +1 @@ +/bazel-* \ No newline at end of file diff --git a/examples/bzlmod/hello_world/BUILD.bazel b/examples/bzlmod/hello_world/BUILD.bazel new file mode 100644 index 0000000000..3e2ec1932e --- /dev/null +++ b/examples/bzlmod/hello_world/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_doc") + +package(default_visibility = ["//visibility:public"]) + +rust_binary( + name = "hello_world", + srcs = ["src/main.rs"], +) + +rust_doc( + name = "hello_world_doc", + crate = ":hello_world", +) diff --git a/examples/bzlmod/hello_world/MODULE.bazel b/examples/bzlmod/hello_world/MODULE.bazel new file mode 100644 index 0000000000..e34a5592e0 --- /dev/null +++ b/examples/bzlmod/hello_world/MODULE.bazel @@ -0,0 +1,22 @@ +module( + name = "hello_world", + version = "1.0", +) + +bazel_dep(name = "rules_rust", version = "0.9.0") + +local_path_override( + module_name = "rules_rust", + path = "../../..", +) + +rust_toolchains = use_extension("@rules_rust//:extensions.bzl", "toolchains") + +rust_toolchains.toolchain(edition = "2021") + +use_repo( + rust_toolchains, + "rust_toolchains", +) + +register_toolchains("@rust_toolchains//:all") diff --git a/examples/bzlmod/hello_world/WORKSPACE b/examples/bzlmod/hello_world/WORKSPACE new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/bzlmod/hello_world/src/main.rs b/examples/bzlmod/hello_world/src/main.rs new file mode 100644 index 0000000000..317f564583 --- /dev/null +++ b/examples/bzlmod/hello_world/src/main.rs @@ -0,0 +1,17 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn main() { + println!("Hello, world!"); +} diff --git a/extensions.bzl b/extensions.bzl new file mode 100644 index 0000000000..7c5e04a8e1 --- /dev/null +++ b/extensions.bzl @@ -0,0 +1,45 @@ +"Module extensions for using rules_rust with bzlmod" + +load("//rust:repositories.bzl", "rust_register_toolchains") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def _toolchains_impl(ctx): + mod = ctx.modules[0] + for toolchain in mod.tags.toolchain: + rust_register_toolchains(edition = toolchain.edition, register_toolchains = False) + +toolchains_toolchain = tag_class(attrs = {"edition": attr.string()}) +toolchains = module_extension( + implementation = _toolchains_impl, + tag_classes = {"toolchain": toolchains_toolchain}, +) + +def _create_build_file_content(name): + return """ +load("@rules_rust//rust/private:rust.bzl", "rust_library_without_process_wrapper") + +rust_library_without_process_wrapper( + name = "{}", + srcs = glob(["src/*.rs"]), + edition = "2018", + visibility = ["@rules_rust//util/process_wrapper:__pkg__"], +) +""".format(name) + +def _raw_cargo_impl(ctx): + for mod in ctx.modules: + for crate in mod.tags.crate: + http_archive( + name = "raw_cargo_{}_{}".format(crate.name, crate.version.replace(".", "_")), + sha256 = crate.sha256, + url = "https://crates.io/api/v1/crates/{}/{}/download".format(crate.name, crate.version), + strip_prefix = "{}-{}".format(crate.name, crate.version), + type = "tar.gz", + build_file_content = _create_build_file_content(crate.name), + ) + +raw_cargo_crate = tag_class(attrs = {"name": attr.string(), "version": attr.string(), "sha256": attr.string()}) +raw_cargo = module_extension( + implementation = _raw_cargo_impl, + tag_classes = {"crate": raw_cargo_crate}, +) diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl index 9bf63cd5a9..6a66e2ba5c 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -220,15 +220,15 @@ load("@rules_rust//rust:toolchain.bzl", "rust_toolchain") rust_toolchain( name = "{toolchain_name}", - rust_doc = "@{workspace_name}//:rustdoc", - rust_std = "@{workspace_name}//:rust_std-{target_triple}", - rustc = "@{workspace_name}//:rustc", + rust_doc = "//:rustdoc", + rust_std = "//:rust_std-{target_triple}", + rustc = "//:rustc", rustfmt = {rustfmt_label}, - cargo = "@{workspace_name}//:cargo", - clippy_driver = "@{workspace_name}//:clippy_driver_bin", + cargo = "//:cargo", + clippy_driver = "//:clippy_driver_bin", llvm_cov = {llvm_cov_label}, llvm_profdata = {llvm_profdata_label}, - rustc_lib = "@{workspace_name}//:rustc_lib", + rustc_lib = "//:rustc_lib", allocator_library = {allocator_library}, binary_ext = "{binary_ext}", staticlib_ext = "{staticlib_ext}", @@ -243,7 +243,6 @@ rust_toolchain( """ def BUILD_for_rust_toolchain( - workspace_name, name, exec_triple, target_triple, @@ -255,7 +254,6 @@ def BUILD_for_rust_toolchain( """Emits a toolchain declaration to match an existing compiler and stdlib. Args: - workspace_name (str): The name of the workspace that this toolchain resides in 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 @@ -276,19 +274,18 @@ def BUILD_for_rust_toolchain( rustfmt_label = "None" if include_rustfmt: - rustfmt_label = "\"@{workspace_name}//:rustfmt_bin\"".format(workspace_name = workspace_name) + rustfmt_label = "\"//:rustfmt_bin\"" llvm_cov_label = "None" llvm_profdata_label = "None" if include_llvm_tools: - llvm_cov_label = "\"@{workspace_name}//:llvm_cov_bin\"".format(workspace_name = workspace_name) - llvm_profdata_label = "\"@{workspace_name}//:llvm_profdata_bin\"".format(workspace_name = workspace_name) + 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) return _build_file_for_rust_toolchain_template.format( toolchain_name = name, - workspace_name = workspace_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), @@ -310,6 +307,7 @@ toolchain( target_compatible_with = {target_constraint_sets_serialized}, toolchain = "{toolchain}", toolchain_type = "{toolchain_type}", + visibility = ["//visibility:public"], {target_settings} ) """ @@ -774,3 +772,68 @@ def select_rust_version(versions): 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 = { + "toolchain_names": attr.string_list(mandatory = True), + "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_types": attr.string_dict( + doc = "The toolchain type of the toolchain to declare, keyed by toolchain name.", + mandatory = True, + ), + }, + implementation = _toolchain_repository_hub_impl, +) diff --git a/rust/repositories.bzl b/rust/repositories.bzl index eb5c190948..2c47539c72 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -24,6 +24,7 @@ load( "load_rustc_dev_nightly", "load_rustfmt", "select_rust_version", + "toolchain_repository_hub", _load_arbitrary_tool = "load_arbitrary_tool", ) @@ -180,6 +181,12 @@ def rust_register_toolchains( if rust_analyzer_version.startswith(("beta", "nightly")): rust_analyzer_version, _, rust_analyzer_iso_date = rustfmt_version.partition("/") + toolchain_names = [] + toolchain_labels = {} + toolchain_types = {} + exec_compatible_with_by_toolchain = {} + target_compatible_with_by_toolchain = {} + maybe( rust_analyzer_toolchain_repository, name = rust_analyzer_repo_name, @@ -189,6 +196,14 @@ def rust_register_toolchains( iso_date = rust_analyzer_iso_date, ) + toolchain_names.append(rust_analyzer_repo_name) + toolchain_labels[rust_analyzer_repo_name] = "@{}_srcs//:rust_analyzer_toolchain".format( + rust_analyzer_repo_name, + ) + exec_compatible_with_by_toolchain[rust_analyzer_repo_name] = [] + target_compatible_with_by_toolchain[rust_analyzer_repo_name] = [] + toolchain_types[rust_analyzer_repo_name] = "@rules_rust//rust/rust_analyzer:toolchain_type" + if register_toolchains: native.register_toolchains("@{}//:toolchain".format( rust_analyzer_repo_name, @@ -234,6 +249,22 @@ def rust_register_toolchains( rustfmt_repo_name, )) + for toolchain in get_toolchain_repositories(name, exec_triple, extra_target_triples, versions, iso_date): + toolchain_names.append(toolchain.name) + toolchain_labels[toolchain.name] = "@{}//:{}".format(toolchain.name + "_tools", "rust_toolchain") + exec_compatible_with_by_toolchain[toolchain.name] = triple_to_constraint_set(exec_triple) + target_compatible_with_by_toolchain[toolchain.name] = triple_to_constraint_set(toolchain.target_triple) + toolchain_types[toolchain.name] = "@rules_rust//rust:toolchain" + + toolchain_repository_hub( + name = "rust_toolchains", + toolchain_names = toolchain_names, + toolchain_labels = toolchain_labels, + toolchain_types = toolchain_types, + exec_compatible_with = exec_compatible_with_by_toolchain, + target_compatible_with = target_compatible_with_by_toolchain, + ) + # buildifier: disable=unnamed-macro def rust_repositories(**kwargs): """**Deprecated**: Use [rules_rust_dependencies](#rules_rust_dependencies) \ @@ -317,7 +348,6 @@ def _rust_toolchain_tools_repository_impl(ctx): allocator_library = ctx.attr.allocator_library, target_triple = target_triple, stdlib_linkflags = stdlib_linkflags, - workspace_name = ctx.attr.name, default_edition = ctx.attr.edition, include_rustfmt = not (not ctx.attr.rustfmt_version), include_llvm_tools = include_llvm_tools, @@ -475,9 +505,6 @@ def rust_toolchain_repository( urls (list, optional): A list of mirror urls containing the tools from the Rust-lang static file server. These must contain the '{}' used to substitute the tool being fetched (using .format). Defaults to ['https://static.rust-lang.org/dist/{}.tar.gz'] auth (dict): Auth object compatible with repository_ctx.download to use when downloading files. See [repository_ctx.download](https://docs.bazel.build/versions/main/skylark/lib/repository_ctx.html#download) for more details. - - Returns: - str: The name of the registerable toolchain created by this rule. """ if rustfmt_version in ("nightly", "beta"): @@ -519,10 +546,6 @@ def rust_toolchain_repository( target_compatible_with = target_compatible_with, ) - return "@{name}//:toolchain".format( - name = name, - ) - def _rust_analyzer_toolchain_tools_repository_impl(repository_ctx): load_rust_src( ctx = repository_ctx, @@ -783,6 +806,40 @@ rust_toolchain_set_repository = repository_rule( implementation = _rust_toolchain_set_repository_impl, ) +def get_toolchain_repositories(name, exec_triple, extra_target_triples, versions, iso_date): + toolchain_repos = [] + + for target_triple in [exec_triple] + extra_target_triples: + # Parse all provided versions while checking for duplicates + channels = {} + for version in versions: + if version.startswith(("beta", "nightly")): + channel, _, date = version.partition("/") + ver = channel + else: + channel = "stable" + date = iso_date + ver = version + + if channel in channels: + fail("Duplicate {} channels provided for {}: {}".format(channel, name, versions)) + + channels.update({channel: struct( + name = channel, + iso_date = date, + version = ver, + )}) + + # Define toolchains for each requested version + for channel in channels.values(): + toolchain_repos.append(struct( + name = "{}__{}__{}".format(name, target_triple, channel.name), + target_triple = target_triple, + channel = channel, + )) + + return toolchain_repos + def rust_repository_set( name, exec_triple, @@ -847,46 +904,25 @@ def rust_repository_set( versions = [version] all_toolchain_names = [] - for target_triple in [exec_triple] + extra_target_triples: - # Parse all provided versions while checking for duplicates - channels = {} - for version in versions: - if version.startswith(("beta", "nightly")): - channel, _, date = version.partition("/") - ver = channel - else: - channel = "stable" - date = iso_date - ver = version - - if channel in channels: - fail("Duplicate {} channels provided for {}: {}".format(channel, name, versions)) - - channels.update({channel: struct( - iso_date = date, - version = ver, - )}) - - # Define toolchains for each requested version - for channel, info in channels.items(): - toolchain_name = "{}__{}__{}".format(name, target_triple, channel) + for toolchain in get_toolchain_repositories(name, exec_triple, extra_target_triples, versions, iso_date): + rust_toolchain_repository( + name = toolchain.name, + allocator_library = allocator_library, + auth = auth, + channel = toolchain.channel.name, + dev_components = dev_components, + edition = edition, + exec_triple = exec_triple, + target_settings = target_settings, + iso_date = toolchain.channel.iso_date, + rustfmt_version = rustfmt_version, + sha256s = sha256s, + target_triple = toolchain.target_triple, + urls = urls, + version = toolchain.channel.version, + ) - all_toolchain_names.append(rust_toolchain_repository( - name = toolchain_name, - allocator_library = allocator_library, - auth = auth, - channel = channel, - dev_components = dev_components, - edition = edition, - exec_triple = exec_triple, - target_settings = target_settings, - iso_date = info.iso_date, - rustfmt_version = rustfmt_version, - sha256s = sha256s, - target_triple = target_triple, - urls = urls, - version = info.version, - )) + all_toolchain_names.append("@{name}//:toolchain".format(name = toolchain.name)) # This repository exists to allow `rust_repository_set` to work with the `maybe` wrapper. rust_toolchain_set_repository(