From cdffe0c58f939490fdfcef56843f8fab4433988b Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sun, 4 Apr 2021 13:17:20 -0600 Subject: prevent rule reuse in inference chains --- src/makefile/mod.rs | 165 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs index 0f8cfd2..ce30623 100644 --- a/src/makefile/mod.rs +++ b/src/makefile/mod.rs @@ -104,6 +104,97 @@ impl<'a> Makefile<'a> { } } + fn infer_target(&self, name: &str, banned_rules: Vec<&InferenceRule>) -> Result<()> { + let mut new_target = None; + + let follow_gnu = cfg!(feature = "full"); + + let vpath_options = match self.macros.get("VPATH") { + Some((_, vpath)) if follow_gnu => { + let vpath = self.expand_macros(vpath, None)?; + env::split_paths(&vpath).collect() + } + _ => vec![], + }; + // When no target rule is found to update a target, the inference rules shall + // be checked. The suffix of the target to be built is compared to the list of + // suffixes specified by the .SUFFIXES special targets. If the .s1 suffix is + // found in .SUFFIXES... + // TODO bring back .SUFFIXES for suffix-based rules + // the inference rules shall be searched in the order defined... + // TODO implement GNUish shortest-stem-first matching + let inference_rule_candidates = self + .inference_rules + .iter() + .chain(self.builtin_inference_rules.iter()) + .filter(|rule| !banned_rules.contains(rule)) + .filter(|rule| rule.matches(name).unwrap_or(false)); + for rule in inference_rule_candidates { + log::trace!( + "{:>58} considering rule to build {:?} from {:?}", + name, + &rule.products, + &rule.prerequisites + ); + // whose prerequisite file ($*.s2) exists. + let prereq_paths = rule + .prereqs(name)? + .inspect(|x| log::trace!("{:>58} prereq {}", name, x)) + .map(|prereq_path_name| { + if name == prereq_path_name { + // we can't build this based on itself! fuck outta here + return None; + } + if self.targets.borrow().contains_key(&prereq_path_name) { + return Some(prereq_path_name); + } + let prereq_path = PathBuf::from(&prereq_path_name); + let prereq_vpath_options = if prereq_path.is_absolute() { + None + } else { + Some(vpath_options.iter().map(|vpath| vpath.join(&prereq_path))) + } + .into_iter() + .flatten(); + std::iter::once(prereq_path.clone()) + .chain(prereq_vpath_options) + .find(|prereq| prereq.exists()) + .map(|path| path.to_string_lossy().to_string()) + .or_else(|| { + let mut banned_rules = banned_rules.clone(); + banned_rules.push(rule); + self.infer_target(&prereq_path_name, banned_rules) + .ok() + .and_then(|_| { + if self.targets.borrow().contains_key(&prereq_path_name) { + Some(prereq_path_name) + } else { + None + } + }) + }) + }) + .collect::>>(); + if let Some(prereqs) = prereq_paths { + new_target = Some(Target { + name: name.into(), + prerequisites: prereqs, + commands: rule.commands.clone(), + already_updated: Cell::new(false), + }); + break; + } + } + + if let Some(new_target) = new_target { + self.targets + .borrow_mut() + .insert(new_target.name.clone(), Rc::new(RefCell::new(new_target))); + } + + Ok(()) + } + pub fn get_target(&self, name: &str) -> Result>> { if self .fucking_bullshit_recursion_stack @@ -118,15 +209,6 @@ impl<'a> Makefile<'a> { // TODO implement .POSIX let follow_gnu = cfg!(feature = "full"); - let vpath_options = match self.macros.get("VPATH") { - Some((_, vpath)) if follow_gnu => { - let vpath = self.expand_macros(vpath, None)?; - env::split_paths(&vpath).collect() - } - _ => vec![], - }; - - let mut new_target = None; let exists_but_infer_anyway = if follow_gnu { self.targets .borrow() @@ -136,71 +218,12 @@ impl<'a> Makefile<'a> { false }; if !self.targets.borrow().contains_key(name) || exists_but_infer_anyway { - // When no target rule is found to update a target, the inference rules shall - // be checked. The suffix of the target to be built... - let suffix = Path::new(name) - .extension() - .map_or_else(String::new, |ext| format!(".{}", ext.to_string_lossy())); - // is compared to the list of suffixes specified by the .SUFFIXES special - // targets. If the .s1 suffix is found in .SUFFIXES... - // TODO figure out if .SUFFIXES is supposed to apply to gnuful patterns - if self.special_target_has_prereq(".SUFFIXES", &suffix) || suffix.is_empty() { - // the inference rules shall be searched in the order defined... - // TODO implement GNUish shortest-stem-first matching - let inference_rule_candidates = self - .inference_rules - .iter() - .chain(self.builtin_inference_rules.iter()) - .filter(|rule| rule.matches(name).unwrap_or(false)); - for rule in inference_rule_candidates { - log::trace!( - "{:>58} considering rule to build {:?} from {:?}", - name, - &rule.products, - &rule.prerequisites - ); - // whose prerequisite file ($*.s2) exists. - let prereq_paths = rule - .prereqs(name)? - .inspect(|x| log::trace!("{:>58} prereq {}", name, x)) - .map(|prereq_path_name| { - if name == prereq_path_name { - // we can't build this based on itself! fuck outta here - return None; - } - // TODO allow chains but only reluctantly - if self.targets.borrow().contains_key(&prereq_path_name) { - return Some(prereq_path_name); - } - let prereq_path = PathBuf::from(prereq_path_name); - let prereq_vpath_options = if prereq_path.is_absolute() { - None - } else { - Some(vpath_options.iter().map(|vpath| vpath.join(&prereq_path))) - } - .into_iter() - .flatten(); - std::iter::once(prereq_path.clone()) - .chain(prereq_vpath_options) - .find(|prereq| prereq.exists()) - .map(|path| path.to_string_lossy().to_string()) - }) - .collect::>>(); - if let Some(prereqs) = prereq_paths { - new_target = Some(Target { - name: name.into(), - prerequisites: prereqs, - commands: rule.commands.clone(), - already_updated: Cell::new(false), - }); - break; - } - } - } + self.infer_target(name, vec![])?; } + let mut new_target = None; let targets = self.targets.borrow(); - if !targets.contains_key(name) && new_target.is_none() { + if !targets.contains_key(name) { // well, inference didn't work. is there a default? if let Some(default) = targets.get(".DEFAULT") { let commands = default.borrow().commands.clone(); -- cgit v1.2.3