praser done, using serde (amazing)
This commit is contained in:
parent
001a9735ee
commit
7865c73006
13 changed files with 335 additions and 500 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -85,6 +85,7 @@ name = "ngen"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -7,4 +7,5 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.1", features = ["derive", "std", "help", "usage"], default-features = false }
|
clap = { version = "4.5.1", features = ["derive", "std", "help", "usage"], default-features = false }
|
||||||
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
toml = "0.8.10"
|
toml = "0.8.10"
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use toml::{Table, Value};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ConfigFileError<'a> {
|
|
||||||
pub key_path: String,
|
|
||||||
pub message: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ConfigFileError<'_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"config file error: key {}: {}.",
|
|
||||||
self.key_path, self.message
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_as_str<'a>(
|
|
||||||
table: &'a Table,
|
|
||||||
key: &str,
|
|
||||||
key_path: String,
|
|
||||||
) -> Result<Option<&'a str>, ConfigFileError<'a>> {
|
|
||||||
if let Some(v) = table.get(key) {
|
|
||||||
if let Value::String(s) = v {
|
|
||||||
Ok(Some(s))
|
|
||||||
} else {
|
|
||||||
Err(ConfigFileError {
|
|
||||||
key_path,
|
|
||||||
message: "invalid type, should be string",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_as_table<'a>(
|
|
||||||
table: &'a Table,
|
|
||||||
key: &str,
|
|
||||||
key_path: String,
|
|
||||||
) -> Result<Option<&'a Table>, ConfigFileError<'a>> {
|
|
||||||
if let Some(v) = table.get(key) {
|
|
||||||
if let Value::Table(s) = v {
|
|
||||||
Ok(Some(s))
|
|
||||||
} else {
|
|
||||||
Err(ConfigFileError {
|
|
||||||
key_path,
|
|
||||||
message: "invalid type, should be a table of key-value pairs",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_as_bool<'a>(
|
|
||||||
table: &'a Table,
|
|
||||||
key: &str,
|
|
||||||
key_path: String,
|
|
||||||
) -> Result<Option<bool>, ConfigFileError<'a>> {
|
|
||||||
if let Some(v) = table.get(key) {
|
|
||||||
if let Value::Boolean(s) = v {
|
|
||||||
Ok(Some(*s))
|
|
||||||
} else {
|
|
||||||
Err(ConfigFileError {
|
|
||||||
key_path,
|
|
||||||
message: "invalid type, should be boolean",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_as_vec_str<'a>(
|
|
||||||
table: &'a Table,
|
|
||||||
key: &str,
|
|
||||||
key_path: String,
|
|
||||||
) -> Result<Option<Vec<&'a str>>, ConfigFileError<'a>> {
|
|
||||||
if let Some(v) = table.get(key) {
|
|
||||||
if let Value::Array(a) = v {
|
|
||||||
let mut ret: Vec<&'a str> = vec![];
|
|
||||||
for elem in a {
|
|
||||||
if let Value::String(s) = elem {
|
|
||||||
ret.push(&s);
|
|
||||||
} else {
|
|
||||||
return Err(ConfigFileError {
|
|
||||||
key_path: format!("{key_path} elemement '{elem}'"),
|
|
||||||
message: "invalid type, should be string",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(ret))
|
|
||||||
} else {
|
|
||||||
Err(ConfigFileError {
|
|
||||||
key_path,
|
|
||||||
message: "invalid type, should be array of strings",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
use toml::Table;
|
|
||||||
|
|
||||||
use crate::config_file_parsing::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ConfigTable<'a> {
|
|
||||||
pub build_dir: &'a str,
|
|
||||||
pub compile_commands: bool,
|
|
||||||
pub compile_commands_target: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ConfigTable<'a> {
|
|
||||||
pub fn get_default() -> Self {
|
|
||||||
ConfigTable {
|
|
||||||
build_dir: "build",
|
|
||||||
compile_commands: false,
|
|
||||||
compile_commands_target: "main",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_table(table: &'a Table, key_path: String) -> Result<Self, ConfigFileError<'a>> {
|
|
||||||
let mut ret = Self::get_default();
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_str(table, "build_dir", format!("{key_path}.build_dir"))? {
|
|
||||||
ret.build_dir = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_bool(
|
|
||||||
table,
|
|
||||||
"compile_commands",
|
|
||||||
format!("{key_path}.compile_commands"),
|
|
||||||
)? {
|
|
||||||
ret.compile_commands = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_str(
|
|
||||||
table,
|
|
||||||
"compile_commands_target",
|
|
||||||
format!("{key_path}.compile_commands_target"),
|
|
||||||
)? {
|
|
||||||
ret.compile_commands_target = v;
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
|
278
src/main.rs
278
src/main.rs
|
@ -1,21 +1,15 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use config_file_parsing::get_key_as_table;
|
use core::panic;
|
||||||
use core::{panic, str};
|
use std::collections::HashMap;
|
||||||
use std::{
|
use std::fs::read_to_string;
|
||||||
fs::{read_to_string, write},
|
|
||||||
path::Path,
|
|
||||||
process::exit,
|
|
||||||
};
|
|
||||||
use toml::{Table, Value};
|
|
||||||
|
|
||||||
mod target;
|
mod ngen_toml;
|
||||||
use target::*;
|
use ngen_toml::*;
|
||||||
|
|
||||||
mod config_table;
|
mod parsed_config_processing;
|
||||||
use config_table::*;
|
use parsed_config_processing::config_table::*;
|
||||||
|
use parsed_config_processing::parsed_config_processing::*;
|
||||||
mod config_file_parsing;
|
use parsed_config_processing::target::*;
|
||||||
mod target_options;
|
|
||||||
|
|
||||||
/// ngen is a build file generator for the ninja build system.
|
/// ngen is a build file generator for the ninja build system.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -32,226 +26,54 @@ struct Args {
|
||||||
write_compile_commands: Option<String>,
|
write_compile_commands: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_ninja_code(target: &Target) -> String {
|
|
||||||
let mut ret = String::from("\n");
|
|
||||||
|
|
||||||
// necessary because you cannot access struct members in a format string :(
|
|
||||||
let name = &target.name;
|
|
||||||
let outfile = &target.outfile;
|
|
||||||
let compiler = &target.compiler;
|
|
||||||
let compiler_flags = &target.compiler_flags.join(" ");
|
|
||||||
let linker = &target.linker;
|
|
||||||
let linker_flags = &target.linker_flags.join(" ");
|
|
||||||
let linker_libs = &target.linker_libs.join(" ");
|
|
||||||
let sources = &target.sources;
|
|
||||||
|
|
||||||
ret.push_str(&format!(
|
|
||||||
"\
|
|
||||||
# BEGIN TARGET {name}
|
|
||||||
rule cc_{name}
|
|
||||||
deps = gcc
|
|
||||||
depfile = $dep
|
|
||||||
command = {compiler} {compiler_flags} -MD -MF $dep -o $out -c $in
|
|
||||||
description = Building $in -> $out
|
|
||||||
rule link_{name}
|
|
||||||
command = {linker} {linker_flags} -o $out $in {linker_libs}
|
|
||||||
description = Linking $out
|
|
||||||
|
|
||||||
build $builddir/{name}/obj: mkdir
|
|
||||||
build $builddir/{name}/dep: mkdir
|
|
||||||
|
|
||||||
"
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut object_list: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
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 target.is_default {
|
|
||||||
ret.push_str(&format!("\ndefault {name}\n"));
|
|
||||||
}
|
|
||||||
ret.push_str(&format!("# END TARGET {name}\n"));
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_compile_commands(target: &Target, build_dir: &str) -> String {
|
|
||||||
let mut ret = String::from("[\n");
|
|
||||||
|
|
||||||
let name = &target.name;
|
|
||||||
let compiler = &target.compiler;
|
|
||||||
let compiler_flags = &target.compiler_flags.join(" ");
|
|
||||||
|
|
||||||
let pwd = Path::new(".");
|
|
||||||
let pwd = pwd
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap_or_else(|e| panic!("cwd is non canonical (something is very wrong): {e}"));
|
|
||||||
let pwd = pwd.to_str().unwrap();
|
|
||||||
|
|
||||||
for (i, s) in target.sources.iter().enumerate() {
|
|
||||||
let new_file = s.replace("/", "-");
|
|
||||||
let obj_name = format!("{build_dir}/{name}/obj/{new_file}.o");
|
|
||||||
|
|
||||||
ret.push_str(&format!(
|
|
||||||
" {{
|
|
||||||
\"directory\": \"{pwd}\",
|
|
||||||
\"command\": \"{compiler} {compiler_flags} -o {obj_name} -c {s}\",
|
|
||||||
\"file\": \"{s}\"
|
|
||||||
}}"
|
|
||||||
));
|
|
||||||
if i != target.sources.len() - 1 {
|
|
||||||
ret.push(',');
|
|
||||||
}
|
|
||||||
ret.push('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.push_str("]\n");
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let output_file = &args.output_file;
|
|
||||||
let config_file_name = &args.config_file;
|
|
||||||
|
|
||||||
let config_file = read_to_string(config_file_name)
|
let config_file = read_to_string(&args.config_file)
|
||||||
.unwrap_or_else(|e| panic!("could not read config file {config_file_name}: {e}"));
|
.unwrap_or_else(|e| panic!("error reading config file `{}`: {e}", args.config_file));
|
||||||
let toml_config = toml::from_str::<Table>(&config_file)
|
let toml_config: NgenToml = toml::from_str(&config_file)
|
||||||
.unwrap_or_else(|e| panic!("error parsing toml file {config_file_name}: {e}"));
|
.unwrap_or_else(|e| panic!("error parsing config file `{}`: {e}", args.config_file));
|
||||||
|
|
||||||
// get config
|
let config_table: ConfigTable;
|
||||||
let config_tbl: ConfigTable;
|
match &toml_config.config {
|
||||||
match get_key_as_table(&toml_config, "config", "".to_string()) {
|
Some(v) => {
|
||||||
Ok(v) => match v {
|
config_table = ConfigTable::from_parsed_config(&v).unwrap_or_else(|e| panic!("{e}"))
|
||||||
Some(c) => {
|
}
|
||||||
config_tbl = ConfigTable::from_table(&c, ".config".to_string())
|
None => config_table = ConfigTable::get_default(),
|
||||||
.unwrap_or_else(|e| panic!("{e}"))
|
}
|
||||||
|
|
||||||
|
let all_parsed_targets: &HashMap<String, ParsedTarget>;
|
||||||
|
match &toml_config.targets {
|
||||||
|
Some(v) => {
|
||||||
|
all_parsed_targets = v;
|
||||||
|
}
|
||||||
|
None => panic!(
|
||||||
|
"{}",
|
||||||
|
ConfigurationError {
|
||||||
|
message: "no targets found, nothing to do. Exiting".to_string()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut targets: HashMap<&str, Target> = HashMap::new();
|
||||||
|
for (name, parsed_target) in all_parsed_targets {
|
||||||
|
if let Some(_) = targets.get(name as &str) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut inheritance_chain: HashMap<&str, &str> = HashMap::new();
|
||||||
|
match Target::from_parsed_target(
|
||||||
|
parsed_target,
|
||||||
|
name,
|
||||||
|
all_parsed_targets,
|
||||||
|
&targets,
|
||||||
|
&mut inheritance_chain,
|
||||||
|
) {
|
||||||
|
Ok(v) => {
|
||||||
|
targets.extend(v.into_iter());
|
||||||
}
|
}
|
||||||
None => config_tbl = ConfigTable::get_default(),
|
|
||||||
},
|
|
||||||
Err(e) => panic!("{e}"),
|
Err(e) => panic!("{e}"),
|
||||||
}
|
}
|
||||||
println!("{:?}", config_tbl);
|
|
||||||
//let mut config = Config::get_default();
|
|
||||||
//if let Some(v) = parsed_config.get("config") {
|
|
||||||
// if let Value::Table(t) = v {
|
|
||||||
// config.set(&t);
|
|
||||||
// } else {
|
|
||||||
// panic!("error parsing config table: config invalid type, must be a table.");
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//----
|
|
||||||
|
|
||||||
// parse the main target first
|
|
||||||
//let mut targets: Vec<Target> = vec![];
|
|
||||||
|
|
||||||
match Target::from_table(&toml_config, "main", "".to_string()) {
|
|
||||||
Ok(v) => println!("{:?}", v),
|
|
||||||
Err(e) => panic!("{e}"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(10);
|
println!("{:#?}", targets);
|
||||||
|
|
||||||
// targets.push(main_target.clone());
|
|
||||||
// let mut target_for_comp_cmds = main_target;
|
|
||||||
// let mut target_for_comp_cmds_changed = false;
|
|
||||||
//
|
|
||||||
// // parse other targets
|
|
||||||
// for k in parsed_config.keys() {
|
|
||||||
// if let Value::Table(t) = parsed_config.get(k).unwrap() {
|
|
||||||
// if k == "config" {
|
|
||||||
// continue; // ignore config
|
|
||||||
// }
|
|
||||||
// let target = Target::new(&t, k, Some(&targets[0]));
|
|
||||||
// targets.push(target);
|
|
||||||
// if let Some(_) = args.write_compile_commands {
|
|
||||||
// if *k == config.compile_commands_target {
|
|
||||||
// target_for_comp_cmds = targets[targets.len() - 1].clone();
|
|
||||||
// target_for_comp_cmds_changed = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let Some(file) = args.write_compile_commands {
|
|
||||||
// if config.compile_commands_target == "" {
|
|
||||||
// eprintln!(
|
|
||||||
// "ngen: warn: no compile_commands target found, using 'main' target by default."
|
|
||||||
// );
|
|
||||||
// } else if !target_for_comp_cmds_changed && config.compile_commands_target != "main" {
|
|
||||||
// let c = &config.compile_commands_target;
|
|
||||||
// eprintln!(
|
|
||||||
// "ngen: warn: compile_commands target {c} not found, using 'main' target instead."
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// write(
|
|
||||||
// &file,
|
|
||||||
// &gen_compile_commands(&target_for_comp_cmds, &config.build_dir),
|
|
||||||
// )
|
|
||||||
// .unwrap_or_else(|e| panic!("fatal: could not write {file}: {e}"));
|
|
||||||
// // no need to write ninja file
|
|
||||||
// exit(0);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let build_dir = &config.build_dir;
|
|
||||||
// let mut ninjafile = format!(
|
|
||||||
// "\
|
|
||||||
//# Generated by ngen. Do not modify by hand.
|
|
||||||
//
|
|
||||||
//builddir = {build_dir}
|
|
||||||
//
|
|
||||||
//rule mkdir
|
|
||||||
// command = mkdir -p $out
|
|
||||||
// description = Creating directory $out
|
|
||||||
//
|
|
||||||
//rule regen_ninjafile
|
|
||||||
// command = ngen -c $in -o $out
|
|
||||||
// generator = 1
|
|
||||||
// description = Regenerating $out
|
|
||||||
//
|
|
||||||
//",
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// if config.compile_commands {
|
|
||||||
// ninjafile.push_str(&format!(
|
|
||||||
// "\
|
|
||||||
//rule regen_compile_commands
|
|
||||||
// command = ngen -c $in --write-compile-commands $out
|
|
||||||
// description = Regenerating $out
|
|
||||||
//
|
|
||||||
//build $builddir: mkdir
|
|
||||||
//
|
|
||||||
//build $builddir/compile_commands.json: regen_compile_commands {config_file_name} || $builddir
|
|
||||||
// pool = console
|
|
||||||
//
|
|
||||||
//build {output_file}: regen_ninjafile {config_file_name} || $builddir/compile_commands.json
|
|
||||||
// pool = console
|
|
||||||
//"
|
|
||||||
// ));
|
|
||||||
// } else {
|
|
||||||
// ninjafile.push_str(&format!(
|
|
||||||
// "build {output_file}: regen_ninjafile {config_file_name}\n pool = console\n"
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for t in targets {
|
|
||||||
// ninjafile.push_str(&gen_ninja_code(&t));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// write(output_file, &ninjafile)
|
|
||||||
// .unwrap_or_else(|e| panic!("could not write ninja file {ninjafile}: {e}"));
|
|
||||||
}
|
}
|
||||||
|
|
38
src/ngen_toml.rs
Normal file
38
src/ngen_toml.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct ParsedTarget {
|
||||||
|
pub outfile: Option<String>,
|
||||||
|
pub compiler: Option<String>,
|
||||||
|
pub compiler_flags: Option<Vec<String>>,
|
||||||
|
pub linker: Option<String>,
|
||||||
|
pub linker_flags: Option<Vec<String>>,
|
||||||
|
pub linker_libs: Option<Vec<String>>,
|
||||||
|
pub sources: Option<Vec<String>>,
|
||||||
|
pub opts: Option<ParsedTargetOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct ParsedTargetOptions {
|
||||||
|
pub inherit: Option<bool>,
|
||||||
|
pub inherit_from: Option<String>,
|
||||||
|
pub default: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct ParsedConfigTable {
|
||||||
|
pub build_dir: Option<String>,
|
||||||
|
pub compile_commands: Option<bool>,
|
||||||
|
pub compile_commands_target: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct NgenToml {
|
||||||
|
pub config: Option<ParsedConfigTable>,
|
||||||
|
pub targets: Option<HashMap<String, ParsedTarget>>,
|
||||||
|
}
|
39
src/parsed_config_processing/config_table.rs
Normal file
39
src/parsed_config_processing/config_table.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use super::parsed_config_processing::*;
|
||||||
|
use crate::ngen_toml::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ConfigTable<'a> {
|
||||||
|
pub build_dir: &'a str,
|
||||||
|
pub compile_commands: bool,
|
||||||
|
pub compile_commands_target: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ConfigTable<'a> {
|
||||||
|
pub fn get_default() -> Self {
|
||||||
|
ConfigTable {
|
||||||
|
build_dir: "build",
|
||||||
|
compile_commands: false,
|
||||||
|
compile_commands_target: "main",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_parsed_config(
|
||||||
|
parsed_config_table: &'a ParsedConfigTable,
|
||||||
|
) -> Result<Self, ConfigurationError> {
|
||||||
|
let mut ret = Self::get_default();
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_config_table.build_dir {
|
||||||
|
ret.build_dir = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_config_table.compile_commands {
|
||||||
|
ret.compile_commands = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_config_table.compile_commands_target {
|
||||||
|
ret.compile_commands_target = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
4
src/parsed_config_processing/mod.rs
Normal file
4
src/parsed_config_processing/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod config_table;
|
||||||
|
pub mod parsed_config_processing;
|
||||||
|
pub mod target;
|
||||||
|
pub mod target_options;
|
12
src/parsed_config_processing/parsed_config_processing.rs
Normal file
12
src/parsed_config_processing/parsed_config_processing.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConfigurationError {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ConfigurationError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "configuration error: {}", self.message)
|
||||||
|
}
|
||||||
|
}
|
151
src/parsed_config_processing/target.rs
Normal file
151
src/parsed_config_processing/target.rs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
use core::str;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::parsed_config_processing::*;
|
||||||
|
use super::target_options::*;
|
||||||
|
use crate::ngen_toml::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Target<'a> {
|
||||||
|
pub outfile: &'a str,
|
||||||
|
pub compiler: &'a str,
|
||||||
|
pub compiler_flags: Vec<&'a str>,
|
||||||
|
pub linker: &'a str,
|
||||||
|
pub linker_flags: Vec<&'a str>,
|
||||||
|
pub linker_libs: Vec<&'a str>,
|
||||||
|
pub sources: Vec<&'a str>,
|
||||||
|
pub opts: TargetOptions<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Target<'a> {
|
||||||
|
pub fn get_default(
|
||||||
|
) -> Self {
|
||||||
|
Target {
|
||||||
|
outfile: "a.out",
|
||||||
|
compiler: "cc",
|
||||||
|
compiler_flags: vec![],
|
||||||
|
linker: "cc",
|
||||||
|
linker_flags: vec![],
|
||||||
|
linker_libs: vec![],
|
||||||
|
sources: vec![],
|
||||||
|
opts: TargetOptions::get_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_parsed_target(
|
||||||
|
parsed_target: &'a ParsedTarget,
|
||||||
|
name: &'a str,
|
||||||
|
all_parsed_targets: &'a HashMap<String, ParsedTarget>,
|
||||||
|
all_targets: &HashMap<&'a str, Target<'a>>,
|
||||||
|
inheritance_chain: &mut HashMap<&'a str, &'a str>
|
||||||
|
) -> Result<HashMap<&'a str, Target<'a>>, ConfigurationError> {
|
||||||
|
let mut retmap: HashMap<&str, Target> = HashMap::new();
|
||||||
|
let mut ret = Self::get_default();
|
||||||
|
|
||||||
|
if ret.opts.inherit_from == name {
|
||||||
|
ret.opts.inherit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_target.opts {
|
||||||
|
ret.opts = TargetOptions::from_parsed_target_options(v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.opts.inherit {
|
||||||
|
let inherit_target: Target;
|
||||||
|
|
||||||
|
match all_targets.get(ret.opts.inherit_from) {
|
||||||
|
Some(v) => {
|
||||||
|
inherit_target = v.clone()
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
match all_parsed_targets.get(ret.opts.inherit_from) {
|
||||||
|
Some(v) => {
|
||||||
|
let inherited_targets: HashMap<&str, Target>;
|
||||||
|
if let Some(_) = inheritance_chain.get(ret.opts.inherit_from) {
|
||||||
|
let mut message = format!("inheritance dependency cylce detected: ");
|
||||||
|
for (targ_name, depends_on) in inheritance_chain {
|
||||||
|
message.push_str(&format!("`{}` depends on `{}`, ", targ_name, depends_on));
|
||||||
|
}
|
||||||
|
message.push_str(&format!("and `{}` depends on `{}`", name, ret.opts.inherit_from));
|
||||||
|
return Err(ConfigurationError { message });
|
||||||
|
}
|
||||||
|
inheritance_chain.insert(name, ret.opts.inherit_from);
|
||||||
|
inherited_targets = Self::from_parsed_target(v, ret.opts.inherit_from, all_parsed_targets, all_targets, inheritance_chain)?;
|
||||||
|
inherit_target = inherited_targets.get(ret.opts.inherit_from).unwrap().clone();
|
||||||
|
retmap.extend(inherited_targets.into_iter());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(ConfigurationError {
|
||||||
|
message:
|
||||||
|
format!("target `{0}` trying to inherit from target `{1}`, but target `{1}` does not exist",
|
||||||
|
name,
|
||||||
|
ret.opts.inherit_from)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if let None = parsed_target.outfile {
|
||||||
|
ret.outfile = inherit_target.outfile;
|
||||||
|
}
|
||||||
|
if let None = parsed_target.compiler {
|
||||||
|
ret.compiler = inherit_target.compiler;
|
||||||
|
}
|
||||||
|
if let None = parsed_target.linker {
|
||||||
|
ret.linker = inherit_target.linker;
|
||||||
|
}
|
||||||
|
|
||||||
|
for string in inherit_target.compiler_flags {
|
||||||
|
ret.compiler_flags.push(string);
|
||||||
|
}
|
||||||
|
for string in inherit_target.linker_flags {
|
||||||
|
ret.linker_flags.push(string);
|
||||||
|
}
|
||||||
|
for string in inherit_target.linker_libs {
|
||||||
|
ret.linker_libs.push(string);
|
||||||
|
}
|
||||||
|
for string in inherit_target.sources {
|
||||||
|
ret.sources.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_target.outfile {
|
||||||
|
ret.outfile = v;
|
||||||
|
}
|
||||||
|
if let Some(v) = &parsed_target.compiler {
|
||||||
|
ret.compiler = v;
|
||||||
|
}
|
||||||
|
if let Some(v) = &parsed_target.compiler_flags {
|
||||||
|
for string in v {
|
||||||
|
ret.compiler_flags.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &parsed_target.linker {
|
||||||
|
ret.linker = v;
|
||||||
|
} else {
|
||||||
|
if let Some(v) = &parsed_target.compiler {
|
||||||
|
ret.linker = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &parsed_target.linker_flags {
|
||||||
|
for string in v {
|
||||||
|
ret.linker_flags.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &parsed_target.linker_libs {
|
||||||
|
for string in v {
|
||||||
|
ret.linker_libs.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &parsed_target.sources {
|
||||||
|
for string in v {
|
||||||
|
ret.sources.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retmap.insert(name, ret);
|
||||||
|
|
||||||
|
Ok(retmap)
|
||||||
|
}
|
||||||
|
}
|
38
src/parsed_config_processing/target_options.rs
Normal file
38
src/parsed_config_processing/target_options.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use super::parsed_config_processing::*;
|
||||||
|
use crate::ngen_toml::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TargetOptions<'a> {
|
||||||
|
pub inherit: bool,
|
||||||
|
pub inherit_from: &'a str,
|
||||||
|
pub default: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TargetOptions<'a> {
|
||||||
|
pub fn get_default() -> Self {
|
||||||
|
TargetOptions {
|
||||||
|
inherit: true,
|
||||||
|
inherit_from: "main",
|
||||||
|
default: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_parsed_target_options(
|
||||||
|
parsed_target_options: &'a ParsedTargetOptions,
|
||||||
|
) -> Result<Self, ConfigurationError> {
|
||||||
|
let mut ret = Self::get_default();
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_target_options.inherit {
|
||||||
|
ret.inherit = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_target_options.inherit_from {
|
||||||
|
ret.inherit_from = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &parsed_target_options.default {
|
||||||
|
ret.default = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
use toml::Table;
|
|
||||||
|
|
||||||
use crate::config_file_parsing::*;
|
|
||||||
use crate::target_options::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Target<'a> {
|
|
||||||
pub name: &'a str,
|
|
||||||
pub outfile: &'a str,
|
|
||||||
pub compiler: &'a str,
|
|
||||||
pub compiler_flags: Vec<&'a str>,
|
|
||||||
pub linker: &'a str,
|
|
||||||
pub linker_flags: Vec<&'a str>,
|
|
||||||
pub linker_libs: Vec<&'a str>,
|
|
||||||
pub sources: Vec<&'a str>,
|
|
||||||
pub is_default: bool,
|
|
||||||
pub opts: TargetOptions<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Target<'a> {
|
|
||||||
pub fn get_default() -> Self {
|
|
||||||
Target {
|
|
||||||
name: "",
|
|
||||||
outfile: "a.out",
|
|
||||||
compiler: "cc",
|
|
||||||
compiler_flags: vec![""],
|
|
||||||
linker: "cc",
|
|
||||||
linker_flags: vec![""],
|
|
||||||
linker_libs: vec![""],
|
|
||||||
sources: vec![""],
|
|
||||||
is_default: false,
|
|
||||||
opts: TargetOptions::get_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_table(
|
|
||||||
table: &'a Table,
|
|
||||||
name: &'a str,
|
|
||||||
key_path: String,
|
|
||||||
) -> Result<Self, ConfigFileError<'a>> {
|
|
||||||
let mut ret = Self::get_default();
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_table(table, "opts", format!("{key_path}.opts"))? {
|
|
||||||
ret.opts = TargetOptions::from_table(&v, format!("{key_path}.opts"))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.name = name;
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_str(table, "outfile", format!("{key_path}.outfile"))? {
|
|
||||||
ret.outfile = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_str(table, "compiler", format!("{key_path}.compiler"))? {
|
|
||||||
ret.compiler = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_vec_str(
|
|
||||||
table,
|
|
||||||
"compiler_flags",
|
|
||||||
format!("{key_path}.compiler_flags"),
|
|
||||||
)? {
|
|
||||||
ret.compiler_flags = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_str(table, "linker", format!("{key_path}.linker"))? {
|
|
||||||
ret.linker = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) =
|
|
||||||
get_key_as_vec_str(table, "linker_flags", format!("{key_path}.linker_flags"))?
|
|
||||||
{
|
|
||||||
ret.linker_flags = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) =
|
|
||||||
get_key_as_vec_str(table, "linker_libs", format!("{key_path}.linker_libs"))?
|
|
||||||
{
|
|
||||||
ret.linker_libs = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_vec_str(table, "sources", format!("{key_path}.sources"))? {
|
|
||||||
ret.sources = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_bool(table, "is_default", format!("{key_path}.is_default"))? {
|
|
||||||
ret.is_default = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
use toml::{Table, Value};
|
|
||||||
|
|
||||||
use crate::config_file_parsing::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TargetOptions<'a> {
|
|
||||||
pub inherit: bool,
|
|
||||||
pub inherit_from: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TargetOptions<'a> {
|
|
||||||
pub fn get_default() -> Self {
|
|
||||||
TargetOptions {
|
|
||||||
inherit: true,
|
|
||||||
inherit_from: "main",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_table(table: &'a Table, key_path: String) -> Result<Self, ConfigFileError<'a>> {
|
|
||||||
let mut ret = Self::get_default();
|
|
||||||
|
|
||||||
if let Some(v) = get_key_as_bool(table, "inherit", format!("{key_path}.inherit"))? {
|
|
||||||
ret.inherit = v;
|
|
||||||
}
|
|
||||||
if let Some(v) = get_key_as_str(table, "inherit_from", format!("{key_path}.inherit_from"))?
|
|
||||||
{
|
|
||||||
ret.inherit_from = v;
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue