148 lines
5.1 KiB
Rust
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
|
|
}
|
|
}
|