aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/lookup_internal.rs
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2024-11-10 23:04:33 -0700
committerMelody Horn <melody@boringcactus.com>2024-11-10 23:04:33 -0700
commit825ff799a2154ffb94ef21bbc14e2afe2afa2006 (patch)
treeea65adacd459f4fef69beac960aa3397ed7a66d0 /src/makefile/lookup_internal.rs
parent87ce694f4d15d84e5737615b2768deaf866a796d (diff)
downloadmakers-825ff799a2154ffb94ef21bbc14e2afe2afa2006.tar.gz
makers-825ff799a2154ffb94ef21bbc14e2afe2afa2006.zip
overhaul lookup_internal
Diffstat (limited to 'src/makefile/lookup_internal.rs')
-rw-r--r--src/makefile/lookup_internal.rs164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/makefile/lookup_internal.rs b/src/makefile/lookup_internal.rs
new file mode 100644
index 0000000..c0fc08b
--- /dev/null
+++ b/src/makefile/lookup_internal.rs
@@ -0,0 +1,164 @@
+use eyre::{bail, eyre, Result};
+use std::cell::RefCell;
+use std::path::Path;
+use std::rc::Rc;
+
+use super::target::Target;
+
+#[derive(Clone)]
+pub enum LookupInternal<'a> {
+ Partial {
+ targets: &'a Vec<&'a str>,
+ },
+ Complete {
+ target: Option<&'a Target>,
+ get_target: &'a dyn Fn(&str) -> Result<Rc<RefCell<Target>>>,
+ },
+}
+
+impl<'a> LookupInternal<'a> {
+ pub fn new_partial(targets: &'a Vec<&str>) -> Self {
+ Self::Partial { targets }
+ }
+
+ pub fn new(
+ target: Option<&'a Target>,
+ get_target: &'a dyn Fn(&str) -> Result<Rc<RefCell<Target>>>,
+ ) -> Self {
+ Self::Complete { target, get_target }
+ }
+
+ pub fn lookup(&self, macro_name: &str) -> Result<String> {
+ let macro_pieces = match macro_name.chars().next() {
+ Some('@') => self.target_name()?,
+ Some('?') => self.newer_prerequisites()?,
+ Some('<') => self.inference_prerequisite()?,
+ Some('*') => self.target_stem()?,
+ #[cfg(feature = "full")]
+ Some('^') => self.all_prerequisites()?,
+ _ => bail!("unknown internal macro {}", macro_name),
+ };
+
+ let macro_pieces = if macro_name.ends_with('D') {
+ macro_pieces
+ .into_iter()
+ .map(|x| {
+ Path::new(&x)
+ .parent()
+ .ok_or_else(|| eyre!("no parent"))
+ .map(|x| x.to_string_lossy().into())
+ })
+ .collect::<Result<_, _>>()?
+ } else if macro_name.ends_with('F') {
+ macro_pieces
+ .into_iter()
+ .map(|x| {
+ Path::new(&x)
+ .file_name()
+ .ok_or_else(|| eyre!("no filename"))
+ .map(|x| x.to_string_lossy().into())
+ })
+ .collect::<Result<_, _>>()?
+ } else {
+ macro_pieces
+ };
+
+ Ok(macro_pieces.join(" "))
+ }
+
+ /// POSIX: The $@ shall evaluate to the full target name of the current target.
+ fn target_name(&self) -> Result<Vec<String>> {
+ match self {
+ Self::Partial { targets } => {
+ Ok(targets.iter().map(|target| target.to_string()).collect())
+ }
+ Self::Complete {
+ target: Some(target),
+ ..
+ } => Ok(vec![target.name.clone()]),
+ Self::Complete { target: None, .. } => {
+ bail!("tried to expand internal macro with no target")
+ }
+ }
+ }
+
+ /// POSIX: The $? macro shall evaluate to the list of prerequisites that are newer than the current target.
+ fn newer_prerequisites(&self) -> Result<Vec<String>> {
+ match self {
+ Self::Partial { .. } => bail!("can’t expand $? when target not defined"),
+ Self::Complete {
+ target: Some(target),
+ get_target,
+ } => Ok(target
+ .prerequisites
+ .iter()
+ .filter(|prereq| {
+ get_target(prereq)
+ .ok()
+ .and_then(|prereq| prereq.borrow().newer_than(target))
+ .unwrap_or(false)
+ })
+ .cloned()
+ .collect()),
+ Self::Complete { target: None, .. } => {
+ bail!("tried to expand internal macro with no target")
+ }
+ }
+ }
+
+ /// POSIX: 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.
+ ///
+ /// GNU: The name of the first prerequisite.
+ fn inference_prerequisite(&self) -> Result<Vec<String>> {
+ match self {
+ Self::Partial { .. } => bail!("can’t expand $< when target not defined"),
+ Self::Complete {
+ target: Some(target),
+ ..
+ } => {
+ // TODO check that exists_but_inferring_anyway won’t break this
+ Ok(vec![target
+ .prerequisites
+ .first()
+ .cloned()
+ .unwrap_or_default()])
+ }
+ Self::Complete { target: None, .. } => {
+ bail!("tried to expand internal macro with no target")
+ }
+ }
+ }
+
+ /// POSIX: The $* macro shall evaluate to the current target name with its suffix deleted.
+ fn target_stem(&self) -> Result<Vec<String>> {
+ match self {
+ Self::Partial { .. } => bail!("can’t expand $* when target not defined"),
+ Self::Complete {
+ target: Some(target),
+ ..
+ } => Ok(vec![target
+ .stem
+ .as_ref()
+ .unwrap_or(&target.name)
+ .to_owned()]),
+ Self::Complete { target: None, .. } => {
+ bail!("tried to expand internal macro with no target")
+ }
+ }
+ }
+
+ /// GNU: The names of all the prerequisites.
+ #[cfg(feature = "full")]
+ fn all_prerequisites(&self) -> Result<Vec<String>> {
+ match self {
+ Self::Partial { .. } => bail!("can’t expand $^ when target not defined"),
+ Self::Complete {
+ target: Some(target),
+ ..
+ } => Ok(target.prerequisites.clone()),
+ Self::Complete { target: None, .. } => {
+ bail!("tried to expand internal macro with no target")
+ }
+ }
+ }
+}