add change to hash and build to hash and build each file independently #12

Merged
sfrembling merged 1 commits from working/#10 into main 2026-03-23 11:00:40 -06:00
Showing only changes of commit 3d1a653d6f - Show all commits

View File

@@ -248,53 +248,87 @@ fn build(mode: &Option<String>, force_recompile: bool) -> std::io::Result<()> {
let start = std::time::Instant::now();
std::fs::create_dir_all(format!("target/{}", build_config.name))?;
let obj_dir = format!("target/{}/obj", build_config.name);
let hash = hash_src_tree()?;
std::fs::create_dir_all(&obj_dir)?;
let old_compute_path = PathBuf::from(format!("target/{}/.build_hash", build_config.name));
let compiler = conf.compiler.as_deref().unwrap_or("gcc");
if old_compute_path.exists() && !force_recompile {
let text = std::fs::read_to_string(old_compute_path)?;
let source_files: Vec<PathBuf> = glob("src/*.c")
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}")))?
.filter_map(|e| e.ok())
.collect();
if hash.trim() == text.trim() {
println!(
" {} (code not changed, recompile not made)",
"Finished".green().bold()
);
return Ok(());
let mut any_changed = false;
for src in &source_files {
let stem = src.file_stem().unwrap().to_string_lossy();
let obj_path = format!("{}/{}.o", obj_dir, stem);
let hash_path = format!("{}/{}.c.hash", obj_dir, stem);
let hash = hash_file(src)?;
let needs_compile = force_recompile
|| match std::fs::read_to_string(&hash_path) {
Ok(old) => old.trim() != hash.trim(),
Err(_) => true,
};
if needs_compile {
println!(" {} {}", "Compiling".green().bold(), src.display());
let status = Command::new(compiler)
.args(&build_config.args)
.arg("-c")
.arg(src)
.arg("-o")
.arg(&obj_path)
.status()?;
if !status.success() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to compile {}", src.display()),
));
}
std::fs::write(&hash_path, &hash)?;
any_changed = true;
}
}
let mut command = Command::new(conf.compiler.as_deref().unwrap_or("gcc"));
let binary = format!("target/{}/{}", build_config.name, conf.name);
let binary_exists = PathBuf::from(&binary).exists();
for arg in &build_config.args {
command.arg(arg);
if !any_changed && binary_exists {
println!(
" {} (code not changed, recompile not made)",
"Finished".green().bold()
);
return Ok(());
}
for entry in glob("src/*.c")
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}").as_str()))?
{
if let Ok(path) = entry {
command.arg(path.to_string_lossy().to_string());
}
}
let obj_files: Vec<String> = source_files
.iter()
.map(|src| {
let stem = src.file_stem().unwrap().to_string_lossy();
format!("{}/{}.o", obj_dir, stem)
})
.collect();
command
let status = Command::new(compiler)
.args(&obj_files)
.arg("-o")
.arg(format!("target/{}/{}", build_config.name, conf.name));
let status = command.status()?;
.arg(&binary)
.status()?;
if !status.success() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("compiler exited with status {status}"),
"linking failed",
));
}
std::fs::write(format!("target/{}/.build_hash", build_config.name), hash)?;
let stop = start.elapsed();
println!(
@@ -308,16 +342,9 @@ fn build(mode: &Option<String>, force_recompile: bool) -> std::io::Result<()> {
Ok(())
}
fn hash_src_tree() -> std::io::Result<String> {
let mut hashes = String::new();
for entry in glob("src/**/*").expect("a valid glob pattern") {
if let Ok(file) = entry {
let text = std::fs::read_to_string(file)?;
let hash = sha256::digest(text);
hashes.push_str(hash.as_str());
}
}
Ok(sha256::digest(hashes))
fn hash_file(path: &Path) -> std::io::Result<String> {
let contents = std::fs::read_to_string(path)?;
Ok(sha256::digest(contents))
}
fn run(mode: &Option<String>, args: Option<Vec<String>>) -> std::io::Result<()> {