From 4a69bda90d7d8180a28108534b9d1000b2d0f4ad Mon Sep 17 00:00:00 2001 From: Noah Swerhun Date: Mon, 4 Mar 2024 17:06:44 -0600 Subject: [PATCH] proper data model, target inheritance done. --- src/main.rs | 146 +++++++++++----------------------------- src/target_config.rs | 157 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 108 deletions(-) create mode 100644 src/target_config.rs diff --git a/src/main.rs b/src/main.rs index be77f4b..08d2f27 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,11 @@ use clap::Parser; +use core::panic; use std::fs::{read, write}; use toml::{Table, Value}; +mod target_config; +use target_config::*; + #[derive(Parser)] struct Args { #[arg(short, long, default_value = "ngen.toml")] @@ -11,74 +15,16 @@ struct Args { output_file: String, } -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 { +fn target_generator(config: &TargetConfig) -> String { let mut ret = String::from("\n"); - - let clang = Value::String(String::from("clang")); - let blank = Value::String(String::from("")); - let aout = Value::String(String::from("a.out")); - - 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 outfile = match config_table.get("outfile").unwrap_or(&aout) { - Value::String(s) => s, - _ => panic!("fatal: {name}.outfile: 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)"), - }; + let name = &config.name; + let outfile = &config.outfile; + let compiler = &config.compiler; + let compiler_flags = &config.compiler_flags.join(" "); + let linker = &config.linker; + let linker_flags = config.linker_flags.join(" "); + let linker_libs = config.linker_libs.join(" "); + let sources = &config.sources; ret.push_str(&format!( "\ @@ -97,39 +43,27 @@ build $builddir/{name}/dep: mkdir " )); - let sources = config_table - .get("sources") - .unwrap_or_else(|| panic!("fatal: must provide sources")); let mut object_list: Vec = Vec::new(); - match sources { - Value::Array(a) => { - for elem in a { - match elem { - Value::String(s) => { - let new_file = s.replace("/", "-"); - let obj_name = format!("$builddir/{name}/obj/{new_file}.o"); - let dep_name = format!("$builddir/{name}/dep/{new_file}.o.d"); - ret.push_str(&format!( - "build {obj_name}: cc_{name} {s}\n dep = {dep_name}\n" - )); - object_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)"), - }; + for s in sources { + let new_file = s.replace("/", "-"); + let obj_name = format!("$builddir/{name}/obj/{new_file}.o"); + let dep_name = format!("$builddir/{name}/dep/{new_file}.o.d"); + ret.push_str(&format!( + "build {obj_name}: cc_{name} {s}\n dep = {dep_name}\n" + )); + object_list.push(obj_name); + } ret.push_str(&format!("\nbuild $builddir/{name}/{outfile}: link_{name} ")); ret.push_str(&object_list.join(" ")); ret.push_str(&format!( " || $builddir/{name}/obj $builddir/{name}/dep\nbuild {name}: phony $builddir/{name}/{outfile}\n" )); + if config.is_default { + ret.push_str(&format!("\ndefault {name}\n")); + } ret.push_str(&format!("# END TARGET {name}\n")); ret @@ -140,12 +74,20 @@ fn main() { let output_file = &args.output_file; let config_file_name = &args.config_file; - let conf_file_bytes = + let config_file_raw = read(config_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::(&conf_file) + let config_file_string = String::from_utf8_lossy(&config_file_raw); + let parsed_config = toml::from_str::
(&config_file_string) .unwrap_or_else(|e| panic!("fatal: could not parse config file: {e}")); + let mut targets: Vec = Vec::new(); + targets.push(TargetConfig::new(&parsed_config, "noname", None)); + for k in parsed_config.keys() { + if let Value::Table(t) = parsed_config.get(k).unwrap() { + targets.push(TargetConfig::new(&t, k, Some(&targets[0]))); + } + } + let mut ninjafile = String::from( "\ # Generated by ngen. Do not modify by hand. @@ -168,20 +110,8 @@ rule regen "build {output_file}: regen {config_file_name}\n pool = console\n" )); - 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, - } + for t in targets { + ninjafile.push_str(&target_generator(&t)); } write(output_file, ninjafile) diff --git a/src/target_config.rs b/src/target_config.rs new file mode 100644 index 0000000..c9cf88a --- /dev/null +++ b/src/target_config.rs @@ -0,0 +1,157 @@ +use toml::{Table, Value}; + +#[derive(Debug, Clone)] +pub struct TargetConfig { + pub name: String, + pub outfile: String, + pub compiler: String, + pub compiler_flags: Vec, + pub linker: String, + pub linker_flags: Vec, + pub linker_libs: Vec, + pub sources: Vec, + pub sources_cmd: String, + pub is_default: bool, +} + +impl TargetConfig { + fn get_default() -> Self { + TargetConfig { + name: String::from("noname"), + outfile: String::from("a.out"), + compiler: String::from("cc"), + compiler_flags: Vec::new(), + linker: String::from(""), + linker_flags: Vec::new(), + linker_libs: Vec::new(), + sources: Vec::new(), + sources_cmd: String::from(""), + is_default: false, + } + } + pub fn new(table: &Table, name: &str, global_conf: Option<&Self>) -> Self { + let mut ret: Self; + let mut linker_set = false; + + if let Some(global) = global_conf { + ret = global.clone(); + } else { + ret = Self::get_default(); + } + + ret.name = String::from(name); + ret.is_default = false; + + for k in table.keys() { + match &*k.to_owned() { + "outfile" => { + if let Value::String(s) = table.get(k).unwrap() { + ret.outfile = s.to_owned(); + } else { + panic!("fatal: {k} invalid type, must be a string."); + } + } + "compiler" => { + if let Value::String(s) = table.get(k).unwrap() { + ret.compiler = s.to_owned(); + } else { + panic!("fatal: {k} invalid type, must be a string."); + } + } + "compiler_flags" => { + if let Value::Array(a) = table.get(k).unwrap() { + for element in a { + if let Value::String(s) = element { + ret.compiler_flags.push(s.to_owned()); + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + "linker" => { + if let Value::String(s) = table.get(k).unwrap() { + ret.linker = s.to_owned(); + linker_set = true; + } else { + panic!("fatal: {k} invalid type, must be a string."); + } + } + "linker_flags" => { + if let Value::Array(a) = table.get(k).unwrap() { + for element in a { + if let Value::String(s) = element { + ret.linker_flags.push(s.to_owned()); + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + "linker_libs" => { + if let Value::Array(a) = table.get(k).unwrap() { + for element in a { + if let Value::String(s) = element { + ret.linker_libs.push(s.to_owned()); + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + "sources" => { + if let Value::Array(a) = table.get(k).unwrap() { + for element in a { + if let Value::String(s) = element { + ret.sources.push(s.to_owned()); + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + } else { + panic!("fatal: {k} invalid type, must be a array of strings."); + } + } + "sources_cmd" => { + if let Value::String(s) = table.get(k).unwrap() { + ret.sources_cmd = s.to_owned(); + } else { + panic!("fatal: {k} invalid type, must be a string."); + } + } + "default" => { + if let Value::Boolean(b) = table.get(k).unwrap() { + ret.is_default = *b; + } else { + panic!("fatal: {k} invalid type, must be a boolean."); + } + } + _ => { + if let Value::Table(_) = table.get(k).unwrap() { + } else { + panic!("fatal: unrecognized key {k}."); + } + } + } + } + + if !linker_set { + ret.linker = ret.compiler.clone(); + } + + if (ret.sources == Vec::::new()) && (ret.sources_cmd == String::from("")) { + panic!("fatal: must specify EITHER sources OR sources_cmd, not both."); + } + if !(ret.sources == Vec::::new()) && !(ret.sources_cmd == String::from("")) { + panic!("fatal: must specify EITHER sources OR sources_cmd"); + } + + ret + } +}