proper data model, target inheritance done.
This commit is contained in:
parent
8199e20885
commit
4a69bda90d
2 changed files with 195 additions and 108 deletions
130
src/main.rs
130
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,17 +43,10 @@ build $builddir/{name}/dep: mkdir
|
|||
|
||||
"
|
||||
));
|
||||
let sources = config_table
|
||||
.get("sources")
|
||||
.unwrap_or_else(|| panic!("fatal: must provide sources"));
|
||||
|
||||
let mut object_list: Vec<String> = Vec::new();
|
||||
|
||||
match sources {
|
||||
Value::Array(a) => {
|
||||
for elem in a {
|
||||
match elem {
|
||||
Value::String(s) => {
|
||||
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");
|
||||
|
@ -116,20 +55,15 @@ build $builddir/{name}/dep: mkdir
|
|||
));
|
||||
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)"),
|
||||
};
|
||||
|
||||
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::<Table>(&conf_file)
|
||||
let config_file_string = String::from_utf8_lossy(&config_file_raw);
|
||||
let parsed_config = toml::from_str::<Table>(&config_file_string)
|
||||
.unwrap_or_else(|e| panic!("fatal: could not parse config file: {e}"));
|
||||
|
||||
let mut targets: Vec<TargetConfig> = 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)
|
||||
|
|
157
src/target_config.rs
Normal file
157
src/target_config.rs
Normal file
|
@ -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<String>,
|
||||
pub linker: String,
|
||||
pub linker_flags: Vec<String>,
|
||||
pub linker_libs: Vec<String>,
|
||||
pub sources: Vec<String>,
|
||||
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::<String>::new()) && (ret.sources_cmd == String::from("")) {
|
||||
panic!("fatal: must specify EITHER sources OR sources_cmd, not both.");
|
||||
}
|
||||
if !(ret.sources == Vec::<String>::new()) && !(ret.sources_cmd == String::from("")) {
|
||||
panic!("fatal: must specify EITHER sources OR sources_cmd");
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue