sagiegurari / cargo-make
1
//! # command
2
//!
3
//! Runs task commands/scripts.
4
//!
5

6
#[cfg(test)]
7
#[path = "./command_test.rs"]
8
mod command_test;
9

10
use crate::logger;
11
use crate::toolchain;
12
use crate::types::{CommandSpec, Step};
13
use envmnt;
14
use run_script;
15
use run_script::{IoOptions, ScriptError, ScriptOptions};
16
use std::io;
17
use std::io::Error;
18
use std::process::{Command, ExitStatus, Output, Stdio};
19

20
/// Returns the exit code (-1 if no exit code found)
21 8
pub(crate) fn get_exit_code(exit_status: Result<ExitStatus, Error>, force: bool) -> i32 {
22 8
    match exit_status {
23 8
        Ok(code) => {
24 8
            if !code.success() {
25 8
                match code.code() {
26 8
                    Some(value) => value,
27 0
                    None => -1,
28
                }
29
            } else {
30 8
                0
31
            }
32
        }
33 8
        Err(error) => {
34 8
            if !force {
35 8
                error!("Error while executing command, error: {:#?}", error);
36
            }
37

38 0
            -1
39 6
        }
40 2
    }
41 8
}
42

43 8
pub(crate) fn get_exit_code_from_output(output: &io::Result<Output>, force: bool) -> i32 {
44 8
    match output {
45 8
        &Ok(ref output_struct) => get_exit_code(Ok(output_struct.status), force),
46 8
        &Err(ref error) => {
47 8
            if !force {
48 8
                error!("Error while executing command, error: {:#?}", error);
49
            }
50

51 8
            -1
52
        }
53
    }
54 8
}
55

56
/// Validates the exit code code and if not 0 or unable to validate it, panic.
57 8
pub(crate) fn validate_exit_code(code: i32) {
58 8
    if code == -1 {
59 8
        error!("Error while executing command, unable to extract exit code.");
60 8
    } else if code != 0 {
61 8
        error!("Error while executing command, exit code: {}", code);
62
    }
63 8
}
64

65 8
fn is_silent() -> bool {
66 8
    let log_level = logger::get_log_level();
67 8
    is_silent_for_level(log_level)
68 8
}
69

70 8
fn is_silent_for_level(log_level: String) -> bool {
71 8
    let level = logger::get_level(&log_level);
72

73 2
    match level {
74 8
        logger::LogLevel::ERROR => true,
75 2
        _ => false,
76
    }
77 8
}
78

79 8
fn should_print_commands_by_default() -> bool {
80 8
    let log_level = logger::get_log_level();
81

82 8
    if should_print_commands_for_level(log_level) {
83 0
        true
84
    } else {
85
        // if log level defaults to not printing the script commands
86
        // we also check if we are running in a CI env.
87
        // Users will not see the commands while CI builds will have the commands printed out.
88 8
        envmnt::is("CARGO_MAKE_CI")
89
    }
90 8
}
91

92 8
fn should_print_commands_for_level(log_level: String) -> bool {
93 8
    let level = logger::get_level(&log_level);
94

95 2
    match level {
96 8
        logger::LogLevel::VERBOSE => true,
97 2
        _ => false,
98
    }
99 8
}
100

101
/// Runs the requested script text and returns its output.
102 8
pub(crate) fn run_script_get_output(
103
    script_lines: &Vec<String>,
104
    script_runner: Option<String>,
105
    cli_arguments: &Vec<String>,
106
    capture_output: bool,
107
    print_commands: Option<bool>,
108
) -> Result<(i32, String, String), ScriptError> {
109 8
    let mut options = ScriptOptions::new();
110 8
    options.runner = script_runner.clone();
111 8
    options.output_redirection = if capture_output {
112 8
        IoOptions::Pipe
113
    } else {
114 8
        IoOptions::Inherit
115
    };
116 8
    options.exit_on_error = true;
117 8
    options.print_commands = match print_commands {
118 8
        Some(bool_value) => bool_value,
119 8
        None => should_print_commands_by_default(),
120
    };
121

122 8
    if is_silent() {
123 8
        options.output_redirection = IoOptions::Pipe;
124 8
        options.print_commands = false;
125 0
    } else if !capture_output && envmnt::is("CARGO_MAKE_SCRIPT_FORCE_PIPE_STDIN") {
126 0
        options.input_redirection = IoOptions::Pipe;
127
    }
128

129 8
    run_script::run(script_lines.join("\n").as_str(), cli_arguments, &options)
130 2
}
131

132
/// Runs the requested script text and panics in case of any script error.
133 8
pub(crate) fn run_script_get_exit_code(
134
    script_lines: &Vec<String>,
135
    script_runner: Option<String>,
136
    cli_arguments: &Vec<String>,
137
    validate: bool,
138
) -> i32 {
139 8
    let output = run_script_get_output(&script_lines, script_runner, cli_arguments, false, None);
140

141 8
    let exit_code = match output {
142 8
        Ok(output_struct) => output_struct.0,
143 0
        _ => -1,
144
    };
145

146 8
    if validate {
147 8
        validate_exit_code(exit_code);
148
    }
149

150 8
    exit_code
151 8
}
152

153
/// Runs the requested command and return its output.
154 8
pub(crate) fn run_command_get_output(
155
    command_string: &str,
156
    args: &Option<Vec<String>>,
157
    capture_output: bool,
158
) -> io::Result<Output> {
159 8
    debug!("Execute Command: {}", &command_string);
160 8
    let mut command = Command::new(&command_string);
161

162 8
    match *args {
163 8
        Some(ref args_vec) => {
164 8
            for arg in args_vec.iter() {
165 8
                command.arg(arg);
166
            }
167
        }
168 8
        None => debug!("No command args defined."),
169
    };
170

171 8
    command.stdin(Stdio::inherit());
172 8
    if !capture_output {
173 8
        command.stdout(Stdio::inherit()).stderr(Stdio::inherit());
174
    }
175 8
    info!("Execute Command: {:#?}", &command);
176

177 8
    let output = command.output();
178 8
    debug!("Output: {:#?}", &output);
179

180 2
    output
181 8
}
182

183
/// Runs the requested command and panics in case of any error.
184 8
pub(crate) fn run_command(command_string: &str, args: &Option<Vec<String>>, validate: bool) -> i32 {
185 8
    let output = run_command_get_output(&command_string, &args, false);
186

187 8
    let exit_code = get_exit_code_from_output(&output, !validate);
188

189 8
    if validate {
190 8
        validate_exit_code(exit_code);
191
    }
192

193 2
    exit_code
194 8
}
195

196
/// Runs the given task command.
197 8
pub(crate) fn run(step: &Step) {
198 8
    let validate = !step.config.should_ignore_errors();
199

200 8
    match step.config.command {
201 8
        Some(ref command_string) => {
202 8
            let command_spec = match step.config.toolchain {
203 8
                Some(ref toolchain) => {
204 4
                    toolchain::wrap_command(&toolchain, &command_string, &step.config.args)
205
                }
206 8
                None => CommandSpec {
207 8
                    command: command_string.to_string(),
208 8
                    args: step.config.args.clone(),
209 0
                },
210
            };
211

212 8
            run_command(&command_spec.command, &command_spec.args, validate);
213 8
        }
214 8
        None => debug!("No command defined."),
215
    };
216 8
}

Read our documentation on viewing source code .

Loading