From 016fe1abb11cc89b66e1f08d4286e3ef6f2d5437 Mon Sep 17 00:00:00 2001 From: Melody Horn 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(-) 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> { + lazy_static! { + static ref INFERENCE_RULE: Regex = + Regex::new(r"^(?P(\.[^/.]+)?)(?P\.[^/.]+)$").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, macros: HashMap, @@ -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::>(); - 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::>(); - - 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::>(); + fn read_rule( + &mut self, + line_tokens: &TokenString, + lines_iter: &mut impl Iterator)>, + ) { + 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::>(); + 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::>(); + + 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::>(); - // 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(\.[^/.]+)?)(?P\.[^/.]+)$").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::>(); - 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::>(); + 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