diff --git a/src/app.rs b/src/app.rs index 2c19683..ebb4612 100644 --- a/src/app.rs +++ b/src/app.rs @@ -248,53 +248,87 @@ fn build(mode: &Option, 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 = 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 = 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, force_recompile: bool) -> std::io::Result<()> { Ok(()) } -fn hash_src_tree() -> std::io::Result { - 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 { + let contents = std::fs::read_to_string(path)?; + Ok(sha256::digest(contents)) } fn run(mode: &Option, args: Option>) -> std::io::Result<()> {