type RecipeFn = dyn Fn(&str, &[String]);
pub struct Target {
name: String,
is_file: bool,
file_dependencies: Vec<String>,
dependencies: Vec<Target>,
recipe: Option<Box<RecipeFn>>,
}
impl Target {
pub fn new_phony(name: &str) -> Self {
Self {
name: name.to_string(),
is_file: false,
file_dependencies: Vec::new(),
dependencies: Vec::new(),
recipe: None,
}
}
pub fn new_file(name: &str) -> Self {
let mut target = Self::new_phony(name);
target.is_file = true;
target
}
pub fn depends_on_file(mut self, file: &str) -> Self {
self.file_dependencies.push(file.to_string());
self
}
pub fn depends_on_target(mut self, target: Target) -> Self {
self.dependencies.push(target);
self
}
pub fn recipe<F>(mut self, recipe: F) -> Self
where
F: Fn(&str, &[String]) + 'static,
{
self.recipe = Some(Box::new(recipe) as Box<RecipeFn>);
self
}
pub fn make(&self) {
if let Some(ref recipe) = self.recipe {
let deps: Vec<String> = self.file_dependencies.to_vec();
recipe(&self.name, &deps);
}
for dep in &self.dependencies {
dep.make();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::process::Command;
#[test]
fn target_construction() {
let obj = Target::new_file("main.o")
.depends_on_file("main.c")
.recipe(|target, deps| {
Command::new("gcc")
.arg("-o")
.arg(target)
.args(deps)
.status()
.unwrap();
});
let bin = Target::new_file("main")
.depends_on_target(obj)
.recipe(|target, deps| {
Command::new("ld")
.arg("-o")
.arg(target)
.args(deps)
.status()
.unwrap();
});
bin.make();
}
}