aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-04-04 13:17:20 -0600
committerMelody Horn <melody@boringcactus.com>2021-04-04 13:17:20 -0600
commitcdffe0c58f939490fdfcef56843f8fab4433988b (patch)
tree9eb1ba1fada98a49a180e5280f1df480bac89a2f
parent2c457b4181340fb078ada4d8de505adb2d0f273c (diff)
downloadmakers-cdffe0c58f939490fdfcef56843f8fab4433988b.tar.gz
makers-cdffe0c58f939490fdfcef56843f8fab4433988b.zip
prevent rule reuse in inference chains
-rw-r--r--src/makefile/mod.rs165
1 files 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::<Option<Vec<String>>>();
+ 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<Rc<RefCell<Target>>> {
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::<Option<Vec<String>>>();
- 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();