From 1aaa443ad2430b8c2f1f15a542a70e6da02de89d Mon Sep 17 00:00:00 2001 From: Adrien Thebo Date: Wed, 8 Nov 2023 18:19:07 -0800 Subject: [PATCH] fix: use `cargo build` for compilation when emulating `cargo run` Given the following binary generated by `cargo new --bin`: ``` // src/main.rs fn main() { println!("Hello, world!"); } ``` Running `cargo debug run` panics due to invalid JSON; cargo-debug assumes that the supplied subcommand is either `test` or `build` but does not validate this, and then assumes that the supplied subcommand only emits JSON formatted manifest information on stdout. The above example that writes to stdout breaks that assumption, causing a panic. ``` cargo debug run Compiling example v0.1.0 (/Users/user/personal/projects/example) Finished dev [unoptimized + debuginfo] target(s) in 0.08s Running `target/debug/example` thread 'main' panicked at src/main.rs:131:23: Invalid cargo JSON message: Error("expected value", line: 3, column: 1) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` This commit resolves this by adding the following: 1. proper adaptation for `cargo-debug run` similar to the special-casing for `cargo-debug test` 2. checks that will warn users if the subcommand isn't known (and may fail) 3. error handling when an invalid cargo metadata message is received (replacing a panic with a graceful exit) --- src/main.rs | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5b04d94..6979ddd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,34 +105,51 @@ fn main() { // Build and execute cargo command let cargo_bin = env::var("CARGO").unwrap_or(String::from("cargo")); - let mut cargo_cmd = Command::new(cargo_bin); - cargo_cmd.arg(&o.subcommand); - cargo_cmd.arg("--message-format=json"); - cargo_cmd.stdout(Stdio::piped()); + let mut cargo_cmd = Command::new(&cargo_bin); - // Add no-run argument to test command - if &o.subcommand == "test" { - cargo_cmd.arg("--no-run"); + match o.subcommand.as_str() { + "build" => { + cargo_cmd.arg("build"); + }, + "test" => { + cargo_cmd.args(["test", "--no-run"]); + }, + "run" => { + // Use 'cargo build' to compile a binary when wrapping 'cargo run' + cargo_cmd.arg("build"); + }, + ref s => { + warn!("Unexpected subcommand {:?}, will run '{} {}' to collect artifact information.", s, &cargo_bin, s); + cargo_cmd.arg(s); + } } + cargo_cmd.arg("--message-format=json"); + cargo_cmd.stdout(Stdio::piped()); + // Attach additional arguments if let Some(opts) = cargo_opts { cargo_cmd.args(opts); } trace!("synthesized cargo command: {:?}", cargo_cmd); - + trace!("launching cargo command"); let mut handle = cargo_cmd.spawn().expect("error starting cargo command"); // Log all output artifacts let mut artifacts = vec![]; + for message in cargo_metadata::parse_messages(handle.stdout.take().unwrap()) { - match message.expect("Invalid cargo JSON message") { - Message::CompilerArtifact(artifact) => { + match message { + Ok(Message::CompilerArtifact(artifact)) => { artifacts.push(artifact); }, - _ => () + Ok(_) => (), + Err(e) => { + error!("Invalid cargo JSON message during build (executed {:?}): {:?}", cargo_cmd, e); + return + } } }