commit d35bc218fdbc083a586cfec7fca1ee3c21b312b7 Author: Noah Swerhun Date: Sun Mar 3 21:46:19 2024 -0600 first commit; core functionality done diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e9471 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +ngen.toml +build.ninja diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c30f4ad --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,145 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "ngen" +version = "0.1.0" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dbc0109 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ngen" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +toml = "0.8.10" diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..86ec2b0 --- /dev/null +++ b/install.sh @@ -0,0 +1,2 @@ +cargo build --release +cp target/release/ngen ~/.local/bin diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9350650 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,164 @@ +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 = 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::(&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}")); +}