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 clap::Parser;
|
||||||
|
use core::panic;
|
||||||
use std::fs::{read, write};
|
use std::fs::{read, write};
|
||||||
use toml::{Table, Value};
|
use toml::{Table, Value};
|
||||||
|
|
||||||
|
mod target_config;
|
||||||
|
use target_config::*;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
#[arg(short, long, default_value = "ngen.toml")]
|
#[arg(short, long, default_value = "ngen.toml")]
|
||||||
|
@ -11,74 +15,16 @@ struct Args {
|
||||||
output_file: String,
|
output_file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tabs(num: usize) {
|
fn target_generator(config: &TargetConfig) -> String {
|
||||||
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("\n");
|
let mut ret = String::from("\n");
|
||||||
|
let name = &config.name;
|
||||||
let clang = Value::String(String::from("clang"));
|
let outfile = &config.outfile;
|
||||||
let blank = Value::String(String::from(""));
|
let compiler = &config.compiler;
|
||||||
let aout = Value::String(String::from("a.out"));
|
let compiler_flags = &config.compiler_flags.join(" ");
|
||||||
|
let linker = &config.linker;
|
||||||
let compiler = match config_table.get("compiler").unwrap_or(&clang) {
|
let linker_flags = config.linker_flags.join(" ");
|
||||||
Value::String(s) => s,
|
let linker_libs = config.linker_libs.join(" ");
|
||||||
_ => panic!("fatal: {name}.compiler: invalid type (must be string)"),
|
let sources = &config.sources;
|
||||||
};
|
|
||||||
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)"),
|
|
||||||
};
|
|
||||||
|
|
||||||
ret.push_str(&format!(
|
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();
|
let mut object_list: Vec<String> = Vec::new();
|
||||||
|
|
||||||
match sources {
|
for s in sources {
|
||||||
Value::Array(a) => {
|
|
||||||
for elem in a {
|
|
||||||
match elem {
|
|
||||||
Value::String(s) => {
|
|
||||||
let new_file = s.replace("/", "-");
|
let new_file = s.replace("/", "-");
|
||||||
let obj_name = format!("$builddir/{name}/obj/{new_file}.o");
|
let obj_name = format!("$builddir/{name}/obj/{new_file}.o");
|
||||||
let dep_name = format!("$builddir/{name}/dep/{new_file}.o.d");
|
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);
|
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(&format!("\nbuild $builddir/{name}/{outfile}: link_{name} "));
|
||||||
ret.push_str(&object_list.join(" "));
|
ret.push_str(&object_list.join(" "));
|
||||||
ret.push_str(&format!(
|
ret.push_str(&format!(
|
||||||
" || $builddir/{name}/obj $builddir/{name}/dep\nbuild {name}: phony $builddir/{name}/{outfile}\n"
|
" || $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.push_str(&format!("# END TARGET {name}\n"));
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
@ -140,12 +74,20 @@ fn main() {
|
||||||
let output_file = &args.output_file;
|
let output_file = &args.output_file;
|
||||||
let config_file_name = &args.config_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}"));
|
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 config_file_string = String::from_utf8_lossy(&config_file_raw);
|
||||||
let conf = toml::from_str::<Table>(&conf_file)
|
let parsed_config = toml::from_str::<Table>(&config_file_string)
|
||||||
.unwrap_or_else(|e| panic!("fatal: could not parse config file: {e}"));
|
.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(
|
let mut ninjafile = String::from(
|
||||||
"\
|
"\
|
||||||
# Generated by ngen. Do not modify by hand.
|
# 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"
|
"build {output_file}: regen {config_file_name}\n pool = console\n"
|
||||||
));
|
));
|
||||||
|
|
||||||
for k in conf.keys() {
|
for t in targets {
|
||||||
match conf.get(k) {
|
ninjafile.push_str(&target_generator(&t));
|
||||||
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(output_file, ninjafile)
|
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