mgen/src/build_rule.rs

148 lines
5.1 KiB
Rust

use std::{path::PathBuf, fs};
use path_clean::PathClean;
use regex::Regex;
use crate::makefile::*;
use crate::ARGS;
#[derive(Debug)]
#[allow(dead_code)]
pub enum BuildRuleKind<T> {
Object(T),
Target(T),
Directory(T),
Convenience(T),
}
#[derive(Debug,Clone)]
pub struct BuildRule {
pub target: String,
pub prerequisites: Vec<String>,
pub recipe_commands: Vec<String>,
}
impl BuildRule {
pub fn get_headers_from_c_file(path: &str, makefile: &Makefile) -> Vec<String> {
let mut headers: Vec<String> = Vec::new();
let file_contents = fs::read_to_string(path).unwrap_or_else(|e| {
panic!("error: could not read file {path}: {e}");
});
let file_path = PathBuf::from(path);
let file_dir = file_path.parent().unwrap();
let includes_regex = Regex::new(r#"#include\s+"(.*?)""#).unwrap();
let caps = includes_regex.captures_iter(&file_contents);
'fileloop: for (_, [include_file]) in caps.map(|c| c.extract()) {
let path = file_dir.join(PathBuf::from(include_file)).clean();
match path.exists() {
true => {
headers.push(String::from(path.to_str().unwrap()));
continue;
},
false => (),
}
for dir in makefile.include_dirs.iter() {
let dir = PathBuf::from(&dir);
let path = dir.join(PathBuf::from(include_file)).clean();
match path.exists() {
true => {
headers.push(String::from(path.to_str().unwrap()));
continue 'fileloop;
},
false => (),
}
}
let path = file_dir.join(PathBuf::from(include_file));
headers.push(String::from(path.to_str().unwrap()));
}
headers
}
pub fn new_from_c_file(path: &str, makefile: &Makefile) -> BuildRule {
let mut prerequisites: Vec<String> = Vec::new();
prerequisites.push((PathBuf::from(path)).clean().to_str().unwrap().to_string());
prerequisites.append(&mut BuildRule::get_headers_from_c_file(path, makefile));
prerequisites.push(String::from("| $(OBJDIR)"));
let file_path = PathBuf::from(path);
let obj_name = file_path.with_extension("o");
let obj_name = obj_name.file_name().unwrap();
let obj_path = PathBuf::from("$(OBJDIR)").join(obj_name);
let obj_path = obj_path.to_str().unwrap();
let compile_command = format!("$(CC) $(CFLAGS) $(INCS) -o {} -c {}", obj_path, path);
BuildRule { target: String::from(obj_path), prerequisites, recipe_commands: vec![compile_command] }
}
}
impl ToString for BuildRuleKind<BuildRule> {
fn to_string(&self) -> String {
let rule = match self {
BuildRuleKind::Object(v) => v,
BuildRuleKind::Target(v) => v,
BuildRuleKind::Directory(v) => v,
BuildRuleKind::Convenience(v) => v,
};
let mut ret = String::new();
ret.push_str(&format!("{}: ", rule.target));
ret.push_str(&rule.prerequisites.join(" "));
ret.push_str(&format!("\n"));
ARGS.with(|args| {
let mut command_prefix = "\t";
let mut special = false;
if args.pretty {
command_prefix = "\t@";
let verb = match self {
BuildRuleKind::Object(_) => "Building",
BuildRuleKind::Target(_) => {
if args.static_library {
"Creating static library"
} else if args.dynamic_library {
"Creating dynamic library"
} else {
"Linking"
}
},
BuildRuleKind::Directory(_) => "Creating directory",
BuildRuleKind::Convenience(_) => {
special = true;
if rule.target.contains("clean") {
ret.push_str(&format!("{}printf ':: Cleaning up ...\\e[0m'\n", command_prefix));
}
if rule.target.contains("run") {
ret.push_str(&format!("{}printf '\\e[36m:: Running\\e[0m \\e[1m%s\\e[0m \\e[36m...\\e[0m\\n' $(TARGET)\n", command_prefix));
}
""
},
};
if !special {
ret.push_str(&format!("{}printf ':: {} \\e[1m%s\\e[0m ...\\e[0m' {}\n", command_prefix, verb,
(PathBuf::from(&rule.target)).file_name().unwrap().to_str().unwrap()));
}
}
ret.push_str(&rule.recipe_commands.iter()
.map(|v| format!("{}{}", command_prefix, v))
.collect::<Vec<_>>()
.join("\n"));
if args.pretty {
ret.push_str(&format!("\n{}printf ' \\e[32mdone\\e[0m\\n'", command_prefix));
}
});
ret
}
}