aboutsummaryrefslogtreecommitdiff
path: root/src/makefile
diff options
context:
space:
mode:
Diffstat (limited to 'src/makefile')
-rw-r--r--src/makefile/functions.rs31
-rw-r--r--src/makefile/input.rs28
-rw-r--r--src/makefile/macro.rs24
3 files changed, 70 insertions, 13 deletions
diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs
index 43ed44b..a22ec3b 100644
--- a/src/makefile/functions.rs
+++ b/src/makefile/functions.rs
@@ -1,10 +1,18 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
use eyre::{bail, Result};
use super::pattern::r#match;
use super::r#macro::{Set as MacroSet, Source as MacroSource};
use super::token::TokenString;
-pub fn expand_call(name: &str, args: &[TokenString], macros: &MacroSet) -> Result<String> {
+pub fn expand_call(
+ name: &str,
+ args: &[TokenString],
+ macros: &MacroSet,
+ to_eval: Option<Rc<RefCell<Vec<String>>>>,
+) -> Result<String> {
match name {
"filter" => {
assert_eq!(args.len(), 2);
@@ -54,7 +62,16 @@ pub fn expand_call(name: &str, args: &[TokenString], macros: &MacroSet) -> Resul
}
// eval
- "eval" => todo!(),
+ "eval" => {
+ assert_eq!(args.len(), 1);
+ let should_eval = eval::eval(macros, &args[0])?;
+ if let Some(to_eval) = to_eval {
+ to_eval.borrow_mut().push(should_eval);
+ } else {
+ bail!("tried to eval something but no eval back-channel was available");
+ }
+ return Ok(String::new());
+ }
"origin" => {
assert_eq!(args.len(), 1);
@@ -268,6 +285,14 @@ mod call {
}
}
+mod eval {
+ use super::*;
+
+ pub fn eval(macros: &MacroSet, arg: &TokenString) -> Result<String> {
+ macros.expand(arg)
+ }
+}
+
mod origin {
use super::*;
@@ -317,7 +342,7 @@ mod test {
type R = Result<()>;
fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> Result<String> {
- super::expand_call(name, args, macros)
+ super::expand_call(name, args, macros, None)
}
macro_rules! call {
diff --git a/src/makefile/input.rs b/src/makefile/input.rs
index 918ce0a..f269fa4 100644
--- a/src/makefile/input.rs
+++ b/src/makefile/input.rs
@@ -2,7 +2,7 @@ use std::cell::Cell;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fs::File;
-use std::io::{BufRead, BufReader, Error as IoError, Lines};
+use std::io::{BufRead, BufReader, Cursor, Error as IoError, Lines};
use std::iter::Peekable;
use std::path::Path;
@@ -181,7 +181,10 @@ impl<'a, R: BufRead> MakefileReader<'a, R> {
// POSIX says we only have to handle a single filename, but GNU make
// handles arbitrarily many filenames, and it's not like that's more work
for field in fields {
- self.extend(MakefileReader::read_file(self.args, field)?);
+ self.extend(
+ MakefileReader::read_file(self.args, field)
+ .with_context(|| format!("while including {}", field))?,
+ );
}
continue;
}
@@ -202,7 +205,18 @@ impl<'a, R: BufRead> MakefileReader<'a, R> {
// before we actually test it, see if it's only visible after expanding macros
let (line_tokens, line_type) = if let LineType::Unknown = line_type {
- let line_tokens = TokenString::text(self.expand_macros(&line_tokens)?);
+ let line_tokens = TokenString::text(self.expand_macros(&line_tokens)?.trim());
+ // and let's eval whatever bullshit needs evaling
+ #[cfg(feature = "full")]
+ {
+ let eval = self.macros.to_eval.take();
+ for eval in eval {
+ self.extend(
+ MakefileReader::read(self.args, Cursor::new(eval))
+ .context("while evaling")?,
+ );
+ }
+ }
let line_type = LineType::of(&line_tokens);
(line_tokens, line_type)
} else {
@@ -499,8 +513,6 @@ impl<'a, R: BufRead> MakefileReader<'a, R> {
#[cfg(test)]
mod test {
- use std::io::Cursor;
-
use super::*;
type R = Result<()>;
@@ -545,7 +557,7 @@ endef
}
#[test]
- #[ignore = "I still haven't implemented `eval` or %-based macro substitution."]
+ #[cfg(feature = "full")]
fn eval() -> R {
// This, for the record, is a terrible misfeature.
// If you need this, you probably shouldn't be using Make.
@@ -574,10 +586,10 @@ endef
$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
$(PROGRAMS):
- $(LINK.o) $^ $(LDLIBS) -o $@
+\t$(LINK.o) $^ $(LDLIBS) -o $@
clean:
- rm -f $(ALL_OBJS) $(PROGRAMS)
+\trm -f $(ALL_OBJS) $(PROGRAMS)
";
let args = Args::empty();
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs
index 7fbb4a6..a1f2378 100644
--- a/src/makefile/macro.rs
+++ b/src/makefile/macro.rs
@@ -1,6 +1,10 @@
+#[cfg(feature = "full")]
+use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::fmt;
+#[cfg(feature = "full")]
+use std::rc::Rc;
use eyre::{bail, Result, WrapErr};
use regex::Regex;
@@ -26,6 +30,8 @@ pub struct Set<'parent, 'lookup> {
parent: Option<&'parent Set<'parent, 'lookup>>,
data: HashMap<String, (Source, TokenString)>,
lookup_internal: Option<&'lookup dyn LookupInternal>,
+ #[cfg(feature = "full")]
+ pub to_eval: Rc<RefCell<Vec<String>>>,
}
impl<'parent, 'lookup> Set<'parent, 'lookup> {
@@ -34,6 +40,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
parent: None,
data: HashMap::new(),
lookup_internal: None,
+ #[cfg(feature = "full")]
+ to_eval: Rc::new(RefCell::new(Vec::new())),
}
}
@@ -58,7 +66,10 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
} else if let Some(parent) = self.parent {
parent.lookup_internal(name)
} else {
- bail!("no lookup possible")
+ bail!(
+ "tried to lookup {:?} but no lookup function is available",
+ name
+ )
}
}
@@ -121,7 +132,12 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
}
#[cfg(feature = "full")]
Token::FunctionCall { name, args } => {
- result.push_str(&functions::expand_call(name, args, self)?);
+ result.push_str(&functions::expand_call(
+ name,
+ args,
+ self,
+ Some(Rc::clone(&self.to_eval)),
+ )?);
}
}
}
@@ -146,6 +162,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
parent: Some(self),
data: HashMap::new(),
lookup_internal: Some(lookup),
+ #[cfg(feature = "full")]
+ to_eval: Default::default(),
}
}
@@ -155,6 +173,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
parent: Some(self),
data: HashMap::new(),
lookup_internal: None,
+ #[cfg(feature = "full")]
+ to_eval: Default::default(),
}
}
}