aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/macro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/makefile/macro.rs')
-rw-r--r--src/makefile/macro.rs323
1 files changed, 81 insertions, 242 deletions
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs
index d77557b..d27cbfa 100644
--- a/src/makefile/macro.rs
+++ b/src/makefile/macro.rs
@@ -1,20 +1,20 @@
-use std::cell::RefCell;
use std::collections::HashMap;
+#[cfg(feature = "full")]
use std::collections::HashSet;
use std::env;
use std::fmt;
-use std::rc::Rc;
-
-use eyre::{bail, Result, WrapErr};
-#[cfg(not(feature = "full"))]
-use regex::Regex;
+#[cfg(feature = "full")]
+use std::io::BufRead;
#[cfg(feature = "full")]
-use super::functions;
-use super::token::{Token, TokenString};
-use super::ItemSource;
+use super::eval_context::DeferredEvalContext;
+#[cfg(feature = "full")]
+use super::MacroScopeStack;
+use super::{ItemSource, TokenString};
+#[cfg(feature = "full")]
+use eyre::Result;
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Macro {
pub source: ItemSource,
pub text: TokenString,
@@ -22,12 +22,8 @@ pub struct Macro {
pub eagerly_expanded: bool,
}
-pub trait LookupInternal: for<'a> Fn(&'a str) -> Result<String> {}
-
-impl<F: for<'a> Fn(&'a str) -> Result<String>> LookupInternal for F {}
-
#[cfg(feature = "full")]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExportConfig {
Only(HashSet<String>),
AllBut(HashSet<String>),
@@ -69,13 +65,6 @@ impl ExportConfig {
}
}
- fn same_type(&self) -> Self {
- match self {
- Self::Only(_) => Self::only(),
- Self::AllBut(_) => Self::all_but(),
- }
- }
-
fn should_export(&self, x: &str) -> bool {
match self {
Self::Only(exported) => exported.contains(x),
@@ -84,29 +73,19 @@ impl ExportConfig {
}
}
-#[derive(Clone)]
-pub struct Set<'parent, 'lookup> {
- parent: Option<&'parent Set<'parent, 'lookup>>,
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Set {
pub data: HashMap<String, Macro>,
- lookup_internal: Option<&'lookup dyn LookupInternal>,
- #[cfg(feature = "full")]
- pub to_eval: Rc<RefCell<Vec<String>>>,
#[cfg(feature = "full")]
pub exported: ExportConfig,
- warnings: Rc<RefCell<HashSet<String>>>,
}
-impl<'parent, 'lookup> Set<'parent, 'lookup> {
+impl Set {
pub fn new() -> Self {
Self {
- parent: None,
data: HashMap::new(),
- lookup_internal: None,
- #[cfg(feature = "full")]
- to_eval: Rc::new(RefCell::new(Vec::new())),
#[cfg(feature = "full")]
exported: ExportConfig::only(),
- warnings: Default::default(),
}
}
@@ -140,49 +119,18 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
}
}
- fn lookup_internal(&self, name: &str) -> Result<String> {
- if let Some(lookup) = self.lookup_internal {
- lookup(name)
- } else if let Some(parent) = self.parent {
- parent.lookup_internal(name)
- } else {
- bail!(
- "tried to lookup {:?} but no lookup function is available",
- name
- )
- }
- }
-
- pub fn get(&self, name: &str) -> Option<&Macro> {
- self.data
- .get(name)
- .or_else(|| self.parent.and_then(|parent| parent.get(name)))
+ /// To properly process inherited macros, use [MacroScopeStack::get].
+ pub fn get_non_recursive(&self, name: &str) -> Option<&Macro> {
+ self.data.get(name)
}
pub fn set(&mut self, name: String, r#macro: Macro) {
self.data.insert(name, r#macro);
}
- #[cfg(feature = "full")]
- pub fn is_defined(&self, name: &str) -> bool {
- self.get(name).map_or(false, |x| !x.text.is_empty())
- }
-
- // `remove` is fine, but I think for "remove-and-return" `pop` is better
- pub fn pop(&mut self, name: &str) -> Option<Macro> {
- // TODO figure out a better way to handle inheritance
- self.data
- .remove(name)
- .or_else(|| self.parent.and_then(|p| p.get(name).cloned()))
- }
-
- pub fn extend(
- &mut self,
- other: HashMap<String, Macro>,
- #[cfg(feature = "full")] other_exports: ExportConfig,
- ) {
+ pub fn extend(&mut self, other: Self) {
#[cfg(feature = "full")]
- match (&mut self.exported, other_exports) {
+ match (&mut self.exported, other.exported) {
(ExportConfig::Only(se), ExportConfig::Only(oe)) => {
se.extend(oe);
}
@@ -190,187 +138,47 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
sne.extend(one);
}
(ExportConfig::Only(se), ExportConfig::AllBut(one)) => {
- se.extend(other.keys().cloned().filter(|name| !one.contains(name)));
+ se.extend(
+ other
+ .data
+ .keys()
+ .filter(|name| !one.contains(*name))
+ .cloned(),
+ );
}
(ExportConfig::AllBut(sne), ExportConfig::Only(oe)) => {
- sne.extend(other.keys().cloned().filter(|name| !oe.contains(name)));
- }
- }
- self.data.extend(other);
- }
-
- fn warn(&self, text: String) {
- if !self.warnings.borrow().contains(&text) {
- log::warn!("{}", &text);
- self.warnings.borrow_mut().insert(text);
- }
- }
-
- pub fn expand(&self, text: &TokenString) -> Result<String> {
- let mut result = String::new();
- for token in text.tokens() {
- match token {
- Token::Text(t) => result.push_str(t),
- Token::MacroExpansion { name, replacement } => {
- let name = self
- .expand(name)
- .wrap_err_with(|| format!("while expanding \"{}\"", name))?;
- let internal_macro_names = &['@', '?', '<', '*', '^'][..];
- let internal_macro_suffices = &['D', 'F'][..];
- let just_internal = name.len() == 1 && name.starts_with(internal_macro_names);
- let suffixed_internal = name.len() == 2
- && name.starts_with(internal_macro_names)
- && name.ends_with(internal_macro_suffices);
- let macro_value = if just_internal || suffixed_internal {
- self.lookup_internal(&name)
- .wrap_err_with(|| format!("while expanding $\"{}\"", name))?
- } else {
- self.get(&name).map_or_else(
- || {
- self.warn(format!("undefined macro {}", name));
- Ok(String::new())
- },
- |x| {
- self.expand(&x.text)
- .wrap_err_with(|| format!("while expanding \"{}\"", &x.text))
- },
- )?
- };
- let macro_value = match replacement {
- Some((subst1, subst2)) => {
- let subst1 = self.expand(subst1)?;
- #[cfg(feature = "full")]
- {
- let (subst1, subst2) = if subst1.contains('%') {
- (subst1, subst2.clone())
- } else {
- let mut real_subst2 = TokenString::text("%");
- real_subst2.extend(subst2.clone());
- (format!("%{}", subst1), real_subst2)
- };
- let args = [
- TokenString::text(subst1),
- subst2,
- TokenString::text(macro_value),
- ];
- functions::expand_call(
- "patsubst",
- &args,
- self,
- Some(Rc::clone(&self.to_eval)),
- )?
- }
- #[cfg(not(feature = "full"))]
- {
- let subst1_suffix = regex::escape(&subst1);
- let subst1_suffix =
- Regex::new(&format!(r"{}(\s|$)", subst1_suffix))
- .context("formed invalid regex somehow")?;
- let subst2 = self.expand(subst2)?;
- subst1_suffix
- .replace_all(&macro_value, |c: &regex::Captures| {
- format!("{}{}", subst2, c.get(1).unwrap().as_str())
- })
- .to_string()
- }
- }
- None => macro_value,
- };
- log::trace!(
- "expanded {} (from {:?}) into \"{}\"",
- token,
- self.get(&name).map(|x| &x.source),
- &macro_value
- );
- result.push_str(&macro_value);
- }
- #[cfg(feature = "full")]
- Token::FunctionCall { name, args } => {
- let name = self.expand(name)?;
- let fn_result =
- functions::expand_call(&name, args, self, Some(Rc::clone(&self.to_eval)))?;
- log::trace!("expanded {} into \"{}\"", token, &fn_result);
- result.push_str(&fn_result);
- }
+ sne.extend(
+ other
+ .data
+ .keys()
+ .filter(|name| !oe.contains(*name))
+ .cloned(),
+ );
}
}
- Ok(result)
+ self.data.extend(other.data);
}
#[cfg(feature = "full")]
- pub fn origin(&self, name: &str) -> &'static str {
- match self.data.get(name) {
- None => self.parent.map_or("undefined", |p| p.origin(name)),
- Some(Macro {
- source: ItemSource::Builtin,
- ..
- }) => "default",
- Some(Macro {
- source: ItemSource::Environment,
- ..
- }) => "environment",
- // TODO figure out when to return "environment override"
- Some(Macro {
- source: ItemSource::File { .. },
- ..
- }) => "file",
- Some(Macro {
- source: ItemSource::CommandLineOrMakeflags,
- ..
- }) => "command line",
- // TODO handle override
- Some(Macro {
- source: ItemSource::FunctionCall,
- ..
- }) => "automatic",
- }
- }
-
- pub fn with_lookup<'l, 's: 'l>(&'s self, lookup: &'l dyn LookupInternal) -> Set<'s, 'l> {
- Set {
- parent: Some(self),
- data: HashMap::new(),
- lookup_internal: Some(lookup),
- #[cfg(feature = "full")]
- to_eval: Rc::clone(&self.to_eval),
- #[cfg(feature = "full")]
- exported: self.exported.same_type(),
- warnings: Rc::clone(&self.warnings),
- }
- }
-
- pub fn with_overlay<'s>(&'s self) -> Set<'s, 'lookup> {
- Set {
- parent: Some(self),
- data: HashMap::new(),
- lookup_internal: None,
- #[cfg(feature = "full")]
- to_eval: Rc::clone(&self.to_eval),
- #[cfg(feature = "full")]
- exported: self.exported.same_type(),
- warnings: Rc::clone(&self.warnings),
- }
- }
-
- #[cfg(feature = "full")]
- pub fn resolve_exports(&self) -> Result<Vec<(&str, String)>> {
+ pub fn resolve_exports<R: BufRead>(
+ &self,
+ mut eval_context: Option<&mut DeferredEvalContext<R>>,
+ ) -> Result<Vec<(&str, String)>> {
let own_exports = self
.data
.iter()
.filter(|(name, _)| self.exported.should_export(name))
- .map(|(name, value)| self.expand(&value.text).map(|text| (name.as_ref(), text)))
+ .map(|(name, value)| {
+ MacroScopeStack::from_scope(self)
+ .expand(&value.text, eval_context.as_deref_mut())
+ .map(|text| (name.as_ref(), text))
+ })
.collect::<Result<Vec<_>>>()?;
- Ok(if let Some(parent) = self.parent {
- let mut parent_exports = parent.resolve_exports()?;
- parent_exports.extend(own_exports);
- parent_exports
- } else {
- own_exports
- })
+ Ok(own_exports)
}
}
-impl fmt::Display for Set<'_, '_> {
+impl fmt::Display for Set {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let pieces = self
.data
@@ -381,9 +189,23 @@ impl fmt::Display for Set<'_, '_> {
}
}
+impl Default for Set {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
fn builtins() -> Vec<(&'static str, TokenString)> {
// Fuck it, might as well.
- macro_rules! handle {
+ macro_rules! handle_key {
+ ($key:ident) => {
+ stringify!($key)
+ };
+ ($key:literal) => {
+ $key
+ };
+ }
+ macro_rules! handle_value {
($value:ident) => {
stringify!($value).parse().unwrap()
};
@@ -395,8 +217,8 @@ fn builtins() -> Vec<(&'static str, TokenString)> {
};
}
macro_rules! make {
- ($($name:ident=$value:tt)+) => {vec![$(
- (stringify!($name), handle!($value))
+ ($($name:tt=$value:tt)+) => {vec![$(
+ (handle_key!($name), handle_value!($value))
),+]};
}
@@ -429,12 +251,19 @@ fn builtins() -> Vec<(&'static str, TokenString)> {
ARFLAGS="rv"
CFLAGS=""
FFLAGS=""
+
+ // yes, Linux, this is definitely GNU Make 4.0+
+ ".FEATURES"="output-sync"
]
}
#[cfg(test)]
mod test {
use super::*;
+ #[cfg(feature = "full")]
+ use crate::makefile::functions::NO_EVAL;
+ use crate::MacroScopeStack;
+ use eyre::Result;
type R = Result<()>;
@@ -450,7 +279,14 @@ mod test {
eagerly_expanded: false,
},
);
- assert_eq!(macros.expand(&"$(oof:;=?)".parse()?)?, "bruh? swag? yeet?");
+ assert_eq!(
+ MacroScopeStack::from_scope(&macros).expand(
+ &"$(oof:;=?)".parse()?,
+ #[cfg(feature = "full")]
+ NO_EVAL
+ )?,
+ "bruh? swag? yeet?"
+ );
Ok(())
}
@@ -466,7 +302,10 @@ mod test {
eagerly_expanded: false,
},
);
- assert_eq!(macros.expand(&"$(m:%=%-objs)".parse()?)?, "conf-objs");
+ assert_eq!(
+ MacroScopeStack::from_scope(&macros).expand(&"$(m:%=%-objs)".parse()?, NO_EVAL)?,
+ "conf-objs"
+ );
Ok(())
}
}