build_helper/
util.rs

1use std::fs::File;
2use std::io::{BufRead, BufReader};
3use std::path::Path;
4use std::process::Command;
5use std::sync::OnceLock;
6
7/// Invokes `build_helper::util::detail_exit` with `cfg!(test)`
8///
9/// This is a macro instead of a function so that it uses `cfg(test)` in the *calling* crate, not in build helper.
10#[macro_export]
11macro_rules! exit {
12    ($code:expr) => {
13        $crate::util::detail_exit($code, cfg!(test));
14    };
15}
16
17/// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
18/// If `is_test` true and code is an error code, it will cause a panic.
19pub fn detail_exit(code: i32, is_test: bool) -> ! {
20    // if in test and code is an error code, panic with status code provided
21    if is_test {
22        panic!("status code: {}", code);
23    } else {
24        // otherwise,exit with provided status code
25        std::process::exit(code);
26    }
27}
28
29pub fn fail(s: &str) -> ! {
30    eprintln!("\n\n{}\n\n", s);
31    detail_exit(1, cfg!(test));
32}
33
34pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> {
35    let status = match cmd.status() {
36        Ok(status) => status,
37        Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
38    };
39    if !status.success() {
40        if print_cmd_on_fail {
41            println!(
42                "\n\ncommand did not execute successfully: {:?}\n\
43                 expected success, got: {}\n\n",
44                cmd, status
45            );
46        }
47        Err(())
48    } else {
49        Ok(())
50    }
51}
52
53/// Returns the submodule paths from the `.gitmodules` file in the given directory.
54pub fn parse_gitmodules(target_dir: &Path) -> &[String] {
55    static SUBMODULES_PATHS: OnceLock<Vec<String>> = OnceLock::new();
56    let gitmodules = target_dir.join(".gitmodules");
57    assert!(gitmodules.exists(), "'{}' file is missing.", gitmodules.display());
58
59    let init_submodules_paths = || {
60        let file = File::open(gitmodules).unwrap();
61
62        let mut submodules_paths = vec![];
63        for line in BufReader::new(file).lines().map_while(Result::ok) {
64            let line = line.trim();
65            if line.starts_with("path") {
66                let actual_path = line.split(' ').last().expect("Couldn't get value of path");
67                submodules_paths.push(actual_path.to_owned());
68            }
69        }
70
71        submodules_paths
72    };
73
74    SUBMODULES_PATHS.get_or_init(|| init_submodules_paths())
75}