Skip to content

Commit

Permalink
Support bzlmod (#1528)
Browse files Browse the repository at this point in the history
This makes rules_rust load dependencies via bzlmod. Currently only basic functionality is completed, such as registering rustc toolchains and compiling crates. Note that it cannot interact with cargo, wasm or load any other rust toolchains such as rustfmt.
    
There is one new module, `examples/bzlmod/hello_world`, that depends on the root `rules_rust` module. This example can be built and run using:
    
```
cd examples/bzlmod/hello_world
bazel run //:hello_world
```

To register toolchains in an ergonomic way, it defines a new "hub" repository that contains all the toolchain proxies, so they can all be registered like so:

```
register_toolchains("@rust_toolchains//:all")
```

closes #1493
  • Loading branch information
cameron-martin authored Mar 29, 2023
1 parent 1074ecb commit eaf5138
Show file tree
Hide file tree
Showing 12 changed files with 303 additions and 64 deletions.
6 changes: 6 additions & 0 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -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",
)
1 change: 1 addition & 0 deletions examples/bzlmod/hello_world/.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build --experimental_enable_bzlmod
1 change: 1 addition & 0 deletions examples/bzlmod/hello_world/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/bazel-*
13 changes: 13 additions & 0 deletions examples/bzlmod/hello_world/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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",
)
19 changes: 19 additions & 0 deletions examples/bzlmod/hello_world/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -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")
Empty file.
17 changes: 17 additions & 0 deletions examples/bzlmod/hello_world/src/main.rs
Original file line number Diff line number Diff line change
@@ -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!");
}
42 changes: 42 additions & 0 deletions rust/extensions.bzl
Original file line number Diff line number Diff line change
@@ -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},
)
12 changes: 12 additions & 0 deletions rust/private/extensions.bzl
Original file line number Diff line number Diff line change
@@ -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,
)
98 changes: 86 additions & 12 deletions rust/private/repository_utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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}",
Expand All @@ -243,7 +255,6 @@ rust_toolchain(
"""

def BUILD_for_rust_toolchain(
workspace_name,
name,
exec_triple,
target_triple,
Expand All @@ -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
Expand All @@ -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),
Expand Down Expand Up @@ -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,
)
Loading

0 comments on commit eaf5138

Please sign in to comment.