major rewrite #2, dependency functionality
This commit is contained in:
parent
e20beb9bf0
commit
8d05da5209
18 changed files with 645 additions and 370 deletions
|
@ -1,14 +1,18 @@
|
||||||
use std::path::Path;
|
use std::{
|
||||||
|
fs::OpenOptions,
|
||||||
|
io::{BufWriter, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::parsed_config_processing::*;
|
use crate::parse_deser::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn gen_compile_commands(
|
pub fn create_compile_commands_content(
|
||||||
name: &str,
|
name: &str,
|
||||||
target: &Target,
|
target: &Target,
|
||||||
config: &ConfigTable,
|
config: &Config,
|
||||||
) -> Result<String, ContentGenerationError> {
|
) -> Result<String> {
|
||||||
let mut ret = "[\n".to_string();
|
let mut ret = "[\n".to_string();
|
||||||
|
|
||||||
let compiler_flags = target.compiler_flags.join(" ");
|
let compiler_flags = target.compiler_flags.join(" ");
|
||||||
|
@ -18,7 +22,8 @@ pub fn gen_compile_commands(
|
||||||
let pwd = pwd.to_str().unwrap();
|
let pwd = pwd.to_str().unwrap();
|
||||||
|
|
||||||
if target.sources.len() == 0 {
|
if target.sources.len() == 0 {
|
||||||
return Err(ContentGenerationError {
|
return Err(Error {
|
||||||
|
kind: ErrorKind::NoSources,
|
||||||
message: format!(
|
message: format!(
|
||||||
"compile_commands_target is set to `{0}`, however `{0}` has no sources.",
|
"compile_commands_target is set to `{0}`, however `{0}` has no sources.",
|
||||||
name
|
name
|
||||||
|
@ -48,3 +53,15 @@ pub fn gen_compile_commands(
|
||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_compile_commands(filename: &str, content: &[u8]) -> std::io::Result<()> {
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.create(true)
|
||||||
|
.open(filename)?;
|
||||||
|
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
writer.write(content)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,11 +1,18 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub struct ContentGenerationError {
|
pub enum ErrorKind {
|
||||||
|
NoSources,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Error {
|
||||||
|
pub kind: ErrorKind,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ContentGenerationError {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "content generation error: {}", self.message)
|
write!(f, "content generation error: {}", self.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
@ -1,7 +1,7 @@
|
||||||
pub mod compile_commands;
|
pub mod compile_commands;
|
||||||
pub mod content_generation_error;
|
pub mod error;
|
||||||
pub mod ninja_file;
|
pub mod ninja_file;
|
||||||
|
|
||||||
pub use compile_commands::*;
|
pub use compile_commands::*;
|
||||||
pub use content_generation_error::*;
|
pub use error::*;
|
||||||
pub use ninja_file::*;
|
pub use ninja_file::*;
|
|
@ -1,8 +1,12 @@
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::BufWriter;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::parsed_config_processing::*;
|
use crate::parse_deser::*;
|
||||||
use crate::Args;
|
use crate::Args;
|
||||||
|
|
||||||
pub fn gen_ninja_header(config: &ConfigTable, cmdline_args: &Args) -> String {
|
pub fn gen_ninja_header(config: &Config, cmdline_args: &Args) -> Result<String> {
|
||||||
let mut ret = "".to_string();
|
let mut ret = "".to_string();
|
||||||
ret.push_str(&format!(
|
ret.push_str(&format!(
|
||||||
"## Generated by ngen
|
"## Generated by ngen
|
||||||
|
@ -45,12 +49,13 @@ build {0}: regen_ninjafile {1} || $builddir/compile_commands.json
|
||||||
cmdline_args.output_file, cmdline_args.config_file
|
cmdline_args.output_file, cmdline_args.config_file
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ret
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_ninja_target(name: &str, target: &Target) -> Result<String, ContentGenerationError> {
|
pub fn gen_ninja_target(name: &str, target: &Target) -> Result<String> {
|
||||||
if target.sources.len() == 0 {
|
if target.sources.len() == 0 {
|
||||||
return Err(ContentGenerationError {
|
return Err(Error {
|
||||||
|
kind: ErrorKind::NoSources,
|
||||||
message: format!("target `{}` has no sources", name),
|
message: format!("target `{}` has no sources", name),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -97,9 +102,10 @@ build $builddir/{0}/dep: mkdir
|
||||||
name, target.outfile
|
name, target.outfile
|
||||||
));
|
));
|
||||||
ret.push_str(&object_list.join(" "));
|
ret.push_str(&object_list.join(" "));
|
||||||
|
let deps = &target.opts.depend_on.join(" ");
|
||||||
ret.push_str(&format!(
|
ret.push_str(&format!(
|
||||||
" || $builddir/{0}/obj $builddir/{0}/dep\nbuild {0}: phony $builddir/{0}/{1}\n",
|
" | {2} || $builddir/{0}/obj $builddir/{0}/dep\nbuild {0}: phony $builddir/{0}/{1}\n",
|
||||||
name, target.outfile
|
name, target.outfile, deps
|
||||||
));
|
));
|
||||||
if target.opts.default {
|
if target.opts.default {
|
||||||
ret.push_str(&format!("\ndefault {}\n", name));
|
ret.push_str(&format!("\ndefault {}\n", name));
|
||||||
|
@ -107,3 +113,39 @@ build $builddir/{0}/dep: mkdir
|
||||||
ret.push_str(&format!("# END TARGET {}\n", name));
|
ret.push_str(&format!("# END TARGET {}\n", name));
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_ninja_file_content(
|
||||||
|
target_list: &TargetList,
|
||||||
|
config: &Config,
|
||||||
|
args: &Args,
|
||||||
|
) -> Result<String> {
|
||||||
|
let mut ret = String::new();
|
||||||
|
ret.push_str(&gen_ninja_header(&config, &args)?);
|
||||||
|
for (name, target) in &target_list.0 {
|
||||||
|
let content = gen_ninja_target(name, target);
|
||||||
|
match content {
|
||||||
|
Ok(v) => ret.push_str(&v),
|
||||||
|
Err(e) => match e.kind {
|
||||||
|
ErrorKind::NoSources => eprintln!("WARNING: {e}. Skipping `{}`", name),
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_ninja_file(filename: &str, content: &[u8]) -> std::io::Result<()> {
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.create(true)
|
||||||
|
.open(filename)?;
|
||||||
|
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
writer.write(content)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ParsedTarget {
|
pub struct DeserTarget {
|
||||||
pub outfile: Option<String>,
|
pub outfile: Option<String>,
|
||||||
pub compiler: Option<String>,
|
pub compiler: Option<String>,
|
||||||
pub compiler_flags: Option<Vec<String>>,
|
pub compiler_flags: Option<Vec<String>>,
|
||||||
|
@ -11,28 +11,31 @@ pub struct ParsedTarget {
|
||||||
pub linker_flags: Option<Vec<String>>,
|
pub linker_flags: Option<Vec<String>>,
|
||||||
pub linker_libs: Option<Vec<String>>,
|
pub linker_libs: Option<Vec<String>>,
|
||||||
pub sources: Option<Vec<String>>,
|
pub sources: Option<Vec<String>>,
|
||||||
pub opts: Option<ParsedTargetOptions>,
|
pub opts: Option<DeserTargetOptions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ParsedTargetOptions {
|
pub struct DeserTargetOptions {
|
||||||
pub inherit: Option<bool>,
|
pub inherit: Option<bool>,
|
||||||
pub inherit_from: Option<String>,
|
pub inherit_from: Option<String>,
|
||||||
|
pub depend_on: Option<Vec<String>>,
|
||||||
pub default: Option<bool>,
|
pub default: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ParsedConfigTable {
|
pub struct DeserConfig {
|
||||||
pub build_dir: Option<String>,
|
pub build_dir: Option<String>,
|
||||||
pub compile_commands: Option<bool>,
|
pub compile_commands: Option<bool>,
|
||||||
pub compile_commands_target: Option<String>,
|
pub compile_commands_target: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type DeserTargetList = HashMap<String, DeserTarget>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct NgenToml {
|
pub struct DeserNgenToml {
|
||||||
pub config: Option<ParsedConfigTable>,
|
pub config: Option<DeserConfig>,
|
||||||
pub targets: Option<HashMap<String, ParsedTarget>>,
|
pub targets: Option<DeserTargetList>,
|
||||||
}
|
}
|
176
src/main.rs
176
src/main.rs
|
@ -1,18 +1,16 @@
|
||||||
use clap::Parser;
|
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, env::args, fs::read_to_string, process::exit};
|
||||||
use std::fs::{read_to_string, OpenOptions};
|
|
||||||
use std::io::{BufWriter, Write};
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
mod ngen_toml;
|
use clap::Parser;
|
||||||
use ngen_toml::*;
|
|
||||||
|
|
||||||
mod parsed_config_processing;
|
mod content_generation;
|
||||||
use parsed_config_processing::*;
|
mod deser;
|
||||||
|
mod parse_deser;
|
||||||
|
mod validation;
|
||||||
|
|
||||||
mod file_writing;
|
use content_generation::*;
|
||||||
use file_writing::*;
|
use deser::*;
|
||||||
|
use parse_deser::*;
|
||||||
|
|
||||||
/// 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,118 +30,72 @@ struct Args {
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let config_file = read_to_string(&args.config_file)
|
let ngen_toml = read_to_string(&args.config_file)
|
||||||
.unwrap_or_else(|e| panic!("error reading config file `{}`: {e}", args.config_file));
|
.unwrap_or_else(|e| panic!("error reading config file `{}`: {e}", args.config_file));
|
||||||
let toml_config: NgenToml = toml::from_str(&config_file)
|
let deser_ngen_toml: DeserNgenToml = toml::from_str(&ngen_toml)
|
||||||
.unwrap_or_else(|e| panic!("error parsing config file `{}`: {e}", args.config_file));
|
.unwrap_or_else(|e| panic!("error parsing config file `{}`: {e}", args.config_file));
|
||||||
|
|
||||||
let config_table: ConfigTable = match &toml_config.config {
|
let target_option_list: TargetOptionList;
|
||||||
Some(v) => ConfigTable::from_parsed_config(&v).unwrap_or_else(|e| panic!("{e}")),
|
let deser_target_list: &DeserTargetList;
|
||||||
None => ConfigTable::get_default(),
|
match &deser_ngen_toml.targets {
|
||||||
|
Some(v) => {
|
||||||
|
target_option_list = TargetOptionList::from(v);
|
||||||
|
deser_target_list = &v;
|
||||||
|
}
|
||||||
|
None => panic!("no targets provided, nothing to do. Exiting"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let all_parsed_targets: &HashMap<String, ParsedTarget> = match &toml_config.targets {
|
target_option_list
|
||||||
Some(v) => v,
|
.check_for_dependency_cycle()
|
||||||
None => panic!(
|
.unwrap_or_else(|e| panic!("{e}. cannont continue. Exiting"));
|
||||||
"{}",
|
|
||||||
ConfigurationError {
|
let inheritance_order = target_option_list
|
||||||
message: "no targets found, nothing to do. Exiting".to_string()
|
.get_inheritance_order()
|
||||||
|
.unwrap_or_else(|e| panic!("{e}. cannot continue. Exiting"));
|
||||||
|
|
||||||
|
let mut target_list: TargetList = TargetList(HashMap::new());
|
||||||
|
for order in &inheritance_order {
|
||||||
|
for name in order.into_iter().rev() {
|
||||||
|
let opts = target_option_list.0.get(name as &str).unwrap();
|
||||||
|
let deser = deser_target_list.get(name as &str).unwrap();
|
||||||
|
|
||||||
|
let parent: Option<&Target>;
|
||||||
|
if opts.inherit && (name != opts.inherit_from) {
|
||||||
|
parent = Some(target_list.0.get(opts.inherit_from as &str).unwrap());
|
||||||
|
} else {
|
||||||
|
parent = None;
|
||||||
}
|
}
|
||||||
),
|
|
||||||
|
target_list.0.insert(name, Target::new(opts, deser, parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target_list
|
||||||
|
.check_if_any_sources_are_provided()
|
||||||
|
.unwrap_or_else(|e| panic!("{e}, nothing to do. Exiting"));
|
||||||
|
|
||||||
|
let config = match &deser_ngen_toml.config {
|
||||||
|
Some(v) => Config::from(v),
|
||||||
|
None => Config::get_default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sources_found = false;
|
config
|
||||||
for target in all_parsed_targets.values() {
|
.check_compile_commands_target(&target_list)
|
||||||
if let Some(_) = target.sources {
|
.unwrap_or_else(|e| panic!("{e}"));
|
||||||
sources_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !sources_found {
|
|
||||||
panic!(
|
|
||||||
"{}",
|
|
||||||
ConfigurationError {
|
|
||||||
message: "no targets have any sources, 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());
|
|
||||||
}
|
|
||||||
Err(e) => panic!("{e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(filename) = &args.write_compile_commands {
|
if let Some(filename) = &args.write_compile_commands {
|
||||||
let target = match targets.get(config_table.compile_commands_target) {
|
let name = config.compile_commands_target;
|
||||||
Some(v) => v,
|
let target = target_list.0.get(name).unwrap();
|
||||||
None => panic!(
|
let content = create_compile_commands_content(name, target, &config)
|
||||||
"{}",
|
.unwrap_or_else(|e| panic!("{e}"));
|
||||||
ContentGenerationError {
|
write_compile_commands(filename, content.as_bytes()).unwrap_or_else(|e| {
|
||||||
message: format!(
|
panic!("could not write compile commands file `{}`: {e}", filename)
|
||||||
"compile_commands_target is set to `{0}`, however target `{0}` does not exist.",
|
});
|
||||||
config_table.compile_commands_target
|
|
||||||
)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let compile_commands = match gen_compile_commands(
|
|
||||||
config_table.compile_commands_target,
|
|
||||||
&target,
|
|
||||||
&config_table,
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => panic!("{e}"),
|
|
||||||
};
|
|
||||||
let mut comp_cmds_file = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.create(true)
|
|
||||||
.open(&filename)
|
|
||||||
.unwrap_or_else(|e| panic!("error opening compile commands file `{}`: {e}", filename));
|
|
||||||
comp_cmds_file
|
|
||||||
.write_all(&compile_commands.as_bytes())
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
panic!("error writing to compile commands file `{}`: {e}", filename)
|
|
||||||
});
|
|
||||||
|
|
||||||
// no need to generate the ninja file
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ninjafile = OpenOptions::new()
|
let ninja_file_content =
|
||||||
.write(true)
|
create_ninja_file_content(&target_list, &config, &args).unwrap_or_else(|e| panic!("{e}"));
|
||||||
.truncate(true)
|
write_ninja_file(&args.output_file, ninja_file_content.as_bytes())
|
||||||
.create(true)
|
.unwrap_or_else(|e| panic!("could not write ninja file `{}`: {e}", args.output_file));
|
||||||
.open(&args.output_file)
|
|
||||||
.unwrap_or_else(|e| panic!("error opening ninja file `{}`: {e}", args.output_file));
|
|
||||||
|
|
||||||
let mut ninjafile_writer = BufWriter::new(ninjafile);
|
|
||||||
ninjafile_writer
|
|
||||||
.write(&gen_ninja_header(&config_table, &args).as_bytes())
|
|
||||||
.unwrap_or_else(|e| panic!("error writing to ninja file `{}`: {e}", args.output_file));
|
|
||||||
for (name, target) in &targets {
|
|
||||||
match gen_ninja_target(name, target) {
|
|
||||||
Ok(v) => {
|
|
||||||
ninjafile_writer.write(&v.as_bytes()).unwrap_or_else(|e| {
|
|
||||||
panic!("error writing to ninja file `{}`: {e}", args.output_file)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("WARNING: {e}. `{}` will NOT be generated", name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,38 @@
|
||||||
use super::*;
|
use crate::deser::*;
|
||||||
use crate::ngen_toml::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ConfigTable<'a> {
|
pub struct Config<'a> {
|
||||||
pub build_dir: &'a str,
|
pub build_dir: &'a str,
|
||||||
pub compile_commands: bool,
|
pub compile_commands: bool,
|
||||||
pub compile_commands_target: &'a str,
|
pub compile_commands_target: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ConfigTable<'a> {
|
impl<'a> From<&'a DeserConfig> for Config<'a> {
|
||||||
|
fn from(value: &'a DeserConfig) -> Self {
|
||||||
|
let mut ret = Self::get_default();
|
||||||
|
|
||||||
|
if let Some(v) = &value.build_dir {
|
||||||
|
ret.build_dir = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &value.compile_commands {
|
||||||
|
ret.compile_commands = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &value.compile_commands_target {
|
||||||
|
ret.compile_commands_target = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Config<'a> {
|
||||||
pub fn get_default() -> Self {
|
pub fn get_default() -> Self {
|
||||||
ConfigTable {
|
Self {
|
||||||
build_dir: "build",
|
build_dir: "build",
|
||||||
compile_commands: false,
|
compile_commands: false,
|
||||||
compile_commands_target: "main",
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
11
src/parse_deser/mod.rs
Normal file
11
src/parse_deser/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
pub mod config;
|
||||||
|
pub mod target;
|
||||||
|
pub mod target_list;
|
||||||
|
pub mod target_options;
|
||||||
|
pub mod target_options_list;
|
||||||
|
|
||||||
|
pub use config::*;
|
||||||
|
pub use target::*;
|
||||||
|
pub use target_list::*;
|
||||||
|
pub use target_options::*;
|
||||||
|
pub use target_options_list::*;
|
99
src/parse_deser/target.rs
Normal file
99
src/parse_deser/target.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::deser::*;
|
||||||
|
|
||||||
|
#[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 new(
|
||||||
|
opts: &TargetOptions<'a>,
|
||||||
|
deser: &'a DeserTarget,
|
||||||
|
parent: Option<&Target<'a>>,
|
||||||
|
) -> Self {
|
||||||
|
let mut ret = Self::get_default();
|
||||||
|
|
||||||
|
ret.opts = opts.clone();
|
||||||
|
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
if let None = deser.outfile {
|
||||||
|
ret.outfile = parent.outfile;
|
||||||
|
}
|
||||||
|
if let None = deser.compiler {
|
||||||
|
ret.compiler = parent.compiler;
|
||||||
|
}
|
||||||
|
if let None = deser.linker {
|
||||||
|
ret.linker = parent.linker;
|
||||||
|
}
|
||||||
|
for string in parent.compiler_flags.clone() {
|
||||||
|
ret.compiler_flags.push(string);
|
||||||
|
}
|
||||||
|
for string in parent.linker_flags.clone() {
|
||||||
|
ret.linker_flags.push(string);
|
||||||
|
}
|
||||||
|
for string in parent.linker_libs.clone() {
|
||||||
|
ret.linker_libs.push(string);
|
||||||
|
}
|
||||||
|
for string in parent.sources.clone() {
|
||||||
|
ret.sources.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &deser.outfile {
|
||||||
|
ret.outfile = v;
|
||||||
|
}
|
||||||
|
if let Some(v) = &deser.compiler {
|
||||||
|
ret.compiler = v;
|
||||||
|
}
|
||||||
|
if let Some(v) = &deser.compiler_flags {
|
||||||
|
for string in v {
|
||||||
|
ret.compiler_flags.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &deser.linker {
|
||||||
|
ret.linker = v;
|
||||||
|
} else {
|
||||||
|
if let Some(v) = &deser.compiler {
|
||||||
|
ret.linker = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &deser.linker_flags {
|
||||||
|
for string in v {
|
||||||
|
ret.linker_flags.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &deser.linker_libs {
|
||||||
|
for string in v {
|
||||||
|
ret.linker_libs.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = &deser.sources {
|
||||||
|
for string in v {
|
||||||
|
ret.sources.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
src/parse_deser/target_list.rs
Normal file
6
src/parse_deser/target_list.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::deser::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TargetList<'a>(pub HashMap<&'a str, Target<'a>>);
|
46
src/parse_deser/target_options.rs
Normal file
46
src/parse_deser/target_options.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::deser::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TargetOptions<'a> {
|
||||||
|
pub inherit: bool,
|
||||||
|
pub inherit_from: &'a str,
|
||||||
|
pub default: bool,
|
||||||
|
pub depend_on: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a DeserTargetOptions> for TargetOptions<'a> {
|
||||||
|
fn from(value: &'a DeserTargetOptions) -> Self {
|
||||||
|
let mut ret = Self::get_default();
|
||||||
|
|
||||||
|
if let Some(v) = &value.inherit {
|
||||||
|
ret.inherit = *v; // dereference the bool to copy it
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &value.inherit_from {
|
||||||
|
ret.inherit_from = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &value.default {
|
||||||
|
ret.default = *v; // dereference the bool to copy it
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = &value.depend_on {
|
||||||
|
for string in v {
|
||||||
|
ret.depend_on.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TargetOptions<'a> {
|
||||||
|
pub fn get_default() -> Self {
|
||||||
|
Self {
|
||||||
|
inherit: true,
|
||||||
|
inherit_from: "main",
|
||||||
|
default: false,
|
||||||
|
depend_on: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/parse_deser/target_options_list.rs
Normal file
22
src/parse_deser/target_options_list.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::deser::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TargetOptionList<'a>(pub HashMap<&'a str, TargetOptions<'a>>);
|
||||||
|
|
||||||
|
impl<'a> From<&'a DeserTargetList> for TargetOptionList<'a> {
|
||||||
|
fn from(value: &'a DeserTargetList) -> Self {
|
||||||
|
let mut ret: Self = TargetOptionList(HashMap::new());
|
||||||
|
|
||||||
|
for (name, target) in value {
|
||||||
|
if let Some(v) = &target.opts {
|
||||||
|
ret.0.insert(name, TargetOptions::from(v));
|
||||||
|
} else {
|
||||||
|
ret.0.insert(name, TargetOptions::get_default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
pub mod config_table;
|
|
||||||
pub mod configuration_error;
|
|
||||||
pub mod target;
|
|
||||||
pub mod target_options;
|
|
||||||
|
|
||||||
pub use config_table::*;
|
|
||||||
pub use configuration_error::*;
|
|
||||||
pub use target::*;
|
|
||||||
pub use target_options::*;
|
|
|
@ -1,150 +0,0 @@
|
||||||
use core::str;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
use super::*;
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
188
src/validation.rs
Normal file
188
src/validation.rs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
use std::{collections::HashMap, fmt::Display};
|
||||||
|
|
||||||
|
use crate::parse_deser::*;
|
||||||
|
|
||||||
|
pub enum ErrorKind {
|
||||||
|
NoSources,
|
||||||
|
DependencyDoesNotExist,
|
||||||
|
DependencyCycle,
|
||||||
|
ParentDoesNotExist,
|
||||||
|
InheritanceCycle,
|
||||||
|
CompileCmdsTargetDoesNotExist,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Error {
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "configuration error: {}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
type Chain<'a> = HashMap<&'a str, &'a str>;
|
||||||
|
|
||||||
|
fn print_chain(chain: &Chain, start: &str, connector: &str) -> String {
|
||||||
|
let mut ret = "target".to_string();
|
||||||
|
let mut i = 0;
|
||||||
|
let mut start = start;
|
||||||
|
while i < chain.len() {
|
||||||
|
let dep = chain.get(start).unwrap();
|
||||||
|
if i == chain.len() - 1 {
|
||||||
|
ret.push_str(&format!(" but target `{}` {} `{}`", start, connector, dep));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.push_str(&format!(" `{}` {} `{}`,", start, connector, dep));
|
||||||
|
start = dep;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TargetList<'a> {
|
||||||
|
pub fn check_if_any_sources_are_provided(&self) -> Result<()> {
|
||||||
|
let mut sources_found = false;
|
||||||
|
for target in self.0.values() {
|
||||||
|
if !target.sources.is_empty() {
|
||||||
|
sources_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sources_found {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error {
|
||||||
|
kind: ErrorKind::NoSources,
|
||||||
|
message: "no targets have any sources".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TargetOptionList<'a> {
|
||||||
|
fn check_target_dep(
|
||||||
|
&self,
|
||||||
|
opts: &TargetOptions<'a>,
|
||||||
|
name: &'a str,
|
||||||
|
dep_chain: &mut Chain<'a>,
|
||||||
|
) -> Result<()> {
|
||||||
|
for dep in opts.depend_on.clone() {
|
||||||
|
match self.0.get(dep) {
|
||||||
|
Some(v) => {
|
||||||
|
dep_chain.insert(name, dep);
|
||||||
|
if let Some(_) = dep_chain.get(dep) {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::DependencyCycle,
|
||||||
|
message: format!(
|
||||||
|
"dependency cycle detected: {}",
|
||||||
|
print_chain(dep_chain, name, "depends on")
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.check_target_dep(v, dep, dep_chain)?;
|
||||||
|
let _ = dep_chain.remove(name);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::DependencyDoesNotExist,
|
||||||
|
message: format!(
|
||||||
|
"target `{0}` depends on `{1}`, but target `{1}` does not exist",
|
||||||
|
name, dep
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_for_dependency_cycle(&self) -> Result<()> {
|
||||||
|
for (name, opts) in &self.0 {
|
||||||
|
let mut dep_chain: Chain = HashMap::new();
|
||||||
|
self.check_target_dep(opts, name, &mut dep_chain)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_target_parent(
|
||||||
|
&self,
|
||||||
|
opts: &TargetOptions<'a>,
|
||||||
|
name: &'a str,
|
||||||
|
dep_chain: &mut Chain<'a>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let inherit = opts.inherit_from;
|
||||||
|
if name == inherit {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
match self.0.get(inherit) {
|
||||||
|
Some(v) => {
|
||||||
|
dep_chain.insert(name, inherit);
|
||||||
|
if let Some(_) = dep_chain.get(inherit) {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::InheritanceCycle,
|
||||||
|
message: format!(
|
||||||
|
"inheritance cycle detected: {}",
|
||||||
|
print_chain(dep_chain, name, "inherits from")
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.check_target_parent(v, inherit, dep_chain)?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::ParentDoesNotExist,
|
||||||
|
message: format!(
|
||||||
|
"target `{0}` inherits from `{1}`, but target `{1}` does not exist",
|
||||||
|
name, inherit
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_inheritance_order(&self) -> Result<Vec<Vec<String>>> {
|
||||||
|
let mut ret: Vec<Vec<String>> = vec![];
|
||||||
|
for (name, opts) in &self.0 {
|
||||||
|
if !opts.inherit {
|
||||||
|
ret.push(vec![name.to_string()]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut dep_chain: Chain = HashMap::new();
|
||||||
|
|
||||||
|
self.check_target_parent(opts, name, &mut dep_chain)?;
|
||||||
|
|
||||||
|
let mut order: Vec<String> = vec![name.to_string()];
|
||||||
|
let mut i = 0;
|
||||||
|
while let Some(v) = dep_chain.get(&order[i] as &str) {
|
||||||
|
order.push(v.to_string());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
ret.push(order);
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Config<'a> {
|
||||||
|
pub fn check_compile_commands_target(&self, target_list: &TargetList) -> Result<()> {
|
||||||
|
if !self.compile_commands {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
match target_list.0.get(self.compile_commands_target) {
|
||||||
|
Some(_) => Ok(()),
|
||||||
|
None => Err(Error {
|
||||||
|
kind: ErrorKind::CompileCmdsTargetDoesNotExist,
|
||||||
|
message: format!(
|
||||||
|
"compile commands target `{}` does not exist",
|
||||||
|
self.compile_commands_target
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
92
test.txt
Normal file
92
test.txt
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
TargetList(
|
||||||
|
{
|
||||||
|
"three": Target {
|
||||||
|
outfile: "foobar",
|
||||||
|
compiler: "gcc",
|
||||||
|
compiler_flags: [
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
],
|
||||||
|
linker: "gcc",
|
||||||
|
linker_flags: [],
|
||||||
|
linker_libs: [],
|
||||||
|
sources: [
|
||||||
|
"main.c",
|
||||||
|
],
|
||||||
|
opts: TargetOptions {
|
||||||
|
inherit: true,
|
||||||
|
inherit_from: "two",
|
||||||
|
default: false,
|
||||||
|
depend_on: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"one": Target {
|
||||||
|
outfile: "foobar",
|
||||||
|
compiler: "gcc",
|
||||||
|
compiler_flags: [],
|
||||||
|
linker: "gcc",
|
||||||
|
linker_flags: [],
|
||||||
|
linker_libs: [],
|
||||||
|
sources: [
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
],
|
||||||
|
opts: TargetOptions {
|
||||||
|
inherit: true,
|
||||||
|
inherit_from: "main",
|
||||||
|
default: false,
|
||||||
|
depend_on: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"two": Target {
|
||||||
|
outfile: "foobar",
|
||||||
|
compiler: "gcc",
|
||||||
|
compiler_flags: [
|
||||||
|
"-Wall",
|
||||||
|
"-Wall",
|
||||||
|
],
|
||||||
|
linker: "gcc",
|
||||||
|
linker_flags: [],
|
||||||
|
linker_libs: [],
|
||||||
|
sources: [
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
],
|
||||||
|
opts: TargetOptions {
|
||||||
|
inherit: true,
|
||||||
|
inherit_from: "one",
|
||||||
|
default: false,
|
||||||
|
depend_on: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"main": Target {
|
||||||
|
outfile: "foobar",
|
||||||
|
compiler: "cc",
|
||||||
|
compiler_flags: [],
|
||||||
|
linker: "cc",
|
||||||
|
linker_flags: [],
|
||||||
|
linker_libs: [],
|
||||||
|
sources: [
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
"main.c",
|
||||||
|
],
|
||||||
|
opts: TargetOptions {
|
||||||
|
inherit: false,
|
||||||
|
inherit_from: "main",
|
||||||
|
default: false,
|
||||||
|
depend_on: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
Loading…
Reference in a new issue