From 016fe1abb11cc89b66e1f08d4286e3ef6f2d5437 Mon Sep 17 00:00:00 2001
From: Melody Horn <melody@boringcactus.com>
Date: Sat, 27 Mar 2021 18:10:59 -0600
Subject: split up read into several sub-methods

---
 src/makefile/mod.rs | 429 +++++++++++++++++++++++++++-------------------------
 1 file changed, 219 insertions(+), 210 deletions(-)

(limited to 'src/makefile')

diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index 34dac04..2f73c70 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -3,7 +3,7 @@ use std::collections::HashMap;
 use std::env;
 use std::fmt;
 use std::fs::File;
-use std::io::{BufRead, BufReader};
+use std::io::{BufRead, BufReader, Error as IoError};
 use std::path::Path;
 use std::rc::Rc;
 
@@ -22,6 +22,39 @@ use inference_rules::InferenceRule;
 use target::Target;
 use token::{tokenize, Token, TokenString};
 
+enum LineType {
+    Rule,
+    Macro,
+    Unknown,
+}
+
+impl LineType {
+    fn of(line_tokens: &TokenString) -> Self {
+        for token in line_tokens.tokens() {
+            if let Token::Text(text) = token {
+                let colon_idx = text.find(':');
+                let equals_idx = text.find('=');
+                match (colon_idx, equals_idx) {
+                    (Some(_), None) => {
+                        return Self::Rule;
+                    }
+                    (Some(c), Some(e)) if c < e => {
+                        return Self::Rule;
+                    }
+                    (None, Some(_)) => {
+                        return Self::Macro;
+                    }
+                    (Some(c), Some(e)) if e < c => {
+                        return Self::Macro;
+                    }
+                    _ => {}
+                }
+            }
+        }
+        Self::Unknown
+    }
+}
+
 enum MacroSource {
     File,
     CommandLineOrMakeflags,
@@ -29,6 +62,30 @@ enum MacroSource {
     Builtin,
 }
 
+fn inference_match<'a>(
+    targets: &[&'a str],
+    prerequisites: &[String],
+) -> Option<regex::Captures<'a>> {
+    lazy_static! {
+        static ref INFERENCE_RULE: Regex =
+            Regex::new(r"^(?P<s2>(\.[^/.]+)?)(?P<s1>\.[^/.]+)$").unwrap();
+        static ref SPECIAL_TARGET: Regex = Regex::new(r"^\.[A-Z]+$").unwrap();
+    }
+
+    let inference_match = INFERENCE_RULE.captures(targets[0]);
+    let special_target_match = SPECIAL_TARGET.captures(targets[0]);
+
+    let inference_rule = targets.len() == 1
+        && prerequisites.is_empty()
+        && inference_match.is_some()
+        && special_target_match.is_none();
+    if inference_rule {
+        inference_match
+    } else {
+        None
+    }
+}
+
 pub(crate) struct Makefile<'a> {
     inference_rules: Vec<InferenceRule>,
     macros: HashMap<String, (MacroSource, TokenString)>,
@@ -130,232 +187,184 @@ impl<'a> Makefile<'a> {
             } else {
                 // unfortunately, rules vs macros can't be determined until after
                 // macro tokenizing. so that's suboptimal.
-                enum LineType {
-                    Rule,
-                    Macro,
-                    Unknown,
-                }
-
-                fn get_line_type(line_tokens: &TokenString) -> LineType {
-                    for token in line_tokens.tokens() {
-                        if let Token::Text(text) = token {
-                            let colon_idx = text.find(':');
-                            let equals_idx = text.find('=');
-                            match (colon_idx, equals_idx) {
-                                (Some(_), None) => {
-                                    return LineType::Rule;
-                                }
-                                (Some(c), Some(e)) if c < e => {
-                                    return LineType::Rule;
-                                }
-                                (None, Some(_)) => {
-                                    return LineType::Macro;
-                                }
-                                (Some(c), Some(e)) if e < c => {
-                                    return LineType::Macro;
-                                }
-                                _ => {}
-                            }
-                        }
-                    }
-                    LineType::Unknown
-                }
 
                 // TODO errors
                 let line_tokens: TokenString = line.parse().unwrap();
 
-                let line_type = get_line_type(&line_tokens);
+                let line_type = LineType::of(&line_tokens);
 
                 match line_type {
-                    LineType::Rule => {
-                        let (targets, not_targets) = line_tokens.split_once(':').unwrap();
-                        let targets = self.expand_macros(&targets, None);
-                        let targets = targets
-                            .split_whitespace()
-                            .map(String::from)
-                            .collect::<Vec<String>>();
-                        let (prerequisites, mut commands) = match not_targets.split_once(';') {
-                            Some((prerequisites, mut command)) => {
-                                while command.ends_with("\\") && lines_iter.peek().is_some() {
-                                    command.strip_suffix("\\");
-                                    command
-                                        .extend(tokenize(&lines_iter.next().unwrap().1.unwrap()));
-                                }
-                                (prerequisites, vec![command])
-                            }
-                            None => (not_targets, vec![]),
-                        };
-                        let prerequisites = self.expand_macros(&prerequisites, None);
-                        let prerequisites = prerequisites
-                            .split_whitespace()
-                            .map(|x| x.into())
-                            .collect::<Vec<String>>();
-
-                        while let Some((_, x)) = lines_iter.next_if(|(_, x)| {
-                            x.as_ref()
-                                .ok()
-                                .map_or(false, |line| line.starts_with('\t') || line.is_empty())
-                        }) {
-                            let mut line = x.unwrap();
-                            if !line.is_empty() {
-                                line.remove(0);
-                            }
-                            if line.is_empty() {
-                                continue;
-                            }
-                            while line.ends_with('\\') {
-                                match lines_iter.next() {
-                                    Some((_, Ok(next_line))) => {
-                                        let next_line =
-                                            next_line.strip_prefix("\t").unwrap_or(&next_line);
-                                        line.push('\n');
-                                        line.push_str(next_line);
-                                    }
-                                    _ => break,
-                                }
-                            }
-                            commands.push(line.parse().unwrap());
-                        }
+                    LineType::Rule => self.read_rule(&line_tokens, &mut lines_iter),
+                    LineType::Macro => self.read_macro(&line_tokens),
+                    LineType::Unknown => {
+                        panic!(
+                            "error: line {}: unknown line {:?}",
+                            line_number, line_tokens
+                        );
+                    }
+                }
+            }
+        }
+    }
 
-                        let commands = commands
-                            .into_iter()
-                            .map(CommandLine::from)
-                            .collect::<Vec<_>>();
+    fn read_rule(
+        &mut self,
+        line_tokens: &TokenString,
+        lines_iter: &mut impl Iterator<Item = (usize, Result<String, IoError>)>,
+    ) {
+        let mut lines_iter = lines_iter.peekable();
+        let (targets, not_targets) = line_tokens.split_once(':').unwrap();
+        let targets = self.expand_macros(&targets, None);
+        let targets = targets.split_whitespace().collect::<Vec<_>>();
+        let (prerequisites, mut commands) = match not_targets.split_once(';') {
+            Some((prerequisites, mut command)) => {
+                while command.ends_with("\\") && lines_iter.peek().is_some() {
+                    command.strip_suffix("\\");
+                    command.extend(tokenize(&lines_iter.next().unwrap().1.unwrap()));
+                }
+                (prerequisites, vec![command])
+            }
+            None => (not_targets, vec![]),
+        };
+        let prerequisites = self.expand_macros(&prerequisites, None);
+        let prerequisites = prerequisites
+            .split_whitespace()
+            .map(|x| x.into())
+            .collect::<Vec<String>>();
+
+        while let Some((_, x)) = lines_iter.next_if(|(_, x)| {
+            x.as_ref()
+                .ok()
+                .map_or(false, |line| line.starts_with('\t') || line.is_empty())
+        }) {
+            let mut line = x.unwrap();
+            if !line.is_empty() {
+                line.remove(0);
+            }
+            if line.is_empty() {
+                continue;
+            }
+            while line.ends_with('\\') {
+                match lines_iter.next() {
+                    Some((_, Ok(next_line))) => {
+                        let next_line = next_line.strip_prefix("\t").unwrap_or(&next_line);
+                        line.push('\n');
+                        line.push_str(next_line);
+                    }
+                    _ => break,
+                }
+            }
+            commands.push(line.parse().unwrap());
+        }
 
-                        if targets.is_empty() {
-                            continue;
-                        }
+        let commands = commands
+            .into_iter()
+            .map(CommandLine::from)
+            .collect::<Vec<_>>();
 
-                        // we don't know yet if it's a target rule or an inference rule
-                        lazy_static! {
-                            static ref INFERENCE_RULE: Regex =
-                                Regex::new(r"^(?P<s2>(\.[^/.]+)?)(?P<s1>\.[^/.]+)$").unwrap();
-                            static ref SPECIAL_TARGET: Regex = Regex::new(r"^\.[A-Z]+$").unwrap();
-                        }
+        if targets.is_empty() {
+            return;
+        }
 
-                        let inference_match = INFERENCE_RULE.captures(&targets[0]);
-                        let special_target_match = SPECIAL_TARGET.captures(&targets[0]);
-
-                        let inference_rule = targets.len() == 1
-                            && prerequisites.is_empty()
-                            && inference_match.is_some()
-                            && special_target_match.is_none();
-                        if inference_rule {
-                            let inference_match = inference_match.unwrap();
-                            let new_rule = InferenceRule {
-                                product: inference_match.name("s1").unwrap().as_str().to_string(),
-                                prereq: inference_match.name("s2").unwrap().as_str().to_string(),
-                                commands,
-                            };
-
-                            self.inference_rules.retain(|existing_rule| {
-                                (&existing_rule.prereq, &existing_rule.product)
-                                    != (&new_rule.prereq, &new_rule.product)
-                            });
-                            self.inference_rules.push(new_rule);
-                        } else {
-                            for target in targets {
-                                if self.first_non_special_target.is_none()
-                                    && !target.starts_with('.')
-                                {
-                                    self.first_non_special_target = Some(target.clone());
-                                }
-                                let mut targets = self.targets.borrow_mut();
-                                match targets.get_mut(&target) {
-                                    Some(old_target)
-                                        if commands.is_empty()
-                                            && !(target == ".SUFIXES"
-                                                && prerequisites.is_empty()) =>
-                                    {
-                                        let mut old_target = old_target.borrow_mut();
-                                        let new_prerequisites = prerequisites
-                                            .iter()
-                                            .filter(|x| !old_target.prerequisites.contains(x))
-                                            .cloned()
-                                            .collect::<Vec<_>>();
-                                        old_target.prerequisites.extend(new_prerequisites);
-                                    }
-                                    _ => {
-                                        let new_target = Target {
-                                            name: target.clone(),
-                                            prerequisites: prerequisites.clone(),
-                                            commands: commands.clone(),
-                                            already_updated: Cell::new(false),
-                                        };
-                                        targets.insert(
-                                            target.clone(),
-                                            Rc::new(RefCell::new(new_target)),
-                                        );
-                                    }
-                                }
-                            }
-                        }
+        // we don't know yet if it's a target rule or an inference rule
+        let inference_match = inference_match(&targets, &prerequisites);
+
+        if let Some(inference_match) = inference_match {
+            let new_rule = InferenceRule {
+                product: inference_match.name("s1").unwrap().as_str().to_string(),
+                prereq: inference_match.name("s2").unwrap().as_str().to_string(),
+                commands,
+            };
+
+            self.inference_rules.retain(|existing_rule| {
+                (&existing_rule.prereq, &existing_rule.product)
+                    != (&new_rule.prereq, &new_rule.product)
+            });
+            self.inference_rules.push(new_rule);
+        } else {
+            for target in targets {
+                if self.first_non_special_target.is_none() && !target.starts_with('.') {
+                    self.first_non_special_target = Some(target.into());
+                }
+                let mut targets = self.targets.borrow_mut();
+                match targets.get_mut(target) {
+                    Some(old_target)
+                        if commands.is_empty()
+                            && !(target == ".SUFIXES" && prerequisites.is_empty()) =>
+                    {
+                        let mut old_target = old_target.borrow_mut();
+                        let new_prerequisites = prerequisites
+                            .iter()
+                            .filter(|x| !old_target.prerequisites.contains(x))
+                            .cloned()
+                            .collect::<Vec<_>>();
+                        old_target.prerequisites.extend(new_prerequisites);
                     }
-                    LineType::Macro => {
-                        let (name, mut value) = line_tokens.split_once('=').unwrap();
-                        let name = self.expand_macros(&name, None);
-                        // GNUisms are annoying, but popular
-                        let mut expand_value = false;
-                        let mut skip_if_defined = false;
-                        let mut append = false;
-                        let name = if let Some(real_name) = name.strip_suffix("::") {
-                            expand_value = true;
-                            real_name
-                        } else if let Some(real_name) = name.strip_suffix(":") {
-                            expand_value = true;
-                            real_name
-                        } else if let Some(real_name) = name.strip_suffix("?") {
-                            skip_if_defined = true;
-                            real_name
-                        } else if let Some(real_name) = name.strip_suffix("+") {
-                            append = true;
-                            real_name
-                        } else {
-                            &name
+                    _ => {
+                        let new_target = Target {
+                            name: target.into(),
+                            prerequisites: prerequisites.clone(),
+                            commands: commands.clone(),
+                            already_updated: Cell::new(false),
                         };
+                        targets.insert(target.into(), Rc::new(RefCell::new(new_target)));
+                    }
+                }
+            }
+        }
+    }
 
-                        let name = name.trim_end();
-                        value.trim_start();
+    fn read_macro(&mut self, line_tokens: &TokenString) {
+        let (name, mut value) = line_tokens.split_once('=').unwrap();
+        let name = self.expand_macros(&name, None);
+        // GNUisms are annoying, but popular
+        let mut expand_value = false;
+        let mut skip_if_defined = false;
+        let mut append = false;
+        let name = if let Some(real_name) = name.strip_suffix("::") {
+            expand_value = true;
+            real_name
+        } else if let Some(real_name) = name.strip_suffix(":") {
+            expand_value = true;
+            real_name
+        } else if let Some(real_name) = name.strip_suffix("?") {
+            skip_if_defined = true;
+            real_name
+        } else if let Some(real_name) = name.strip_suffix("+") {
+            append = true;
+            real_name
+        } else {
+            &name
+        };
 
-                        let value = if expand_value {
-                            TokenString::text(self.expand_macros(&value, None))
-                        } else {
-                            value
-                        };
+        let name = name.trim_end();
+        value.trim_start();
 
-                        match self.macros.get(name) {
-                            // We always let command line or MAKEFLAGS macros override macros from the file.
-                            Some((MacroSource::CommandLineOrMakeflags, _)) => continue,
-                            // We let environment variables override macros from the file only if the command-line argument to do that was given
-                            Some((MacroSource::Environment, _))
-                                if self.args.environment_overrides =>
-                            {
-                                continue
-                            }
-                            _ if skip_if_defined => continue,
-                            _ => {}
-                        }
+        let value = if expand_value {
+            TokenString::text(self.expand_macros(&value, None))
+        } else {
+            value
+        };
 
-                        let value = match self.macros.remove(name) {
-                            Some((_, mut old_value)) if append => {
-                                // TODO eagerly expand if appending to eagerly-expanded macro
-                                old_value.extend(TokenString::text(" "));
-                                old_value.extend(value);
-                                old_value
-                            }
-                            _ => value,
-                        };
-                        self.macros.insert(name.into(), (MacroSource::File, value));
-                    }
-                    LineType::Unknown => {
-                        panic!(
-                            "error: line {}: unknown line {:?}",
-                            line_number, line_tokens
-                        );
-                    }
-                }
-            }
+        match self.macros.get(name) {
+            // We always let command line or MAKEFLAGS macros override macros from the file.
+            Some((MacroSource::CommandLineOrMakeflags, _)) => return,
+            // We let environment variables override macros from the file only if the command-line argument to do that was given
+            Some((MacroSource::Environment, _)) if self.args.environment_overrides => return,
+            _ if skip_if_defined => return,
+            _ => {}
         }
+
+        let value = match self.macros.remove(name) {
+            Some((_, mut old_value)) if append => {
+                // TODO eagerly expand if appending to eagerly-expanded macro
+                old_value.extend(TokenString::text(" "));
+                old_value.extend(value);
+                old_value
+            }
+            _ => value,
+        };
+        self.macros.insert(name.into(), (MacroSource::File, value));
     }
 
     fn special_target_has_prereq(&self, target: &str, name: &str) -> bool {
-- 
cgit v1.2.3