From bf9d0289a73c1d0fa3718e389607695b04c2d23f Mon Sep 17 00:00:00 2001 From: Till Wegmueller Date: Mon, 28 Mar 2022 20:27:15 -0300 Subject: [PATCH] Add recursive Parsing of makefiles --- pkg6dev/src/main.rs | 5 +- userland/src/lib.rs | 175 ++++++++++++++++++++++++++++++++----- userland/src/makefile.pest | 2 +- 3 files changed, 155 insertions(+), 27 deletions(-) diff --git a/pkg6dev/src/main.rs b/pkg6dev/src/main.rs index f8dae48..c363c73 100644 --- a/pkg6dev/src/main.rs +++ b/pkg6dev/src/main.rs @@ -75,13 +75,14 @@ fn diff_component(component_path: impl AsRef) -> Result<()> { fn show_component_info>(component_path: P) -> Result<()> { let makefile_path = component_path.as_ref().join("Makefile"); - let makefile = Makefile::parse_file(&makefile_path)?; + let initial_makefile = Makefile::parse_single_file(&makefile_path)?; + let makefile = initial_makefile.parse_all()?; let mut name = String::new(); if let Some(var) = makefile.get("COMPONENT_NAME") { println!("Name: {}", var.replace("\n", "\n\t")); - if let Some(component_name) = makefile.get_first("COMPONENT_NAME") { + if let Some(component_name) = makefile.get_first_value_of_variable_by_name("COMPONENT_NAME") { name = component_name.clone(); } } diff --git a/userland/src/lib.rs b/userland/src/lib.rs index dc2165b..aace224 100644 --- a/userland/src/lib.rs +++ b/userland/src/lib.rs @@ -8,8 +8,10 @@ extern crate pest_derive; use anyhow::{anyhow, Result}; -use std::collections::HashMap; -use std::fs::read_to_string; +use std::collections::{HashMap}; +use std::env; +use std::io::Error as IOError; +use std::fs::{read_to_string, canonicalize}; use pest::iterators::{Pairs}; use std::path::{Path, PathBuf}; use pest::Parser; @@ -21,11 +23,31 @@ use regex::Regex; #[grammar = "makefile.pest"] struct MakefileParser; -#[derive(Debug, Default, PartialEq, Clone)] +#[derive(Debug, Default, Clone)] pub struct Makefile { - pub variables: HashMap>, - // pub includes: Vec, - // pub targets: HashMap, + path: PathBuf, + variables: HashMap, + includes: Vec, + // targets: HashMap, +} + +#[derive(Debug, Default, PartialEq, Clone)] +pub struct MakefileVariable { + pub key: String, + pub values: Vec, + pub mode: VariableMode, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum VariableMode { + Add, + Set +} + +impl Default for VariableMode { + fn default() -> Self { + Self::Set + } } #[derive(Error, Debug)] @@ -34,21 +56,66 @@ pub enum ParserError { MakefileReadError { file: PathBuf, reason: anyhow::Error, - } + }, + #[error("could not find include {0}")] + IncludeNotFound(String, #[source] IOError), } impl Makefile { - pub fn parse_file>(path: P) -> Result { + pub fn parse_single_file>(path: P) -> Result { let content = read_to_string(&path)?; - parse_string(content).map_err(|err| + let mut m = parse_string(content).map_err(|err| anyhow!(ParserError::MakefileReadError{file: path.as_ref().to_path_buf(), reason: anyhow!(err)}) - ) + )?; + m.path = path.as_ref().into(); + Ok(m) } pub fn parse_string(content: String) -> Result { parse_string(content) } + pub fn parse_all(&self) -> Result { + let includes = self.parse_included_makefiles()?; + let mut final_makefile = Self::default(); + + for incl in includes { + if incl.has_includes() { + let final_include = incl.parse_all()?; + final_makefile.merge(&final_include)?; + } else { + final_makefile.merge(&incl)?; + } + } + + final_makefile.merge(self)?; + Ok(final_makefile) + } + + pub fn merge(&mut self, other: &Self) -> Result<()> { + for (key, var) in other.variables.clone() { + match var.mode { + VariableMode::Add => { + if let Some(v) = self.variables.get(&key) { + let mut new_variable = v.clone(); + for val in var.values { + new_variable.values.push(val); + } + new_variable.mode = var.mode; + self.variables.insert(key, new_variable); + } else { + self.variables.insert(key, var); + } + } + VariableMode::Set => { + self.variables.insert(key, var); + } + } + } + + Ok(()) + } + pub fn get(&self, var_name: &str) -> Option { if let Some(var) = self.variables.get(var_name) { let vars_resolved = self.resolve_nested_variables(var); @@ -58,12 +125,48 @@ impl Makefile { } } - fn resolve_nested_variables(&self, var: &Vec) -> Vec { + pub fn get_includes(&self) -> Option> { + if self.includes.len() > 0 { + Some(self.includes.clone()) + } else { + None + } + } + + pub fn has_includes(&self) -> bool { + self.includes.len() > 0 + } + + pub fn parse_included_makefiles(&self) -> Result> { + if let Some(includes) = self.get_includes() { + let mut included_makefiles: Vec = Vec::new(); + + let dir_of_makefile = self.path.parent(); + if let Some(d) = dir_of_makefile { + env::set_current_dir(d)?; + } else { + env::set_current_dir("/")?; + }; + + for incl in includes { + let incl_path = canonicalize(&incl).map_err(|err| + anyhow!(ParserError::IncludeNotFound(incl, err)))?; + let m = Self::parse_single_file(incl_path)?; + included_makefiles.push(m); + } + + Ok(included_makefiles) + } else { + Ok(Vec::new()) + } + } + + fn resolve_nested_variables(&self, var: &MakefileVariable) -> Vec { // Make a mutable copy of the variables so we can replace nested variables with their final strings - let mut vars_copy = var.clone(); + let mut vars_copy = var.values.clone(); // Logic to resolve all the nested Variables when we access them. - for (i, maybe_nested_var) in var.iter().enumerate() { + for (i, maybe_nested_var) in var.values.iter().enumerate() { lazy_static! { static ref VARRE: Regex = Regex::new(r"(?P\$\(.+?\))").unwrap(); } @@ -81,7 +184,7 @@ impl Makefile { vars_copy } - pub fn get_first(&self, var_name: &str) -> Option { + pub fn get_first_value_of_variable_by_name(&self, var_name: &str) -> Option { if let Some(var) = self.variables.get(var_name) { let vars_resolved = self.resolve_nested_variables(var); Some(vars_resolved.first().unwrap().clone()) @@ -125,7 +228,9 @@ fn parse_makefile(pairs: Pairs, m: &mut Makefile) -> Result<()> { parse_variable(p.into_inner(), m)?; } Rule::comment_string => (), - Rule::include => (), + Rule::include => { + parse_include(p.into_inner(), m)?; + }, Rule::target => (), Rule::define => { parse_define(p.into_inner(), m)?; @@ -138,17 +243,29 @@ fn parse_makefile(pairs: Pairs, m: &mut Makefile) -> Result<()> { Ok(()) } +fn parse_include(include_pair: Pairs, m: &mut Makefile) -> Result<()> { + for p in include_pair { + match p.as_rule() { + Rule::variable_value => { + m.includes.push(p.as_str().to_string()); + } + _ => panic!("unexpected rule {:?} inside include rule expected variable_value", p.as_rule()) + } + } + Ok(()) +} + fn parse_define(define_pair: Pairs, m: &mut Makefile) -> Result<()> { - let mut var = (String::new(), Vec::::new()); + let mut var = (String::new(), MakefileVariable::default()); for p in define_pair { match p.as_rule() { Rule::variable_name => { var.0 = p.as_str().to_string(); } Rule::define_value => { - var.1.push(p.as_str().to_string()); + var.1.values.push(p.as_str().to_string()); } - _ => panic!("unexpected rule {:?} inside makefile rule expected variable_name, define_value", p.as_rule()), + _ => panic!("unexpected rule {:?} inside define rule expected variable_name, define_value", p.as_rule()), } } m.variables.insert(var.0, var.1); @@ -157,20 +274,30 @@ fn parse_define(define_pair: Pairs, m: &mut Makefile) -> Result<()> } fn parse_variable(variable_pair: Pairs, m: &mut Makefile) -> Result<()> { - let mut var = (String::new(), Vec::::new()); + let mut var = (String::new(), MakefileVariable::default()); for p in variable_pair { match p.as_rule() { Rule::variable_name => { var.0 = p.as_str().to_string(); } - Rule::variable_set => (), + Rule::variable_set => { + var.1.mode = VariableMode::Set + }, Rule::variable_add => { - if m.variables.contains_key(&var.0) { - var.1 = m.variables.get(&var.0).unwrap().clone() - } + var.1.mode = VariableMode::Add } Rule::variable_value => { - var.1.push(p.as_str().to_string()); + match var.1.mode { + VariableMode::Add => { + if m.variables.contains_key(&var.0) { + var.1 = m.variables.get(&var.0).unwrap().clone() + } + var.1.values.push(p.as_str().to_string()); + } + VariableMode::Set => { + var.1.values.push(p.as_str().to_string()); + } + } } _ => panic!("unexpected rule {:?} inside makefile rule expected variable_name, variable_set, variable_add, variable_value", p.as_rule()), } diff --git a/userland/src/makefile.pest b/userland/src/makefile.pest index a3c78d4..b4183bb 100644 --- a/userland/src/makefile.pest +++ b/userland/src/makefile.pest @@ -16,7 +16,7 @@ comment_character = _{ ~ ANY // then consume one character } -variable_name_character = { UPPERCASE | ASCII_DIGIT | "_" } +variable_name_character = { UPPERCASE | ASCII_DIGIT | "_" | "." } variable_name = @{ variable_name_character* } variable_value_character = { !NEWLINE