Skip to content

Commit

Permalink
Implement working version
Browse files Browse the repository at this point in the history
  • Loading branch information
VOID404 committed Jun 28, 2024
1 parent 262aef9 commit b411e4e
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 57 deletions.
217 changes: 165 additions & 52 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use std::{collections::HashMap, env, fs, io};
use std::{
collections::{HashMap, HashSet, VecDeque},
env, fs, io,
mem::MaybeUninit,
path::{self, Path, PathBuf},
};

use ast::Term;
use pest::{iterators::Pair, Parser};
Expand Down Expand Up @@ -53,30 +58,25 @@ fn parse(data: &str) -> Vec<Term> {
}

#[derive(Debug)]
struct Var<'a>(&'a str, &'a str, &'a str);
struct Var(String, String, String);
#[derive(Debug)]
struct Task<'a> {
struct Task {
phony: bool,
name: &'a str,
deps: Vec<&'a str>,
body: Vec<&'a str>,
name: String,
deps: Vec<String>,
body: Vec<String>,
}

fn task_from_ast<'a>(
phonies: &Vec<&str>,
name: &'a str,
deps: Vec<&'a str>,
body: Vec<&'a str>,
) -> Task<'a> {
fn task_from_ast(phonies: &Vec<&str>, name: String, deps: Vec<String>, body: Vec<String>) -> Task {
Task {
phony: phonies.contains(&name),
phony: phonies.contains(&name.as_str()),
name,
deps,
body,
}
}

fn from_ast(terms: Vec<Term>) -> (HashMap<&str, Task>, HashMap<&str, Var>) {
fn from_ast(terms: Vec<Term>) -> (HashMap<String, Task>, HashMap<String, Var>) {
let empty = vec![];
let phonies: Vec<&str> = {
terms
Expand All @@ -93,13 +93,16 @@ fn from_ast(terms: Vec<Term>) -> (HashMap<&str, Task>, HashMap<&str, Var>) {
.collect()
};

let mut tasks = HashMap::new();
let mut vars = HashMap::new();
let mut tasks: HashMap<String, Task> = HashMap::new();
let mut vars: HashMap<String, Var> = HashMap::new();

for t in terms {
match t {
Term::Var(name, eq, val) => {
vars.insert(name, Var(name, eq, val));
vars.insert(
name.to_string(),
Var(name.to_string(), eq.to_string(), val.to_string()),
);
}
Term::Task {
name,
Expand All @@ -109,10 +112,18 @@ fn from_ast(terms: Vec<Term>) -> (HashMap<&str, Task>, HashMap<&str, Var>) {
match tasks.get_mut(name) {
Some(t) => {
let t: &mut Task = t;
t.deps.append(&mut deps);
t.deps.extend(deps.into_iter().map(|s| s.to_string()));
}
None => {
tasks.insert(name, task_from_ast(&phonies, name, deps, body));
tasks.insert(
name.to_string(),
task_from_ast(
&phonies,
name.to_string(),
deps.into_iter().map(|s| s.to_string()).collect(),
body.into_iter().map(|s| s.to_string()).collect(),
),
);
}
};
}
Expand All @@ -123,68 +134,170 @@ fn from_ast(terms: Vec<Term>) -> (HashMap<&str, Task>, HashMap<&str, Var>) {
(tasks, vars)
}

fn parse_file(path: impl AsRef<Path>) -> io::Result<(HashMap<String, Task>, HashMap<String, Var>)> {
let file = fs::read_to_string(path)?;
let terms = parse(&file);
let (tasks, vars) = from_ast(terms);
Ok((tasks, vars))
}

type ID = String;

struct IDGen {
prefix: String,
uuid: i64,
}

impl IDGen {
fn new() -> Self {
IDGen { uuid: 0 }
Self::with("id")
}
fn with(prefix: &str) -> Self {
Self {
prefix: prefix.to_string(),
uuid: 0,
}
}
fn next(&mut self) -> String {
fn next(&mut self) -> ID {
let id = self.uuid;
self.uuid += 1;

format!("id{}", self.uuid)
format!("{}{}", &self.prefix, id)
}
}

struct SubGraph {
name: String,
nodes: Vec<Node>,
fn walk_makefile(path: impl AsRef<Path>) -> io::Result<Vec<Makefile>> {
let re_var = Regex::new(r"\$\{\s*(\w+)\s*\}").unwrap();
let re_make = Regex::new(r"make\s+-C\s*([^\s]+) ([\w\-._]+)").unwrap();

let mut queue = VecDeque::from([path::absolute(path.as_ref())?.canonicalize()?]);
let mut makefile;
let mut makefiles = vec![];

let mut ids = IDGen::new();
let mut id = || ids.next();

while let Some(path) = queue.pop_back() {
eprintln!("Parsing {}", path.to_string_lossy());
makefile = Makefile {
name: path.to_path_buf(),
tasks: HashMap::new(),
external: vec![],
};
let (tasks, vars) = parse_file(path)?;

for (_, task) in tasks {
let task_id = id();
let external_deps = task.body.iter().filter(|l| l.contains("@make"));
for dep in external_deps {
let out = re_var.replace(dep, |caps: &Captures| match vars.get(&caps[1]) {
Some(v) => v.2.to_string(),
None => caps[0].to_string(),
});
match re_make.captures(&out) {
Some(c) => {
let mut path: PathBuf = makefile.name.clone();
path.pop();
path.push(&c[1]);
path.push("Makefile");
let path = path.canonicalize()?;
let this = task.name.clone();
let other = c[2].to_string();
// println!("Adding {:?}, {}, {}", path, this, other);
if !queue.contains(&path)
&& !makefiles
.iter()
.find(|m: &&Makefile| m.name == path)
.is_some()
{
queue.push_front(path.clone());
}
// eprintln!(
// "Adding external: {} {} from {}",
// &task_id,
// other,
// path.to_string_lossy()
// );
makefile.external.push((path, task_id.clone(), other));
}
None => {}
};
}
makefile.tasks.insert(task_id, task);
}
makefiles.push(makefile);
}

Ok(makefiles)
}

struct Node {
id: String,
label: String,
children: Vec<String>,
#[derive(Debug)]
struct Makefile {
name: PathBuf,
tasks: HashMap<String, Task>,
external: Vec<(PathBuf, String, String)>,
}

impl Node {
fn new(id_provider: &mut IDGen, label: String) -> Self {
Self {
id: id_provider.next(),
label,
children: vec![],
}
impl Makefile {
fn get_id(&self, name: &str) -> Option<&str> {
self.tasks
.iter()
.find(|(_, task)| task.name == name)
.map(|(id, _)| id.as_str())
}
}

fn main() -> io::Result<()> {
let re_var = Regex::new(r"\$\{\s*(\w+)\s*\}").unwrap();

let args: Vec<String> = env::args().collect();
assert!(args.len() == 2);
let path: &str = &args[1];

let file = fs::read_to_string(path)?;
let terms = parse(&file);
let (tasks, vars) = from_ast(terms);
let mut ids = IDGen::with("cluster_");
let makefiles = walk_makefile(path)?;
eprintln!("Parsed {:#?} Makefiles", makefiles.len());

println!("strict graph {{");
for (_, task) in tasks {
for dep in task.deps {
println!("\t\"{}\" -- \"{}\"", task.name, dep);
println!("digraph G {{\n\tranksep=3");
for makefile in &makefiles {
println!(
"\tsubgraph {} {{\n\t\tlabel=\"{}\"",
ids.next(),
makefile.name.to_string_lossy()
);
for (id, task) in &makefile.tasks {
println!("\t\t{}[label=\"{}\"]", id, task.name);
for dep in &task.deps {
match makefile.get_id(&dep) {
Some(dep) => println!("\t\t{} -> {}", id, dep),
None => eprintln!(
"Bad task name: {} in {}",
dep,
makefile.name.to_string_lossy()
),
}
}
}
let external_deps = task.body.iter().filter(|l| l.contains("@make"));
for dep in external_deps {
let out = re_var.replace(dep, |caps: &Captures| match vars.get(&caps[1]) {
Some(v) => v.2.to_string(),
None => caps[0].to_string(),
});
println!("\t# {}", out);

println!("\t}}");
for (source, task, dep) in &makefile.external {
// eprintln!(
// "Finding {}: {} from {}",
// task,
// dep,
// source.to_string_lossy()
// );
match makefiles.iter().find(|m| &m.name == source) {
Some(m) => match m.get_id(&dep) {
Some(dep) => {
// eprintln!("found: {} -> {}", task, dep);
println!("\t{} -> {}", task, dep)
}
None => eprintln!("Bad task name: {} in {}", dep, m.name.to_string_lossy()),
},
None => {}
}
}
}

println!("}}");

Ok(())
Expand Down
15 changes: 10 additions & 5 deletions src/makefile.pest
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
makefile = { SOI ~ (task | var | empty)* ~ EOI }
makefile = { SOI ~ (task | var | empty | shit)* ~ EOI }

task = { ident ~ ":" ~ deps ~ comment? ~ eol ~ body }
deps = {(ident ~ ws*)*}
deps = { (ident | ws)* }
var = { ident ~ eq ~ anyRest ~ comment? ~ eol }

empty = _{ comment? ~ NEWLINE }

body = { ("\t" ~ line ~ eol)* }
body = { (("\t" ~ line ~ eol) | (comment ~ eol))* }
line = { (!NEWLINE ~ ANY)+ }
anyRest = { (!NEWLINE ~ !"#" ~ ANY)+ }
comment = _{ "#" ~ (!NEWLINE ~ ANY)+ }

ident = @{ ('a'..'z' | 'A'..'Z' | '0'..'9' | "_" | "-" | ".")+ }
eq = { "=" | "?=" }
ident = @{ "$("? ~ ('a'..'z' | 'A'..'Z' | '0'..'9' | "_" | "-" | ".")+ ~ ")"? }
eq = { "=" | "?=" }

shit = _{ ifs | define | include }
ifs = _{ ("ifneq" | "ifeq" | "ifndef") ~ (!"endif" ~ ANY)+ ~ "endif" }
define = _{ "define" ~ (!"endef" ~ ANY)+ ~ "endef" }
include = _{ "include" ~ (!eol ~ ANY)+ ~ eol }

WHITESPACE = _{ " " | "\\\n" }
ws = _{ " " | "\t" | "\\\n" }
Expand Down

0 comments on commit b411e4e

Please sign in to comment.