From c46b9a70bd9fedb7fe3e004850a3d8dab9ca84fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 30 Aug 2019 19:51:55 -0700 Subject: [PATCH 1/4] Add support for rustc's `-Z terminal-width`. This commit adds support for rustc's `-Z terminal-width` flag, which is used to trim diagnostic output to fit within the current terminal. Co-authored-by: David Wood Signed-off-by: David Wood --- src/cargo/core/compiler/mod.rs | 12 ++++++++++++ src/cargo/core/features.rs | 12 ++++++++++++ src/cargo/core/shell.rs | 17 +++++++++++++++++ tests/testsuite/build.rs | 28 ++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 9399c50428c..1456ff3ac56 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -47,6 +47,7 @@ pub use self::lto::Lto; use self::output_depinfo::output_depinfo; use self::unit_graph::UnitDep; pub use crate::core::compiler::unit::{Unit, UnitInterner}; +use crate::core::features::nightly_features_allowed; use crate::core::manifest::TargetSourcePath; use crate::core::profiles::{PanicStrategy, Profile, Strip}; use crate::core::{Edition, Feature, PackageId, Target}; @@ -709,6 +710,7 @@ fn add_error_format_and_color( // to emit a message that cargo will intercept. json.push_str(",artifacts"); } + match cx.bcx.build_config.message_format { MessageFormat::Short | MessageFormat::Json { short: true, .. } => { json.push_str(",diagnostic-short"); @@ -716,6 +718,16 @@ fn add_error_format_and_color( _ => {} } cmd.arg(json); + + if nightly_features_allowed() { + if let (Some(width), _) | (_, Some(width)) = ( + cx.bcx.config.cli_unstable().terminal_width, + cx.bcx.config.shell().accurate_err_width(), + ) { + cmd.arg(format!("-Zterminal-width={}", width)); + } + } + Ok(()) } diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 8ed33b2f615..bdcd24929f5 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -357,6 +357,7 @@ pub struct CliUnstable { pub separate_nightlies: bool, pub multitarget: bool, pub rustdoc_map: bool, + pub terminal_width: Option, } impl CliUnstable { @@ -411,6 +412,16 @@ impl CliUnstable { Ok(true) }; + fn parse_usize_opt(value: Option<&str>) -> CargoResult> { + Ok(match value { + Some(value) => match value.parse::() { + Ok(value) => Some(value), + Err(e) => bail!("expected a number, found: {}", e), + }, + None => None, + }) + } + match k { "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?, "unstable-options" => self.unstable_options = parse_empty(k, v)?, @@ -437,6 +448,7 @@ impl CliUnstable { "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, "multitarget" => self.multitarget = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, + "terminal-width" => self.terminal_width = parse_usize_opt(v)?, _ => bail!("unknown `-Z` flag specified: {}", k), } diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index e1198f17689..63197f565ac 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -134,6 +134,15 @@ impl Shell { } } + /// Returns the width of the terminal in spaces, if any. Always `None` in Windows. + pub fn accurate_err_width(&self) -> Option { + if self.is_err_tty() { + imp::accurate_stderr_width() + } else { + None + } + } + /// Returns `true` if stderr is a tty. pub fn is_err_tty(&self) -> bool { match self.output { @@ -411,6 +420,10 @@ mod imp { use super::Shell; use std::mem; + pub fn accurate_stderr_width() -> Option { + stderr_width() + } + pub fn stderr_width() -> Option { unsafe { let mut winsize: libc::winsize = mem::zeroed(); @@ -447,6 +460,10 @@ mod imp { pub(super) use super::default_err_erase_line as err_erase_line; + pub fn accurate_stderr_width() -> Option { + None + } + pub fn stderr_width() -> Option { unsafe { let stdout = GetStdHandle(STD_ERROR_HANDLE); diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index 6bed2549f6d..a6b628c93ad 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -7,8 +7,8 @@ use cargo::{ use cargo_test_support::paths::{root, CargoPathExt}; use cargo_test_support::registry::Package; use cargo_test_support::{ - basic_bin_manifest, basic_lib_manifest, basic_manifest, lines_match, main_file, project, - rustc_host, sleep_ms, symlink_supported, t, Execs, ProjectBuilder, + basic_bin_manifest, basic_lib_manifest, basic_manifest, is_nightly, lines_match, main_file, + project, rustc_host, sleep_ms, symlink_supported, t, Execs, ProjectBuilder, }; use std::env; use std::fs; @@ -5103,3 +5103,27 @@ fn target_directory_backup_exclusion() { p.cargo("build").run(); assert!(!&cachedir_tag.is_file()); } + +#[cargo_test] +fn simple_terminal_width() { + if !is_nightly() { + // --terminal-width is unstable + return; + } + let p = project() + .file( + "src/lib.rs", + r#" + fn main() { + let _: () = 42; + } + "#, + ) + .build(); + + p.cargo("build -Zterminal-width=20") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr_contains("3 | ..._: () = 42;") + .run(); +} From 6d740aa2e628da9e9aad20987aee5e7e5c387f4e Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 30 Jun 2020 20:29:09 +0100 Subject: [PATCH 2/4] Make `-Z terminal-width` opt-in. This commit modifies the parsing of `-Z terminal-width` so that it can optionally take a value and only uses `accurate_err_width` when the user does not provide a value - therefore making the emission of `-Z terminal-width` opt-in. Signed-off-by: David Wood --- src/cargo/core/compiler/mod.rs | 17 ++++++++++++----- src/cargo/core/features.rs | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 1456ff3ac56..40445956586 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -720,11 +720,18 @@ fn add_error_format_and_color( cmd.arg(json); if nightly_features_allowed() { - if let (Some(width), _) | (_, Some(width)) = ( - cx.bcx.config.cli_unstable().terminal_width, - cx.bcx.config.shell().accurate_err_width(), - ) { - cmd.arg(format!("-Zterminal-width={}", width)); + let config = cx.bcx.config; + match (config.cli_unstable().terminal_width, config.shell().accurate_err_width()) { + // Terminal width explicitly provided - only useful for testing. + (Some(Some(width)), _) => { + cmd.arg(format!("-Zterminal-width={}", width)); + } + // Terminal width was not explicitly provided but flag was provided - common case. + (Some(None), Some(width)) => { + cmd.arg(format!("-Zterminal-width={}", width)); + } + // User didn't opt-in. + _ => (), } } diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index bdcd24929f5..47d5f9cd9e6 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -357,7 +357,7 @@ pub struct CliUnstable { pub separate_nightlies: bool, pub multitarget: bool, pub rustdoc_map: bool, - pub terminal_width: Option, + pub terminal_width: Option>, } impl CliUnstable { @@ -448,7 +448,7 @@ impl CliUnstable { "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, "multitarget" => self.multitarget = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, - "terminal-width" => self.terminal_width = parse_usize_opt(v)?, + "terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?), _ => bail!("unknown `-Z` flag specified: {}", k), } From 4177f66fd26f572e5af2fa1c184f0da37ff7f182 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 30 Jun 2020 20:37:50 +0100 Subject: [PATCH 3/4] Add documentation for `-Z terminal-width` This commit adds relevant documentation for the `-Z terminal-width` flag. Signed-off-by: David Wood --- src/bin/cargo/cli.rs | 1 + src/doc/src/reference/unstable.md | 34 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index 0bd98340d06..74b9e436eb1 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -37,6 +37,7 @@ Available unstable (nightly-only) flags: -Z timings -- Display concurrency information -Z doctest-xcompile -- Compile and run doctests for non-host target using runner config -Z crate-versions -- Add crate versions to generated docs + -Z terminal-width -- Provide a terminal width to rustc for error truncation Run with 'cargo -Z [FLAG] [SUBCOMMAND]'" ); diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 658231e3541..363f0415dee 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -826,3 +826,37 @@ sysroot. If you are using rustup, this documentation can be installed with The default value is `"remote"`. The value may also take a URL for a custom location. + +### terminal-width +This feature provides a new flag, `-Z terminal-width`, which is used to pass +a terminal width to `rustc` so that error messages containing long lines +can be intelligently truncated. + +For example, passing `-Z terminal-width=20` (an arbitrarily low value) might +produce the following error: + +```text +error[E0308]: mismatched types + --> src/main.rs:2:17 + | +2 | ..._: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to previous error +``` + +In contrast, without `-Z terminal-width`, the error would look as shown below: + +```text +error[E0308]: mismatched types + --> src/main.rs:2:17 + | +2 | let _: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to previous error +``` From af924d4ecfbb78d2d13e3204990c30baaa19469d Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 2 Jul 2020 12:00:54 +0100 Subject: [PATCH 4/4] Introduce `TtyWidth` This commit introduces a `TtyWidth` enum which enables better handling of the tty-width on Windows. Signed-off-by: David Wood --- src/cargo/core/compiler/mod.rs | 5 ++- src/cargo/core/shell.rs | 78 ++++++++++++++++++++-------------- src/cargo/util/progress.rs | 4 +- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 40445956586..91d444cce05 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -721,7 +721,10 @@ fn add_error_format_and_color( if nightly_features_allowed() { let config = cx.bcx.config; - match (config.cli_unstable().terminal_width, config.shell().accurate_err_width()) { + match ( + config.cli_unstable().terminal_width, + config.shell().err_width().diagnostic_terminal_width(), + ) { // Terminal width explicitly provided - only useful for testing. (Some(Some(width)), _) => { cmd.arg(format!("-Zterminal-width={}", width)); diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index 63197f565ac..e155709e144 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -6,6 +6,31 @@ use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor}; use crate::util::errors::CargoResult; +pub enum TtyWidth { + NoTty, + Known(usize), + Guess(usize), +} + +impl TtyWidth { + /// Returns the width provided with `-Z terminal-width` to rustc to truncate diagnostics with + /// long lines. + pub fn diagnostic_terminal_width(&self) -> Option { + match *self { + TtyWidth::NoTty | TtyWidth::Guess(_) => None, + TtyWidth::Known(width) => Some(width), + } + } + + /// Returns the width used by progress bars for the tty. + pub fn progress_max_width(&self) -> Option { + match *self { + TtyWidth::NoTty => None, + TtyWidth::Known(width) | TtyWidth::Guess(width) => Some(width), + } + } +} + /// The requested verbosity of output. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Verbosity { @@ -125,21 +150,12 @@ impl Shell { } /// Returns the width of the terminal in spaces, if any. - pub fn err_width(&self) -> Option { + pub fn err_width(&self) -> TtyWidth { match self.output { ShellOut::Stream { stderr_tty: true, .. } => imp::stderr_width(), - _ => None, - } - } - - /// Returns the width of the terminal in spaces, if any. Always `None` in Windows. - pub fn accurate_err_width(&self) -> Option { - if self.is_err_tty() { - imp::accurate_stderr_width() - } else { - None + _ => TtyWidth::NoTty, } } @@ -417,25 +433,21 @@ impl ColorChoice { #[cfg(unix)] mod imp { - use super::Shell; + use super::{Shell, TtyWidth}; use std::mem; - pub fn accurate_stderr_width() -> Option { - stderr_width() - } - - pub fn stderr_width() -> Option { + pub fn stderr_width() -> TtyWidth { unsafe { let mut winsize: libc::winsize = mem::zeroed(); // The .into() here is needed for FreeBSD which defines TIOCGWINSZ // as c_uint but ioctl wants c_ulong. if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 { - return None; + return TtyWidth::NoTty; } if winsize.ws_col > 0 { - Some(winsize.ws_col as usize) + TtyWidth::Known(winsize.ws_col as usize) } else { - None + TtyWidth::NoTty } } } @@ -458,18 +470,14 @@ mod imp { use winapi::um::wincon::*; use winapi::um::winnt::*; - pub(super) use super::default_err_erase_line as err_erase_line; - - pub fn accurate_stderr_width() -> Option { - None - } + pub(super) use super::{default_err_erase_line as err_erase_line, TtyWidth}; - pub fn stderr_width() -> Option { + pub fn stderr_width() -> TtyWidth { unsafe { let stdout = GetStdHandle(STD_ERROR_HANDLE); let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed(); if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 { - return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize); + return TtyWidth::Known((csbi.srWindow.Right - csbi.srWindow.Left) as usize); } // On mintty/msys/cygwin based terminals, the above fails with @@ -485,7 +493,7 @@ mod imp { ptr::null_mut(), ); if h == INVALID_HANDLE_VALUE { - return None; + return TtyWidth::NoTty; } let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed(); @@ -501,17 +509,21 @@ mod imp { // resize the console correctly, but there's no reasonable way // to detect which kind of terminal we are running in, or if // GetConsoleScreenBufferInfo returns accurate information. - return Some(cmp::min(60, width)); + return TtyWidth::Guess(cmp::min(60, width)); } - None + + TtyWidth::NoTty } } } #[cfg(windows)] fn default_err_erase_line(shell: &mut Shell) { - if let Some(max_width) = imp::stderr_width() { - let blank = " ".repeat(max_width); - drop(write!(shell.output.stderr(), "{}\r", blank)); + match imp::stderr_width() { + TtyWidth::Known(max_width) | TtyWidth::Guess(max_width) => { + let blank = " ".repeat(max_width); + drop(write!(shell.output.stderr(), "{}\r", blank)); + } + _ => (), } } diff --git a/src/cargo/util/progress.rs b/src/cargo/util/progress.rs index d62600379cb..76536b8d16d 100644 --- a/src/cargo/util/progress.rs +++ b/src/cargo/util/progress.rs @@ -50,7 +50,7 @@ impl<'cfg> Progress<'cfg> { } Progress { - state: cfg.shell().err_width().map(|n| State { + state: cfg.shell().err_width().progress_max_width().map(|n| State { config: cfg, format: Format { style, @@ -216,7 +216,7 @@ impl<'cfg> State<'cfg> { } fn try_update_max_width(&mut self) { - if let Some(n) = self.config.shell().err_width() { + if let Some(n) = self.config.shell().err_width().progress_max_width() { self.format.max_width = n; } }