use std::path::{Path, PathBuf};
use std::collections::HashSet;
use anyhow::Result;
use super::detector::ChangeDetector;
use crate::core::cache::BuildCache;
pub struct IncrementalCompiler {
cache: BuildCache,
change_detector: ChangeDetector,
}
impl IncrementalCompiler {
pub fn new(project_root: PathBuf) -> Result<Self> {
let cache = BuildCache::load(&project_root)?;
let change_detector = ChangeDetector::new();
Ok(Self {
cache,
change_detector,
})
}
pub fn compile_incremental<F>(
&mut self,
sources: Vec<PathBuf>,
mut compile_fn: F,
) -> Result<CompilationResult>
where
F: FnMut(&Path) -> Result<CompileOutput>,
{
let changes = self.change_detector.detect_changes();
let recompile_set = self.change_detector.get_recompilation_set(&changes);
let stale = self.cache.get_stale_sources(&sources);
let mut to_compile: HashSet<_> = recompile_set.iter().chain(stale.iter()).collect();
to_compile.retain(|p| sources.contains(p));
let mut outputs = Vec::new();
let mut failed = Vec::new();
for source in &sources {
if to_compile.contains(source) {
match compile_fn(source) {
Ok(output) => {
if let Ok(modified) = std::fs::metadata(source).and_then(|m| m.modified()) {
let checksum = BuildCache::calculate_checksum(source)?;
let entry = crate::core::cache::CacheEntry {
source_path: source.clone(),
source_modified: modified,
output_path: output.output_path.clone(),
dependencies: output.dependencies.clone(),
checksum,
success: true,
};
self.cache.update_entry(source.clone(), entry);
}
outputs.push(output);
}
Err(e) => {
failed.push((source.clone(), e));
}
}
} else {
if let Some(entry) = self.cache.entries.get(source) {
outputs.push(CompileOutput {
source_path: source.clone(),
output_path: entry.output_path.clone(),
dependencies: entry.dependencies.clone(),
success: true,
});
}
}
}
let _ = self.cache.save();
Ok(CompilationResult {
compiled: to_compile.len(),
cached: sources.len() - to_compile.len(),
outputs,
failed,
})
}
pub fn compile_full<F>(&mut self, sources: Vec<PathBuf>, mut compile_fn: F) -> Result<CompilationResult>
where
F: FnMut(&Path) -> Result<CompileOutput>,
{
let mut outputs = Vec::new();
let mut failed = Vec::new();
for source in &sources {
match compile_fn(source) {
Ok(output) => {
if let Ok(modified) = std::fs::metadata(source).and_then(|m| m.modified()) {
let checksum = BuildCache::calculate_checksum(source)?;
let entry = crate::core::cache::CacheEntry {
source_path: source.clone(),
source_modified: modified,
output_path: output.output_path.clone(),
dependencies: output.dependencies.clone(),
checksum,
success: true,
};
self.cache.update_entry(source.clone(), entry);
}
outputs.push(output);
}
Err(e) => {
failed.push((source.clone(), e));
}
}
}
let _ = self.cache.save();
Ok(CompilationResult {
compiled: sources.len(),
cached: 0,
outputs,
failed,
})
}
pub fn clean(&mut self) {
self.cache.clean();
}
}
#[derive(Debug)]
pub struct CompilationResult {
pub compiled: usize,
pub cached: usize,
pub outputs: Vec<CompileOutput>,
pub failed: Vec<(PathBuf, anyhow::Error)>,
}
#[derive(Debug, Clone)]
pub struct CompileOutput {
pub source_path: PathBuf,
pub output_path: PathBuf,
pub dependencies: Vec<PathBuf>,
pub success: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_incremental_compiler() {
}
}