mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
Add recursive Parsing of makefiles
This commit is contained in:
parent
09635558ae
commit
bf9d0289a7
3 changed files with 155 additions and 27 deletions
|
|
@ -75,13 +75,14 @@ fn diff_component(component_path: impl AsRef<Path>) -> Result<()> {
|
||||||
fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
|
fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
|
||||||
let makefile_path = component_path.as_ref().join("Makefile");
|
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();
|
let mut name = String::new();
|
||||||
|
|
||||||
if let Some(var) = makefile.get("COMPONENT_NAME") {
|
if let Some(var) = makefile.get("COMPONENT_NAME") {
|
||||||
println!("Name: {}", var.replace("\n", "\n\t"));
|
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();
|
name = component_name.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ extern crate pest_derive;
|
||||||
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap};
|
||||||
use std::fs::read_to_string;
|
use std::env;
|
||||||
|
use std::io::Error as IOError;
|
||||||
|
use std::fs::{read_to_string, canonicalize};
|
||||||
use pest::iterators::{Pairs};
|
use pest::iterators::{Pairs};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
|
|
@ -21,11 +23,31 @@ use regex::Regex;
|
||||||
#[grammar = "makefile.pest"]
|
#[grammar = "makefile.pest"]
|
||||||
struct MakefileParser;
|
struct MakefileParser;
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Makefile {
|
pub struct Makefile {
|
||||||
pub variables: HashMap<String, Vec<String>>,
|
path: PathBuf,
|
||||||
// pub includes: Vec<String>,
|
variables: HashMap<String, MakefileVariable>,
|
||||||
// pub targets: HashMap<String, String>,
|
includes: Vec<String>,
|
||||||
|
// targets: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct MakefileVariable {
|
||||||
|
pub key: String,
|
||||||
|
pub values: Vec<String>,
|
||||||
|
pub mode: VariableMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum VariableMode {
|
||||||
|
Add,
|
||||||
|
Set
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VariableMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Set
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
@ -34,21 +56,66 @@ pub enum ParserError {
|
||||||
MakefileReadError {
|
MakefileReadError {
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
reason: anyhow::Error,
|
reason: anyhow::Error,
|
||||||
}
|
},
|
||||||
|
#[error("could not find include {0}")]
|
||||||
|
IncludeNotFound(String, #[source] IOError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Makefile {
|
impl Makefile {
|
||||||
pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Self> {
|
pub fn parse_single_file<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
let content = read_to_string(&path)?;
|
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)})
|
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<Self> {
|
pub fn parse_string(content: String) -> Result<Self> {
|
||||||
parse_string(content)
|
parse_string(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_all(&self) -> Result<Self> {
|
||||||
|
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<String> {
|
pub fn get(&self, var_name: &str) -> Option<String> {
|
||||||
if let Some(var) = self.variables.get(var_name) {
|
if let Some(var) = self.variables.get(var_name) {
|
||||||
let vars_resolved = self.resolve_nested_variables(var);
|
let vars_resolved = self.resolve_nested_variables(var);
|
||||||
|
|
@ -58,12 +125,48 @@ impl Makefile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_nested_variables(&self, var: &Vec<String>) -> Vec<String> {
|
pub fn get_includes(&self) -> Option<Vec<String>> {
|
||||||
|
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<Vec<Self>> {
|
||||||
|
if let Some(includes) = self.get_includes() {
|
||||||
|
let mut included_makefiles: Vec<Makefile> = 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<String> {
|
||||||
// Make a mutable copy of the variables so we can replace nested variables with their final strings
|
// 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.
|
// 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! {
|
lazy_static! {
|
||||||
static ref VARRE: Regex = Regex::new(r"(?P<var_name>\$\(.+?\))").unwrap();
|
static ref VARRE: Regex = Regex::new(r"(?P<var_name>\$\(.+?\))").unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +184,7 @@ impl Makefile {
|
||||||
vars_copy
|
vars_copy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_first(&self, var_name: &str) -> Option<String> {
|
pub fn get_first_value_of_variable_by_name(&self, var_name: &str) -> Option<String> {
|
||||||
if let Some(var) = self.variables.get(var_name) {
|
if let Some(var) = self.variables.get(var_name) {
|
||||||
let vars_resolved = self.resolve_nested_variables(var);
|
let vars_resolved = self.resolve_nested_variables(var);
|
||||||
Some(vars_resolved.first().unwrap().clone())
|
Some(vars_resolved.first().unwrap().clone())
|
||||||
|
|
@ -125,7 +228,9 @@ fn parse_makefile(pairs: Pairs<crate::Rule>, m: &mut Makefile) -> Result<()> {
|
||||||
parse_variable(p.into_inner(), m)?;
|
parse_variable(p.into_inner(), m)?;
|
||||||
}
|
}
|
||||||
Rule::comment_string => (),
|
Rule::comment_string => (),
|
||||||
Rule::include => (),
|
Rule::include => {
|
||||||
|
parse_include(p.into_inner(), m)?;
|
||||||
|
},
|
||||||
Rule::target => (),
|
Rule::target => (),
|
||||||
Rule::define => {
|
Rule::define => {
|
||||||
parse_define(p.into_inner(), m)?;
|
parse_define(p.into_inner(), m)?;
|
||||||
|
|
@ -138,17 +243,29 @@ fn parse_makefile(pairs: Pairs<crate::Rule>, m: &mut Makefile) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_include(include_pair: Pairs<crate::Rule>, 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<crate::Rule>, m: &mut Makefile) -> Result<()> {
|
fn parse_define(define_pair: Pairs<crate::Rule>, m: &mut Makefile) -> Result<()> {
|
||||||
let mut var = (String::new(), Vec::<String>::new());
|
let mut var = (String::new(), MakefileVariable::default());
|
||||||
for p in define_pair {
|
for p in define_pair {
|
||||||
match p.as_rule() {
|
match p.as_rule() {
|
||||||
Rule::variable_name => {
|
Rule::variable_name => {
|
||||||
var.0 = p.as_str().to_string();
|
var.0 = p.as_str().to_string();
|
||||||
}
|
}
|
||||||
Rule::define_value => {
|
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);
|
m.variables.insert(var.0, var.1);
|
||||||
|
|
@ -157,20 +274,30 @@ fn parse_define(define_pair: Pairs<crate::Rule>, m: &mut Makefile) -> Result<()>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_variable(variable_pair: Pairs<crate::Rule>, m: &mut Makefile) -> Result<()> {
|
fn parse_variable(variable_pair: Pairs<crate::Rule>, m: &mut Makefile) -> Result<()> {
|
||||||
let mut var = (String::new(), Vec::<String>::new());
|
let mut var = (String::new(), MakefileVariable::default());
|
||||||
for p in variable_pair {
|
for p in variable_pair {
|
||||||
match p.as_rule() {
|
match p.as_rule() {
|
||||||
Rule::variable_name => {
|
Rule::variable_name => {
|
||||||
var.0 = p.as_str().to_string();
|
var.0 = p.as_str().to_string();
|
||||||
}
|
}
|
||||||
Rule::variable_set => (),
|
Rule::variable_set => {
|
||||||
|
var.1.mode = VariableMode::Set
|
||||||
|
},
|
||||||
Rule::variable_add => {
|
Rule::variable_add => {
|
||||||
if m.variables.contains_key(&var.0) {
|
var.1.mode = VariableMode::Add
|
||||||
var.1 = m.variables.get(&var.0).unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Rule::variable_value => {
|
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()),
|
_ => panic!("unexpected rule {:?} inside makefile rule expected variable_name, variable_set, variable_add, variable_value", p.as_rule()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ comment_character = _{
|
||||||
~ ANY // then consume one character
|
~ ANY // then consume one character
|
||||||
}
|
}
|
||||||
|
|
||||||
variable_name_character = { UPPERCASE | ASCII_DIGIT | "_" }
|
variable_name_character = { UPPERCASE | ASCII_DIGIT | "_" | "." }
|
||||||
variable_name = @{ variable_name_character* }
|
variable_name = @{ variable_name_character* }
|
||||||
variable_value_character = {
|
variable_value_character = {
|
||||||
!NEWLINE
|
!NEWLINE
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue