Skip to main content

objdiff_core/build/
mod.rs

1pub mod watcher;
2
3use std::process::Command;
4
5use typed_path::{Utf8PlatformPathBuf, Utf8UnixPath};
6
7pub struct BuildStatus {
8    pub success: bool,
9    pub cmdline: String,
10    pub stdout: String,
11    pub stderr: String,
12}
13
14impl Default for BuildStatus {
15    fn default() -> Self {
16        BuildStatus {
17            success: true,
18            cmdline: String::new(),
19            stdout: String::new(),
20            stderr: String::new(),
21        }
22    }
23}
24
25#[derive(Debug, Clone)]
26pub struct BuildConfig {
27    pub project_dir: Option<Utf8PlatformPathBuf>,
28    pub custom_make: Option<String>,
29    pub custom_args: Option<Vec<String>>,
30    #[allow(unused)]
31    pub selected_wsl_distro: Option<String>,
32}
33
34pub fn run_make(config: &BuildConfig, arg: &Utf8UnixPath) -> BuildStatus {
35    let Some(cwd) = &config.project_dir else {
36        return BuildStatus {
37            success: false,
38            stderr: "Missing project dir".to_string(),
39            ..Default::default()
40        };
41    };
42    let make = config.custom_make.as_deref().unwrap_or("make");
43    let make_args = config.custom_args.as_deref().unwrap_or(&[]);
44    #[cfg(not(windows))]
45    let mut command = {
46        let mut command = Command::new(make);
47        command.current_dir(cwd).args(make_args).arg(arg);
48        command
49    };
50    #[cfg(windows)]
51    let mut command = {
52        use alloc::borrow::Cow;
53        use std::os::windows::process::CommandExt;
54
55        let mut command = if config.selected_wsl_distro.is_some() {
56            Command::new("wsl")
57        } else {
58            Command::new(make)
59        };
60        if let Some(distro) = &config.selected_wsl_distro {
61            // Strip distro root prefix \\wsl.localhost\{distro}
62            let wsl_path_prefix = format!("\\\\wsl.localhost\\{}", distro);
63            let cwd = match cwd.strip_prefix(wsl_path_prefix) {
64                // Convert to absolute Unix path
65                Ok(new_cwd) => Cow::Owned(
66                    Utf8UnixPath::new("/").join(new_cwd.with_unix_encoding()).into_string(),
67                ),
68                // Otherwise, use the Windows path as is
69                Err(_) => Cow::Borrowed(cwd.as_str()),
70            };
71
72            command
73                .arg("--cd")
74                .arg::<&str>(cwd.as_ref())
75                .arg("-d")
76                .arg(distro)
77                .arg("--")
78                .arg(make)
79                .args(make_args)
80                .arg(arg.as_str());
81        } else {
82            command.current_dir(cwd).args(make_args).arg(arg.as_str());
83        }
84        command.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
85        command
86    };
87    let mut cmdline = shell_escape::escape(command.get_program().to_string_lossy()).into_owned();
88    for arg in command.get_args() {
89        cmdline.push(' ');
90        cmdline.push_str(shell_escape::escape(arg.to_string_lossy()).as_ref());
91    }
92    let output = match command.output() {
93        Ok(output) => output,
94        Err(e) => {
95            return BuildStatus {
96                success: false,
97                cmdline,
98                stdout: Default::default(),
99                stderr: e.to_string(),
100            };
101        }
102    };
103    // Try from_utf8 first to avoid copying the buffer if it's valid, then fall back to from_utf8_lossy
104    let stdout = String::from_utf8(output.stdout)
105        .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned());
106    let stderr = String::from_utf8(output.stderr)
107        .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned());
108    BuildStatus { success: output.status.success(), cmdline, stdout, stderr }
109}