ngen/src/main.rs

164 lines
5.2 KiB
Rust

use core::panic;
use std::fs::{read, write};
use toml::{Table, Value};
fn print_tabs(num: usize) {
for _ in 0..num {
print!(" ");
}
}
fn print_table(table: &Table, depth: usize) {
for k in table.keys() {
match table.get(k) {
Some(v) => match v {
Value::Table(t) => {
print_tabs(depth);
println!("{k}:");
print_table(&t, depth + 1)
}
Value::String(s) => {
print_tabs(depth);
println!("{k}: {s}");
}
Value::Array(a) => {
print_tabs(depth);
println!("(array){k}:");
for element in a {
match element {
Value::String(s) => println!(" {}", s),
_ => todo!(),
}
}
}
_ => continue,
},
None => continue,
}
}
}
fn target_generator(name: &str, config_table: &Table) -> String {
let mut ret = String::from("");
let clang = Value::String(String::from("clang"));
let blank = Value::String(String::from(""));
let compiler = match config_table.get("compiler").unwrap_or(&clang) {
Value::String(s) => s,
_ => panic!("fatal: {name}.compiler: invalid type (must be string)"),
};
let compiler_flags = match config_table.get("compiler_flags").unwrap_or(&blank) {
Value::String(s) => s,
_ => panic!("fatal: {name}.compiler_flags: invalid type (must be string)"),
};
let linker_flags = match config_table.get("linker_flags").unwrap_or(&blank) {
Value::String(s) => s,
_ => panic!("fatal: {name}.linker_flags: invalid type (must be string)"),
};
let linker_libs = match config_table.get("linker_libs").unwrap_or(&blank) {
Value::String(s) => s,
_ => panic!("fatal: {name}.linker_libs: invalid type (must be string)"),
};
let compiler_linker = Value::String(compiler.to_string());
let linker = match config_table.get("linker").unwrap_or(&compiler_linker) {
Value::String(s) => s,
_ => panic!("fatal: {name}.linker: invalid type (must be string)"),
};
ret.push_str(&format!(
r#"# BEGIN TARGET {name}
cflags_{name} = {compiler_flags}
linker_flags_{name} = {linker_flags}
linker_libs_{name} = {linker_libs}
rule cc_{name}
deps = gcc
depfile = $out.d
command = {compiler} $cflags_{name} -MD -MF $out.d -o $out -c $in
description = Building [{name}] object $out
rule link_{name}
command = {linker} $linker_flags_{name} -o $out $in $linker_libs_{name}
description = Linking [{name}] $out
build $builddir/{name}: mkdir
"#
));
let sources = config_table
.get("sources")
.unwrap_or_else(|| panic!("fatal: must provide sources"));
let mut source_list: Vec<String> = Vec::new();
match sources {
Value::Array(a) => {
for elem in a {
match elem {
Value::String(s) => {
let obj_name = format!("$builddir/{name}/{s}.o");
ret.push_str(&format!(
"build {obj_name}: cc_{name} {s} | build.ninja || $builddir/{name}\n"
));
source_list.push(obj_name);
}
_ => panic!(
"fatal: element in {name}.sources: invalid source type (must be a string)"
),
}
}
}
_ => panic!("fatal: {name}.sources: invalid sources type (must be a list of strings)"),
};
ret.push_str(&format!("\nbuild $builddir/{name}/{name}: link_{name} "));
ret.push_str(&source_list.join(" "));
ret.push_str(&format!(" \nbuild {name}: phony $builddir/{name}/{name}\n"));
ret.push_str(&format!("# END TARGET {name}\n\n"));
ret
}
fn main() {
let conf_file_name = "ngen.toml";
let conf_file_bytes =
read(conf_file_name).unwrap_or_else(|e| panic!("fatal: could not read file: {e}"));
let conf_file = String::from_utf8_lossy(&conf_file_bytes);
let conf = toml::from_str::<Table>(&conf_file)
.unwrap_or_else(|e| panic!("fatal: could not parse config file: {e}"));
let mut ninjafile = String::from(
r#"builddir = build
rule mkdir
command = mkdir -p $out
description = Creating directory $out
rule regen
command = ngen -f $in
generator = 1
description = Regenerating build.ninja
build build.ninja: regen ngen.toml
pool = console
"#,
);
for k in conf.keys() {
match conf.get(k) {
Some(v) => match v {
Value::Table(t) => {
//print_table(&t, 0);
ninjafile.push_str(&target_generator(&k, &t));
}
Value::String(s) => {
println!("{k}: {s}");
}
_ => continue,
},
None => continue,
}
}
write("build.ninja", ninjafile)
.unwrap_or_else(|e| panic!("fatal: could not write ninja file: {e}"));
}