Skip to content

Commit

Permalink
Bzlmod spec stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
dzbarsky committed Mar 20, 2024
1 parent f8ffba5 commit 95b563a
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 16 deletions.
83 changes: 68 additions & 15 deletions crate_universe/extension.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ _generate_repo = repository_rule(
),
)

def _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations):
cargo_lockfile = module_ctx.path(cfg.cargo_lockfile)
def _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations, cargo_lockfile = None, manifests = {}, packages = {}):
tag_path = module_ctx.path(cfg.name)

rendering_config = json.decode(render_config(
Expand All @@ -67,32 +66,35 @@ def _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations):
),
)

manifests = {module_ctx.path(m): m for m in cfg.manifests}
splicing_manifest = tag_path.get_child("splicing_manifest.json")
module_ctx.file(
splicing_manifest,
executable = False,
content = generate_splicing_manifest(
packages = {},
packages = packages,
splicing_config = "",
cargo_config = cfg.cargo_config,
manifests = {str(k): str(v) for k, v in manifests.items()},
manifests = manifests,
manifest_to_path = module_ctx.path,
),
)

splicing_output_dir = tag_path.get_child("splicing-output")
cargo_bazel([
splice_args = [
"splice",
"--output-dir",
splicing_output_dir,
"--config",
config_file,
"--splicing-manifest",
splicing_manifest,
"--cargo-lockfile",
cargo_lockfile,
])
]
if cargo_lockfile:
splice_args.extend([
"--cargo-lockfile",
cargo_lockfile,
])
cargo_bazel(splice_args)

# Create a lockfile, since we need to parse it to generate spoke
# repos.
Expand All @@ -102,7 +104,7 @@ def _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations):
cargo_bazel([
"generate",
"--cargo-lockfile",
cargo_lockfile,
cargo_lockfile or splicing_output_dir.get_child("Cargo.lock"),
"--config",
config_file,
"--splicing-manifest",
Expand Down Expand Up @@ -181,6 +183,15 @@ def _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations):
else:
fail("Invalid repo: expected Http or Git to exist for crate %s-%s, got %s" % (name, version, repo))

def _package_to_json(p):
# Avoid adding unspecified properties.
# If we add them as empty strings, cargo-bazel will be unhappy.
return json.encode({
k: v
for k, v in structs.to_dict(p).items()
if v
})

def _crate_impl(module_ctx):
cargo_bazel = get_cargo_bazel_runner(module_ctx)
all_repos = []
Expand Down Expand Up @@ -216,18 +227,31 @@ def _crate_impl(module_ctx):
).append(annotation)

local_repos = []
for cfg in mod.tags.from_cargo:

for cfg in mod.tags.from_cargo + mod.tags.from_specs:
if cfg.name in local_repos:
fail("Defined two crate universes with the same name in the same MODULE.bazel file. Use the name tag to give them different names.")
elif cfg.name in all_repos:
fail("Defined two crate universes with the same name in different MODULE.bazel files. Either give one a different name, or use use_extension(isolate=True)")
all_repos.append(cfg.name)
local_repos.append(cfg.name)

annotations = {k: v for k, v in module_annotations.items()}
for cfg in mod.tags.from_cargo:
annotations = dict(module_annotations)
for crate, values in repo_specific_annotations.get(cfg.name, {}).items():
_get_or_insert(annotations, crate, []).extend(values)
_generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations)
all_repos.append(cfg.name)
local_repos.append(cfg.name)

cargo_lockfile = module_ctx.path(cfg.cargo_lockfile)
manifests = {str(module_ctx.path(m)): str(m) for m in cfg.manifests}
_generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations, cargo_lockfile = cargo_lockfile, manifests = manifests)

for cfg in mod.tags.from_specs:
annotations = dict(module_annotations)
for crate, values in repo_specific_annotations.get(cfg.name, {}).items():
_get_or_insert(annotations, crate, []).extend(values)

packages = {p.package: _package_to_json(p) for p in mod.tags.spec}
_generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations, packages = packages)

for repo in repo_specific_annotations:
if repo not in local_repos:
Expand Down Expand Up @@ -290,10 +314,39 @@ _annotation = tag_class(
),
)

_from_specs = tag_class(
doc = "Generates a repo @crates from a Cargo.toml / Cargo.lock pair",
attrs = dict(
name = attr.string(doc = "The name of the repo to generate", default = "crates"),
cargo_config = CRATES_VENDOR_ATTRS["cargo_config"],
generate_binaries = CRATES_VENDOR_ATTRS["generate_binaries"],
generate_build_scripts = CRATES_VENDOR_ATTRS["generate_build_scripts"],
supported_platform_triples = CRATES_VENDOR_ATTRS["supported_platform_triples"],
),
)

# This should be kept in sync with crate_universe/private/crate.bzl.
_spec = tag_class(
attrs = dict(
package = attr.string(doc = "The explicit name of the package.", mandatory = True),
version = attr.string(doc = "The exact version of the crate. Cannot be used with `git`."),
artifact = attr.string(doc = "Set to 'bin' to pull in a binary crate as an artifact dependency. Requires a nightly Cargo."),
lib = attr.bool(doc = "If using `artifact = 'bin'`, additionally setting `lib = True` declares a dependency on both the package's library and binary, as opposed to just the binary."),
default_features = attr.bool(doc = "Maps to the `default-features` flag."),
features = attr.string_list(doc = "A list of features to use for the crate."),
git = attr.string(doc = "The Git url to use for the crate. Cannot be used with `version`."),
branch = attr.string(doc = "The git branch of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified. Specifying `rev` is recommended for fully-reproducible builds."),
tag = attr.string(doc = "The git tag of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified. Specifying `rev` is recommended for fully-reproducible builds."),
rev = attr.string(doc = "The git revision of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified."),
),
)

crate = module_extension(
implementation = _crate_impl,
tag_classes = dict(
from_cargo = _from_cargo,
annotation = _annotation,
from_specs = _from_specs,
spec = _spec,
),
)
2 changes: 1 addition & 1 deletion crate_universe/private/crates_vendor.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def _write_splicing_manifest(ctx):
return args, runfiles

def generate_splicing_manifest(packages, splicing_config, cargo_config, manifests, manifest_to_path):
# Deserialize information about direct packges
# Deserialize information about direct packages
direct_packages_info = {
# Ensure the data is using kebab-case as that's what `cargo_toml::DependencyDetail` expects.
pkg: kebab_case_keys(dict(json.decode(data)))
Expand Down
3 changes: 3 additions & 0 deletions examples/bzlmod/hello_world_no_cargo/.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
common --experimental_enable_bzlmod
common --noenable_workspace
common --enable_runfiles
1 change: 1 addition & 0 deletions examples/bzlmod/hello_world_no_cargo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/bazel-*
11 changes: 11 additions & 0 deletions examples/bzlmod/hello_world_no_cargo/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("@rules_rust//rust:defs.bzl", "rust_binary")

package(default_visibility = ["//visibility:public"])

rust_binary(
name = "hello_world",
srcs = ["src/main.rs"],
deps = [
"@crates//:anyhow",
],
)
26 changes: 26 additions & 0 deletions examples/bzlmod/hello_world_no_cargo/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""bazelbuild/rules_rust - bzlmod no-cargo example"""

module(name = "hello_world_no_cargo")

bazel_dep(
name = "rules_rust",
version = "0.0.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")

crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
crate.spec(
package = "anyhow",
version = "1.0.77",
)
crate.from_specs()
use_repo(crate, "crates")
18 changes: 18 additions & 0 deletions examples/bzlmod/hello_world_no_cargo/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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() -> anyhow::Result<()> {
println!("Hello, world!");
Ok(())
}

0 comments on commit 95b563a

Please sign in to comment.