Skip to content

Commit 42776e3

Browse files
Speedup manifest splicing by removing workspace discovery logic (#3570)
The original implementation required users to specify all manifests belonging to the workspace, and had a validation pass that walked the repo to verify the list was in sync. This PR swaps that for a `cargo metadata` call, which lists the manifests making up the workspace. We can then call `ctx.watch` on them to invalidate the extension when they are modified (which was previously impossible if they were not passed to the extension as labels). This also means users only need to specify the workspace-root Cargo.toml now :) There is still an edge-case where a newly-added Cargo.toml won't trigger invalidation if it's covered by one of the wildcard `members` patterns, but that was already a pre-existing issue. In practice, we'd expect invalidation in that case to be triggered by a Cargo.lock change, or to be unnecessary if the crate does not add new deps to crate resolution. Fixes #3425 Fixes #3515 Fixes #3518 Obsoletes #3387 Co-authored-by: Daniel Wagner-Hall <[email protected]>
1 parent 3c5afe5 commit 42776e3

File tree

9 files changed

+74
-724
lines changed

9 files changed

+74
-724
lines changed

crate_universe/extensions.bzl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ def _generate_hub_and_spokes(
628628
splicing_manifest = splicing_manifest,
629629
)
630630

631+
# The workspace root when one is explicitly provided.
632+
nonhermetic_root_bazel_workspace_dir = module_ctx.path(Label("@@//:MODULE.bazel")).dirname
633+
631634
# If re-pinning is enabled, gather additional inputs for the generator
632635
kwargs = dict()
633636
if repin:
@@ -658,8 +661,10 @@ def _generate_hub_and_spokes(
658661
"metadata": splice_outputs.metadata,
659662
})
660663

661-
# The workspace root when one is explicitly provided.
662-
nonhermetic_root_bazel_workspace_dir = module_ctx.path(Label("@@//:MODULE.bazel")).dirname
664+
for path_to_track in splice_outputs.extra_paths_to_track:
665+
# We can only watch paths in our workspace.
666+
if path_to_track.startswith(str(nonhermetic_root_bazel_workspace_dir)):
667+
module_ctx.watch(path_to_track)
663668

664669
paths_to_track_file = tag_path.get_child("paths_to_track.json")
665670
warnings_output_file = tag_path.get_child("warnings_output.json")

crate_universe/private/crates_repository.bzl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ def _crates_repository_impl(repository_ctx):
7676
repin_instructions = repository_ctx.attr.repin_instructions,
7777
)
7878

79+
nonhermetic_root_bazel_workspace_dir = repository_ctx.workspace_root
80+
7981
# If re-pinning is enabled, gather additional inputs for the generator
8082
kwargs = dict()
8183
if repin:
@@ -92,6 +94,11 @@ def _crates_repository_impl(repository_ctx):
9294
repository_name = repository_ctx.name,
9395
)
9496

97+
for path_to_track in splice_outputs.extra_paths_to_track:
98+
# We can only watch paths in our workspace.
99+
if path_to_track.startswith(str(nonhermetic_root_bazel_workspace_dir)):
100+
repository_ctx.watch(path_to_track)
101+
95102
kwargs.update({
96103
"metadata": splice_outputs.metadata,
97104
})
@@ -109,7 +116,7 @@ def _crates_repository_impl(repository_ctx):
109116
lockfile_path = lockfiles.bazel,
110117
cargo_lockfile_path = lockfiles.cargo,
111118
repository_dir = repository_ctx.path("."),
112-
nonhermetic_root_bazel_workspace_dir = repository_ctx.workspace_root,
119+
nonhermetic_root_bazel_workspace_dir = nonhermetic_root_bazel_workspace_dir,
113120
paths_to_track_file = paths_to_track_file,
114121
warnings_output_file = warnings_output_file,
115122
skip_cargo_lockfile_overwrite = repository_ctx.attr.skip_cargo_lockfile_overwrite,

crate_universe/private/splicing_utils.bzl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,14 @@ def splice_workspace_manifest(
188188
if not spliced_metadata.exists:
189189
fail("Metadata file does not exist: " + str(spliced_metadata))
190190

191+
extra_paths_to_track_path = repository_ctx.path(output_dir.get_child("extra_paths_to_track"))
192+
if extra_paths_to_track_path.exists:
193+
extra_paths_to_track = repository_ctx.read(extra_paths_to_track_path).split("\n")
194+
else:
195+
extra_paths_to_track = []
196+
191197
return struct(
192198
metadata = spliced_metadata,
193199
cargo_lock = spliced_lockfile,
200+
extra_paths_to_track = extra_paths_to_track,
194201
)

crate_universe/private/srcs.bzl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ CARGO_BAZEL_SRCS = [
2828
Label("//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh"),
2929
Label("//crate_universe:src/metadata/dependency.rs"),
3030
Label("//crate_universe:src/metadata/metadata_annotation.rs"),
31-
Label("//crate_universe:src/metadata/workspace_discoverer.rs"),
3231
Label("//crate_universe:src/rendering.rs"),
3332
Label("//crate_universe:src/rendering/template_engine.rs"),
3433
Label("//crate_universe:src/rendering/templates/module_bzl.j2"),

crate_universe/src/cli/splice.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ use std::path::PathBuf;
55
use anyhow::Context;
66
use camino::Utf8PathBuf;
77
use clap::Parser;
8+
use itertools::Itertools;
89

910
use crate::cli::Result;
1011
use crate::config::Config;
1112
use crate::metadata::{
1213
write_metadata, Cargo, CargoUpdateRequest, Generator, MetadataGenerator, TreeResolver,
1314
};
14-
use crate::splicing::{generate_lockfile, Splicer, SplicingManifest, WorkspaceMetadata};
15+
use crate::splicing::{
16+
generate_lockfile, Splicer, SplicerKind, SplicingManifest, WorkspaceMetadata,
17+
};
1518

1619
/// Command line options for the `splice` subcommand
1720
#[derive(Parser, Debug)]
@@ -81,13 +84,14 @@ pub fn splice(opt: SpliceOptions) -> Result<()> {
8184
};
8285

8386
// Generate a splicer for creating a Cargo workspace manifest
84-
let splicer = Splicer::new(splicing_dir, splicing_manifest)?;
87+
let splicer = Splicer::new(splicing_dir.clone(), splicing_manifest)?;
88+
let prepared_splicer = splicer.prepare()?;
8589

8690
let cargo = Cargo::new(opt.cargo, opt.rustc.clone());
8791

8892
// Splice together the manifest
89-
let manifest_path = splicer
90-
.splice_workspace()
93+
let manifest_path = prepared_splicer
94+
.splice(&splicing_dir)
9195
.with_context(|| format!("Failed to splice workspace {}", opt.repository_name))?;
9296

9397
// Generate a lockfile
@@ -121,8 +125,8 @@ pub fn splice(opt: SpliceOptions) -> Result<()> {
121125

122126
// Write metadata to the workspace for future reuse
123127
let (cargo_metadata, _) = Generator::new()
124-
.with_cargo(cargo)
125-
.with_rustc(opt.rustc)
128+
.with_cargo(cargo.clone())
129+
.with_rustc(opt.rustc.clone())
126130
.generate(manifest_path.as_path_buf())
127131
.context("Failed to generate cargo metadata")?;
128132

@@ -147,5 +151,22 @@ pub fn splice(opt: SpliceOptions) -> Result<()> {
147151
std::fs::copy(cargo_lockfile_path, output_dir.join("Cargo.lock"))
148152
.context("Failed to copy lockfile")?;
149153

154+
if let SplicerKind::Workspace { path, .. } = prepared_splicer {
155+
let metadata = cargo.metadata_command_with_options(
156+
path.as_std_path(),
157+
vec![String::from("--no-deps")],
158+
)?.exec().with_context(|| {
159+
format!(
160+
"Error spawning cargo in child process to compute crate paths for workspace '{}'",
161+
path
162+
)
163+
})?;
164+
let contents = metadata
165+
.packages
166+
.iter()
167+
.map(|package| package.manifest_path.clone())
168+
.join("\n");
169+
std::fs::write(opt.output_dir.join("extra_paths_to_track"), contents)?;
170+
}
150171
Ok(())
151172
}

crate_universe/src/metadata.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ mod cargo_bin;
44
mod cargo_tree_resolver;
55
mod dependency;
66
mod metadata_annotation;
7-
mod workspace_discoverer;
87

98
use std::env;
109
use std::fs;
@@ -21,7 +20,6 @@ pub(crate) use self::cargo_bin::*;
2120
pub(crate) use self::cargo_tree_resolver::*;
2221
pub(crate) use self::dependency::*;
2322
pub(crate) use self::metadata_annotation::*;
24-
pub(crate) use self::workspace_discoverer::*;
2523

2624
// TODO: This should also return a set of [crate-index::IndexConfig]s for packages in metadata.packages
2725
/// A Trait for generating metadata (`cargo metadata` output and a lock file) from a Cargo manifest.

0 commit comments

Comments
 (0)