1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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 <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 {
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(())
}
}
|