164 lines
5.2 KiB
Rust
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}"));
|
|
}
|