cargo_e/
e_runner.rs

1use crate::prelude::*;
2// #[cfg(not(feature = "equivalent"))]
3// use ctrlc;
4use once_cell::sync::Lazy;
5
6// Global shared container for the currently running child process.
7pub static GLOBAL_CHILD: Lazy<Arc<Mutex<Option<Child>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
8
9/// Registers a global Ctrl+C handler once.
10/// The handler checks GLOBAL_CHILD and kills the child process if present.
11pub fn register_ctrlc_handler() -> Result<(), Box<dyn Error>> {
12    ctrlc::set_handler(move || {
13        let mut child_lock = GLOBAL_CHILD.lock().unwrap();
14        if let Some(child) = child_lock.as_mut() {
15            eprintln!("Ctrl+C pressed, terminating running child process...");
16            let _ = child.kill();
17        } else {
18            eprintln!("Ctrl+C pressed, no child process running. Exiting nicely.");
19            exit(0);
20        }
21    })?;
22    Ok(())
23}
24
25/// In "equivalent" mode, behave exactly like "cargo run --example <name>"
26#[cfg(feature = "equivalent")]
27pub fn run_equivalent_example(
28    cli: &crate::Cli,
29) -> Result<std::process::ExitStatus, Box<dyn Error>> {
30    // In "equivalent" mode, behave exactly like "cargo run --example <name>"
31    let mut cmd = Command::new("cargo");
32    cmd.args([
33        "run",
34        "--example",
35        cli.explicit_example.as_deref().unwrap_or(""),
36    ]);
37    if !cli.extra.is_empty() {
38        cmd.arg("--").args(cli.extra.clone());
39    }
40    // Inherit the standard input (as well as stdout/stderr) so that input is passed through.
41    use std::process::Stdio;
42    cmd.stdin(Stdio::inherit())
43        .stdout(Stdio::inherit())
44        .stderr(Stdio::inherit());
45
46    let status = cmd.status()?;
47    std::process::exit(status.code().unwrap_or(1));
48}
49
50/// Runs the given example (or binary) target.
51pub fn run_example(
52    cli: &crate::Cli,
53    target: &crate::e_target::CargoTarget,
54) -> anyhow::Result<std::process::ExitStatus> {
55    // Retrieve the current package name at compile time.
56    let current_bin = env!("CARGO_PKG_NAME");
57
58    // Avoid running our own binary.
59    if target.kind == crate::e_target::TargetKind::Binary && target.name == current_bin {
60        return Err(anyhow::anyhow!(
61            "Skipping automatic run: {} is the same as the running binary",
62            target.name
63        ));
64    }
65
66    // Build the command using the CargoCommandBuilder.
67    let mut builder = crate::e_command_builder::CargoCommandBuilder::new()
68        .with_target(target)
69        .with_required_features(&target.manifest_path, target)
70        .with_cli(cli);
71
72    if !cli.extra.is_empty() {
73        builder = builder.with_extra_args(&cli.extra);
74    }
75
76    // Build the command.
77    let mut cmd = builder.clone().build_command();
78
79    // Before spawning, determine the directory to run from.
80    // If a custom execution directory was set (e.g. for Tauri targets), that is used.
81    // Otherwise, if the target is extended, run from its parent directory.
82    if let Some(exec_dir) = builder.execution_dir {
83        cmd.current_dir(exec_dir);
84    } else if target.extended {
85        if let Some(dir) = target.manifest_path.parent() {
86            cmd.current_dir(dir);
87        }
88    }
89
90    // Print the full command for debugging.
91    let full_command = format!(
92        "{} {}",
93        cmd.get_program().to_string_lossy(),
94        cmd.get_args()
95            .map(|arg| arg.to_string_lossy())
96            .collect::<Vec<_>>()
97            .join(" ")
98    );
99    println!("Running: {}", full_command);
100
101    // Check if the manifest triggers the workspace error.
102    let maybe_backup = crate::e_manifest::maybe_patch_manifest_for_run(&target.manifest_path)?;
103
104    // Spawn the process.
105    let child = cmd.spawn()?;
106    {
107        let mut global = GLOBAL_CHILD.lock().unwrap();
108        *global = Some(child);
109    }
110    let status = {
111        let mut global = GLOBAL_CHILD.lock().unwrap();
112        if let Some(mut child) = global.take() {
113            child.wait()?
114        } else {
115            return Err(anyhow::anyhow!("Child process missing"));
116        }
117    };
118
119    // Restore the manifest if we patched it.
120    if let Some(original) = maybe_backup {
121        fs::write(&target.manifest_path, original)?;
122    }
123
124    Ok(status)
125}
126// /// Runs an example or binary target, applying a temporary manifest patch if a workspace error is detected.
127// /// This function uses the same idea as in the collection helpers: if the workspace error is found,
128// /// we patch the manifest, run the command, and then restore the manifest.
129// pub fn run_example(
130//     target: &crate::e_target::CargoTarget,
131//     extra_args: &[String],
132// ) -> Result<std::process::ExitStatus, Box<dyn Error>> {
133//     // Retrieve the current package name (or binary name) at compile time.
134
135//     use crate::e_target::TargetKind;
136
137//     let current_bin = env!("CARGO_PKG_NAME");
138
139//     // Avoid running our own binary if the target's name is the same.
140//     if target.kind == TargetKind::Binary && target.name == current_bin {
141//         return Err(format!(
142//             "Skipping automatic run: {} is the same as the running binary",
143//             target.name
144//         )
145//         .into());
146//     }
147
148//     let mut cmd = Command::new("cargo");
149//     // Determine which manifest file is used.
150//     let manifest_path: PathBuf;
151
152//     match target.kind {
153//         TargetKind::Bench => {
154//             manifest_path = PathBuf::from(target.manifest_path.clone());
155//             cmd.args([
156//                 "bench",
157//                 "--bench",
158//                 &target.name,
159//                 "--manifest-path",
160//                 &target.manifest_path.to_str().unwrap_or_default().to_owned(),
161//             ]);
162//         }
163//         TargetKind::Test => {
164//             manifest_path = PathBuf::from(target.manifest_path.clone());
165//             cmd.args([
166//                 "test",
167//                 "--test",
168//                 &target.name,
169//                 "--manifest-path",
170//                 &target.manifest_path.to_str().unwrap_or_default().to_owned(),
171//             ]);
172//         }
173//         TargetKind::Manifest => {
174//             manifest_path = PathBuf::from(target.manifest_path.clone());
175//             cmd.args([
176//                 "run",
177//                 "--release",
178//                 "--manifest-path",
179//                 &target.manifest_path.to_str().unwrap_or_default().to_owned(),
180//                 "-p",
181//                 &target.name,
182//             ]);
183//         }
184//         TargetKind::Example => {
185//             if target.extended {
186//                 println!(
187//                     "Running extended example in folder: examples/{}",
188//                     target.name
189//                 );
190//                 // For extended examples, assume the manifest is inside the example folder.
191//                 manifest_path = PathBuf::from(format!("examples/{}/Cargo.toml", target.name));
192//                 cmd.arg("run")
193//                     .current_dir(format!("examples/{}", target.name));
194//             } else {
195//                 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
196//                 cmd.args([
197//                     "run",
198//                     "--release",
199//                     "--example",
200//                     &target.name,
201//                     "--manifest-path",
202//                     &target.manifest_path.to_str().unwrap_or_default().to_owned(),
203//                 ]);
204//             }
205//         }
206//         TargetKind::Binary => {
207//             println!("Running binary: {}", target.name);
208//             manifest_path = PathBuf::from(crate::locate_manifest(false)?);
209//             cmd.args([
210//                 "run",
211//                 "--release",
212//                 "--bin",
213//                 &target.name,
214//                 "--manifest-path",
215//                 &target.manifest_path.to_str().unwrap_or_default().to_owned(),
216//             ]);
217//         }
218//         TargetKind::ExtendedBinary => {
219//             println!("Running extended binary: {}", target.name);
220//             manifest_path = PathBuf::from(crate::locate_manifest(false)?);
221//             cmd.args([
222//                 "run",
223//                 "--release",
224//                 "--manifest-path",
225//                 &target.manifest_path.to_str().unwrap_or_default().to_owned(),
226//                 "--bin",
227//                 &target.name,
228//             ]);
229//         }
230//         TargetKind::ExtendedExample => {
231//             println!("Running extended example: {}", target.name);
232//             manifest_path = PathBuf::from(crate::locate_manifest(false)?);
233//             cmd.args([
234//                 "run",
235//                 "--release",
236//                 "--manifest-path",
237//                 &target.manifest_path.to_str().unwrap_or_default().to_owned(),
238//                 "--example",
239//                 &target.name,
240//             ]);
241//         }
242//         TargetKind::ManifestTauri => {
243//             println!("Running tauri: {}", target.name);
244//             // For a Tauri example, run `cargo tauri dev`
245//             manifest_path = PathBuf::from(target.manifest_path.clone());
246//             let manifest_dir = PathBuf::from(manifest_path.parent().expect("expected a parent"));
247//             // Start a new command for tauri dev
248//             cmd.arg("tauri").arg("dev").current_dir(manifest_dir); // run from the folder where Cargo.toml is located
249//         }
250//         TargetKind::ManifestDioxus => {
251//             println!("Running dioxus: {}", target.name);
252//             cmd = Command::new("dx");
253//             // For a Tauri example, run `cargo tauri dev`
254//             manifest_path = PathBuf::from(target.manifest_path.clone());
255//             let manifest_dir = PathBuf::from(manifest_path.parent().expect("expected a parent"));
256//             // Start a new command for tauri dev
257//             cmd.arg("serve").current_dir(manifest_dir); // run from the folder where Cargo.toml is located
258//         }
259//         TargetKind::ManifestDioxusExample => {
260//             println!("Running dioxus: {}", target.name);
261//             cmd = Command::new("dx");
262//             // For a Tauri example, run `cargo tauri dev`
263//             manifest_path = PathBuf::from(target.manifest_path.clone());
264//             let manifest_dir = PathBuf::from(manifest_path.parent().expect("expected a parent"));
265//             // Start a new command for tauri dev
266//             cmd.arg("serve")
267//                 .arg("--example")
268//                 .arg(&target.name)
269//                 .current_dir(manifest_dir); // run from the folder where Cargo.toml is located
270//         }
271//     }
272
273//     // --- Add required-features support ---
274//     // This call will search the provided manifest, and if it's a workspace,
275//     // it will search workspace members for the target.
276//     if let Some(features) = crate::e_manifest::get_required_features_from_manifest(
277//         manifest_path.as_path(),
278//         &target.kind,
279//         &target.name,
280//     ) {
281//         cmd.args(&["--features", &features]);
282//     }
283//     // --- End required-features support ---
284
285//     if !extra_args.is_empty() {
286//         cmd.arg("--").args(extra_args);
287//     }
288
289//     let full_command = format!(
290//         "{} {}",
291//         cmd.get_program().to_string_lossy(),
292//         cmd.get_args()
293//             .map(|arg| arg.to_string_lossy())
294//             .collect::<Vec<_>>()
295//             .join(" ")
296//     );
297//     println!("Running: {}", full_command);
298
299//     // Before spawning, check if the manifest triggers the workspace error.
300//     // If so, patch it temporarily.
301//     let maybe_backup = crate::e_manifest::maybe_patch_manifest_for_run(&manifest_path)?;
302
303//     // Spawn the process.
304//     let child = cmd.spawn()?;
305//     {
306//         let mut global = GLOBAL_CHILD.lock().unwrap();
307//         *global = Some(child);
308//     }
309//     let status = {
310//         let mut global = GLOBAL_CHILD.lock().unwrap();
311//         if let Some(mut child) = global.take() {
312//             child.wait()?
313//         } else {
314//             return Err("Child process missing".into());
315//         }
316//     };
317
318//     // Restore the manifest if we patched it.
319//     if let Some(original) = maybe_backup {
320//         fs::write(&manifest_path, original)?;
321//     }
322
323//     //    println!("Process exited with status: {:?}", status.code());
324//     Ok(status)
325// }
326/// Helper function to spawn a cargo process.
327/// On Windows, this sets the CREATE_NEW_PROCESS_GROUP flag.
328pub fn spawn_cargo_process(args: &[&str]) -> Result<Child, Box<dyn Error>> {
329    #[cfg(windows)]
330    {
331        use std::os::windows::process::CommandExt;
332        const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
333        let child = Command::new("cargo")
334            .args(args)
335            .creation_flags(CREATE_NEW_PROCESS_GROUP)
336            .spawn()?;
337        Ok(child)
338    }
339    #[cfg(not(windows))]
340    {
341        let child = Command::new("cargo").args(args).spawn()?;
342        Ok(child)
343    }
344}