From 028e1a631629c7f5959e522dee80188ea7534d8f Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sat, 27 Mar 2021 15:50:39 -0600 Subject: refactor makefile elements into submodules --- src/makefile/command_line.rs | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/makefile/command_line.rs (limited to 'src/makefile/command_line.rs') diff --git a/src/makefile/command_line.rs b/src/makefile/command_line.rs new file mode 100644 index 0000000..0e0e745 --- /dev/null +++ b/src/makefile/command_line.rs @@ -0,0 +1,102 @@ +use std::fmt; + +use crate::makefile::target::Target; +use crate::makefile::token::{Token, TokenString}; +use crate::makefile::Makefile; + +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct CommandLine { + /// If the command prefix contains a , 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 , 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 { + pub 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, + } + } + + pub 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(()) + } +} -- cgit v1.2.3