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
14 changes: 0 additions & 14 deletions crates/rspack_cacheable/src/with/as_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,3 @@ where
AsStringConverter::from_str(field.as_str())
}
}

// for pathbuf
use std::path::PathBuf;
impl AsStringConverter for PathBuf {
fn to_string(&self) -> Result<String> {
Ok(self.to_string_lossy().to_string())
}
fn from_str(s: &str) -> Result<Self>
where
Self: Sized,
{
Ok(PathBuf::from(s))
}
}
31 changes: 30 additions & 1 deletion crates/rspack_cacheable/src/with/custom.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use std::hash::Hash;

use rkyv::{
Archive, Deserialize, Place, Serialize,
de::Pooling,
rancor::Fallible,
ser::Sharing,
with::{ArchiveWith, DeserializeWith, SerializeWith},
};
use rspack_cacheable_macros::enable_cacheable as cacheable;

use crate::{ContextGuard, Error, Result, cacheable};
use crate::{ContextGuard, Error, Result};

/// A trait for writing custom serialization and deserialization.
///
Expand All @@ -27,6 +30,32 @@ pub struct Custom;
#[cacheable(crate=crate)]
pub struct DataBox<T: Archive>(T);

// impl hashable for ArchivedDataBox
impl<T> Hash for ArchivedDataBox<T>
where
T: Archive,
T::Archived: Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T> PartialEq for ArchivedDataBox<T>
where
T: Archive,
T::Archived: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for ArchivedDataBox<T>
where
T: Archive,
T::Archived: Eq,
{
}

pub struct CustomResolver<A: Archive> {
resolver: DataBoxResolver<A>,
value: DataBox<A>,
Expand Down
24 changes: 13 additions & 11 deletions crates/rspack_collections/src/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use std::{
use dashmap::{DashMap, DashSet};
use hashlink::{LinkedHashMap, LinkedHashSet};
use indexmap::{IndexMap, IndexSet};
use rspack_cacheable::{cacheable, with, with::AsPreset};
use rspack_cacheable::{
ContextGuard, Error as CacheableError, cacheable,
utils::PortableString,
with::{Custom, CustomConverter},
};
use serde::Serialize;
use ustr::Ustr;

Expand All @@ -34,10 +38,10 @@ pub type IdentifierIndexSet = IndexSet<Identifier, BuildHasherDefault<Identifier
pub type IdentifierDashSet = DashSet<Identifier, BuildHasherDefault<IdentifierHasher>>;
pub type IdentifierLinkedSet = LinkedHashSet<Identifier, BuildHasherDefault<IdentifierHasher>>;

#[cacheable(hashable)]
#[cacheable(with=Custom, hashable)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
#[cfg_attr(allocative, derive(allocative::Allocative))]
pub struct Identifier(#[cacheable(with=AsPreset)] Ustr);
pub struct Identifier(Ustr);

impl Deref for Identifier {
type Target = Ustr;
Expand Down Expand Up @@ -99,14 +103,12 @@ impl fmt::Display for Identifier {
}

// for Identifier
impl with::AsRefStrConverter for Identifier {
fn as_str(&self) -> &str {
self.0.as_str()
impl CustomConverter for Identifier {
type Target = PortableString;
fn serialize(&self, guard: &ContextGuard) -> Result<Self::Target, CacheableError> {
Ok(PortableString::new(self.as_str(), guard.project_root()))
}
fn from_str(s: &str) -> Self
where
Self: Sized,
{
s.into()
fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result<Self, CacheableError> {
Ok(Self::from(data.into_path_string(guard.project_root())))
}
}
25 changes: 22 additions & 3 deletions crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_hash::FxHashSet as HashSet;

use self::helper::{Helper, is_node_package_path};
use super::{
codec::CacheCodec,
snapshot::{Snapshot, SnapshotOptions},
storage::Storage,
};
Expand Down Expand Up @@ -40,6 +41,7 @@ impl BuildDeps {
snapshot_options: &SnapshotOptions,
fs: Arc<dyn ReadableFileSystem>,
storage: Arc<dyn Storage>,
codec: Arc<CacheCodec>,
) -> Self {
Self {
added: Default::default(),
Expand All @@ -49,6 +51,7 @@ impl BuildDeps {
snapshot_options.clone(),
fs.clone(),
storage.clone(),
codec,
),
storage,
fs,
Expand Down Expand Up @@ -112,7 +115,10 @@ mod test {
use rspack_fs::{MemoryFileSystem, WritableFileSystem};
use rspack_storage::Storage;

use super::{super::storage::MemoryStorage, BuildDeps, SCOPE, SnapshotOptions};
use super::{
super::{codec::CacheCodec, storage::MemoryStorage},
BuildDeps, SCOPE, SnapshotOptions,
};
#[tokio::test]
async fn build_dependencies_test() {
let fs = Arc::new(MemoryFileSystem::default());
Expand Down Expand Up @@ -151,13 +157,26 @@ mod test {
let options = vec![PathBuf::from("/index.js"), PathBuf::from("/configs")];
let snapshot_options = SnapshotOptions::default();
let storage = Arc::new(MemoryStorage::default());
let mut build_deps = BuildDeps::new(&options, &snapshot_options, fs.clone(), storage.clone());
let codec = Arc::new(CacheCodec::new(None));
let mut build_deps = BuildDeps::new(
&options,
&snapshot_options,
fs.clone(),
storage.clone(),
codec.clone(),
);
let warnings = build_deps.add(vec![].into_iter()).await;
assert_eq!(warnings.len(), 1);
let data = storage.load(SCOPE).await.expect("should load success");
assert_eq!(data.len(), 9);

let mut build_deps = BuildDeps::new(&options, &snapshot_options, fs.clone(), storage.clone());
let mut build_deps = BuildDeps::new(
&options,
&snapshot_options,
fs.clone(),
storage.clone(),
codec,
);
fs.write("/b.js".into(), r#"require("./c")"#.as_bytes())
.await
.unwrap();
Expand Down
8 changes: 0 additions & 8 deletions crates/rspack_core/src/cache/persistent/cacheable_context.rs

This file was deleted.

64 changes: 64 additions & 0 deletions crates/rspack_core/src/cache/persistent/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::path::Path;

use rspack_cacheable::{
__private::rkyv::{Archive, Deserialize, Serialize, bytecheck::CheckBytes},
Deserializer, Serializer, Validator, from_bytes, to_bytes,
};
use rspack_error::Result;
use rspack_paths::Utf8PathBuf;

/// Internal cacheable context for serialization
#[derive(Debug, Clone)]
struct Context {
project_path: Option<Utf8PathBuf>,
}

impl rspack_cacheable::CacheableContext for Context {
fn project_root(&self) -> Option<&Path> {
self.project_path.as_ref().map(|p| p.as_std_path())
}
}

/// Cache codec for encoding and decoding cacheable data
///
/// This struct encapsulates the serialization and deserialization logic,
/// automatically passing the project context to rspack_cacheable's to_bytes and from_bytes.
///
/// # Example
///
/// ```ignore
/// let codec = CacheCodec::new(project_path);
///
/// // Encode data to bytes
/// let bytes = codec.encode(&my_data)?;
///
/// // Decode bytes back to data
/// let my_data: MyType = codec.decode(&bytes)?;
/// ```
#[derive(Debug, Clone)]
pub struct CacheCodec {
context: Context,
}

impl CacheCodec {
pub fn new(project_path: Option<Utf8PathBuf>) -> Self {
Self {
context: Context { project_path },
}
}

pub fn encode<T>(&self, data: &T) -> Result<Vec<u8>>
where
T: for<'a> Serialize<Serializer<'a>>,
{
to_bytes(data, &self.context).map_err(|e| rspack_error::error!(e.to_string()))
}

pub fn decode<T>(&self, bytes: &[u8]) -> Result<T>
where
T: Archive,
T::Archived: for<'a> CheckBytes<Validator<'a>> + Deserialize<T, Deserializer>,
{
from_bytes(bytes, &self.context).map_err(|e| rspack_error::error!(e.to_string()))
}
}
41 changes: 29 additions & 12 deletions crates/rspack_core/src/cache/persistent/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod build_dependencies;
mod cacheable_context;
pub mod codec;
pub mod occasion;
pub mod snapshot;
pub mod storage;
Expand All @@ -9,13 +9,18 @@ use std::{
sync::Arc,
};

pub use cacheable_context::CacheableContext;
use rspack_cacheable::{
cacheable,
utils::PortablePath,
with::{As, AsVec},
};
use rspack_fs::{IntermediateFileSystem, ReadableFileSystem};
use rspack_paths::ArcPathSet;
use rspack_workspace::rspack_pkg_version;

use self::{
build_dependencies::{BuildDeps, BuildDepsOptions},
codec::CacheCodec,
occasion::{MakeOccasion, MetaOccasion},
snapshot::{Snapshot, SnapshotOptions},
storage::{Storage, StorageOptions, create_storage},
Expand All @@ -26,8 +31,10 @@ use crate::{
compilation::build_module_graph::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState},
};

#[cacheable]
#[derive(Debug, Clone, Hash)]
pub struct PersistentCacheOptions {
#[cacheable(with=AsVec<As<PortablePath>>)]
pub build_dependencies: BuildDepsOptions,
pub version: String,
pub snapshot: SnapshotOptions,
Expand All @@ -40,10 +47,10 @@ pub struct PersistentCache {
initialized: bool,
build_deps: BuildDeps,
snapshot: Snapshot,
storage: Arc<dyn Storage>,
make_occasion: MakeOccasion,
meta_occasion: MetaOccasion,
async_mode: bool,
storage: Arc<dyn Storage>,
// TODO replace to logger and output warnings directly.
warnings: Vec<String>,
}
Expand All @@ -57,33 +64,43 @@ impl PersistentCache {
intermediate_filesystem: Arc<dyn IntermediateFileSystem>,
) -> Self {
let async_mode = compiler_options.mode.is_development();
let codec = Arc::new(CacheCodec::new(None));
// use codec.encode to transform the absolute path in option,
// it will ensure that same project in different directory have the same version.
let option_bytes = codec
.encode(option)
.expect("should persistent cache options can be serialized");
let version = {
let mut hasher = DefaultHasher::new();
compiler_path.hash(&mut hasher);
option.hash(&mut hasher);
option_bytes.hash(&mut hasher);
rspack_pkg_version!().hash(&mut hasher);
compiler_options.name.hash(&mut hasher);
compiler_options.mode.hash(&mut hasher);
hex::encode(hasher.finish().to_ne_bytes())
};
let storage = create_storage(option.storage.clone(), version, intermediate_filesystem);
let context = Arc::new(CacheableContext);
let make_occasion = MakeOccasion::new(storage.clone(), context);
let meta_occasion = MetaOccasion::new(storage.clone());

Self {
initialized: false,
build_deps: BuildDeps::new(
&option.build_dependencies,
&option.snapshot,
input_filesystem.clone(),
storage.clone(),
codec.clone(),
),
snapshot: Snapshot::new(option.snapshot.clone(), input_filesystem, storage.clone()),
storage,
make_occasion,
meta_occasion,
async_mode,
snapshot: Snapshot::new(
option.snapshot.clone(),
input_filesystem,
storage.clone(),
codec.clone(),
),
make_occasion: MakeOccasion::new(storage.clone(), codec.clone()),
meta_occasion: MetaOccasion::new(storage.clone(), codec),
warnings: Default::default(),
async_mode,
storage,
}
}

Expand Down
12 changes: 6 additions & 6 deletions crates/rspack_core/src/cache/persistent/occasion/make/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rspack_collections::IdentifierSet;
use rspack_error::Result;
use rustc_hash::FxHashSet;

use super::super::{Storage, cacheable_context::CacheableContext};
use super::super::{Storage, codec::CacheCodec};
use crate::{
FactorizeInfo,
compilation::build_module_graph::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState},
Expand All @@ -18,13 +18,13 @@ use crate::{
/// Make Occasion is used to save MakeArtifact
#[derive(Debug)]
pub struct MakeOccasion {
context: Arc<CacheableContext>,
codec: Arc<CacheCodec>,
storage: Arc<dyn Storage>,
}

impl MakeOccasion {
pub fn new(storage: Arc<dyn Storage>, context: Arc<CacheableContext>) -> Self {
Self { storage, context }
pub fn new(storage: Arc<dyn Storage>, codec: Arc<CacheCodec>) -> Self {
Self { storage, codec }
}

#[tracing::instrument(name = "Cache::Occasion::Make::save", skip_all)]
Expand Down Expand Up @@ -65,14 +65,14 @@ impl MakeOccasion {
affected_modules.removed(),
&need_update_modules,
&self.storage,
&self.context,
&self.codec,
);
}

#[tracing::instrument(name = "Cache::Occasion::Make::recovery", skip_all)]
pub async fn recovery(&self) -> Result<BuildModuleGraphArtifact> {
let (mg, module_to_lazy_make, entry_dependencies) =
module_graph::recovery_module_graph(&self.storage, &self.context).await?;
module_graph::recovery_module_graph(&self.storage, &self.codec).await?;

// regenerate statistical data
// recovery make_failed_module
Expand Down
Loading
Loading