From 58d33da293e3803737bb9218235f0c6d138b3af5 Mon Sep 17 00:00:00 2001 From: Noah Swerhun Date: Sun, 24 Sep 2023 15:53:16 -0500 Subject: [PATCH] main functionality done, working --- Cargo.toml | 2 +- src/main.rs | 137 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 114 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eea6847..64714b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.4.4", features = ["derive"] } +clap = { version = "4.4.4", default_features = false, features = ["derive", "help", "usage", "std", "error-context", "suggestions"] } regex = "1.9.5" diff --git a/src/main.rs b/src/main.rs index f4c12ba..1b4db01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ use std::fs; -use std::io::Read; +use std::io::{Read, Write}; use std::path::Path; use regex::Regex; use clap::Parser; #[derive(Debug)] struct CFile { - path: String, - filename: String, + file_path: String, + obj_path: String, + obj_name: String, headers: Vec, } @@ -16,30 +17,37 @@ struct Args { /// Replace default make output with nice build messages #[arg(short, long)] pretty: bool, + + /// Path to makefile + #[arg(short, long, default_value = "./Makefile")] + makefile: String, + /// Directory to place build files #[arg(default_value = "./build")] build_dir: String, } -fn parse_c_file(path: &Path, re: &Regex) -> CFile { - let strpath = path.to_str().unwrap(); - let filename = path.file_stem().unwrap().to_str().unwrap(); - +fn parse_c_file(path: &Path, obj_dir: &Path, re: &Regex) -> CFile { let mut file_handle = fs::File::open(path).unwrap(); let mut contents = String::new(); file_handle.read_to_string(&mut contents).unwrap(); let mut headers: Vec = Vec::new(); - let caps = re.captures_iter(&contents); - for (_, [include_file]) in caps.map(|c| c.extract()) { + let captures = re.captures_iter(&contents); + for (_, [include_file]) in captures.map(|c| c.extract()) { headers.push(path.parent().unwrap() .join(Path::new(include_file)) .to_str().unwrap() .to_string()); } - return CFile { path: strpath.to_string(), filename: filename.to_string(), headers } - + let obj_path = obj_dir.join(Path::new(path.file_name().unwrap()).with_extension("o")); + return CFile { + file_path: path.to_str().unwrap().to_string(), + obj_path: obj_path.to_str().unwrap().to_string(), + obj_name: obj_path.file_name().unwrap().to_str().unwrap().to_string(), + headers + } } fn recurse_into_dir(path: &Path, file_callback: &mut F) { @@ -66,14 +74,13 @@ fn recurse_into_dir(path: &Path, file_callback: &mut F) } fn main() { - let mut project_files: Vec = Vec::new(); - let includes_regex = Regex::new(r#"#include\s+"(.*?)""#).unwrap(); - let args = Args::parse(); let build_dir = Path::new(&args.build_dir); let pretty = args.pretty; + let mut project_files: Vec = Vec::new(); let obj_dir = build_dir.join("obj"); + let includes_regex = Regex::new(r#"#include\s+"(.*?)""#).unwrap(); recurse_into_dir(Path::new("./"), &mut |file| { let path = file.path(); @@ -83,7 +90,7 @@ fn main() { if v != "c" { return; } - project_files.push(parse_c_file(&path, &includes_regex)); + project_files.push(parse_c_file(&path, &obj_dir, &includes_regex)); }, None => { return; @@ -91,21 +98,103 @@ fn main() { } }); - for file in project_files.iter() { - let mut build_rule: String = "#=mgen_rule=#\n".to_owned(); - - let objname = file.filename.to_owned() + ".o"; - let objpath = obj_dir.join(&objname); - let objpath = objpath.to_str().unwrap(); + if !Path::new(&args.makefile).exists() { + fs::File::create(&args.makefile).unwrap(); + } - build_rule.push_str(&format!("{}: {}", objpath, file.path)); + let mut makefile_handle = fs::OpenOptions::new() + .read(true) + .open(args.makefile.clone()).unwrap(); + let mut makefile = String::new(); + makefile_handle.read_to_string(&mut makefile).unwrap(); + + let start_key = "#=mgen_start=#"; + let end_key = "#=mgen_end=#"; + + let mut build_rules = String::new(); + + build_rules.push_str(&start_key); + + build_rules.push_str("\n"); + + let target_path = build_dir.join(Path::new("$(TARGET)")); + let target_path = target_path.to_str().unwrap(); + + build_rules.push_str(&format!("{}: ", target_path)); + for file in project_files.iter() { + build_rules.push_str(&format!(" {}", file.obj_path)); + } + build_rules.push_str("\n"); + + if pretty { + build_rules.push_str(&format!("\t@printf ':: Linking %s ... ' $(TARGET)\n")); + } + + build_rules.push_str(&format!("\t{}$(CC) $(LDFLAGS) -o {} ", + if pretty {"@"} else {""}, target_path)); + for file in project_files.iter() { + build_rules.push_str(&format!(" {}", file.obj_path)); + } + build_rules.push_str(" $(LDLIBS)\n"); + + if pretty { + build_rules.push_str(&format!("\t@printf 'done\\n'\n")); + } + + for file in project_files.iter() { + let mut build_rule = String::new(); + + build_rule.push_str("\n"); + + build_rule.push_str(&format!("{}: {}", file.obj_path, file.file_path)); for header in file.headers.iter() { build_rule.push_str(&format!(" {}", header)); } + build_rule.push_str("\n"); - build_rule.push_str(&format!("\n\t$(CC) $(CFLAGS) -o {} -c {}\n", objpath, file.path )); + if pretty { + build_rule.push_str(&format!("\t@printf ':: Building {} ... '\n", + file.obj_name)); + } - println!("'{}'", build_rule); + build_rule.push_str(&format!("\t{}$(CC) $(CFLAGS) -o {} -c {}\n", + if pretty {"@"} else {""}, + file.obj_path, file.file_path )); + + if pretty { + build_rule.push_str(&format!("\t@printf 'done\\n'\n")); + } + + + build_rules.push_str(&build_rule); } + build_rules.push_str(&end_key); + + if let Some(start) = makefile.find(start_key) { + if let Some(end) = makefile.find(end_key) { + let mut makefile_handle = fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(&args.makefile).unwrap(); + makefile.replace_range(start..(end+end_key.len()), &build_rules); + makefile_handle.write(makefile.as_bytes()).unwrap(); + makefile_handle.flush().unwrap(); + } else { + panic!("Makefile corrupted. Missing `#=mgen_end=#`, but `#=mgen_start=#` is present. Cannot perform replacement") + } + } else { + if let Some(_) = makefile.find(end_key) { + panic!("Makefile corrupted. Missing `#=mgen_start=#`, but `#=mgen_end=#` is present. Cannot perform replacement") + } else { + let mut makefile_handle = fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(&args.makefile).unwrap(); + makefile.push_str("\n"); + makefile.push_str(&build_rules); + makefile_handle.write(makefile.as_bytes()).unwrap(); + makefile_handle.flush().unwrap(); + } + } }