robust header file locator
This commit is contained in:
parent
7c5ee2e70a
commit
cd2c92d415
7 changed files with 713 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
/target
|
||||
/testing_grounds
|
||||
/build
|
||||
|
|
261
Cargo.lock
generated
261
Cargo.lock
generated
|
@ -2,6 +2,267 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "mgen"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
|
|
@ -6,3 +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.10", features = ["derive"] }
|
||||
regex = "1.10.2"
|
||||
|
|
27
Makefile
Normal file
27
Makefile
Normal file
|
@ -0,0 +1,27 @@
|
|||
TARGET = foo
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99
|
||||
LDLIBS = -lm
|
||||
#=mgen_start=#
|
||||
BUILDDIR = ./build
|
||||
OBJDIR = $(BUILDDIR)/obj
|
||||
$(BUILDDIR)/$(TARGET): $(OBJDIR)/main.o $(OBJDIR)/ops.o $(OBJDIR)/traps.o | $(OBJDIR) $(BUILDDIR)
|
||||
$(CC) $(LDFLAGS) -o $(BUILDDIR)/$(TARGET) $(OBJDIR)/main.o $(OBJDIR)/ops.o $(OBJDIR)/traps.o $(LDLIBS)
|
||||
$(OBJDIR)/main.o: ./testing_grounds/src/main.c ./testing_grounds/src/main.h ./testing_grounds/src/ops.h ./testing_grounds/src/traps.h | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -o $(OBJDIR)/main.o -c ./testing_grounds/src/main.c
|
||||
$(OBJDIR)/ops.o: ./testing_grounds/src/ops.c ./testing_grounds/src/main.h | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -o $(OBJDIR)/ops.o -c ./testing_grounds/src/ops.c
|
||||
$(OBJDIR)/traps.o: ./testing_grounds/src/traps.c ./testing_grounds/src/traps.h ./testing_grounds/src/main.h | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -o $(OBJDIR)/traps.o -c ./testing_grounds/src/traps.c
|
||||
$(BUILDDIR):
|
||||
mkdir -p $(BUILDDIR)
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -r $(OBJDIR)
|
||||
rm -r $(BUILDDIR)
|
||||
.PHONY: run
|
||||
run: $(BUILDDIR)/$(TARGET)
|
||||
./$(BUILDDIR)/$(TARGET)
|
||||
#=mgen_end=#
|
3
install.sh
Normal file
3
install.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
cp ./target/release/mgen /usr/bin/mgen
|
416
src/main.rs
Normal file
416
src/main.rs
Normal file
|
@ -0,0 +1,416 @@
|
|||
use std::path::PathBuf;
|
||||
use std::io::ErrorKind;
|
||||
use std::fs::{self, DirEntry};
|
||||
|
||||
use regex::Regex;
|
||||
use clap::Parser;
|
||||
|
||||
const START_KEY: &str = "#=mgen_start=#\n";
|
||||
const END_KEY: &str = "#=mgen_end=#";
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
/// Replace default make output with nice build messages (not yet implemented)
|
||||
#[arg(short, long)]
|
||||
pretty: bool,
|
||||
|
||||
/// Path to makefile
|
||||
#[arg(short, long, default_value = "./Makefile")]
|
||||
makefile: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum BuildRuleKind<T> {
|
||||
Object(T),
|
||||
Target(T),
|
||||
Directory(T),
|
||||
Convenience(T),
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
struct BuildRule {
|
||||
target: String,
|
||||
prerequisites: Vec<String>,
|
||||
recipe_commands: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct Makefile {
|
||||
path: String,
|
||||
prologue: String,
|
||||
mgen_zone: String,
|
||||
epilogue: String,
|
||||
include_dirs: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RuleList(Vec<BuildRuleKind<BuildRule>>);
|
||||
|
||||
impl BuildRule {
|
||||
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);
|
||||
for (_, [include_file]) in caps.map(|c| c.extract()) {
|
||||
match file_dir.join(PathBuf::from(include_file)).canonicalize() {
|
||||
Ok(path) => {
|
||||
headers.push(String::from(path.to_str().unwrap()));
|
||||
continue;
|
||||
},
|
||||
Err(_) => (),
|
||||
}
|
||||
|
||||
let mut done = false;
|
||||
for dir in makefile.include_dirs.iter() {
|
||||
let dir = PathBuf::from(&dir);
|
||||
match dir.join(PathBuf::from(include_file)).canonicalize() {
|
||||
Ok(path) => {
|
||||
headers.push(String::from(path.to_str().unwrap()));
|
||||
done = true;
|
||||
break;
|
||||
},
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
if done {
|
||||
continue;
|
||||
}
|
||||
let path = file_dir.join(PathBuf::from(include_file));
|
||||
headers.push(String::from(path.to_str().unwrap()));
|
||||
}
|
||||
|
||||
headers
|
||||
}
|
||||
fn new_from_c_file(path: &str, makefile: &Makefile) -> BuildRule {
|
||||
|
||||
let mut prerequisites: Vec<String> = Vec::new();
|
||||
|
||||
prerequisites.push(String::from(path));
|
||||
|
||||
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) -o {} -c {}", obj_path, path);
|
||||
|
||||
BuildRule { target: String::from(obj_path), prerequisites, recipe_commands: vec![compile_command] }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for BuildRule {
|
||||
fn to_string(&self) -> String {
|
||||
let mut ret = String::new();
|
||||
ret.push_str(&format!("{}:", self.target));
|
||||
for prereq in &self.prerequisites {
|
||||
ret.push_str(&format!(" {}", prereq));
|
||||
}
|
||||
ret.push_str(&format!("\n"));
|
||||
for recipe_comm in &self.recipe_commands {
|
||||
ret.push_str(&format!("\t{}\n", recipe_comm));
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Makefile {
|
||||
fn new_from_path(path: &str) -> Makefile {
|
||||
let contents = match fs::read_to_string(path) {
|
||||
Ok(v) => v,
|
||||
Err(e) => match e.kind() {
|
||||
// doesn't matter if the file doesn't exist, it will be created
|
||||
// later.
|
||||
ErrorKind::NotFound => return Makefile {
|
||||
path: String::from(path), prologue: String::from(""),
|
||||
mgen_zone: String::from(""), epilogue: String::from(""),
|
||||
include_dirs: vec![String::from("")] },
|
||||
_ => panic!("error: cannot read makefile: {e}")
|
||||
}
|
||||
};
|
||||
|
||||
let contains_start = contents.find(START_KEY).is_some();
|
||||
let contains_end = contents.find(END_KEY).is_some();
|
||||
|
||||
if (contains_start && !contains_end) || (!contains_start &&
|
||||
contains_end) {
|
||||
panic!("error: makefile contains one mgen guard but not the other. cannot continue.");
|
||||
}
|
||||
|
||||
if ! contains_start && contains_end {
|
||||
let include_dirs = Makefile::get_include_dirs(&contents);
|
||||
return Makefile { path: String::from(path), prologue: contents,
|
||||
mgen_zone: String::from(""), epilogue: String::from(""), include_dirs };
|
||||
}
|
||||
|
||||
// contains the part before the start key and everything after
|
||||
let pre_and_after: Vec<&str> = contents.split(START_KEY).collect();
|
||||
// contains the space between start and end keys and also the space after
|
||||
let gen_and_post: Vec<&str> = pre_and_after[1].split(END_KEY).collect();
|
||||
|
||||
let include_dirs = Makefile::get_include_dirs(&contents);
|
||||
|
||||
Makefile {
|
||||
path: String::from(path),
|
||||
prologue: String::from(pre_and_after[0]),
|
||||
mgen_zone: String::from(gen_and_post[0]),
|
||||
epilogue: String::from(gen_and_post[1]),
|
||||
include_dirs
|
||||
}
|
||||
}
|
||||
|
||||
fn get_include_dirs(search: &String) -> Vec<String> {
|
||||
let makefile_regex = Regex::new(r"^INCS\s*=\s*(.*?)$").unwrap();
|
||||
let caps = makefile_regex.captures_iter(search);
|
||||
let (_, [dirlist]) = caps.map(|c| c.extract()).last().unwrap();
|
||||
|
||||
let mut include_dirs: Vec<String> = Vec::new();
|
||||
|
||||
let incdir_regex = Regex::new(r"-s*-I\s*(.*?)\s*").unwrap();
|
||||
let caps = incdir_regex.captures_iter(&dirlist);
|
||||
for (_, [dir]) in caps.map(|c| c.extract()) {
|
||||
include_dirs.push(String::from(dir));
|
||||
}
|
||||
|
||||
include_dirs
|
||||
}
|
||||
|
||||
fn contains_builddir_def(&self) -> bool {
|
||||
let re = Regex::new(r"\s+BUILDDIR = ").unwrap();
|
||||
re.is_match(&(self.prologue)) || re.is_match(&(self.epilogue))
|
||||
}
|
||||
|
||||
fn contains_objdir_def(&self) -> bool {
|
||||
let re = Regex::new(r"\s+OBJDIR = ").unwrap();
|
||||
re.is_match(&(self.prologue)) || re.is_match(&(self.epilogue))
|
||||
}
|
||||
|
||||
fn write_new_generated_content(&self, generated_content: String) -> std::io::Result<()> {
|
||||
let content = [&self.prologue, START_KEY, &generated_content, END_KEY, &self.epilogue].concat();
|
||||
|
||||
fs::write(&self.path, &content)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn join_build_rule_vec(vector: &Vec<BuildRule>, seperator: &str) -> String{
|
||||
let mut ret = String::new();
|
||||
|
||||
for (i, rule) in vector.iter().enumerate() {
|
||||
if i != 0 {
|
||||
ret.push_str(seperator);
|
||||
}
|
||||
ret.push_str(&rule.to_string());
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
impl ToString for RuleList {
|
||||
fn to_string(&self) -> String {
|
||||
let targets = join_build_rule_vec(&self.get_target_rules(), "");
|
||||
let objects = join_build_rule_vec(&self.get_object_rules(), "");
|
||||
let directories = join_build_rule_vec(&self.get_directory_rules(), "");
|
||||
let conveniences = join_build_rule_vec(&self.get_convenience_rules(), "");
|
||||
|
||||
[targets, objects, directories, conveniences].concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl RuleList {
|
||||
fn new() -> RuleList{
|
||||
RuleList(vec![])
|
||||
}
|
||||
|
||||
fn recursively_gen_objects(&mut self, proj_dir_path: &str, makefile: &Makefile) -> std::io::Result<()>{
|
||||
let files = fs::read_dir(proj_dir_path)?;
|
||||
for file in files {
|
||||
let file = file?;
|
||||
let is_dir = file.metadata()?.is_dir();
|
||||
|
||||
// ignore hidden files
|
||||
if file_is_hidden(&file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = file.path();
|
||||
|
||||
if is_dir {
|
||||
self.recursively_gen_objects(path.to_str().unwrap(), makefile)?;
|
||||
} else {
|
||||
match path.extension() {
|
||||
Some(v) => {
|
||||
let v = v.to_str().unwrap();
|
||||
if v != "c" {
|
||||
continue;
|
||||
}
|
||||
let path = path.to_str().unwrap();
|
||||
self.0.push(BuildRuleKind::Object(BuildRule::new_from_c_file(path, makefile)));
|
||||
},
|
||||
None => {
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_obj_targets(&self) -> Vec<String>{
|
||||
let mut ret: Vec<String> = Vec::new();
|
||||
for rule in &self.0 {
|
||||
match rule {
|
||||
BuildRuleKind::Object(r) => ret.push(r.target.clone()),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn add_target_rule(&mut self) {
|
||||
let mut objects = self.get_obj_targets();
|
||||
let target_comm = String::from(
|
||||
format!("$(CC) $(LDFLAGS) -o $(BUILDDIR)/$(TARGET) {} $(LDLIBS)", objects.join(" "))
|
||||
);
|
||||
|
||||
objects.push(String::from("| $(OBJDIR) $(BUILDDIR)"));
|
||||
let prereqs = objects;
|
||||
|
||||
self.0.push(BuildRuleKind::Target(BuildRule {
|
||||
target: String::from("$(BUILDDIR)/$(TARGET)"),
|
||||
prerequisites: prereqs,
|
||||
recipe_commands: vec![target_comm],
|
||||
}))
|
||||
}
|
||||
|
||||
fn add_directory_rule(&mut self, dir: &str) {
|
||||
self.0.push(BuildRuleKind::Directory(BuildRule {
|
||||
target: dir.to_string(),
|
||||
prerequisites: vec![],
|
||||
recipe_commands: vec![format!("mkdir -p {dir}").to_string()]
|
||||
}));
|
||||
}
|
||||
|
||||
fn add_clean_rule(&mut self) {
|
||||
self.0.push(BuildRuleKind::Convenience(BuildRule {
|
||||
target: String::from(".PHONY: clean\nclean"),
|
||||
prerequisites: vec![],
|
||||
recipe_commands: vec![String::from("rm -r $(OBJDIR)"), String::from("rm -r $(BUILDDIR)")]
|
||||
}));
|
||||
}
|
||||
|
||||
fn add_run_rule(&mut self) {
|
||||
self.0.push(BuildRuleKind::Convenience(BuildRule {
|
||||
target: String::from(".PHONY: run\nrun"),
|
||||
prerequisites: vec![String::from("$(BUILDDIR)/$(TARGET)")],
|
||||
recipe_commands: vec![String::from("./$(BUILDDIR)/$(TARGET)")]
|
||||
}));
|
||||
}
|
||||
|
||||
fn get_target_rules(&self) -> Vec<BuildRule> {
|
||||
let mut ret: Vec<BuildRule> = Vec::new();
|
||||
for rule in &self.0 {
|
||||
match rule {
|
||||
BuildRuleKind::Target(r) => ret.push(r.clone()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_object_rules(&self) -> Vec<BuildRule> {
|
||||
let mut ret: Vec<BuildRule> = Vec::new();
|
||||
for rule in &self.0 {
|
||||
match rule {
|
||||
BuildRuleKind::Object(r) => ret.push(r.clone()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_directory_rules(&self) -> Vec<BuildRule> {
|
||||
let mut ret: Vec<BuildRule> = Vec::new();
|
||||
for rule in &self.0 {
|
||||
match rule {
|
||||
BuildRuleKind::Directory(r) => ret.push(r.clone()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_convenience_rules(&self) -> Vec<BuildRule> {
|
||||
let mut ret: Vec<BuildRule> = Vec::new();
|
||||
for rule in &self.0 {
|
||||
match rule {
|
||||
BuildRuleKind::Convenience(r) => ret.push(r.clone()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
fn file_is_hidden(file: &DirEntry) -> bool{
|
||||
let file_name = file.file_name();
|
||||
let str_name = file_name.to_str().unwrap();
|
||||
let first_char = str_name.chars().nth(0).unwrap();
|
||||
if first_char == '.' {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let makefile = Makefile::new_from_path(&args.makefile);
|
||||
|
||||
let mut generated_content = String::new();
|
||||
|
||||
// Only include directory defaults if the user has not specified them
|
||||
if ! makefile.contains_builddir_def() {
|
||||
generated_content.push_str("BUILDDIR = ./build\n");
|
||||
}
|
||||
|
||||
if ! makefile.contains_objdir_def() {
|
||||
generated_content.push_str("OBJDIR = $(BUILDDIR)/obj\n");
|
||||
}
|
||||
|
||||
let mut rule_list = RuleList::new();
|
||||
rule_list.recursively_gen_objects(".", &makefile).unwrap_or_else(|e| {
|
||||
panic!("error: problem in recursive search: {e}");
|
||||
});
|
||||
|
||||
rule_list.add_target_rule();
|
||||
|
||||
rule_list.add_directory_rule("$(BUILDDIR)");
|
||||
rule_list.add_directory_rule("$(OBJDIR)");
|
||||
rule_list.add_clean_rule();
|
||||
rule_list.add_run_rule();
|
||||
|
||||
generated_content.push_str(&rule_list.to_string());
|
||||
|
||||
// Write makefile
|
||||
makefile.write_new_generated_content(generated_content).unwrap_or_else(|e| {
|
||||
panic!("error: could not write makefile at {}: {e}", makefile.path);
|
||||
});
|
||||
}
|
3
uninstall.sh
Normal file
3
uninstall.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
rm /usr/bin/mgen
|
Loading…
Reference in a new issue