feat: Implement build state tracking and interactive package specification creation

- Added StateTracker to manage build steps and allow resuming interrupted builds.
- Updated post_extract function to utilize StateTracker for patch and command execution.
- Introduced makefile.rs to handle building and installing packages via Makefile.
- Created interactive.rs for an interactive package specification creator with SHA256 computation for source URLs.
- Enhanced checksum verification to support multiple algorithms (SHA256, SHA512, MD5).
- Added example configuration files in contrib for system-wide and user-level Depot configurations.
- Updated tests to cover new functionality and ensure correctness of state tracking and interactive creation.
This commit is contained in:
2026-02-16 21:38:05 -06:00
parent 00ca2ebac6
commit 9a00608104
30 changed files with 3501 additions and 434 deletions
+89
View File
@@ -0,0 +1,89 @@
//! Build state tracking to allow resuming interrupted builds.
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BuildStep {
PatchesApplied,
PostExtractDone,
Configured,
PostCompileDone,
PostInstallDone,
}
#[derive(Debug, Default, Serialize, Deserialize)]
struct State {
completed_steps: HashSet<BuildStep>,
}
pub struct StateTracker {
state_file: PathBuf,
state: State,
}
impl StateTracker {
pub fn new(source_dir: &Path) -> Result<Self> {
let state_file = source_dir.join(".depot_state");
let state = if state_file.exists() {
let content = fs::read_to_string(&state_file)?;
toml::from_str(&content).unwrap_or_default()
} else {
State::default()
};
Ok(Self { state_file, state })
}
pub fn is_done(&self, step: BuildStep) -> bool {
self.state.completed_steps.contains(&step)
}
pub fn mark_done(&mut self, step: BuildStep) -> Result<()> {
self.state.completed_steps.insert(step);
self.save()
}
fn save(&self) -> Result<()> {
let content = toml::to_string_pretty(&self.state)?;
fs::write(&self.state_file, content)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_state_tracker_init() -> Result<()> {
let dir = tempdir()?;
let tracker = StateTracker::new(dir.path())?;
assert!(!tracker.state_file.exists());
Ok(())
}
#[test]
fn test_mark_done_and_persistence() -> Result<()> {
let dir = tempdir()?;
let mut tracker = StateTracker::new(dir.path())?;
assert!(!tracker.is_done(BuildStep::Configured));
tracker.mark_done(BuildStep::Configured)?;
assert!(tracker.is_done(BuildStep::Configured));
// Check file exists
assert!(tracker.state_file.exists());
// Reload
let tracker2 = StateTracker::new(dir.path())?;
assert!(tracker2.is_done(BuildStep::Configured));
assert!(!tracker2.is_done(BuildStep::PostCompileDone));
Ok(())
}
}