diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index 4b5ab5c9370..2f2e893720d 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -70,12 +70,11 @@ use super::job::{ use super::timings::Timings; use super::{BuildContext, BuildPlan, CompileMode, Context, Unit}; use crate::core::{PackageId, TargetKind}; -use crate::util; use crate::util::diagnostic_server::{self, DiagnosticPrinter}; -use crate::util::Queue; -use crate::util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder}; -use crate::util::{Config, DependencyQueue}; -use crate::util::{Progress, ProgressStyle}; +use crate::util::machine_message::{self, Message as _}; +use crate::util::{self, internal, profile}; +use crate::util::{CargoResult, CargoResultExt, ProcessBuilder}; +use crate::util::{Config, DependencyQueue, Progress, ProgressStyle, Queue}; /// This structure is backed by the `DependencyQueue` type and manages the /// queueing of compilation steps for each package. Packages enqueue units of @@ -701,6 +700,13 @@ impl<'a, 'cfg> DrainState<'a, 'cfg> { let time_elapsed = util::elapsed(cx.bcx.config.creation_time().elapsed()); self.timings.finished(cx.bcx, &error)?; + if cx.bcx.build_config.emit_json() { + let msg = machine_message::BuildFinished { + success: error.is_none(), + } + .to_json_string(); + cx.bcx.config.shell().stdout_println(msg); + } if let Some(e) = error { Err(e) diff --git a/src/cargo/util/machine_message.rs b/src/cargo/util/machine_message.rs index 632dde1f162..ab3c32e3676 100644 --- a/src/cargo/util/machine_message.rs +++ b/src/cargo/util/machine_message.rs @@ -90,3 +90,14 @@ impl<'a> Message for TimingInfo<'a> { "timing-info" } } + +#[derive(Serialize)] +pub struct BuildFinished { + pub success: bool, +} + +impl Message for BuildFinished { + fn reason(&self) -> &str { + "build-finished" + } +} diff --git a/src/doc/src/reference/external-tools.md b/src/doc/src/reference/external-tools.md index 9aaf85dc8b3..410db2440b1 100644 --- a/src/doc/src/reference/external-tools.md +++ b/src/doc/src/reference/external-tools.md @@ -217,6 +217,30 @@ may be found in [the chapter on build scripts](build-scripts.md). } ``` +#### Build finished + +The "build-finished" message is emitted at the end of the build. + +```javascript +{ + /* The "reason" indicates the kind of message. */ + "reason": "build-finished", + /* Whether or not the build finished successfully. */ + "success": true, +} +```` + +This message can be helpful for tools to know when to stop reading JSON +messages. Commands such as `cargo test` or `cargo run` can produce additional +output after the build has finished. This message lets a tool know that Cargo +will not produce additional JSON messages, but there may be additional output +that may be generated afterwards (such as the output generated by the program +executed by `cargo run`). + +> Note: There is experimental nightly-only support for JSON output for tests, +> so additional test-specific JSON messages may begin arriving after the +> "build-finished" message if that is enabled. + ### Custom subcommands Cargo is designed to be extensible with new subcommands without having to modify diff --git a/tests/testsuite/bench.rs b/tests/testsuite/bench.rs index 13d68a9936b..225d5c9625e 100644 --- a/tests/testsuite/bench.rs +++ b/tests/testsuite/bench.rs @@ -1628,6 +1628,8 @@ fn json_artifact_includes_executable_for_benchmark() { "src_path": "[..]/foo/benches/benchmark.rs" } } + + {"reason": "build-finished", "success": true} "#, ) .run(); diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index ea84beea17c..1e84d7c1b0a 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -3169,6 +3169,8 @@ fn compiler_json_error_format() { "filenames": "{...}", "fresh": $FRESH } + + {"reason": "build-finished", "success": true} "# .replace("$FRESH", fresh) }; @@ -3249,6 +3251,8 @@ fn message_format_json_forward_stderr() { "filenames": "{...}", "fresh": false } + + {"reason": "build-finished", "success": true} "#, ) .run(); diff --git a/tests/testsuite/message_format.rs b/tests/testsuite/message_format.rs index df02cc079ec..a62be3d967f 100644 --- a/tests/testsuite/message_format.rs +++ b/tests/testsuite/message_format.rs @@ -64,7 +64,10 @@ fn cargo_renders() { p.cargo("build --message-format json-render-diagnostics") .with_status(101) - .with_stdout("{\"reason\":\"compiler-artifact\",[..]") + .with_stdout( + "{\"reason\":\"compiler-artifact\",[..]\n\ + {\"reason\":\"build-finished\",\"success\":false}", + ) .with_stderr_contains( "\ [COMPILING] bar [..] diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index 7fe5665e049..b0f1f5628f0 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -3363,6 +3363,8 @@ fn json_artifact_includes_test_flag() { "filenames":["[..]/foo-[..]"], "fresh": false } + + {"reason": "build-finished", "success": true} "#, ) .run(); @@ -3395,6 +3397,8 @@ fn json_artifact_includes_executable_for_library_tests() { "src_path": "[..]/foo/src/lib.rs" } } + + {"reason": "build-finished", "success": true} "#, ) .run(); @@ -3429,6 +3433,8 @@ fn json_artifact_includes_executable_for_integration_tests() { "src_path": "[..]/foo/tests/integration_test.rs" } } + + {"reason": "build-finished", "success": true} "#, ) .run();