diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 1715758759..aa530e0a15 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -342,6 +342,12 @@ tasks: - "//..." build_flags: *aspects_flags soft_fail: yes + ubuntu2004_examples_bzlmod: + name: Bzlmod Examples + platform: ubuntu2004 + working_directory: examples/bzlmod/hello_world + build_targets: + - "//..." rbe_ubuntu1604_examples: name: Examples platform: rbe_ubuntu1604 diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000000..480a1e9d26 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,17 @@ +module( + name = "rules_rust", + version = "0.20.0", +) + +print("WARNING: The rules_rust Bazel module is still highly experimental and subject to change at any time. Only use it to try out bzlmod for now.") # buildifier: disable=print + +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 = "1.3.1") + +internal_deps = use_extension("//rust/private:extensions.bzl", "internal_deps") +use_repo( + internal_deps, + "rules_rust_tinyjson", +) 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..dca22cebb4 --- /dev/null +++ b/examples/bzlmod/hello_world/MODULE.bazel @@ -0,0 +1,19 @@ +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 = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain(edition = "2021") +use_repo( + rust, + "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/rust/extensions.bzl b/rust/extensions.bzl new file mode 100644 index 0000000000..ce323ba7ad --- /dev/null +++ b/rust/extensions.bzl @@ -0,0 +1,42 @@ +"Module extensions for using rules_rust with bzlmod" + +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: + rust_register_toolchains( + dev_components = toolchain.dev_components, + edition = toolchain.edition, + allocator_library = toolchain.allocator_library, + rustfmt_version = toolchain.rustfmt_version, + rust_analyzer_version = toolchain.rust_analyzer_version, + sha256s = toolchain.sha256s, + extra_target_triples = toolchain.extra_target_triples, + urls = toolchain.urls, + versions = toolchain.versions, + register_toolchains = False, + ) + +rust_toolchain = tag_class(attrs = { + "allocator_library": attr.string(), + "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 = []), +}) + +rust = module_extension( + implementation = _rust_impl, + tag_classes = {"toolchain": rust_toolchain}, +) diff --git a/rust/private/extensions.bzl b/rust/private/extensions.bzl new file mode 100644 index 0000000000..05f9076d4a --- /dev/null +++ b/rust/private/extensions.bzl @@ -0,0 +1,12 @@ +"""Bzlmod module extensions that are only used internally""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("//rust/private:repository_utils.bzl", "TINYJSON_KWARGS") + +def _internal_deps_impl(_module_ctx): + http_archive(**TINYJSON_KWARGS) + +internal_deps = module_extension( + doc = "Dependencies for rules_rust", + implementation = _internal_deps_impl, +) diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl index 9bf63cd5a9..4356248b0d 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -8,9 +8,21 @@ load( "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.gz"] +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 = "1a8304da9f9370f6a6f9020b7903b044aa9ce3470f300a1fba5bc77c78145a16", + url = "https://crates.io/api/v1/crates/tinyjson/2.3.0/download", + strip_prefix = "tinyjson-2.3.0", + type = "tar.gz", + build_file = "@rules_rust//util/process_wrapper:BUILD.tinyjson.bazel", +) _build_file_for_compiler_template = """\ filegroup( @@ -220,15 +232,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 +255,6 @@ rust_toolchain( """ def BUILD_for_rust_toolchain( - workspace_name, name, exec_triple, target_triple, @@ -255,7 +266,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 +286,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), @@ -774,3 +783,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 = { + "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, +) diff --git a/rust/repositories.bzl b/rust/repositories.bzl index 5b67333b55..697822ae4c 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -12,7 +12,10 @@ load( "BUILD_for_rust_toolchain", "BUILD_for_rustfmt_toolchain", "BUILD_for_toolchain", + "DEFAULT_EXTRA_TARGET_TRIPLES", + "DEFAULT_NIGHTLY_VERSION", "DEFAULT_STATIC_RUST_URL_TEMPLATES", + "TINYJSON_KWARGS", "check_version_valid", "includes_rust_analyzer_proc_macro_srv", "load_cargo", @@ -24,6 +27,7 @@ load( "load_rustc_dev_nightly", "load_rustfmt", "select_rust_version", + "toolchain_repository_hub", _load_arbitrary_tool = "load_arbitrary_tool", ) @@ -82,19 +86,12 @@ def rules_rust_dependencies(): # process_wrapper needs a low-dependency way to process json. maybe( http_archive, - name = "rules_rust_tinyjson", - sha256 = "1a8304da9f9370f6a6f9020b7903b044aa9ce3470f300a1fba5bc77c78145a16", - url = "https://crates.io/api/v1/crates/tinyjson/2.3.0/download", - strip_prefix = "tinyjson-2.3.0", - type = "tar.gz", - build_file = "@rules_rust//util/process_wrapper:BUILD.tinyjson.bazel", + **TINYJSON_KWARGS ) -_DEFAULT_NIGHTLY_VERSION = "nightly/{}".format(DEFAULT_NIGHTLY_ISO_DATE) - _RUST_TOOLCHAIN_VERSIONS = [ rust_common.default_version, - _DEFAULT_NIGHTLY_VERSION, + DEFAULT_NIGHTLY_VERSION, ] # buildifier: disable=unnamed-macro @@ -104,10 +101,10 @@ def rust_register_toolchains( allocator_library = None, iso_date = None, register_toolchains = True, - rustfmt_version = _DEFAULT_NIGHTLY_VERSION, + rustfmt_version = DEFAULT_NIGHTLY_VERSION, rust_analyzer_version = None, sha256s = None, - extra_target_triples = ["wasm32-unknown-unknown", "wasm32-wasi"], + extra_target_triples = DEFAULT_EXTRA_TARGET_TRIPLES, urls = DEFAULT_STATIC_RUST_URL_TEMPLATES, version = None, versions = []): @@ -180,6 +177,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 +192,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 +245,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 +344,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, @@ -783,6 +809,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 +907,23 @@ 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) - - 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, - )) + for toolchain in _get_toolchain_repositories(name, exec_triple, extra_target_triples, versions, iso_date): + all_toolchain_names.append(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, + )) # This repository exists to allow `rust_repository_set` to work with the `maybe` wrapper. rust_toolchain_set_repository(