aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/makefile/mod.rs')
-rw-r--r--src/makefile/mod.rs194
1 files changed, 7 insertions, 187 deletions
diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index d968bb3..e07cdfd 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -2,206 +2,26 @@ use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::env;
use std::fmt;
-use std::fs::{metadata, File};
+use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::rc::Rc;
-use std::time::SystemTime;
use lazy_static::lazy_static;
use regex::Regex;
use crate::args::Args;
+mod command_line;
+mod inference_rules;
+mod target;
mod token;
+use command_line::CommandLine;
+use inference_rules::InferenceRule;
+use target::Target;
use token::{tokenize, Token, TokenString};
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct InferenceRule {
- /// POSIX calls this ".s1" but that's not useful.
- product: String,
- /// POSIX calls this ".s2" but that's not useful.
- prereq: String,
- commands: Vec<CommandLine>,
-}
-
-impl fmt::Display for InferenceRule {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, "{}{}:", &self.prereq, &self.product)?;
- for command in &self.commands {
- writeln!(f, "\t{}", command)?;
- }
- Ok(())
- }
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct Target {
- name: String,
- prerequisites: Vec<String>,
- commands: Vec<CommandLine>,
- already_updated: Cell<bool>,
-}
-
-impl Target {
- fn modified_time(&self) -> Option<SystemTime> {
- metadata(&self.name)
- .and_then(|metadata| metadata.modified())
- .ok()
- }
-
- fn newer_than(&self, other: &Target) -> Option<bool> {
- let self_updated = self.already_updated.get();
- let other_updated = other.already_updated.get();
- Some(match (self.modified_time(), other.modified_time()) {
- (Some(self_mtime), Some(other_mtime)) => self_mtime >= other_mtime,
- // per POSIX: "If the target does not exist after the target has been
- // successfully made up-to-date, the target shall be treated as being
- // newer than any target for which it is a prerequisite."
- (None, _) if self_updated && other.prerequisites.contains(&self.name) => true,
- (_, None) if other_updated && self.prerequisites.contains(&other.name) => false,
- _ => return None,
- })
- }
-
- fn is_up_to_date(&self, file: &Makefile) -> bool {
- if self.already_updated.get() {
- return true;
- }
- let exists = metadata(&self.name).is_ok();
- let newer_than_all_dependencies = self.prerequisites.iter().all(|t| {
- self.newer_than(&file.get_target(t).borrow())
- .unwrap_or(false)
- });
- exists && newer_than_all_dependencies
- }
-
- pub fn update(&self, file: &Makefile) {
- for prereq in &self.prerequisites {
- file.update_target(prereq);
- }
- if !self.is_up_to_date(file) {
- self.execute_commands(file);
- }
- self.already_updated.set(true);
- }
-
- fn execute_commands(&self, file: &Makefile) {
- for command in &self.commands {
- command.execute(file, self);
- }
- }
-}
-
-impl fmt::Display for Target {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let prereqs = self.prerequisites.join(" ");
- writeln!(f, "{}: {}", &self.name, prereqs)?;
- for command in &self.commands {
- writeln!(f, "\t{}", command)?;
- }
- Ok(())
- }
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct CommandLine {
- /// If the command prefix contains a <hyphen-minus>, or the -i option is present, or
- /// the special target .IGNORE has either the current target as a prerequisite or has
- /// no prerequisites, any error found while executing the command shall be ignored.
- ignore_errors: bool,
- /// If the command prefix contains an at-sign and the make utility command line -n
- /// option is not specified, or the -s option is present, or the special target
- /// .SILENT has either the current target as a prerequisite or has no prerequisites,
- /// the command shall not be written to standard output before it is executed.
- silent: bool,
- /// If the command prefix contains a <plus-sign>, this indicates a makefile command
- /// line that shall be executed even if -n, -q, or -t is specified.
- always_execute: bool,
- execution_line: TokenString,
-}
-
-impl CommandLine {
- fn from(mut line: TokenString) -> Self {
- let mut ignore_errors = false;
- let mut silent = false;
- let mut always_execute = false;
-
- if let Token::Text(text) = line.first_token_mut() {
- let mut text_chars = text.chars().peekable();
- while let Some(x) = text_chars.next_if(|x| matches!(x, '-' | '@' | '+')) {
- match x {
- '-' => ignore_errors = true,
- '@' => silent = true,
- '+' => always_execute = true,
- _ => unreachable!(),
- }
- }
- *text = text_chars.collect();
- }
-
- CommandLine {
- ignore_errors,
- silent,
- always_execute,
- execution_line: line,
- }
- }
-
- fn execute(&self, file: &Makefile, target: &Target) {
- let ignore_error = self.ignore_errors
- || file.args.ignore_errors
- || file.special_target_has_prereq(".IGNORE", &target.name);
- let silent = (self.silent && !file.args.dry_run)
- || file.args.silent
- || file.special_target_has_prereq(".SILENT", &target.name);
-
- let execution_line = file.expand_macros(&self.execution_line, Some(target));
-
- if !silent {
- println!("{}", execution_line);
- }
-
- let should_execute =
- self.always_execute || !(file.args.dry_run || file.args.question || file.args.touch);
- if !should_execute {
- return;
- }
-
- // TODO don't fuck this up
- let execution_line = ::std::ffi::CString::new(execution_line.as_bytes())
- .expect("execution line shouldn't have a null in the middle");
- // TODO pass shell "-e" if errors are not ignored
- let return_value = unsafe { libc::system(execution_line.as_ptr()) };
- if return_value != 0 {
- // apparently there was an error. do we care?
- if !ignore_error {
- // TODO handle this error gracefully
- panic!("error from command execution!");
- }
- }
- }
-}
-
-impl fmt::Display for CommandLine {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.ignore_errors {
- write!(f, "-")?;
- }
- if self.silent {
- write!(f, "@")?;
- }
- if self.always_execute {
- write!(f, "+")?;
- }
- let execution_line = format!("{}", &self.execution_line);
- let execution_line = execution_line.replace("\n", "↵\n");
- write!(f, "{}", execution_line)?;
- Ok(())
- }
-}
-
enum MacroSource {
File,
CommandLineOrMAKEFLAGS,