main functionality done, working
This commit is contained in:
		
							parent
							
								
									9f46b676f7
								
							
						
					
					
						commit
						58d33da293
					
				
					 2 changed files with 114 additions and 25 deletions
				
			
		| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										137
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								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<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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<String> = 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<F: FnMut(fs::DirEntry)>(path: &Path, file_callback: &mut F) {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,14 +74,13 @@ fn recurse_into_dir<F: FnMut(fs::DirEntry)>(path: &Path, file_callback: &mut F)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let mut project_files: Vec<CFile> = 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<CFile> = 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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue