Skip to content

Commit 1b1a8ac

Browse files
Extract JSON computation and caching into a separate crate
1 parent c5aa5a5 commit 1b1a8ac

File tree

30 files changed

+2065
-1591
lines changed

30 files changed

+2065
-1591
lines changed

Cargo.lock

Lines changed: 36 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pavex_test_runner = { path = "compiler/pavex_test_runner", version = "0.2.7" }
3535
pavexc = { path = "compiler/pavexc", version = "0.2.10" }
3636
pavexc_attr_parser = { path = "compiler/pavexc_attr_parser", version = "0.2.10" }
3737
pavexc_cli_client = { path = "compiler/pavexc_cli_client", version = "0.2.10" }
38+
pavexc_rustdoc_cache = { path = "compiler/pavexc_rustdoc_cache", version = "0.2.10" }
3839
persist_if_changed = { path = "compiler/persist_if_changed", version = "0.2.10" }
3940
# Our own fork of `rustdoc-types` to minimise (de)ser overhead.
4041
rustdoc-types = { path = "compiler/pavex_rustdoc_types", version = "0.2.10", features = ["rustc-hash"], package = "pavex_rustdoc_types" }

compiler/pavexc/Cargo.toml

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ license.workspace = true
1111
clippy = { large_enum_variant = "allow", result_large_err = "allow" }
1212

1313
[build-dependencies]
14-
vergen-gitcl = { workspace = true }
14+
xxhash-rust = { workspace = true, features = ["xxh64"] }
15+
globwalk = { workspace = true }
1516
anyhow = { workspace = true }
17+
toml = { workspace = true }
1618

1719
[features]
1820
# Enable additional debug assertions to ensure correctness
@@ -24,6 +26,7 @@ debug_assertions = []
2426
[dependencies]
2527
pavex = { workspace = true }
2628
pavexc_attr_parser = { path = "../pavexc_attr_parser", version = "=0.2.10" }
29+
pavexc_rustdoc_cache = { path = "../pavexc_rustdoc_cache", version = "=0.2.10" }
2730
pavex_bp_schema = { path = "../pavex_bp_schema", version = "=0.2.10" }
2831
pavex_cli_shell = { path = "../pavex_cli_shell", version = "=0.2.10" }
2932
pavex_cli_diagnostic = { path = "../pavex_cli_diagnostic", version = "=0.2.10" }
@@ -63,19 +66,10 @@ persist_if_changed = { workspace = true }
6366
matchit = { workspace = true }
6467
relative-path = { workspace = true }
6568
camino = { workspace = true }
66-
xxhash-rust = { workspace = true, features = ["xxh64"] }
6769
rustc-hash = { workspace = true }
68-
globwalk = { workspace = true }
6970
rkyv = { workspace = true }
70-
71-
# Sqlite cache
72-
xdg-home = { workspace = true }
73-
rusqlite = { workspace = true, features = ["bundled"] }
74-
r2d2_sqlite = { workspace = true }
75-
r2d2 = { workspace = true }
7671
bincode = { workspace = true, features = ["serde"] }
7772
rayon = { workspace = true }
78-
num_cpus = { workspace = true }
7973
px_workspace_hack = { version = "0.1", path = "../../px_workspace_hack" }
8074

8175
[dev-dependencies]

compiler/pavexc/build.rs

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,111 @@
1-
use anyhow::Result;
2-
use vergen_gitcl::{Emitter, GitclBuilder};
1+
use std::collections::BTreeSet;
2+
use std::path::{Path, PathBuf};
3+
4+
use anyhow::{Context, Result};
35

46
pub fn main() -> Result<()> {
5-
Emitter::default()
6-
.add_instructions(
7-
&GitclBuilder::default()
8-
.describe(true, false, None)
9-
.build()?,
10-
)?
11-
.emit()?;
7+
// Compute checksum of pavexc_rustdoc_cache and its local dependencies.
8+
// This checksum is used as part of the cache fingerprint to ensure
9+
// the cache invalidates when the caching logic or serialized types change.
10+
let base_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
11+
let cache_crate_path = base_path.join("pavexc_rustdoc_cache");
12+
13+
// Find all local crates that pavexc_rustdoc_cache depends on (transitively)
14+
let crates_to_checksum = collect_local_dependencies(&cache_crate_path)?;
15+
16+
let mut combined_hasher = xxhash_rust::xxh64::Xxh64::new(24);
17+
for crate_path in &crates_to_checksum {
18+
let checksum = checksum_directory(crate_path)?;
19+
combined_hasher.update(&checksum.to_le_bytes());
20+
21+
// Rerun if any of these crates change
22+
println!("cargo::rerun-if-changed={}/src", crate_path.display());
23+
println!("cargo::rerun-if-changed={}/Cargo.toml", crate_path.display());
24+
}
25+
26+
let checksum = combined_hasher.digest();
27+
println!("cargo::rustc-env=RUSTDOC_CACHE_SOURCE_HASH={checksum:x}");
28+
1229
Ok(())
1330
}
31+
32+
/// Collect all local path dependencies of a crate, including the crate itself.
33+
/// This is done recursively to capture transitive local dependencies.
34+
fn collect_local_dependencies(crate_path: &Path) -> Result<BTreeSet<PathBuf>> {
35+
let mut visited = BTreeSet::new();
36+
let mut to_visit = vec![crate_path.to_path_buf()];
37+
38+
while let Some(current) = to_visit.pop() {
39+
let canonical = current
40+
.canonicalize()
41+
.with_context(|| format!("Failed to canonicalize path: {}", current.display()))?;
42+
43+
if !visited.insert(canonical.clone()) {
44+
continue;
45+
}
46+
47+
// Parse Cargo.toml to find path dependencies
48+
let cargo_toml_path = canonical.join("Cargo.toml");
49+
let cargo_toml_content = std::fs::read_to_string(&cargo_toml_path)
50+
.with_context(|| format!("Failed to read {}", cargo_toml_path.display()))?;
51+
52+
let cargo_toml: toml::Table = toml::from_str(&cargo_toml_content)
53+
.with_context(|| format!("Failed to parse {}", cargo_toml_path.display()))?;
54+
55+
// Check [dependencies] section for path dependencies
56+
if let Some(toml::Value::Table(deps)) = cargo_toml.get("dependencies") {
57+
for (_name, value) in deps {
58+
if let Some(path) = value.get("path").and_then(|p| p.as_str()) {
59+
let dep_path = canonical.join(path);
60+
if dep_path.exists() {
61+
to_visit.push(dep_path);
62+
}
63+
}
64+
}
65+
}
66+
}
67+
68+
Ok(visited)
69+
}
70+
71+
/// Checksum the contents of a crate directory.
72+
fn checksum_directory(root_path: &Path) -> Result<u64> {
73+
let paths = get_file_paths(root_path)?;
74+
75+
let mut hasher = xxhash_rust::xxh64::Xxh64::new(24);
76+
for path in paths {
77+
let contents = std::fs::read(&path)
78+
.with_context(|| format!("Failed to read file at `{}`", path.display()))?;
79+
hasher.update(&contents);
80+
// Include the file path in the hash to detect renames
81+
if let Ok(relative) = path.strip_prefix(root_path) {
82+
hasher.update(relative.to_string_lossy().as_bytes());
83+
}
84+
}
85+
Ok(hasher.digest())
86+
}
87+
88+
/// Get all source files in a crate directory.
89+
fn get_file_paths(root_dir: &Path) -> Result<BTreeSet<PathBuf>> {
90+
let root_dir = root_dir
91+
.canonicalize()
92+
.context("Failed to canonicalize the path to the root directory")?;
93+
94+
let patterns = vec!["src/**/*.rs", "Cargo.toml"];
95+
96+
let glob_walker = globwalk::GlobWalkerBuilder::from_patterns(&root_dir, &patterns).build()?;
97+
98+
let included_files: BTreeSet<PathBuf> = glob_walker
99+
.into_iter()
100+
.filter_map(|entry| {
101+
let Ok(entry) = entry else {
102+
return None;
103+
};
104+
if !entry.file_type().is_file() {
105+
return None;
106+
}
107+
Some(entry.into_path())
108+
})
109+
.collect();
110+
Ok(included_files)
111+
}

compiler/pavexc/src/compiler/analyses/user_components/annotations/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ use crate::{
3636
ResolvedType,
3737
},
3838
rustdoc::{
39-
AnnotationCoordinates, Crate, CrateCollection, GlobalItemId, ImplInfo, RustdocKindExt,
39+
AnnotationCoordinates, Crate, CrateCollection, ExternalReExportsExt, GlobalItemId,
40+
ImplInfo, RustdocKindExt,
4041
},
4142
};
4243
use pavex_bp_schema::{CloningPolicy, Lifecycle, Lint, LintSetting};

compiler/pavexc/src/rustdoc/annotations/diagnostic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use pavex_cli_diagnostic::{AnnotatedSource, CompilerDiagnostic, HelpWithSnippet}
66
use pavexc_attr_parser::{AnnotationKind, errors::AttributeParserError};
77
use rustdoc_types::Item;
88

9-
use super::items::IdConflict;
9+
use pavexc_rustdoc_cache::IdConflict;
1010

1111
pub(crate) fn invalid_diagnostic_attribute(
1212
e: AttributeParserError,

compiler/pavexc/src/rustdoc/annotations/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
mod diagnostic;
2-
mod items;
32
mod parser;
43
mod queue;
54

@@ -12,7 +11,7 @@ use rustdoc_types::{Enum, ItemEnum, Struct, Trait};
1211
use std::collections::BTreeSet;
1312

1413
pub(crate) use diagnostic::invalid_diagnostic_attribute;
15-
pub use items::{AnnotatedItem, AnnotatedItems, ImplInfo};
14+
pub use pavexc_rustdoc_cache::{AnnotatedItem, AnnotatedItems, ImplInfo};
1615
pub(crate) use parser::parse_pavex_attributes;
1716
pub(crate) use queue::QueueItem;
1817

0 commit comments

Comments
 (0)