diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/makefile/mod.rs | 429 | 
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 {  |