aboutsummaryrefslogtreecommitdiff
path: root/src/makefile
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-03-27 18:10:59 -0600
committerMelody Horn <melody@boringcactus.com>2021-03-27 18:10:59 -0600
commit016fe1abb11cc89b66e1f08d4286e3ef6f2d5437 (patch)
tree7e68d16b9345d6d916e6cd4ec6139b7958e613c4 /src/makefile
parente7300dc5f2c0787623286d83d54f6a26b08ff907 (diff)
downloadmakers-016fe1abb11cc89b66e1f08d4286e3ef6f2d5437.tar.gz
makers-016fe1abb11cc89b66e1f08d4286e3ef6f2d5437.zip
split up read into several sub-methods
Diffstat (limited to 'src/makefile')
-rw-r--r--src/makefile/mod.rs429
1 files 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<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 {