sagiegurari / cargo-make
1
//! # cli
2
//!
3
//! The cargo-make cli
4
//!
5

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

10
use crate::cli_commands;
11
use crate::config;
12
use crate::descriptor;
13
use crate::environment;
14
use crate::logger;
15
use crate::logger::LoggerOptions;
16
use crate::profile;
17
use crate::recursion_level;
18
use crate::runner;
19
use crate::types::{CliArgs, GlobalConfig};
20
use crate::version;
21
use clap::{App, Arg, ArgMatches, SubCommand};
22

23
static VERSION: &str = env!("CARGO_PKG_VERSION");
24
static AUTHOR: &str = env!("CARGO_PKG_AUTHORS");
25
static DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
26
static DEFAULT_TOML: &str = "Makefile.toml";
27
static DEFAULT_LOG_LEVEL: &str = "info";
28
static DEFAULT_TASK_NAME: &str = "default";
29
static DEFAULT_OUTPUT_FORMAT: &str = "default";
30

31 2
fn run(cli_args: CliArgs, global_config: &GlobalConfig) {
32 2
    recursion_level::increment();
33

34 2
    logger::init(&LoggerOptions {
35 2
        level: cli_args.log_level.clone(),
36 2
        color: !cli_args.disable_color,
37 2
    });
38

39 2
    if recursion_level::is_top() {
40 0
        info!("{} {}", &cli_args.command, &VERSION);
41 0
        debug!("Written By {}", &AUTHOR);
42
    }
43

44 2
    debug!("Cli Args {:#?}", &cli_args);
45 2
    debug!("Global Configuration {:#?}", &global_config);
46

47
    // only run check for updates if we are not in a CI env and user didn't ask to skip the check
48 2
    if !cli_args.disable_check_for_updates
49 2
        && !ci_info::is_ci()
50 0
        && version::should_check(&global_config)
51
    {
52 0
        version::check();
53
    }
54

55 2
    let cwd_string_option = match cli_args.cwd.clone() {
56 2
        Some(value) => Some(value),
57 2
        None => match global_config.search_project_root {
58 2
            Some(search) => {
59 2
                if search {
60 2
                    match environment::get_project_root() {
61 2
                        Some(value) => Some(value.clone()),
62 0
                        None => None,
63
                    }
64 0
                } else {
65 2
                    None
66
                }
67
            }
68 0
            None => None,
69
        },
70 0
    };
71 2
    let cwd = match cwd_string_option {
72 2
        Some(ref value) => Some(value.as_ref()),
73 2
        None => None,
74
    };
75 2
    let home = environment::setup_cwd(cwd);
76

77 2
    let force_makefile = cli_args.build_file.is_some();
78 2
    let build_file = &cli_args
79
        .build_file
80 2
        .clone()
81 2
        .unwrap_or(DEFAULT_TOML.to_string());
82 2
    let task = &cli_args.task;
83 2
    let profile_name = &cli_args
84
        .profile
85 2
        .clone()
86 2
        .unwrap_or(profile::DEFAULT_PROFILE.to_string());
87 2
    let normalized_profile_name = profile::set(&profile_name);
88

89 2
    environment::load_env_file(cli_args.env_file.clone());
90

91 2
    let env = cli_args.env.clone();
92

93 2
    let experimental = cli_args.experimental;
94 2
    let descriptor_load_result = descriptor::load(&build_file, force_makefile, env, experimental);
95

96 0
    let config = match descriptor_load_result {
97 2
        Ok(config) => config,
98 0
        Err(ref min_version) => {
99 0
            error!(
100 0
                "{} version: {} does not meet minimum required version: {}",
101 0
                &cli_args.command, &VERSION, &min_version
102
            );
103 0
            panic!(
104 0
                "{} version: {} does not meet minimum required version: {}",
105 0
                &cli_args.command, &VERSION, &min_version
106
            );
107
        }
108
    };
109

110 2
    match config.config.additional_profiles {
111 2
        Some(ref profiles) => profile::set_additional(profiles),
112 0
        None => profile::set_additional(&vec![]),
113
    };
114

115 2
    let env_info = environment::setup_env(&cli_args, &config, &task, home);
116

117 2
    let crate_name = envmnt::get_or("CARGO_MAKE_CRATE_NAME", "");
118 2
    if crate_name.len() > 0 {
119 2
        info!("Project: {}", &crate_name);
120
    }
121 2
    info!("Build File: {}", &build_file);
122 2
    info!("Task: {}", &task);
123 2
    info!("Profile: {}", &normalized_profile_name);
124

125
    // ensure profile env was not overridden
126 2
    profile::set(&normalized_profile_name);
127

128 2
    if cli_args.list_all_steps {
129 2
        cli_commands::list_steps::run(&config, &cli_args.output_format, &cli_args.output_file);
130 2
    } else if cli_args.diff_execution_plan {
131 2
        let default_config = descriptor::load_internal_descriptors(true, experimental, None);
132 2
        cli_commands::diff_steps::run(&default_config, &config, &task, &cli_args);
133 2
    } else if cli_args.print_only {
134 2
        cli_commands::print_steps::print(
135
            &config,
136 2
            &task,
137 2
            &cli_args.output_format,
138 2
            cli_args.disable_workspace,
139
        );
140
    } else {
141 2
        runner::run(config, &task, env_info, &cli_args);
142
    }
143 2
}
144

145
/// Handles the command line arguments and executes the runner.
146 8
fn run_for_args(
147
    matches: ArgMatches,
148
    global_config: &GlobalConfig,
149
    command_name: &String,
150
    sub_command: bool,
151
) {
152 8
    let cmd_matches = if sub_command {
153 8
        match matches.subcommand_matches(command_name) {
154 8
            Some(value) => value,
155 8
            None => panic!("cargo-{} not invoked via cargo command.", &command_name),
156
        }
157
    } else {
158 0
        &matches
159
    };
160

161 2
    let mut cli_args = CliArgs::new();
162

163 2
    cli_args.command = if sub_command {
164 2
        let mut binary = "cargo ".to_string();
165 2
        binary.push_str(&command_name);
166 2
        binary
167 0
    } else {
168 0
        command_name.clone()
169
    };
170

171 2
    cli_args.env = cmd_matches.values_of_lossy("env");
172

173 2
    cli_args.build_file = if cmd_matches.occurrences_of("makefile") == 0 {
174 2
        None
175
    } else {
176 2
        let makefile = cmd_matches
177
            .value_of("makefile")
178 2
            .unwrap_or(&DEFAULT_TOML)
179
            .to_string();
180 2
        Some(makefile)
181
    };
182

183 2
    cli_args.cwd = match cmd_matches.value_of("cwd") {
184 2
        Some(value) => Some(value.to_string()),
185 2
        None => None,
186
    };
187

188 2
    let default_log_level = match global_config.log_level {
189 2
        Some(ref value) => value.as_str(),
190 2
        None => &DEFAULT_LOG_LEVEL,
191
    };
192 2
    cli_args.log_level = if cmd_matches.is_present("v") {
193 2
        "verbose".to_string()
194
    } else {
195 2
        cmd_matches
196
            .value_of("loglevel")
197 2
            .unwrap_or(default_log_level)
198
            .to_string()
199
    };
200

201 2
    let default_disable_color = match global_config.disable_color {
202 2
        Some(value) => value,
203 2
        None => false,
204
    };
205 2
    cli_args.disable_color = cmd_matches.is_present("no-color")
206 2
        || envmnt::is("CARGO_MAKE_DISABLE_COLOR")
207 0
        || default_disable_color;
208

209 2
    cli_args.print_time_summary =
210 2
        cmd_matches.is_present("time-summary") || envmnt::is("CARGO_MAKE_PRINT_TIME_SUMMARY");
211

212 2
    cli_args.env_file = match cmd_matches.value_of("envfile") {
213 2
        Some(value) => Some(value.to_string()),
214 2
        None => None,
215
    };
216

217 2
    cli_args.output_format = cmd_matches
218
        .value_of("output-format")
219 2
        .unwrap_or(DEFAULT_OUTPUT_FORMAT)
220
        .to_string();
221

222 2
    cli_args.output_file = match cmd_matches.value_of("output_file") {
223 2
        Some(value) => Some(value.to_string()),
224 2
        None => None,
225
    };
226

227 2
    let profile_name = cmd_matches
228 2
        .value_of("profile".to_string())
229 2
        .unwrap_or(profile::DEFAULT_PROFILE);
230 2
    cli_args.profile = Some(profile_name.to_string());
231

232 2
    cli_args.disable_check_for_updates = cmd_matches.is_present("disable-check-for-updates");
233 2
    cli_args.experimental = cmd_matches.is_present("experimental");
234 2
    cli_args.print_only = cmd_matches.is_present("print-steps");
235 2
    cli_args.disable_workspace = cmd_matches.is_present("no-workspace");
236 2
    cli_args.disable_on_error = cmd_matches.is_present("no-on-error");
237 2
    cli_args.allow_private = cmd_matches.is_present("allow-private");
238 2
    cli_args.skip_init_end_tasks = cmd_matches.is_present("skip-init-end-tasks");
239 2
    cli_args.list_all_steps = cmd_matches.is_present("list-steps");
240 2
    cli_args.diff_execution_plan = cmd_matches.is_present("diff-steps");
241

242 2
    let default_task_name = match global_config.default_task_name {
243 2
        Some(ref value) => value.as_str(),
244 2
        None => &DEFAULT_TASK_NAME,
245
    };
246 2
    let task = cmd_matches.value_of("task").unwrap_or(default_task_name);
247 2
    cli_args.task = cmd_matches.value_of("TASK").unwrap_or(task).to_string();
248

249 2
    cli_args.arguments = match cmd_matches.values_of("TASK_ARGS") {
250 2
        Some(values) => {
251 2
            let args_str: Vec<&str> = values.collect();
252 2
            let args_strings = args_str.iter().map(|item| item.to_string()).collect();
253 2
            Some(args_strings)
254 2
        }
255 2
        None => None,
256
    };
257

258 2
    run(cli_args, global_config);
259 8
}
260

261 8
fn create_cli<'a, 'b>(
262
    global_config: &'a GlobalConfig,
263
    command_name: &String,
264
    sub_command: bool,
265
) -> App<'a, 'b> {
266 8
    let default_task_name = match global_config.default_task_name {
267 8
        Some(ref value) => value.as_str(),
268 8
        None => &DEFAULT_TASK_NAME,
269
    };
270 8
    let default_log_level = match global_config.log_level {
271 8
        Some(ref value) => value.as_str(),
272 8
        None => &DEFAULT_LOG_LEVEL,
273
    };
274

275 8
    let mut cli_app = if sub_command {
276 8
        SubCommand::with_name(&command_name)
277
    } else {
278 8
        let name = command_name.as_str();
279 8
        App::new(name).bin_name(name)
280
    };
281

282 8
    cli_app = cli_app
283 8
        .version(VERSION)
284 8
        .author(AUTHOR)
285 8
        .about(DESCRIPTION)
286
        .arg(
287 8
            Arg::with_name("makefile")
288
                .long("--makefile")
289
                .value_name("FILE")
290
                .help("The optional toml file containing the tasks definitions")
291 8
                .default_value(&DEFAULT_TOML),
292 8
        )
293
        .arg(
294 8
            Arg::with_name("task")
295
                .short("-t")
296
                .long("--task")
297
                .value_name("TASK")
298
                .help(
299
                    "The task name to execute \
300
                     (can omit the flag if the task name is the last argument)",
301
                )
302 8
                .default_value(default_task_name),
303 8
        )
304
        .arg(
305 8
            Arg::with_name("profile")
306
                .short("-p")
307
                .long("--profile")
308
                .value_name("PROFILE")
309
                .help(
310
                    "The profile name (will be converted to lower case)",
311
                )
312 8
                .default_value(&profile::DEFAULT_PROFILE),
313 8
        )
314
        .arg(
315 8
            Arg::with_name("cwd")
316
                .long("--cwd")
317
                .value_name("DIRECTORY")
318
                .help(
319
                    "Will set the current working directory. \
320
                     The search for the makefile will be from this directory if defined.",
321
                ),
322 8
        )
323 8
        .arg(Arg::with_name("no-workspace").long("--no-workspace").help(
324
            "Disable workspace support (tasks are triggered on workspace and not on members)",
325 8
        ))
326
        .arg(
327 8
            Arg::with_name("no-on-error")
328
                .long("--no-on-error")
329
                .help("Disable on error flow even if defined in config sections"),
330 8
        )
331
        .arg(
332 8
            Arg::with_name("allow-private")
333
                .long("--allow-private")
334
                .help("Allow invocation of private tasks"),
335 8
        )
336
        .arg(
337 8
            Arg::with_name("skip-init-end-tasks")
338
                .long("--skip-init-end-tasks")
339
                .help("If set, init and end tasks are skipped"),
340 8
        )
341
        .arg(
342 8
            Arg::with_name("envfile")
343
                .long("--env-file")
344
                .value_name("FILE")
345
                .help("Set environment variables from provided file"),
346 8
        )
347
        .arg(
348 8
            Arg::with_name("env")
349
                .long("--env")
350
                .short("-e")
351
                .value_name("ENV")
352
                .multiple(true)
353
                .takes_value(true)
354
                .number_of_values(1)
355
                .help("Set environment variables"),
356 8
        )
357
        .arg(
358 8
            Arg::from_usage("-l, --loglevel=[LOG LEVEL] 'The log level'")
359 2
                .possible_values(&["verbose", "info", "error"])
360 8
                .default_value(default_log_level),
361 8
        )
362
        .arg(
363 8
            Arg::with_name("v")
364
                .short("-v")
365
                .long("--verbose")
366
                .help("Sets the log level to verbose (shorthand for --loglevel verbose)"),
367 8
        )
368
        .arg(
369 8
            Arg::with_name("no-color")
370
                .long("--no-color")
371
                .help("Disables colorful output"),
372 8
        )
373
        .arg(
374 8
            Arg::with_name("time-summary")
375
                .long("--time-summary")
376
                .help("Print task level time summary at end of flow"),
377 8
        )
378
        .arg(
379 8
            Arg::with_name("experimental")
380
                .long("--experimental")
381
                .help("Allows access unsupported experimental predefined tasks."),
382 8
        )
383
        .arg(
384 8
            Arg::with_name("disable-check-for-updates")
385
                .long("--disable-check-for-updates")
386
                .help("Disables the update check during startup"),
387 8
        )
388
        .arg(
389 8
            Arg::from_usage("--output-format=[OUTPUT FORMAT] 'The print/list steps format (some operations do not support all formats)'")
390 2
                .possible_values(&["default", "short-description", "markdown", "markdown-single-page", "markdown-sub-section"])
391 8
                .default_value(DEFAULT_OUTPUT_FORMAT),
392 8
        )
393
        .arg(
394 8
            Arg::with_name("output_file")
395
                .long("--output-file")
396
                .value_name("OUTPUT_FILE")
397
                .help("The list steps output file name"),
398 8
        )
399 8
        .arg(Arg::with_name("print-steps").long("--print-steps").help(
400
            "Only prints the steps of the build in the order they will \
401
             be invoked but without invoking them",
402 8
        ))
403
        .arg(
404 8
            Arg::with_name("list-steps")
405
                .long("--list-all-steps")
406
                .help("Lists all known steps"),
407 8
        )
408
        .arg(
409 8
            Arg::with_name("diff-steps")
410
                .long("--diff-steps")
411
                .help("Runs diff between custom flow and prebuilt flow (requires git)"),
412 8
        )
413 8
        .arg(Arg::with_name("TASK").help("The task name to execute"))
414
        .arg(
415 8
            Arg::with_name("TASK_ARGS")
416
                .multiple(true)
417
                .help("Task arguments which can be accessed in the task itself."),
418 8
        );
419

420 8
    if sub_command {
421 8
        App::new("cargo").bin_name("cargo").subcommand(cli_app)
422
    } else {
423 8
        cli_app
424
    }
425 8
}
426

427
/// Handles the command line arguments and executes the runner.
428 8
pub(crate) fn run_cli(command_name: String, sub_command: bool) {
429 8
    let global_config = config::load();
430

431 8
    let app = create_cli(&global_config, &command_name, sub_command);
432

433 8
    let matches = app.get_matches();
434

435 0
    run_for_args(matches, &global_config, &command_name, sub_command);
436 0
}

Read our documentation on viewing source code .

Loading