From f6117f5a1b8e5aa426b959c0fe5571de626070dc Mon Sep 17 00:00:00 2001
From: Melody Horn <melody@boringcactus.com>
Date: Sun, 4 Apr 2021 14:07:14 -0600
Subject: keep track of pattern stem to correctly populate `$*`

---
 src/makefile/inference_rules.rs |  2 +-
 src/makefile/input.rs           |  1 +
 src/makefile/mod.rs             | 27 ++++++++++++++++++---------
 src/makefile/target.rs          |  1 +
 4 files changed, 21 insertions(+), 10 deletions(-)

(limited to 'src')

diff --git a/src/makefile/inference_rules.rs b/src/makefile/inference_rules.rs
index 86e5598..57756c9 100644
--- a/src/makefile/inference_rules.rs
+++ b/src/makefile/inference_rules.rs
@@ -23,7 +23,7 @@ impl InferenceRule {
         }
     }
 
-    fn first_match<'s, 't: 's>(&'s self, target_name: &'t str) -> Result<Option<Captures<'t>>> {
+    pub fn first_match<'s, 't: 's>(&'s self, target_name: &'t str) -> Result<Option<Captures<'t>>> {
         self.products
             .iter()
             .map(|pattern| r#match(pattern, target_name))
diff --git a/src/makefile/input.rs b/src/makefile/input.rs
index 6ee5392..b02cdb6 100644
--- a/src/makefile/input.rs
+++ b/src/makefile/input.rs
@@ -446,6 +446,7 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> {
                             name: target.into(),
                             prerequisites: prerequisites.clone(),
                             commands: commands.clone(),
+                            stem: None,
                             already_updated: Cell::new(false),
                         };
                         self.targets.insert(target.into(), new_target);
diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index ce30623..cda5559 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -180,6 +180,9 @@ impl<'a> Makefile<'a> {
                     name: name.into(),
                     prerequisites: prereqs,
                     commands: rule.commands.clone(),
+                    stem: rule
+                        .first_match(name)?
+                        .and_then(|x| x.get(0).map(|x| x.as_str().to_owned())),
                     already_updated: Cell::new(false),
                 });
                 break;
@@ -231,6 +234,7 @@ impl<'a> Makefile<'a> {
                     name: name.into(),
                     prerequisites: vec![],
                     commands,
+                    stem: None,
                     already_updated: Cell::new(false),
                 });
             } else {
@@ -240,6 +244,7 @@ impl<'a> Makefile<'a> {
                         name: name.into(),
                         prerequisites: vec![],
                         commands: vec![],
+                        stem: None,
                         already_updated: Cell::new(true),
                     });
                 }
@@ -269,15 +274,15 @@ impl<'a> Makefile<'a> {
 
     fn expand_macros(&self, text: &TokenString, target: Option<&Target>) -> Result<String> {
         let target = target.cloned();
-        let lookup_internal = move |name: &str| {
+        let lookup_internal = move |macro_name: &str| {
             let target = target
                 .as_ref()
                 .ok_or_else(|| eyre!("internal macro but no current target!"))?;
-            let macro_pieces = if name.starts_with('@') {
+            let macro_pieces = if macro_name.starts_with('@') {
                 // The $@ shall evaluate to the full target name of the
                 // current target.
                 vec![target.name.clone()]
-            } else if name.starts_with('?') {
+            } else if macro_name.starts_with('?') {
                 // The $? macro shall evaluate to the list of prerequisites
                 // that are newer than the current target.
                 target
@@ -291,22 +296,25 @@ impl<'a> Makefile<'a> {
                     })
                     .cloned()
                     .collect()
-            } else if name.starts_with('<') {
+            } else if macro_name.starts_with('<') {
                 // In an inference rule, the $< macro shall evaluate to the
                 // filename whose existence allowed the inference rule to be
                 // chosen for the target. In the .DEFAULT rule, the $< macro
                 // shall evaluate to the current target name.
                 // TODO make that actually be the case (rn exists_but_inferring_anyway might fuck that up)
                 vec![target.prerequisites.get(0).cloned().unwrap_or_default()]
-            } else if name.starts_with('*') {
+            } else if macro_name.starts_with('*') {
                 // The $* macro shall evaluate to the current target name with
-                // its suffix deleted.
-                vec![Path::new(name).with_extension("").to_string_lossy().into()]
+                // its suffix deleted. (GNUism: the match stem)
+                vec![Path::new(target.stem.as_ref().unwrap_or(&target.name))
+                    .with_extension("")
+                    .to_string_lossy()
+                    .into()]
             } else {
                 unreachable!()
             };
 
-            let macro_pieces = if name.ends_with('D') {
+            let macro_pieces = if macro_name.ends_with('D') {
                 macro_pieces
                     .into_iter()
                     .map(|x| {
@@ -316,7 +324,7 @@ impl<'a> Makefile<'a> {
                             .map(|x| x.to_string_lossy().into())
                     })
                     .collect::<Result<_, _>>()?
-            } else if name.ends_with('F') {
+            } else if macro_name.ends_with('F') {
                 macro_pieces
                     .into_iter()
                     .map(|x| {
@@ -433,6 +441,7 @@ fn builtin_targets() -> Vec<Target> {
             .map(String::from)
             .collect(),
         commands: vec![],
+        stem: None,
         already_updated: Cell::new(false),
     }]
 }
diff --git a/src/makefile/target.rs b/src/makefile/target.rs
index 1c382d0..506bde8 100644
--- a/src/makefile/target.rs
+++ b/src/makefile/target.rs
@@ -14,6 +14,7 @@ pub struct Target {
     pub name: String,
     pub prerequisites: Vec<String>,
     pub commands: Vec<CommandLine>,
+    pub stem: Option<String>,
     pub already_updated: Cell<bool>,
 }
 
-- 
cgit v1.2.3