aboutsummaryrefslogtreecommitdiff
path: root/src/makefile
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2024-11-10 21:20:34 -0700
committerMelody Horn <melody@boringcactus.com>2024-11-10 21:20:34 -0700
commitfca10b517b448b4023ad8c3225e59dcefd4004e4 (patch)
tree7507e959d1f690044945dd6fcf34b5fef3385b14 /src/makefile
parentf945ff33f9312a534ae52f1c763b4150ae41dcf6 (diff)
downloadmakers-fca10b517b448b4023ad8c3225e59dcefd4004e4.tar.gz
makers-fca10b517b448b4023ad8c3225e59dcefd4004e4.zip
overhaul eval architecture
Diffstat (limited to 'src/makefile')
-rw-r--r--src/makefile/command_line.rs2
-rw-r--r--src/makefile/conditional.rs4
-rw-r--r--src/makefile/eval_context.rs52
-rw-r--r--src/makefile/functions.rs284
-rw-r--r--src/makefile/input.rs64
-rw-r--r--src/makefile/macro.rs60
-rw-r--r--src/makefile/mod.rs6
7 files changed, 314 insertions, 158 deletions
diff --git a/src/makefile/command_line.rs b/src/makefile/command_line.rs
index ae6c9e9..c993a10 100644
--- a/src/makefile/command_line.rs
+++ b/src/makefile/command_line.rs
@@ -36,7 +36,7 @@ fn execute_command_line(
let mut command = Command::new(program);
command.args(args);
#[cfg(feature = "full")]
- command.envs(macros.resolve_exports()?);
+ command.envs(macros.resolve_exports::<&[u8]>(None)?);
Ok(command.status()?)
}
diff --git a/src/makefile/conditional.rs b/src/makefile/conditional.rs
index 98400e6..6eed14a 100644
--- a/src/makefile/conditional.rs
+++ b/src/makefile/conditional.rs
@@ -99,7 +99,7 @@ fn decode_condition_args(line_body: &str) -> Option<(TokenString, TokenString)>
impl Line {
pub fn from(
line: &str,
- expand_macro: impl Fn(&TokenString) -> Result<String>,
+ mut expand_macro: impl FnMut(&TokenString) -> Result<String>,
) -> Result<Option<Self>> {
let line = line.trim_start();
Ok(Some(if let Some(line) = line.strip_prefix("ifeq ") {
@@ -134,7 +134,7 @@ impl Line {
&self,
current_state: Option<&State>,
is_macro_defined: impl Fn(&str) -> bool,
- expand_macro: impl Fn(&TokenString) -> Result<String>,
+ mut expand_macro: impl FnMut(&TokenString) -> Result<String>,
) -> Result<StateAction> {
Ok(match self {
Self::IfEqual(arg1, arg2) => {
diff --git a/src/makefile/eval_context.rs b/src/makefile/eval_context.rs
new file mode 100644
index 0000000..06f4fbf
--- /dev/null
+++ b/src/makefile/eval_context.rs
@@ -0,0 +1,52 @@
+use eyre::{Result, WrapErr};
+use std::io::{BufRead, Cursor};
+use std::rc::Rc;
+
+use crate::makefile::input::FinishedMakefileReader;
+use crate::makefile::MakefileReader;
+
+pub struct DeferredEvalContext<'parent, 'args, 'grandparent, R: BufRead> {
+ parent: &'parent MakefileReader<'args, 'grandparent, R>,
+ children: Vec<FinishedMakefileReader>,
+}
+
+impl<'parent, 'args, 'grandparent, R: BufRead>
+ DeferredEvalContext<'parent, 'args, 'grandparent, R>
+{
+ pub fn new(parent: &'parent MakefileReader<'args, 'grandparent, R>) -> Self {
+ Self {
+ parent,
+ children: Vec::new(),
+ }
+ }
+
+ pub fn push(&mut self, child: FinishedMakefileReader) {
+ self.children.push(child);
+ }
+
+ pub fn eval(&mut self, to_eval: String) -> Result<()> {
+ let child_macros = self.parent.macros.with_overlay();
+ let child = MakefileReader::read(
+ self.parent.args,
+ child_macros,
+ Cursor::new(to_eval),
+ "<eval>",
+ Rc::clone(&self.parent.file_names),
+ )
+ .context("while evaling")?
+ .finish();
+ self.push(child);
+ Ok(())
+ }
+}
+
+impl<'parent, 'args, 'grandparent, R: BufRead> IntoIterator
+ for DeferredEvalContext<'parent, 'args, 'grandparent, R>
+{
+ type Item = FinishedMakefileReader;
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.children.into_iter()
+ }
+}
diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs
index 5d5e222..e76a0d2 100644
--- a/src/makefile/functions.rs
+++ b/src/makefile/functions.rs
@@ -1,129 +1,131 @@
-use std::cell::RefCell;
use std::env;
+use std::io::BufRead;
use std::process::{Command, Stdio};
-use std::rc::Rc;
use eyre::{bail, Result, WrapErr};
+use super::eval_context::DeferredEvalContext;
use super::pattern::r#match;
use super::r#macro::{Macro, Set as MacroSet};
use super::token::TokenString;
use super::ItemSource;
+pub const NO_EVAL: Option<&mut DeferredEvalContext<&[u8]>> = None;
+
#[allow(clippy::cognitive_complexity)]
pub fn expand_call(
name: &str,
args: &[TokenString],
macros: &MacroSet,
- to_eval: Option<Rc<RefCell<Vec<String>>>>,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
match name {
"subst" => {
assert_eq!(args.len(), 3);
- text::subst(macros, &args[0], &args[1], &args[2])
+ text::subst(macros, &args[0], &args[1], &args[2], eval_context)
}
"patsubst" => {
assert_eq!(args.len(), 3);
- text::patsubst(macros, &args[0], &args[1], &args[2])
+ text::patsubst(macros, &args[0], &args[1], &args[2], eval_context)
}
"strip" => {
assert_eq!(args.len(), 1);
- text::strip(macros, &args[0])
+ text::strip(macros, &args[0], eval_context)
}
"findstring" => {
assert_eq!(args.len(), 2);
- text::findstring(macros, &args[0], &args[1])
+ text::findstring(macros, &args[0], &args[1], eval_context)
}
"filter" => {
assert_eq!(args.len(), 2);
- text::filter(macros, &args[0], &args[1])
+ text::filter(macros, &args[0], &args[1], eval_context)
}
"filter-out" => {
assert_eq!(args.len(), 2);
- text::filter_out(macros, &args[0], &args[1])
+ text::filter_out(macros, &args[0], &args[1], eval_context)
}
"sort" => {
assert_eq!(args.len(), 1);
- text::sort(macros, &args[0])
+ text::sort(macros, &args[0], eval_context)
}
"word" => {
assert_eq!(args.len(), 2);
- text::word(macros, &args[0], &args[1])
+ text::word(macros, &args[0], &args[1], eval_context)
}
"words" => {
assert_eq!(args.len(), 1);
- text::words(macros, &args[0])
+ text::words(macros, &args[0], eval_context)
}
"firstword" => {
assert_eq!(args.len(), 1);
- text::firstword(macros, &args[0])
+ text::firstword(macros, &args[0], eval_context)
}
"lastword" => {
assert_eq!(args.len(), 1);
- text::lastword(macros, &args[0])
+ text::lastword(macros, &args[0], eval_context)
}
"dir" => {
assert_eq!(args.len(), 1);
- file_name::dir(macros, &args[0])
+ file_name::dir(macros, &args[0], eval_context)
}
"notdir" => {
assert_eq!(args.len(), 1);
- file_name::notdir(macros, &args[0])
+ file_name::notdir(macros, &args[0], eval_context)
}
"basename" => {
assert_eq!(args.len(), 1);
- file_name::basename(macros, &args[0])
+ file_name::basename(macros, &args[0], eval_context)
}
"addsuffix" => {
assert_eq!(args.len(), 2);
- file_name::addsuffix(macros, &args[0], &args[1])
+ file_name::addsuffix(macros, &args[0], &args[1], eval_context)
}
"addprefix" => {
assert_eq!(args.len(), 2);
- file_name::addprefix(macros, &args[0], &args[1])
+ file_name::addprefix(macros, &args[0], &args[1], eval_context)
}
"wildcard" => {
assert_eq!(args.len(), 1);
- file_name::wildcard(macros, &args[0])
+ file_name::wildcard(macros, &args[0], eval_context)
}
"realpath" => {
assert_eq!(args.len(), 1);
- file_name::realpath(macros, &args[0])
+ file_name::realpath(macros, &args[0], eval_context)
}
"abspath" => {
assert_eq!(args.len(), 1);
- file_name::abspath(macros, &args[0])
+ file_name::abspath(macros, &args[0], eval_context)
}
"if" => {
assert!(args.len() == 2 || args.len() == 3);
- conditional::r#if(macros, &args[0], &args[1], args.get(2))
+ conditional::r#if(macros, &args[0], &args[1], args.get(2), eval_context)
}
"or" => {
assert!(!args.is_empty());
- conditional::or(macros, args.iter())
+ conditional::or(macros, args.iter(), eval_context)
}
"and" => {
assert!(!args.is_empty());
- conditional::and(macros, args.iter())
+ conditional::and(macros, args.iter(), eval_context)
}
"foreach" => {
assert_eq!(args.len(), 3);
- foreach(macros, &args[0], &args[1], &args[2])
+ foreach(macros, &args[0], &args[1], &args[2], eval_context)
}
"call" => {
assert!(!args.is_empty());
- call(macros, args.iter())
+ call(macros, args.iter(), eval_context)
}
"eval" => {
assert_eq!(args.len(), 1);
- let should_eval = eval(macros, &args[0])?;
- if let Some(to_eval) = to_eval {
- to_eval.borrow_mut().push(should_eval);
+ let should_eval = eval(macros, &args[0], eval_context.as_deref_mut())?;
+ if let Some(eval_context) = eval_context {
+ eval_context.eval(should_eval)?;
} else {
bail!("tried to eval something but no eval back-channel was available");
}
@@ -132,17 +134,17 @@ pub fn expand_call(
"origin" => {
assert_eq!(args.len(), 1);
- origin(macros, &args[0])
+ origin(macros, &args[0], eval_context)
}
"error" => {
assert_eq!(args.len(), 1);
- meta::error(macros, &args[0])
+ meta::error(macros, &args[0], eval_context)
}
"shell" => {
assert_eq!(args.len(), 1);
- shell(macros, &args[0])
+ shell(macros, &args[0], eval_context)
}
// fallback
@@ -159,10 +161,11 @@ mod text {
from: &TokenString,
to: &TokenString,
text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let from = macros.expand(from)?;
- let to = macros.expand(to)?;
- let text = macros.expand(text)?;
+ let from = macros.expand(from, eval_context.as_deref_mut())?;
+ let to = macros.expand(to, eval_context.as_deref_mut())?;
+ let text = macros.expand(text, eval_context)?;
Ok(text.replace(&from, &to))
}
@@ -171,10 +174,11 @@ mod text {
from: &TokenString,
to: &TokenString,
text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let from = macros.expand(from)?;
- let to = macros.expand(to)?;
- let text = macros.expand(text)?;
+ let from = macros.expand(from, eval_context.as_deref_mut())?;
+ let to = macros.expand(to, eval_context.as_deref_mut())?;
+ let text = macros.expand(text, eval_context)?;
let words =
text.split_whitespace()
.map(|word| {
@@ -186,8 +190,12 @@ mod text {
Ok(words.join(" "))
}
- pub fn strip(macros: &MacroSet, text: &TokenString) -> Result<String> {
- let text = macros.expand(text)?;
+ pub fn strip(
+ macros: &MacroSet,
+ text: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let text = macros.expand(text, eval_context)?;
// TODO don't allocate this vec
let words = text.split_whitespace().collect::<Vec<_>>();
Ok(words.join(" "))
@@ -197,9 +205,10 @@ mod text {
macros: &MacroSet,
needle: &TokenString,
haystack: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let needle = macros.expand(needle)?;
- let haystack = macros.expand(haystack)?;
+ let needle = macros.expand(needle, eval_context.as_deref_mut())?;
+ let haystack = macros.expand(haystack, eval_context)?;
if haystack.contains(&needle) {
Ok(needle)
} else {
@@ -207,10 +216,15 @@ mod text {
}
}
- pub fn filter(macros: &MacroSet, patterns: &TokenString, text: &TokenString) -> Result<String> {
- let patterns = macros.expand(patterns)?;
+ pub fn filter(
+ macros: &MacroSet,
+ patterns: &TokenString,
+ text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let patterns = macros.expand(patterns, eval_context.as_deref_mut())?;
let patterns = patterns.split_whitespace().collect::<Vec<_>>();
- let text = macros.expand(text)?;
+ let text = macros.expand(text, eval_context)?;
let text = text.split_whitespace();
let mut result_pieces = vec![];
for word in text {
@@ -228,10 +242,11 @@ mod text {
macros: &MacroSet,
patterns: &TokenString,
text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let patterns = macros.expand(patterns)?;
+ let patterns = macros.expand(patterns, eval_context.as_deref_mut())?;
let patterns = patterns.split_whitespace().collect::<Vec<_>>();
- let text = macros.expand(text)?;
+ let text = macros.expand(text, eval_context)?;
let text = text.split_whitespace();
let mut result_pieces = vec![];
for word in text {
@@ -245,18 +260,27 @@ mod text {
Ok(result_pieces.join(" "))
}
- pub fn sort(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn sort(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
let mut words = words.split_whitespace().collect::<Vec<_>>();
words.sort_unstable();
words.dedup();
Ok(words.join(" "))
}
- pub fn word(macros: &MacroSet, n: &TokenString, text: &TokenString) -> Result<String> {
- let n = macros.expand(n)?;
+ pub fn word(
+ macros: &MacroSet,
+ n: &TokenString,
+ text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let n = macros.expand(n, eval_context.as_deref_mut())?;
let n: usize = n.parse().wrap_err("while calling `word`")?;
- let text = macros.expand(text)?;
+ let text = macros.expand(text, eval_context)?;
Ok(text
.split_whitespace()
.nth(n.saturating_add(1))
@@ -264,18 +288,30 @@ mod text {
.to_owned())
}
- pub fn words(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn words(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
Ok(words.split_whitespace().count().to_string())
}
- pub fn firstword(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn firstword(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
Ok(words.split_whitespace().next().unwrap_or("").to_owned())
}
- pub fn lastword(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn lastword(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
Ok(words.split_whitespace().last().unwrap_or("").to_owned())
}
}
@@ -285,14 +321,19 @@ mod file_name {
use std::env;
use std::ffi::OsStr;
use std::fs;
+ use std::io::BufRead;
use std::path::{Path, MAIN_SEPARATOR};
- use eyre::WrapErr;
-
use super::*;
+ use crate::makefile::eval_context::DeferredEvalContext;
+ use eyre::WrapErr;
- pub fn dir(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn dir(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
let words = words
.split_whitespace()
.map(|word| {
@@ -307,8 +348,12 @@ mod file_name {
Ok(words.join(" "))
}
- pub fn notdir(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn notdir(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
let words = words
.split_whitespace()
.map(|word| {
@@ -321,8 +366,12 @@ mod file_name {
Ok(words.join(" "))
}
- pub fn basename(macros: &MacroSet, words: &TokenString) -> Result<String> {
- let words = macros.expand(words)?;
+ pub fn basename(
+ macros: &MacroSet,
+ words: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let words = macros.expand(words, eval_context)?;
let words = words
.split_whitespace()
.map(|word| {
@@ -339,9 +388,10 @@ mod file_name {
macros: &MacroSet,
suffix: &TokenString,
targets: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let suffix = macros.expand(suffix)?;
- let targets = macros.expand(targets)?;
+ let suffix = macros.expand(suffix, eval_context.as_deref_mut())?;
+ let targets = macros.expand(targets, eval_context)?;
let results = targets
.split_whitespace()
.map(|t| format!("{}{}", t, suffix))
@@ -353,9 +403,10 @@ mod file_name {
macros: &MacroSet,
prefix: &TokenString,
targets: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let prefix = macros.expand(prefix)?;
- let targets = macros.expand(targets)?;
+ let prefix = macros.expand(prefix, eval_context.as_deref_mut())?;
+ let targets = macros.expand(targets, eval_context)?;
let results = targets
.split_whitespace()
.map(|t| format!("{}{}", prefix, t))
@@ -363,8 +414,12 @@ mod file_name {
Ok(results.join(" "))
}
- pub fn wildcard(macros: &MacroSet, pattern: &TokenString) -> Result<String> {
- let pattern = macros.expand(pattern)?;
+ pub fn wildcard(
+ macros: &MacroSet,
+ pattern: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let pattern = macros.expand(pattern, eval_context)?;
let home_dir = env::var("HOME")
.ok()
.or_else(|| dirs::home_dir().and_then(|p| p.to_str().map(String::from)));
@@ -383,8 +438,12 @@ mod file_name {
Ok(results.join(" "))
}
- pub fn realpath(macros: &MacroSet, targets: &TokenString) -> Result<String> {
- let targets = macros.expand(targets)?;
+ pub fn realpath(
+ macros: &MacroSet,
+ targets: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let targets = macros.expand(targets, eval_context)?;
let results = targets
.split_whitespace()
.map(|x| {
@@ -396,9 +455,13 @@ mod file_name {
Ok(results.join(" "))
}
- pub fn abspath(macros: &MacroSet, targets: &TokenString) -> Result<String> {
+ pub fn abspath(
+ macros: &MacroSet,
+ targets: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
// TODO don't resolve symlinks
- realpath(macros, targets)
+ realpath(macros, targets, eval_context)
}
}
@@ -411,24 +474,29 @@ mod conditional {
condition: &TokenString,
if_true: &TokenString,
if_false: Option<&TokenString>,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
let mut condition = condition.clone();
condition.trim_start();
condition.trim_end();
- let condition = macros.expand(&condition)?;
+ let condition = macros.expand(&condition, eval_context.as_deref_mut())?;
if condition.is_empty() {
- if_false.map_or_else(|| Ok(String::new()), |if_false| macros.expand(if_false))
+ if_false.map_or_else(
+ || Ok(String::new()),
+ |if_false| macros.expand(if_false, eval_context),
+ )
} else {
- macros.expand(if_true)
+ macros.expand(if_true, eval_context)
}
}
pub fn or<'a>(
macros: &MacroSet,
args: impl Iterator<Item = &'a TokenString>,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
for arg in args {
- let arg = macros.expand(arg)?;
+ let arg = macros.expand(arg, eval_context.as_deref_mut())?;
if !arg.is_empty() {
return Ok(arg);
}
@@ -439,10 +507,11 @@ mod conditional {
pub fn and<'a>(
macros: &MacroSet,
args: impl Iterator<Item = &'a TokenString>,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
let mut last = String::new();
for arg in args {
- last = macros.expand(arg)?;
+ last = macros.expand(arg, eval_context.as_deref_mut())?;
if last.is_empty() {
return Ok(String::new());
}
@@ -456,9 +525,10 @@ pub fn foreach(
var: &TokenString,
list: &TokenString,
text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
) -> Result<String> {
- let var = macros.expand(var)?;
- let list = macros.expand(list)?;
+ let var = macros.expand(var, eval_context.as_deref_mut())?;
+ let list = macros.expand(list, eval_context.as_deref_mut())?;
let words = list.split_whitespace();
let mut macros = macros.with_overlay();
@@ -473,15 +543,19 @@ pub fn foreach(
eagerly_expanded: false,
},
);
- macros.expand(text)
+ macros.expand(text, eval_context.as_deref_mut())
})
.collect::<Result<Vec<_>, _>>()?;
Ok(results.join(" "))
}
-pub fn call<'a>(macros: &MacroSet, args: impl Iterator<Item = &'a TokenString>) -> Result<String> {
+pub fn call<'a>(
+ macros: &MacroSet,
+ args: impl Iterator<Item = &'a TokenString>,
+ mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+) -> Result<String> {
let args = args
- .map(|arg| macros.expand(arg))
+ .map(|arg| macros.expand(arg, eval_context.as_deref_mut()))
.collect::<Result<Vec<_>, _>>()?;
let function = args[0].clone();
// TODO if function is a builtin, call the builtin instead
@@ -498,31 +572,47 @@ pub fn call<'a>(macros: &MacroSet, args: impl Iterator<Item = &'a TokenString>)
},
);
}
- macros.expand(&TokenString::r#macro(function))
+ macros.expand(&TokenString::r#macro(function), eval_context)
}
// TODO consider bringing eval logic in here since we put the Vec in MacroSet IIRC
-pub fn eval(macros: &MacroSet, arg: &TokenString) -> Result<String> {
- macros.expand(arg)
+pub fn eval(
+ macros: &MacroSet,
+ arg: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+) -> Result<String> {
+ macros.expand(arg, eval_context)
}
-pub fn origin(macros: &MacroSet, variable: &TokenString) -> Result<String> {
- let variable = macros.expand(variable)?;
+pub fn origin(
+ macros: &MacroSet,
+ variable: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+) -> Result<String> {
+ let variable = macros.expand(variable, eval_context)?;
Ok(macros.origin(&variable).to_owned())
}
mod meta {
use super::*;
- pub fn error(macros: &MacroSet, text: &TokenString) -> Result<String> {
- let text = macros.expand(text)?;
+ pub fn error(
+ macros: &MacroSet,
+ text: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+ ) -> Result<String> {
+ let text = macros.expand(text, eval_context)?;
bail!("{}", text);
}
}
-pub fn shell(macros: &MacroSet, command: &TokenString) -> Result<String> {
+pub fn shell(
+ macros: &MacroSet,
+ command: &TokenString,
+ eval_context: Option<&mut DeferredEvalContext<impl BufRead>>,
+) -> Result<String> {
// TODO bring this in from command_line
- let command = macros.expand(command)?;
+ let command = macros.expand(command, eval_context)?;
let (program, args) = if cfg!(windows) {
let cmd = env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".into());
let args = vec!["/c", &command];
@@ -551,7 +641,7 @@ mod test {
type R = Result<()>;
fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> Result<String> {
- super::expand_call(name, args, macros, None)
+ super::expand_call(name, args, macros, NO_EVAL)
}
macro_rules! call {
diff --git a/src/makefile/input.rs b/src/makefile/input.rs
index ed1140e..f1d4f70 100644
--- a/src/makefile/input.rs
+++ b/src/makefile/input.rs
@@ -2,8 +2,6 @@ use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fs::File;
-#[cfg(feature = "full")]
-use std::io::Cursor;
use std::io::{BufRead, BufReader, Error as IoError, ErrorKind as IoErrorKind, Lines};
use std::iter::Peekable;
use std::path::Path;
@@ -18,6 +16,7 @@ use crate::args::Args;
use super::command_line::CommandLine;
#[cfg(feature = "full")]
use super::conditional::{Line as ConditionalLine, State as ConditionalState};
+use super::eval_context::DeferredEvalContext;
use super::inference_rules::InferenceRule;
#[cfg(feature = "full")]
use super::r#macro::ExportConfig;
@@ -97,10 +96,10 @@ fn inference_match<'a>(
) -> Option<InferenceMatch<'a>> {
lazy_static! {
static ref INFERENCE_RULE: Regex = #[allow(clippy::unwrap_used)]
- Regex::new(r"^(?P<s2>(\.[^/.]+)?)(?P<s1>\.[^/.]+)$")
+ Regex::new(r"^(?P<s2>(\.[^/.]+)?)(?P<s1>\.[^/.]+)$")
.unwrap();
static ref SPECIAL_TARGET: Regex = #[allow(clippy::unwrap_used)]
- Regex::new(r"^\.[A-Z]+$").unwrap();
+ Regex::new(r"^\.[A-Z]+$").unwrap();
}
let inference_match = INFERENCE_RULE.captures(targets[0]);
@@ -192,13 +191,13 @@ pub struct MakefileReader<'a, 'parent, R: BufRead> {
built_in_targets: HashMap<String, Target>,
pub first_non_special_target: Option<String>,
pub failed_includes: Vec<String>,
- args: &'a Args,
+ pub args: &'a Args,
lines_iter: Peekable<LineNumbers<String, IoError, Lines<R>>>,
// join with escaped_newline_replacement to get the actual line
pending_line: Option<(usize, Vec<String>)>,
#[cfg(feature = "full")]
conditional_stack: Vec<ConditionalState>,
- file_names: Rc<RefCell<Vec<String>>>,
+ pub file_names: Rc<RefCell<Vec<String>>>,
}
impl<'a, 'parent> MakefileReader<'a, 'parent, BufReader<File>> {
@@ -311,24 +310,6 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> {
.wrap_err_with(|| format!("while parsing line {}", line_number))?
.trim(),
);
- // and let's eval whatever bullshit needs evaling
- #[cfg(feature = "full")]
- {
- let eval = self.macros.to_eval.take();
- for eval in eval {
- let child_macros = self.macros.with_overlay();
- let child = MakefileReader::read(
- self.args,
- child_macros,
- Cursor::new(eval),
- "<eval>",
- Rc::clone(&self.file_names),
- )
- .context("while evaling")?
- .finish();
- self.extend(child);
- }
- }
let line_type = LineType::of(&line_tokens);
(line_tokens, line_type)
} else {
@@ -471,15 +452,19 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> {
Err(err) => return Some((line_number, Err(err))),
};
if let Some(line) = cond_line {
+ let mut deferred_eval_context = DeferredEvalContext::new(self);
let action = line
.action(
self.conditional_stack.last(),
|name| self.macros.is_defined(name),
- |t| self.expand_macros(t),
+ |t| self.expand_macros_deferred_eval(t, &mut deferred_eval_context),
)
.wrap_err_with(|| {
format!("while applying conditional on line {}", line_number)
});
+ for child in deferred_eval_context {
+ self.extend(child);
+ }
let action = match action {
Ok(x) => x,
Err(err) => return Some((line_number, Err(err))),
@@ -609,6 +594,7 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> {
log::error!("rule-specific macros are not implemented yet");
return Ok(());
}
+ let mut deferred_eval_context = DeferredEvalContext::new(self);
let prerequisites = self
.macros
.with_lookup(&|macro_name: &str| {
@@ -644,7 +630,10 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> {
Ok(macro_pieces.join(" "))
})
- .expand(&prerequisites)?;
+ .expand(&prerequisites, Some(&mut deferred_eval_context))?;
+ for child in deferred_eval_context {
+ self.extend(child);
+ }
let prerequisites = prerequisites
.split_whitespace()
.map(|x| x.into())
@@ -913,9 +902,22 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> {
Ok(name.to_owned())
}
- fn expand_macros(&self, text: &TokenString) -> Result<String> {
+ fn expand_macros(&mut self, text: &TokenString) -> Result<String> {
+ let mut deferred_eval_context = DeferredEvalContext::new(self);
+ let result = self.expand_macros_deferred_eval(text, &mut deferred_eval_context);
+ for child in deferred_eval_context {
+ self.extend(child);
+ }
+ result
+ }
+
+ fn expand_macros_deferred_eval(
+ &self,
+ text: &TokenString,
+ deferred_eval_context: &mut DeferredEvalContext<R>,
+ ) -> Result<String> {
self.macros
- .expand(text)
+ .expand(text, Some(deferred_eval_context))
.wrap_err_with(|| format!("while expanding \"{}\"", text))
}
@@ -1006,7 +1008,7 @@ worked = perhaps
endif
";
let args = Args::empty();
- let makefile = MakefileReader::read(
+ let mut makefile = MakefileReader::read(
&args,
MacroSet::new(),
Cursor::new(file),
@@ -1052,7 +1054,7 @@ baz
endef
";
let args = Args::empty();
- let makefile = MakefileReader::read(
+ let mut makefile = MakefileReader::read(
&args,
MacroSet::new(),
Cursor::new(file),
@@ -1080,7 +1082,7 @@ endif
FOO = bar
";
let args = Args::empty();
- let makefile = MakefileReader::read(
+ let mut makefile = MakefileReader::read(
&args,
MacroSet::new(),
Cursor::new(file),
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs
index 7b43b13..beae404 100644
--- a/src/makefile/macro.rs
+++ b/src/makefile/macro.rs
@@ -3,16 +3,17 @@ use std::collections::HashMap;
use std::collections::HashSet;
use std::env;
use std::fmt;
+use std::io::BufRead;
use std::rc::Rc;
-use eyre::{bail, Result, WrapErr};
-#[cfg(not(feature = "full"))]
-use regex::Regex;
-
#[cfg(feature = "full")]
use super::functions;
use super::token::{Token, TokenString};
use super::ItemSource;
+use crate::makefile::eval_context::DeferredEvalContext;
+use eyre::{bail, Result, WrapErr};
+#[cfg(not(feature = "full"))]
+use regex::Regex;
#[derive(Debug, Clone)]
pub struct Macro {
@@ -90,8 +91,6 @@ pub struct Set<'parent, 'lookup> {
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>>>,
}
@@ -103,8 +102,6 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
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(),
}
@@ -206,14 +203,18 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
}
}
- pub fn expand(&self, text: &TokenString) -> Result<String> {
+ pub fn expand<R: BufRead>(
+ &self,
+ text: &TokenString,
+ mut eval_context: Option<&mut DeferredEvalContext<R>>,
+ ) -> 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)
+ .expand(name, eval_context.as_deref_mut())
.wrap_err_with(|| format!("while expanding \"{}\"", name))?;
let internal_macro_names = &['@', '?', '<', '*', '^'][..];
let internal_macro_suffices = &['D', 'F'][..];
@@ -231,14 +232,14 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
Ok(String::new())
},
|x| {
- self.expand(&x.text)
+ self.expand(&x.text, eval_context.as_deref_mut())
.wrap_err_with(|| format!("while expanding \"{}\"", &x.text))
},
)?
};
let macro_value = match replacement {
Some((subst1, subst2)) => {
- let subst1 = self.expand(subst1)?;
+ let subst1 = self.expand(subst1, eval_context.as_deref_mut())?;
#[cfg(feature = "full")]
{
let (subst1, subst2) = if subst1.contains('%') {
@@ -257,7 +258,7 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
"patsubst",
&args,
self,
- Some(Rc::clone(&self.to_eval)),
+ eval_context.as_deref_mut(),
)?
}
#[cfg(not(feature = "full"))]
@@ -286,9 +287,9 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
}
#[cfg(feature = "full")]
Token::FunctionCall { name, args } => {
- let name = self.expand(name)?;
+ let name = self.expand(name, eval_context.as_deref_mut())?;
let fn_result =
- functions::expand_call(&name, args, self, Some(Rc::clone(&self.to_eval)))?;
+ functions::expand_call(&name, args, self, eval_context.as_deref_mut())?;
log::trace!("expanded {} into \"{}\"", token, &fn_result);
result.push_str(&fn_result);
}
@@ -332,8 +333,6 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
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),
}
@@ -345,23 +344,27 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
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)| {
+ 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()?;
+ let mut parent_exports = parent.resolve_exports(eval_context)?;
parent_exports.extend(own_exports);
parent_exports
} else {
@@ -377,8 +380,6 @@ impl fmt::Debug for Set<'_, '_> {
r#struct.field("data", &self.data);
r#struct.field("lookup_internal", &self.lookup_internal.map(|_| ()));
#[cfg(feature = "full")]
- r#struct.field("to_eval", &self.to_eval);
- #[cfg(feature = "full")]
r#struct.field("exported", &self.exported);
r#struct.field("warnings", &self.warnings);
r#struct.finish()
@@ -456,6 +457,7 @@ fn builtins() -> Vec<(&'static str, TokenString)> {
#[cfg(test)]
mod test {
use super::*;
+ use crate::makefile::functions::NO_EVAL;
type R = Result<()>;
@@ -471,7 +473,10 @@ mod test {
eagerly_expanded: false,
},
);
- assert_eq!(macros.expand(&"$(oof:;=?)".parse()?)?, "bruh? swag? yeet?");
+ assert_eq!(
+ macros.expand(&"$(oof:;=?)".parse()?, NO_EVAL)?,
+ "bruh? swag? yeet?"
+ );
Ok(())
}
@@ -487,7 +492,10 @@ mod test {
eagerly_expanded: false,
},
);
- assert_eq!(macros.expand(&"$(m:%=%-objs)".parse()?)?, "conf-objs");
+ assert_eq!(
+ macros.expand(&"$(m:%=%-objs)".parse()?, NO_EVAL)?,
+ "conf-objs"
+ );
Ok(())
}
}
diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index 62cc416..c6a8c0a 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -16,10 +16,12 @@ use target::{DynamicTargetSet, Target};
use token::TokenString;
use crate::args::Args;
+use crate::makefile::functions::NO_EVAL;
mod command_line;
#[cfg(feature = "full")]
mod conditional;
+mod eval_context;
#[cfg(feature = "full")]
mod functions;
mod inference_rules;
@@ -410,7 +412,9 @@ impl<'a> Makefile<'a> {
Ok(macro_pieces.join(" "))
};
- self.macros.with_lookup(&lookup_internal).expand(text)
+ self.macros
+ .with_lookup(&lookup_internal)
+ .expand(text, NO_EVAL)
}
}