Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pavex_test_runner = { path = "compiler/pavex_test_runner", version = "0.2.7" }
pavexc = { path = "compiler/pavexc", version = "0.2.10" }
pavexc_attr_parser = { path = "compiler/pavexc_attr_parser", version = "0.2.10" }
pavexc_cli_client = { path = "compiler/pavexc_cli_client", version = "0.2.10" }
pavexc_rustdoc_cache = { path = "compiler/pavexc_rustdoc_cache", version = "0.2.10" }
persist_if_changed = { path = "compiler/persist_if_changed", version = "0.2.10" }
# Our own fork of `rustdoc-types` to minimise (de)ser overhead.
rustdoc-types = { path = "compiler/pavex_rustdoc_types", version = "0.2.10", features = ["rustc-hash"], package = "pavex_rustdoc_types" }
Expand Down
14 changes: 4 additions & 10 deletions compiler/pavexc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ license.workspace = true
clippy = { large_enum_variant = "allow", result_large_err = "allow" }

[build-dependencies]
vergen-gitcl = { workspace = true }
xxhash-rust = { workspace = true, features = ["xxh64"] }
globwalk = { workspace = true }
anyhow = { workspace = true }
toml = { workspace = true }

[features]
# Enable additional debug assertions to ensure correctness
Expand All @@ -24,6 +26,7 @@ debug_assertions = []
[dependencies]
pavex = { workspace = true }
pavexc_attr_parser = { path = "../pavexc_attr_parser", version = "=0.2.10" }
pavexc_rustdoc_cache = { path = "../pavexc_rustdoc_cache", version = "=0.2.10" }
pavex_bp_schema = { path = "../pavex_bp_schema", version = "=0.2.10" }
pavex_cli_shell = { path = "../pavex_cli_shell", version = "=0.2.10" }
pavex_cli_diagnostic = { path = "../pavex_cli_diagnostic", version = "=0.2.10" }
Expand Down Expand Up @@ -63,19 +66,10 @@ persist_if_changed = { workspace = true }
matchit = { workspace = true }
relative-path = { workspace = true }
camino = { workspace = true }
xxhash-rust = { workspace = true, features = ["xxh64"] }
rustc-hash = { workspace = true }
globwalk = { workspace = true }
rkyv = { workspace = true }

# Sqlite cache
xdg-home = { workspace = true }
rusqlite = { workspace = true, features = ["bundled"] }
r2d2_sqlite = { workspace = true }
r2d2 = { workspace = true }
bincode = { workspace = true, features = ["serde"] }
rayon = { workspace = true }
num_cpus = { workspace = true }
px_workspace_hack = { version = "0.1", path = "../../px_workspace_hack" }

[dev-dependencies]
Expand Down
116 changes: 107 additions & 9 deletions compiler/pavexc/build.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,111 @@
use anyhow::Result;
use vergen_gitcl::{Emitter, GitclBuilder};
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};

pub fn main() -> Result<()> {
Emitter::default()
.add_instructions(
&GitclBuilder::default()
.describe(true, false, None)
.build()?,
)?
.emit()?;
// Compute checksum of pavexc_rustdoc_cache and its local dependencies.
// This checksum is used as part of the cache fingerprint to ensure
// the cache invalidates when the caching logic or serialized types change.
let base_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let cache_crate_path = base_path.join("pavexc_rustdoc_cache");

// Find all local crates that pavexc_rustdoc_cache depends on (transitively)
let crates_to_checksum = collect_local_dependencies(&cache_crate_path)?;

let mut combined_hasher = xxhash_rust::xxh64::Xxh64::new(24);
for crate_path in &crates_to_checksum {
let checksum = checksum_directory(crate_path)?;
combined_hasher.update(&checksum.to_le_bytes());

// Rerun if any of these crates change
println!("cargo::rerun-if-changed={}/src", crate_path.display());
println!("cargo::rerun-if-changed={}/Cargo.toml", crate_path.display());
}

let checksum = combined_hasher.digest();
println!("cargo::rustc-env=RUSTDOC_CACHE_SOURCE_HASH={checksum:x}");

Ok(())
}

/// Collect all local path dependencies of a crate, including the crate itself.
/// This is done recursively to capture transitive local dependencies.
fn collect_local_dependencies(crate_path: &Path) -> Result<BTreeSet<PathBuf>> {
let mut visited = BTreeSet::new();
let mut to_visit = vec![crate_path.to_path_buf()];

while let Some(current) = to_visit.pop() {
let canonical = current
.canonicalize()
.with_context(|| format!("Failed to canonicalize path: {}", current.display()))?;

if !visited.insert(canonical.clone()) {
continue;
}

// Parse Cargo.toml to find path dependencies
let cargo_toml_path = canonical.join("Cargo.toml");
let cargo_toml_content = std::fs::read_to_string(&cargo_toml_path)
.with_context(|| format!("Failed to read {}", cargo_toml_path.display()))?;

let cargo_toml: toml::Table = toml::from_str(&cargo_toml_content)
.with_context(|| format!("Failed to parse {}", cargo_toml_path.display()))?;

// Check [dependencies] section for path dependencies
if let Some(toml::Value::Table(deps)) = cargo_toml.get("dependencies") {
for (_name, value) in deps {
if let Some(path) = value.get("path").and_then(|p| p.as_str()) {
let dep_path = canonical.join(path);
if dep_path.exists() {
to_visit.push(dep_path);
}
}
}
}
}

Ok(visited)
}

/// Checksum the contents of a crate directory.
fn checksum_directory(root_path: &Path) -> Result<u64> {
let paths = get_file_paths(root_path)?;

let mut hasher = xxhash_rust::xxh64::Xxh64::new(24);
for path in paths {
let contents = std::fs::read(&path)
.with_context(|| format!("Failed to read file at `{}`", path.display()))?;
hasher.update(&contents);
// Include the file path in the hash to detect renames
if let Ok(relative) = path.strip_prefix(root_path) {
hasher.update(relative.to_string_lossy().as_bytes());
}
}
Ok(hasher.digest())
}

/// Get all source files in a crate directory.
fn get_file_paths(root_dir: &Path) -> Result<BTreeSet<PathBuf>> {
let root_dir = root_dir
.canonicalize()
.context("Failed to canonicalize the path to the root directory")?;

let patterns = vec!["src/**/*.rs", "Cargo.toml"];

let glob_walker = globwalk::GlobWalkerBuilder::from_patterns(&root_dir, &patterns).build()?;

let included_files: BTreeSet<PathBuf> = glob_walker
.into_iter()
.filter_map(|entry| {
let Ok(entry) = entry else {
return None;
};
if !entry.file_type().is_file() {
return None;
}
Some(entry.into_path())
})
.collect();
Ok(included_files)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ use crate::{
ResolvedType,
},
rustdoc::{
AnnotationCoordinates, Crate, CrateCollection, GlobalItemId, ImplInfo, RustdocKindExt,
AnnotationCoordinates, Crate, CrateCollection, ExternalReExportsExt, GlobalItemId,
ImplInfo, RustdocKindExt,
},
};
use pavex_bp_schema::{CloningPolicy, Lifecycle, Lint, LintSetting};
Expand Down
2 changes: 1 addition & 1 deletion compiler/pavexc/src/rustdoc/annotations/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use pavex_cli_diagnostic::{AnnotatedSource, CompilerDiagnostic, HelpWithSnippet}
use pavexc_attr_parser::{AnnotationKind, errors::AttributeParserError};
use rustdoc_types::Item;

use super::items::IdConflict;
use pavexc_rustdoc_cache::IdConflict;

pub(crate) fn invalid_diagnostic_attribute(
e: AttributeParserError,
Expand Down
3 changes: 1 addition & 2 deletions compiler/pavexc/src/rustdoc/annotations/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
mod diagnostic;
mod items;
mod parser;
mod queue;

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

pub(crate) use diagnostic::invalid_diagnostic_attribute;
pub use items::{AnnotatedItem, AnnotatedItems, ImplInfo};
pub use pavexc_rustdoc_cache::{AnnotatedItem, AnnotatedItems, ImplInfo};
pub(crate) use parser::parse_pavex_attributes;
pub(crate) use queue::QueueItem;

Expand Down
Loading
Loading