aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/command_line.rs
blob: 0e0e745b6addaca70c64be7eff2b654e32cf1561 (plain)
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(())
    }
}