From 8b6bbb1c8ca5025de7b6ab322ea260a4181bad5d Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Sat, 21 Jan 2017 13:38:11 -0500 Subject: [PATCH] Add `eprint!` and `eprintln!` macros to the prelude. These are exactly the same as `print!` and `println!` except that they write to stderr instead of stdout. Issue #39228. --- src/libstd/io/mod.rs | 2 + src/libstd/io/stdio.rs | 36 +++++++++++++++ src/libstd/macros.rs | 45 +++++++++++++++++++ .../run-pass/print-stdout-eprint-stderr.rs | 40 +++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 src/test/run-pass/print-stdout-eprint-stderr.rs diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 5450a10c9bd9f..d80fa93c649c3 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -277,6 +277,8 @@ pub use self::util::{copy, sink, Sink, empty, Empty, repeat, Repeat}; pub use self::stdio::{stdin, stdout, stderr, _print, Stdin, Stdout, Stderr}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::stdio::{StdoutLock, StderrLock, StdinLock}; +#[unstable(feature = "eprint", issue="39228")] +pub use self::stdio::_eprint; #[unstable(feature = "libstd_io_internals", issue = "0")] #[doc(no_inline, hidden)] pub use self::stdio::{set_panic, set_print}; diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 9d1c8942f8cf8..cdcedcc33fe70 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -694,6 +694,42 @@ pub fn _print(args: fmt::Arguments) { } } +#[unstable(feature = "eprint_internal", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "0")] +#[doc(hidden)] +pub fn _eprint(args: fmt::Arguments) { + // As an implementation of the `eprintln!` macro, we want to try our best to + // not panic wherever possible and get the output somewhere. There are + // currently two possible vectors for panics we take care of here: + // + // 1. If the TLS key for the local stderr has been destroyed, accessing it + // would cause a panic. Note that we just lump in the uninitialized case + // here for convenience, we're not trying to avoid a panic. + // 2. If the local stderr is currently in use (e.g. we're in the middle of + // already printing) then accessing again would cause a panic. + // + // If, however, the actual I/O causes an error, we do indeed panic. + use panicking::LOCAL_STDERR; + let result = match LOCAL_STDERR.state() { + LocalKeyState::Uninitialized | + LocalKeyState::Destroyed => stderr().write_fmt(args), + LocalKeyState::Valid => { + LOCAL_STDERR.with(|s| { + if let Ok(mut borrowed) = s.try_borrow_mut() { + if let Some(w) = borrowed.as_mut() { + return w.write_fmt(args); + } + } + stderr().write_fmt(args) + }) + } + }; + if let Err(e) = result { + panic!("failed printing to stderr: {}", e); + } +} + #[cfg(test)] mod tests { use thread; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index d79a9a202d9e4..d4616bb90d6d9 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -68,6 +68,9 @@ macro_rules! panic { /// necessary to use `io::stdout().flush()` to ensure the output is emitted /// immediately. /// +/// Use `print!` only for the primary output of your program. Use +/// `eprint!` instead to print error and progress messages. +/// /// # Panics /// /// Panics if writing to `io::stdout()` fails. @@ -105,6 +108,9 @@ macro_rules! print { /// Use the `format!` syntax to write data to the standard output. /// See `std::fmt` for more information. /// +/// Use `println!` only for the primary output of your program. Use +/// `eprintln!` instead to print error and progress messages. +/// /// # Panics /// /// Panics if writing to `io::stdout()` fails. @@ -124,6 +130,45 @@ macro_rules! println { ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); } +/// Macro for printing to the standard error. +/// +/// Equivalent to the `print!` macro, except that output goes to +/// `io::stderr()` instead of `io::stdout()`. See `print!` for +/// example usage. +/// +/// Use `eprint!` only for error and progress messages. Use `print!` +/// instead for the primary output of your program. +/// +/// # Panics +/// +/// Panics if writing to `io::stderr()` fails. +#[macro_export] +#[unstable(feature = "eprint", issue="39228")] +#[allow_internal_unstable] +macro_rules! eprint { + ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))); +} + +/// Macro for printing to the standard error, with a newline. +/// +/// Equivalent to the `println!` macro, except that output goes to +/// `io::stderr()` instead of `io::stdout()`. See `println!` for +/// example usage. +/// +/// Use `eprintln!` only for error and progress messages. Use `println!` +/// instead for the primary output of your program. +/// +/// # Panics +/// +/// Panics if writing to `io::stderr()` fails. +#[macro_export] +#[unstable(feature = "eprint", issue="39228")] +macro_rules! eprintln { + () => (eprint!("\n")); + ($fmt:expr) => (eprint!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*)); +} + /// A macro to select an event from a number of receivers. /// /// This macro is used to wait for the first event to occur on a number of diff --git a/src/test/run-pass/print-stdout-eprint-stderr.rs b/src/test/run-pass/print-stdout-eprint-stderr.rs new file mode 100644 index 0000000000000..8ca6e1c63567e --- /dev/null +++ b/src/test/run-pass/print-stdout-eprint-stderr.rs @@ -0,0 +1,40 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(eprint)] + +use std::{env, process}; + +fn child() { + print!("[stdout 0]"); + print!("[stdout {}]", 1); + println!("[stdout {}]", 2); + println!(); + eprint!("[stderr 0]"); + eprint!("[stderr {}]", 1); + eprintln!("[stderr {}]", 2); + eprintln!(); +} + +fn parent() { + let this = env::args().next().unwrap(); + let output = process::Command::new(this).arg("-").output().unwrap(); + assert!(output.status.success()); + + let stdout = String::from_utf8(output.stdout).unwrap(); + let stderr = String::from_utf8(output.stderr).unwrap(); + + assert_eq!(stdout, "[stdout 0][stdout 1][stdout 2]\n\n"); + assert_eq!(stderr, "[stderr 0][stderr 1][stderr 2]\n\n"); +} + +fn main() { + if env::args().count() == 2 { child() } else { parent() } +}