Add support for `annotate-snippets`'s `no-graphics` output format (#17735) *[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/cargo/pull/17735)* ### What does this PR try to resolve? This PR adds the initial support for `annotate-snippets`'s [`no-graphics` output format](https://docs.rs/annotate-snippets/latest/annotate_snippets/struct.Renderer.html#method.no_graphics). The `no-graphics` output format doesn't show source snippets or use any graphical decorations and is primarily designed for Text-To-Speech and Braille displays. The goal at this time is to get `no-graphics` in front of users so we can iterate on and improve the output. Rust added support for it in [rust#165708](https://github.com/rust-lang/rust/pull/165708) and made it in yesterday's nightly. ### Notes to reviewers _What might the reviewers need to know about the change and how to review it?_ _Example: the larger context this fits within_ _Example: manual testing performed and why it wasn't automated_ --- 🤖 LLM involvement: An LLM helped review these changes
diff --git a/crates/cargo-util-config/config.schema.json b/crates/cargo-util-config/config.schema.json index 6de67b9..6a7086e 100644 --- a/crates/cargo-util-config/config.schema.json +++ b/crates/cargo-util-config/config.schema.json
@@ -1695,6 +1695,12 @@ "type": "null" } ] + }, + "graphics": { + "type": [ + "boolean", + "null" + ] } } },
diff --git a/crates/cargo-util-config/src/schema.rs b/crates/cargo-util-config/src/schema.rs index 0997af4..7b68ada 100644 --- a/crates/cargo-util-config/src/schema.rs +++ b/crates/cargo-util-config/src/schema.rs
@@ -741,6 +741,7 @@ pub color: Option<String>, pub hyperlinks: Option<bool>, pub unicode: Option<bool>, + pub graphics: Option<bool>, pub progress: Option<ProgressConfig>, }
diff --git a/crates/cargo-util-terminal/src/shell.rs b/crates/cargo-util-terminal/src/shell.rs index ecea854..2b0eca5 100644 --- a/crates/cargo-util-terminal/src/shell.rs +++ b/crates/cargo-util-terminal/src/shell.rs
@@ -48,11 +48,13 @@ let auto_clr = ColorChoice::CargoAuto; let stdout_choice = auto_clr.to_anstream_color_choice(); let stderr_choice = auto_clr.to_anstream_color_choice(); + let no_graphics = std::env::var_os("NO_GRAPHICS").is_some_and(|v| !v.is_empty()); Shell { output: ShellOut::Stream { stdout: AutoStream::new(std::io::stdout(), stdout_choice), stderr: AutoStream::new(std::io::stderr(), stderr_choice), color_choice: auto_clr, + graphics: !no_graphics, hyperlinks: supports_hyperlinks(), stderr_tty: std::io::stderr().is_terminal(), stdout_unicode: supports_unicode(&std::io::stdout()), @@ -399,6 +401,20 @@ Ok(()) } + pub fn graphics(&self) -> bool { + match &self.output { + ShellOut::Write(_) => true, + ShellOut::Stream { graphics, .. } => *graphics, + } + } + + pub fn set_graphics(&mut self, yes: bool) -> CargoResult<()> { + if let ShellOut::Stream { graphics, .. } = &mut self.output { + *graphics = yes; + } + Ok(()) + } + /// Prints a message to stderr and translates ANSI escape code into console colors. pub fn print_ansi_stderr(&mut self, message: &[u8]) -> CargoResult<()> { if self.needs_clear { @@ -446,6 +462,7 @@ let rendered = Renderer::styled() .term_width(term_width) .decor_style(decor_style) + .no_graphics(!self.graphics()) .render(report); self.err().write_all(rendered.as_bytes())?; self.err().write_all(b"\n")?; @@ -469,6 +486,7 @@ stderr: AutoStream<std::io::Stderr>, stderr_tty: bool, color_choice: ColorChoice, + graphics: bool, hyperlinks: bool, stdout_unicode: bool, stderr_unicode: bool,
diff --git a/doc/book/src/reference/unstable.md b/doc/book/src/reference/unstable.md index f386ef9..92bed21 100644 --- a/doc/book/src/reference/unstable.md +++ b/doc/book/src/reference/unstable.md
@@ -123,6 +123,7 @@ * [`cargo rustc --print`](#rustc---print) --- Calls rustc with `--print` to display information from rustc. * [Build analysis](#build-analysis) --- Record and persist detailed build metrics across runs, with new commands to query past builds. * [`rustc-unicode`](#rustc-unicode) --- Enables `rustc`'s unicode error format in Cargo's error messages + * [`term-graphics`](#term-graphics) --- Controls graphics in its terminal output * Configuration * [`cargo config`](#cargo-config) --- Adds a new subcommand for viewing config files. * Registries @@ -2047,6 +2048,27 @@ `builtin://.#<name>`. For example, `builtin://.#core` identifies the builtin core package, distinguishing it from packages named "core" from other sources. +## term-graphics +* Tracking Issue: [rust-lang/rust#165164](https://github.com/rust-lang/rust/issues/165164) + +The `-Z term-graphics` enables the `term.graphics` configuration option to +control whether Cargo and `rustc` use graphical decorations and source +snippets in their diagnostic output. +If the `-Z term-graphics` unstable flag is not enabled, then the +`term.term-graphics` configuration will be ignored. + + +### Added to Configuration + +- Type: boolean +- Default: auto-detect +- Environment: `CARGO_TERM_GRAPHICS` + +Controls the use of graphical decorations and source snippets in diagnostic +output. The output when this is `false`, was primarily designed to be usable by +users relying on Text-To-Speech and Braille displays. + + # Stabilized and removed features ## Compile progress
diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 4ee0358..b6bf9ab 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs
@@ -1311,6 +1311,13 @@ { json.push_str(",diagnostic-unicode"); } + + if !build_runner.bcx.gctx.shell().graphics() + && build_runner.bcx.gctx.cli_unstable().term_graphics + { + json.push_str(",diagnostic-no-graphics"); + } + if enable_timings { json.push_str(",timings"); }
diff --git a/src/context/mod.rs b/src/context/mod.rs index 8b56c3e..5d81695 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs
@@ -954,6 +954,21 @@ self.shell() .set_unstable_flags_rustc_unicode(self.unstable_flags.rustc_unicode)?; + // Without this a user could set `NO_GRAPHICS=1` and be able to use an + // unstable feature without it being enabled + if !self.unstable_flags.term_graphics { + self.shell().set_graphics(true)?; + } + + if let Some(graphics) = term.graphics { + if self.unstable_flags.term_graphics { + self.shell().set_graphics(graphics)?; + } else { + self.shell() + .warn("ignoring `term.graphics` without `-Zterm-graphics`")?; + } + } + Ok(()) }
diff --git a/src/workspace/features.rs b/src/workspace/features.rs index 70e4253..6befb82 100644 --- a/src/workspace/features.rs +++ b/src/workspace/features.rs
@@ -933,6 +933,7 @@ separate_nightlies: bool, skip_rustdoc_fingerprint: bool, target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"), + term_graphics: bool = ("Enable the `term.graphics` key in the .cargo/config.toml file"), unstable_options: bool = ("Allow the usage of unstable options"), ); @@ -1477,6 +1478,7 @@ "script" => self.script = parse_empty(k, v)?, "target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?, "panic-immediate-abort" => self.panic_immediate_abort = parse_empty(k, v)?, + "term-graphics" => self.term_graphics = parse_empty(k, v)?, "unstable-options" => self.unstable_options = parse_empty(k, v)?, _ => bail!( "\
diff --git a/tests/testsuite/cargo/z_help/stdout.term.svg b/tests/testsuite/cargo/z_help/stdout.term.svg index 462ed15..7906709 100644 --- a/tests/testsuite/cargo/z_help/stdout.term.svg +++ b/tests/testsuite/cargo/z_help/stdout.term.svg
@@ -1,4 +1,4 @@ -<svg width="1255px" height="992px" xmlns="http://www.w3.org/2000/svg"> +<svg width="1255px" height="1010px" xmlns="http://www.w3.org/2000/svg"> <style> .fg { fill: #AAAAAA } .bg { fill: #000000 } @@ -112,17 +112,19 @@ </tspan> <tspan x="10px" y="874px"><tspan> -Z target-applies-to-host Enable the `target-applies-to-host` key in the .cargo/config.toml file</tspan> </tspan> - <tspan x="10px" y="892px"><tspan> -Z unstable-options Allow the usage of unstable options</tspan> + <tspan x="10px" y="892px"><tspan> -Z term-graphics Enable the `term.graphics` key in the .cargo/config.toml file</tspan> </tspan> - <tspan x="10px" y="910px"> + <tspan x="10px" y="910px"><tspan> -Z unstable-options Allow the usage of unstable options</tspan> </tspan> - <tspan x="10px" y="928px"><tspan>Run with `cargo -Z [FLAG] [COMMAND]`</tspan> + <tspan x="10px" y="928px"> </tspan> - <tspan x="10px" y="946px"> + <tspan x="10px" y="946px"><tspan>Run with `cargo -Z [FLAG] [COMMAND]`</tspan> </tspan> - <tspan x="10px" y="964px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags.</tspan> + <tspan x="10px" y="964px"> </tspan> - <tspan x="10px" y="982px"> + <tspan x="10px" y="982px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags.</tspan> +</tspan> + <tspan x="10px" y="1000px"> </tspan> </text>
diff --git a/tests/testsuite/lints/mod.rs b/tests/testsuite/lints/mod.rs index 2c24dac..5f40c5d 100644 --- a/tests/testsuite/lints/mod.rs +++ b/tests/testsuite/lints/mod.rs
@@ -484,3 +484,45 @@ "#]]) .run(); } + +#[cargo_test(nightly, reason = "diagnostic-no-graphics is unstable")] +fn lint_rendering_respects_no_graphics() { + let p = project() + .file( + ".cargo/config.toml", + r#" +[term] +graphics = false + "#, + ) + .file( + "Cargo.toml", + r#" +cargo-features = ["test-dummy-unstable"] + +[package] +name = "foo" +version = "0.0.1" +edition = "2015" +authors = [] +im-a-teapot = true + +[lints.cargo] +im_a_teapot = "warn" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch") + .arg("-Zterm-graphics") + .masquerade_as_nightly_cargo(&["test-dummy-unstable", "term-graphics"]) + .with_stderr_data(str![[r#" +[WARNING] `im_a_teapot` is specified + at Cargo.toml:9:1 + [NOTE] `cargo::im_a_teapot` is set to `warn` in `[lints]` +[WARNING] `foo` (manifest) generated 1 warning + +"#]]) + .run(); +}
diff --git a/tests/testsuite/message_format.rs b/tests/testsuite/message_format.rs index 4383bc6..1d767a7 100644 --- a/tests/testsuite/message_format.rs +++ b/tests/testsuite/message_format.rs
@@ -202,3 +202,85 @@ "#]]) .run(); } + +#[cargo_test(nightly, reason = "diagnostic-no-graphics is unstable")] +fn cargo_passes_no_graphics() { + let p = project() + .file( + ".cargo/config.toml", + r#" +[term] +graphics = false + "#, + ) + .file( + "Cargo.toml", + r#" +[package] +name = "foo" +version = "0.0.1" +edition = "2015" +"#, + ) + .file( + "src/lib.rs", + "\ +mod tests { + #[test] + fn t1() { + use std::io; + } +} + ", + ) + .build(); + + p.cargo("check -v") + .arg("-Zterm-graphics") + .masquerade_as_nightly_cargo(&["term-graphics"]) + .with_stderr_data(str![[r#" +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `rustc [..] --json=diagnostic-rendered-ansi,artifacts,future-incompat,unused-externs-silent,diagnostic-no-graphics [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test(nightly, reason = "diagnostic-no-graphics is unstable")] +fn cargo_respects_no_graphics_env_var() { + let p = project() + .file( + "Cargo.toml", + r#" +[package] +name = "foo" +version = "0.0.1" +edition = "2015" +"#, + ) + .file( + "src/lib.rs", + "\ +mod tests { + #[test] + fn t1() { + use std::io; + } +} + ", + ) + .build(); + + p.cargo("check -v") + .env("NO_GRAPHICS", "1") + .arg("-Zterm-graphics") + .masquerade_as_nightly_cargo(&["term-graphics"]) + .with_stderr_data(str![[r#" +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `rustc [..] --json=diagnostic-rendered-ansi,artifacts,future-incompat,unused-externs-silent,diagnostic-no-graphics [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +}