Skip to content

Commit 8c0028a

Browse files
committed
feat: cargo::error build script directive.
1 parent f2e8d6e commit 8c0028a

File tree

5 files changed

+90
-62
lines changed

5 files changed

+90
-62
lines changed

src/cargo/core/compiler/custom_build.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ use std::path::{Path, PathBuf};
5050
use std::str::{self, FromStr};
5151
use std::sync::{Arc, Mutex};
5252

53+
/// A build script instruction that tells Cargo to display an error after the
54+
/// build script has finished running. Read [the doc] for more.
55+
///
56+
/// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-error
57+
const CARGO_ERROR_SYNTAX: &str = "cargo::error=";
5358
/// Deprecated: A build script instruction that tells Cargo to display a warning after the
5459
/// build script has finished running. Read [the doc] for more.
5560
///
@@ -82,10 +87,11 @@ pub struct BuildOutput {
8287
pub rerun_if_changed: Vec<PathBuf>,
8388
/// Environment variables which, when changed, will cause a rebuild.
8489
pub rerun_if_env_changed: Vec<String>,
85-
/// Warnings generated by this build.
90+
/// Errors and warnings generated by this build.
8691
///
8792
/// These are only displayed if this is a "local" package, `-vv` is used,
8893
/// or there is a build error for any target in this package.
94+
pub errors: Vec<String>,
8995
pub warnings: Vec<String>,
9096
}
9197

@@ -473,10 +479,14 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
473479
state.running(&cmd);
474480
let timestamp = paths::set_invocation_time(&script_run_dir)?;
475481
let prefix = format!("[{} {}] ", id.name(), id.version());
482+
let mut errors_in_case_of_panic = Vec::new();
476483
let mut warnings_in_case_of_panic = Vec::new();
477484
let output = cmd
478485
.exec_with_streaming(
479486
&mut |stdout| {
487+
if let Some(error) = stdout.strip_prefix(CARGO_ERROR_SYNTAX) {
488+
errors_in_case_of_panic.push(error.to_owned());
489+
}
480490
if let Some(warning) = stdout
481491
.strip_prefix(OLD_CARGO_WARNING_SYNTAX)
482492
.or(stdout.strip_prefix(NEW_CARGO_WARNING_SYNTAX))
@@ -522,10 +532,11 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
522532
});
523533

524534
if let Err(error) = output {
525-
insert_warnings_in_build_outputs(
535+
insert_errors_and_warnings_in_build_outputs(
526536
build_script_outputs,
527537
id,
528538
metadata_hash,
539+
errors_in_case_of_panic,
529540
warnings_in_case_of_panic,
530541
);
531542
return Err(error);
@@ -610,22 +621,25 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
610621
Ok(job)
611622
}
612623

613-
/// When a build script run fails, store only warnings and nuke other outputs,
614-
/// as they are likely broken.
615-
fn insert_warnings_in_build_outputs(
624+
/// When a build script run fails, store only errors and warnings, and nuke
625+
/// other outputs, as they are likely broken.
626+
fn insert_errors_and_warnings_in_build_outputs(
616627
build_script_outputs: Arc<Mutex<BuildScriptOutputs>>,
617628
id: PackageId,
618629
metadata_hash: Metadata,
630+
errors: Vec<String>,
619631
warnings: Vec<String>,
620632
) {
621-
let build_output_with_only_warnings = BuildOutput {
633+
let build_output_with_only_errors_and_warnings = BuildOutput {
634+
errors,
622635
warnings,
623636
..BuildOutput::default()
624637
};
625-
build_script_outputs
626-
.lock()
627-
.unwrap()
628-
.insert(id, metadata_hash, build_output_with_only_warnings);
638+
build_script_outputs.lock().unwrap().insert(
639+
id,
640+
metadata_hash,
641+
build_output_with_only_errors_and_warnings,
642+
);
629643
}
630644

631645
impl BuildOutput {
@@ -677,6 +691,7 @@ impl BuildOutput {
677691
let mut metadata = Vec::new();
678692
let mut rerun_if_changed = Vec::new();
679693
let mut rerun_if_env_changed = Vec::new();
694+
let mut errors = Vec::new();
680695
let mut warnings = Vec::new();
681696
let whence = format!("build script of `{}`", pkg_descr);
682697
// Old syntax:
@@ -962,6 +977,7 @@ impl BuildOutput {
962977
env.push((key, val));
963978
}
964979
}
980+
"error" => errors.push(value.to_string()),
965981
"warning" => warnings.push(value.to_string()),
966982
"rerun-if-changed" => rerun_if_changed.push(PathBuf::from(value)),
967983
"rerun-if-env-changed" => rerun_if_env_changed.push(value.to_string()),
@@ -987,6 +1003,7 @@ impl BuildOutput {
9871003
metadata,
9881004
rerun_if_changed,
9891005
rerun_if_env_changed,
1006+
errors,
9901007
warnings,
9911008
})
9921009
}

src/cargo/core/compiler/job_queue/mod.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,9 @@ impl<'gctx> DrainState<'gctx> {
684684
self.queue.finish(&unit, &artifact);
685685
}
686686
Err(error) => {
687-
let msg = "The following warnings were emitted during compilation:";
688-
self.emit_warnings(Some(msg), &unit, build_runner)?;
687+
let msg =
688+
"The following errors and warnings were emitted during compilation:";
689+
self.emit_errors_and_warnings(Some(msg), &unit, build_runner)?;
689690
self.back_compat_notice(build_runner, &unit)?;
690691
return Err(ErrorToHandle {
691692
error,
@@ -962,7 +963,7 @@ impl<'gctx> DrainState<'gctx> {
962963
}
963964
}
964965

965-
fn emit_warnings(
966+
fn emit_errors_and_warnings(
966967
&mut self,
967968
msg: Option<&str>,
968969
unit: &Unit,
@@ -974,16 +975,20 @@ impl<'gctx> DrainState<'gctx> {
974975
};
975976
let bcx = &mut build_runner.bcx;
976977
if let Some(output) = outputs.get(metadata) {
977-
if !output.warnings.is_empty() {
978+
if !output.warnings.is_empty() || !output.errors.is_empty() {
978979
if let Some(msg) = msg {
979980
writeln!(bcx.gctx.shell().err(), "{}\n", msg)?;
980981
}
981982

982-
for warning in output.warnings.iter() {
983-
let warning_with_package =
984-
format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), warning);
983+
let msg_with_package =
984+
|msg: &str| format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), msg);
985+
986+
for error in output.errors.iter() {
987+
bcx.gctx.shell().error(msg_with_package(error))?;
988+
}
985989

986-
bcx.gctx.shell().warn(warning_with_package)?;
990+
for warning in output.warnings.iter() {
991+
bcx.gctx.shell().warn(msg_with_package(warning))?;
987992
}
988993

989994
if msg.is_some() {
@@ -1099,7 +1104,7 @@ impl<'gctx> DrainState<'gctx> {
10991104
build_runner: &mut BuildRunner<'_, '_>,
11001105
) -> CargoResult<()> {
11011106
if unit.mode.is_run_custom_build() && unit.show_warnings(build_runner.bcx.gctx) {
1102-
self.emit_warnings(None, unit, build_runner)?;
1107+
self.emit_errors_and_warnings(None, unit, build_runner)?;
11031108
}
11041109
let unlocked = self.queue.finish(unit, &artifact);
11051110
match artifact {

src/doc/src/reference/build-scripts.md

+11-5
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ one detailed below.
128128
* [`cargo::rustc-env=VAR=VALUE`](#rustc-env) --- Sets an environment variable.
129129
* [`cargo::rustc-cdylib-link-arg=FLAG`](#rustc-cdylib-link-arg) --- Passes custom
130130
flags to a linker for cdylib crates.
131+
- [`cargo::error=MESSAGE`](#cargo-error) --- Displays an error on the terminal.
131132
* [`cargo::warning=MESSAGE`](#cargo-warning) --- Displays a warning on the
132133
terminal.
133134
* [`cargo::metadata=KEY=VALUE`](#the-links-manifest-key) --- Metadata, used by `links`
@@ -313,13 +314,18 @@ link-arg=FLAG` option][link-arg] to the compiler, but only when building a
313314
`cdylib` library target. Its usage is highly platform specific. It is useful
314315
to set the shared library version or the runtime-path.
315316

316-
### `cargo::warning=MESSAGE` {#cargo-warning}
317+
### `cargo::error=MESSAGE` {#cargo-error}
317318

318-
The `warning` instruction tells Cargo to display a warning after the build
319-
script has finished running. Warnings are only shown for `path` dependencies
320-
(that is, those you're working on locally), so for example warnings printed
319+
The `error` instruction tells Cargo to display an error after the build
320+
script has finished running. Errors are only shown for `path` dependencies
321+
(that is, those you're working on locally), so for example errors printed
321322
out in [crates.io] crates are not emitted by default. The `-vv` "very verbose"
322-
flag may be used to have Cargo display warnings for all crates.
323+
flag may be used to have Cargo display errors for all crates.
324+
325+
### `cargo::warning=MESSAGE` {#cargo-warning}
326+
327+
The `warning` instruction is just like [`error`](#cargo-error), except that it
328+
will output warnings.
323329

324330
## Build Dependencies
325331

tests/testsuite/build_script.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -3874,14 +3874,16 @@ fn errors_and_warnings_emitted() {
38743874
.build();
38753875

38763876
p.cargo("build -v")
3877-
.with_status(101)
38783877
.with_stderr_data(str![[r#"
38793878
[COMPILING] foo v0.5.0 ([ROOT]/foo)
38803879
[RUNNING] `rustc --crate-name build_script_build [..]`
38813880
[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build`
3882-
[ERROR] invalid output in build script of `foo v0.5.0 ([ROOT]/foo)`: `cargo::error=foo err`
3883-
Unknown key: `error`.
3884-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs.
3881+
[ERROR] [email protected]: foo err
3882+
[ERROR] [email protected]: bar err
3883+
[WARNING] [email protected]: foo
3884+
[WARNING] [email protected]: bar
3885+
[RUNNING] `rustc --crate-name foo [..]`
3886+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
38853887
38863888
"#]])
38873889
.run();
@@ -3921,6 +3923,8 @@ fn errors_and_warnings_emitted_when_build_script_panics() {
39213923
.with_stdout_data("")
39223924
.with_stderr_data(str![[r#"
39233925
...
3926+
[ERROR] [email protected]: foo err
3927+
[ERROR] [email protected]: bar err
39243928
[WARNING] [email protected]: foo
39253929
[WARNING] [email protected]: bar
39263930
...
@@ -3974,7 +3978,6 @@ fn errors_and_warnings_hidden_for_upstream() {
39743978
.build();
39753979

39763980
p.cargo("build -v")
3977-
.with_status(101)
39783981
.with_stderr_data(str![[r#"
39793982
[UPDATING] `dummy-registry` index
39803983
[LOCKING] 2 packages to latest compatible versions
@@ -3983,9 +3986,10 @@ fn errors_and_warnings_hidden_for_upstream() {
39833986
[COMPILING] bar v0.1.0
39843987
[RUNNING] `rustc --crate-name build_script_build [..]`
39853988
[RUNNING] `[ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build`
3986-
[ERROR] invalid output in build script of `bar v0.1.0`: `cargo::error=foo err`
3987-
Unknown key: `error`.
3988-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs.
3989+
[RUNNING] `rustc --crate-name bar [..]`
3990+
[COMPILING] foo v0.5.0 ([ROOT]/foo)
3991+
[RUNNING] `rustc --crate-name foo [..]`
3992+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
39893993
39903994
"#]])
39913995
.run();
@@ -4037,7 +4041,6 @@ fn errors_and_warnings_printed_on_vv() {
40374041
.build();
40384042

40394043
p.cargo("build -vv")
4040-
.with_status(101)
40414044
.with_stderr_data(str![[r#"
40424045
[UPDATING] `dummy-registry` index
40434046
[LOCKING] 2 packages to latest compatible versions
@@ -4046,9 +4049,14 @@ fn errors_and_warnings_printed_on_vv() {
40464049
[COMPILING] bar v0.1.0
40474050
[RUNNING] `[..] rustc --crate-name build_script_build [..]`
40484051
[RUNNING] `[..] [ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build`
4049-
[ERROR] invalid output in build script of `bar v0.1.0`: `cargo::error=foo err`
4050-
Unknown key: `error`.
4051-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs.
4052+
[ERROR] [email protected]: foo err
4053+
[ERROR] [email protected]: bar err
4054+
[WARNING] [email protected]: foo
4055+
[WARNING] [email protected]: bar
4056+
[RUNNING] `[..] rustc --crate-name bar [..]`
4057+
[COMPILING] foo v0.5.0 ([ROOT]/foo)
4058+
[RUNNING] `[..] rustc --crate-name foo [..]`
4059+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
40524060
40534061
"#]])
40544062
.run();

tests/testsuite/warn_on_failure.rs

+18-26
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,14 @@ fn no_error_or_warning_on_success() {
6666
let upstream = make_upstream("");
6767
upstream
6868
.cargo("build")
69-
.with_status(101)
7069
.with_stderr_data(str![[r#"
7170
[UPDATING] `dummy-registry` index
7271
[LOCKING] 2 packages to latest compatible versions
7372
[DOWNLOADING] crates ...
7473
[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
7574
[COMPILING] bar v0.0.1
76-
[ERROR] invalid output in build script of `bar v0.0.1`: `cargo::error=Hello! I'm an error. :)`
77-
Unknown key: `error`.
78-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs.
75+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
76+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
7977
8078
"#]])
8179
.run();
@@ -90,17 +88,14 @@ fn no_error_or_warning_on_bin_failure() {
9088
.cargo("build")
9189
.with_status(101)
9290
.with_stdout_does_not_contain("hidden stdout")
93-
.with_stderr_data(str![[r#"
94-
[UPDATING] `dummy-registry` index
95-
[LOCKING] 2 packages to latest compatible versions
96-
[DOWNLOADING] crates ...
97-
[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
98-
[COMPILING] bar v0.0.1
99-
[ERROR] invalid output in build script of `bar v0.0.1`: `cargo::error=Hello! I'm an error. :)`
100-
Unknown key: `error`.
101-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs.
102-
103-
"#]])
91+
.with_stderr_does_not_contain("hidden stderr")
92+
.with_stderr_does_not_contain(&format!("[ERROR] {}", ERROR))
93+
.with_stderr_does_not_contain(&format!("[WARNING] {}", WARNING1))
94+
.with_stderr_does_not_contain(&format!("[WARNING] {}", WARNING2))
95+
.with_stderr_contains("[UPDATING] `[..]` index")
96+
.with_stderr_contains("[DOWNLOADED] bar v0.0.1 ([..])")
97+
.with_stderr_contains("[COMPILING] bar v0.0.1")
98+
.with_stderr_contains("[COMPILING] foo v0.0.1 ([..])")
10499
.run();
105100
}
106101

@@ -113,16 +108,13 @@ fn errors_and_warnings_on_lib_failure() {
113108
.cargo("build")
114109
.with_status(101)
115110
.with_stdout_does_not_contain("hidden stdout")
116-
.with_stderr_data(str![[r#"
117-
[UPDATING] `dummy-registry` index
118-
[LOCKING] 2 packages to latest compatible versions
119-
[DOWNLOADING] crates ...
120-
[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
121-
[COMPILING] bar v0.0.1
122-
[ERROR] invalid output in build script of `bar v0.0.1`: `cargo::error=Hello! I'm an error. :)`
123-
Unknown key: `error`.
124-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs.
125-
126-
"#]])
111+
.with_stderr_does_not_contain("hidden stderr")
112+
.with_stderr_does_not_contain("[COMPILING] foo v0.0.1 ([..])")
113+
.with_stderr_contains("[UPDATING] `[..]` index")
114+
.with_stderr_contains("[DOWNLOADED] bar v0.0.1 ([..])")
115+
.with_stderr_contains("[COMPILING] bar v0.0.1")
116+
.with_stderr_contains(&format!("[ERROR] [email protected]: {}", ERROR))
117+
.with_stderr_contains(&format!("[WARNING] [email protected]: {}", WARNING1))
118+
.with_stderr_contains(&format!("[WARNING] [email protected]: {}", WARNING2))
127119
.run();
128120
}

0 commit comments

Comments
 (0)