]> git.proxmox.com Git - cargo.git/commitdiff
Ignore broken console output in some situations.
authorEric Huss <eric@huss.org>
Tue, 12 May 2020 15:03:35 +0000 (08:03 -0700)
committerEric Huss <eric@huss.org>
Wed, 13 May 2020 23:22:24 +0000 (16:22 -0700)
25 files changed:
src/bin/cargo/cli.rs
src/bin/cargo/commands/locate_project.rs
src/bin/cargo/commands/metadata.rs
src/bin/cargo/commands/pkgid.rs
src/bin/cargo/commands/read_manifest.rs
src/bin/cargo/commands/tree.rs
src/bin/cargo/commands/verify_project.rs
src/bin/cargo/commands/version.rs
src/cargo/core/compiler/job_queue.rs
src/cargo/core/compiler/mod.rs
src/cargo/core/compiler/timings.rs
src/cargo/core/compiler/unit_graph.rs
src/cargo/core/manifest.rs
src/cargo/core/resolver/features.rs
src/cargo/core/shell.rs
src/cargo/lib.rs
src/cargo/ops/cargo_install.rs
src/cargo/ops/cargo_package.rs
src/cargo/ops/registry.rs
src/cargo/ops/tree/mod.rs
src/cargo/ops/vendor.rs
src/cargo/util/config/mod.rs
tests/testsuite/cargo_command.rs
tests/testsuite/owner.rs
tests/testsuite/search.rs

index 7fc1da57a8655122c1cac9e2efe70bd843ad5826..23ed0ca53a381411defdb49931fa3f83112131bd 100644 (file)
@@ -1,5 +1,5 @@
 use cargo::core::features;
-use cargo::{self, CliResult, Config};
+use cargo::{self, drop_print, drop_println, CliResult, Config};
 use clap::{AppSettings, Arg, ArgMatches};
 
 use super::commands;
@@ -25,7 +25,8 @@ pub fn main(config: &mut Config) -> CliResult {
     };
 
     if args.value_of("unstable-features") == Some("help") {
-        println!(
+        drop_println!(
+            config,
             "
 Available unstable (nightly-only) flags:
 
@@ -40,7 +41,8 @@ Available unstable (nightly-only) flags:
 Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
         );
         if !features::nightly_features_allowed() {
-            println!(
+            drop_println!(
+                config,
                 "\nUnstable flags are only available on the nightly channel \
                  of Cargo, but this is the `{}` channel.\n\
                  {}",
@@ -48,7 +50,8 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
                 features::SEE_CHANNELS
             );
         }
-        println!(
+        drop_println!(
+            config,
             "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
              for more information about these flags."
         );
@@ -58,7 +61,7 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
     let is_verbose = args.occurrences_of("verbose") > 0;
     if args.is_present("version") {
         let version = get_version_string(is_verbose);
-        print!("{}", version);
+        drop_print!(config, "{}", version);
         return Ok(());
     }
 
@@ -69,19 +72,19 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
     }
 
     if args.is_present("list") {
-        println!("Installed Commands:");
+        drop_println!(config, "Installed Commands:");
         for command in list_commands(config) {
             match command {
                 CommandInfo::BuiltIn { name, about } => {
                     let summary = about.unwrap_or_default();
                     let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
-                    println!("    {:<20} {}", name, summary)
+                    drop_println!(config, "    {:<20} {}", name, summary);
                 }
                 CommandInfo::External { name, path } => {
                     if is_verbose {
-                        println!("    {:<20} {}", name, path.display())
+                        drop_println!(config, "    {:<20} {}", name, path.display());
                     } else {
-                        println!("    {}", name)
+                        drop_println!(config, "    {}", name);
                     }
                 }
             }
index df0c424aa4a137c87c23559f7e39f93df2762bec..5897de108b343b64de5e839580669600ea8c03fe 100644 (file)
@@ -1,6 +1,4 @@
 use crate::command_prelude::*;
-
-use cargo::print_json;
 use serde::Serialize;
 
 pub fn cli() -> App {
@@ -30,6 +28,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
 
     let location = ProjectLocation { root };
 
-    print_json(&location);
+    config.shell().print_json(&location);
     Ok(())
 }
index 1130f074e8f40628fb8feb0c2915883b44278138..616df735379e345594765cac91faceace9796ca5 100644 (file)
@@ -1,7 +1,5 @@
 use crate::command_prelude::*;
-
 use cargo::ops::{self, OutputMetadataOptions};
-use cargo::print_json;
 
 pub fn cli() -> App {
     subcommand("metadata")
@@ -54,6 +52,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
     };
 
     let result = ops::output_metadata(&ws, &options)?;
-    print_json(&result);
+    config.shell().print_json(&result);
     Ok(())
 }
index 57be0d11877b6644df454ebac7740f4c74a7dc0d..453c95a184d12c31fb3c7a955556de211e53d1f9 100644 (file)
@@ -37,6 +37,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
     let ws = args.workspace(config)?;
     let spec = args.value_of("spec").or_else(|| args.value_of("package"));
     let spec = ops::pkgid(&ws, spec)?;
-    println!("{}", spec);
+    cargo::drop_println!(config, "{}", spec);
     Ok(())
 }
index fe2528b18aab9a76eacaa765f077866ee3e426bb..96cba1e082a3a79c14b422c83bff11a567b7e631 100644 (file)
@@ -1,7 +1,5 @@
 use crate::command_prelude::*;
 
-use cargo::print_json;
-
 pub fn cli() -> App {
     subcommand("read-manifest")
         .about(
@@ -17,6 +15,6 @@ Deprecated, use `cargo metadata --no-deps` instead.\
 
 pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
     let ws = args.workspace(config)?;
-    print_json(&ws.current()?);
+    config.shell().print_json(&ws.current()?);
     Ok(())
 }
index 9cfff98fe480566a52cda1782209b8fe10b0c26e..7eeac6cb8e710021ed0b6ee60941c95432898495 100644 (file)
@@ -102,7 +102,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
     if args.is_present("version") {
         let verbose = args.occurrences_of("verbose") > 0;
         let version = cli::get_version_string(verbose);
-        print!("{}", version);
+        cargo::drop_print!(config, "{}", version);
         return Ok(());
     }
     let prefix = if args.is_present("no-indent") {
index fe2b42aebedc734cff8bb270c624c1c36b2aa605..ea5ac11780327dba3716c75093b04c27b1bd236e 100644 (file)
@@ -3,8 +3,6 @@ use crate::command_prelude::*;
 use std::collections::HashMap;
 use std::process;
 
-use cargo::print_json;
-
 pub fn cli() -> App {
     subcommand("verify-project")
         .about("Check correctness of crate manifest")
@@ -13,19 +11,15 @@ pub fn cli() -> App {
 }
 
 pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
-    fn fail(reason: &str, value: &str) -> ! {
+    if let Err(e) = args.workspace(config) {
         let mut h = HashMap::new();
-        h.insert(reason.to_string(), value.to_string());
-        print_json(&h);
+        h.insert("invalid".to_string(), e.to_string());
+        config.shell().print_json(&h);
         process::exit(1)
     }
 
-    if let Err(e) = args.workspace(config) {
-        fail("invalid", &e.to_string())
-    }
-
     let mut h = HashMap::new();
     h.insert("success".to_string(), "true".to_string());
-    print_json(&h);
+    config.shell().print_json(&h);
     Ok(())
 }
index 81c6838e7abe50b2f7f317fad12ce37486656f9d..73172826150da021fa013ad0c9784e47bbda36a4 100644 (file)
@@ -1,6 +1,5 @@
-use crate::command_prelude::*;
-
 use crate::cli;
+use crate::command_prelude::*;
 
 pub fn cli() -> App {
     subcommand("version")
@@ -8,9 +7,9 @@ pub fn cli() -> App {
         .arg(opt("quiet", "No output printed to stdout").short("q"))
 }
 
-pub fn exec(_config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
+pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
     let verbose = args.occurrences_of("verbose") > 0;
     let version = cli::get_version_string(verbose);
-    print!("{}", version);
+    cargo::drop_print!(config, "{}", version);
     Ok(())
 }
index 7a103b13d21e5cd0871155f6e7e1bdfd046e0cd5..a1f2bba16d37a79f7b56bdf27109f35282c87da2 100644 (file)
@@ -500,7 +500,7 @@ impl<'cfg> DrainState<'cfg> {
                 plan.update(&module_name, &cmd, &filenames)?;
             }
             Message::Stdout(out) => {
-                cx.bcx.config.shell().stdout_println(out);
+                writeln!(cx.bcx.config.shell().out(), "{}", out)?;
             }
             Message::Stderr(err) => {
                 let mut shell = cx.bcx.config.shell();
@@ -700,7 +700,7 @@ impl<'cfg> DrainState<'cfg> {
                 success: error.is_none(),
             }
             .to_json_string();
-            cx.bcx.config.shell().stdout_println(msg);
+            writeln!(cx.bcx.config.shell().out(), "{}", msg)?;
         }
 
         if let Some(e) = error {
index 8a22e4aa05058b90ff878e5dd42ab1c6f99ccc03..f662495a1a1d590dce3395cde133f8e87b92b372 100644 (file)
@@ -146,7 +146,7 @@ fn compile<'cfg>(
                     &unit.target,
                     cx.files().message_cache_path(unit),
                     cx.bcx.build_config.message_format,
-                    cx.bcx.config.shell().supports_color(),
+                    cx.bcx.config.shell().err_supports_color(),
                 )
             } else {
                 Work::noop()
@@ -1109,7 +1109,7 @@ struct OutputOptions {
 impl OutputOptions {
     fn new(cx: &Context<'_, '_>, unit: &Unit) -> OutputOptions {
         let look_for_metadata_directive = cx.rmeta_required(unit);
-        let color = cx.bcx.config.shell().supports_color();
+        let color = cx.bcx.config.shell().err_supports_color();
         let path = cx.files().message_cache_path(unit);
         // Remove old cache, ignore ENOENT, which is the common case.
         drop(fs::remove_file(&path));
index 20b4c7f600befb1e25ac5eefc11ad7679f6dfa8e..8302475defd444902289cd3053ceb201ac53c303 100644 (file)
@@ -245,7 +245,7 @@ impl<'cfg> Timings<'cfg> {
                 rmeta_time: unit_time.rmeta_time,
             }
             .to_json_string();
-            self.config.shell().stdout_println(msg);
+            crate::drop_println!(self.config, "{}", msg);
         }
         self.unit_times.push(unit_time);
     }
index 350c040abadcccda744a5398bc2c45f7c1fbcf1f..d242f6b0497f67bd62bec0859cf89d726fe5a9c6 100644 (file)
@@ -113,6 +113,6 @@ pub fn emit_serialized_unit_graph(root_units: &[Unit], unit_graph: &UnitGraph) -
     let stdout = std::io::stdout();
     let mut lock = stdout.lock();
     serde_json::to_writer(&mut lock, &s)?;
-    writeln!(lock)?;
+    drop(writeln!(lock));
     Ok(())
 }
index 994603aca574304727a21e759e11324c5dfec5e6..221b769295b9f32514194700caed618c2a6c07d4 100644 (file)
@@ -500,7 +500,7 @@ impl Manifest {
     pub fn print_teapot(&self, config: &Config) {
         if let Some(teapot) = self.im_a_teapot {
             if config.cli_unstable().print_im_a_teapot {
-                println!("im-a-teapot = {}", teapot);
+                crate::drop_println!(config, "im-a-teapot = {}", teapot);
             }
         }
     }
index cea01d113a49505ccda5c7834e331431f5796a8f..8f71007f61092b8201afaee28a6ccfb9764b0edf 100644 (file)
@@ -573,9 +573,13 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
         for ((pkg_id, dep_kind), features) in &self.activated_features {
             let r_features = self.resolve.features(*pkg_id);
             if !r_features.iter().eq(features.iter()) {
-                eprintln!(
+                crate::drop_eprintln!(
+                    self.ws.config(),
                     "{}/{:?} features mismatch\nresolve: {:?}\nnew: {:?}\n",
-                    pkg_id, dep_kind, r_features, features
+                    pkg_id,
+                    dep_kind,
+                    r_features,
+                    features
                 );
                 found = true;
             }
index eae8a70cc8db154b925d8633c41e050a5fedd9e3..a3e72b5261ee4948c295479c408ab839b5e67b1f 100644 (file)
@@ -14,13 +14,13 @@ pub enum Verbosity {
     Quiet,
 }
 
-/// An abstraction around a `Write`able object that remembers preferences for output verbosity and
-/// color.
+/// An abstraction around console output that remembers preferences for output
+/// verbosity and color.
 pub struct Shell {
-    /// the `Write`able object, either with or without color support (represented by different enum
-    /// variants)
-    err: ShellOut,
-    /// How verbose messages should be
+    /// Wrapper around stdout/stderr. This helps with supporting sending
+    /// output to a memory buffer which is useful for tests.
+    output: ShellOut,
+    /// How verbose messages should be.
     verbosity: Verbosity,
     /// Flag that indicates the current line needs to be cleared before
     /// printing. Used when a progress bar is currently displayed.
@@ -29,7 +29,7 @@ pub struct Shell {
 
 impl fmt::Debug for Shell {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.err {
+        match self.output {
             ShellOut::Write(_) => f
                 .debug_struct("Shell")
                 .field("verbosity", &self.verbosity)
@@ -49,8 +49,9 @@ enum ShellOut {
     Write(Box<dyn Write>),
     /// Color-enabled stdio, with information on whether color should be used
     Stream {
-        stream: StandardStream,
-        tty: bool,
+        stdout: StandardStream,
+        stderr: StandardStream,
+        stderr_tty: bool,
         color_choice: ColorChoice,
     },
 }
@@ -70,11 +71,13 @@ impl Shell {
     /// Creates a new shell (color choice and verbosity), defaulting to 'auto' color and verbose
     /// output.
     pub fn new() -> Shell {
+        let auto = ColorChoice::CargoAuto.to_termcolor_color_choice();
         Shell {
-            err: ShellOut::Stream {
-                stream: StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
+            output: ShellOut::Stream {
+                stdout: StandardStream::stdout(auto),
+                stderr: StandardStream::stderr(auto),
                 color_choice: ColorChoice::CargoAuto,
-                tty: atty::is(atty::Stream::Stderr),
+                stderr_tty: atty::is(atty::Stream::Stderr),
             },
             verbosity: Verbosity::Verbose,
             needs_clear: false,
@@ -84,7 +87,7 @@ impl Shell {
     /// Creates a shell from a plain writable object, with no color, and max verbosity.
     pub fn from_write(out: Box<dyn Write>) -> Shell {
         Shell {
-            err: ShellOut::Write(out),
+            output: ShellOut::Write(out),
             verbosity: Verbosity::Verbose,
             needs_clear: false,
         }
@@ -105,18 +108,12 @@ impl Shell {
                 if self.needs_clear {
                     self.err_erase_line();
                 }
-                self.err.print(status, message, color, justified)
+                self.output
+                    .message_stderr(status, message, color, justified)
             }
         }
     }
 
-    pub fn stdout_println(&mut self, message: impl fmt::Display) {
-        if self.needs_clear {
-            self.err_erase_line();
-        }
-        println!("{}", message);
-    }
-
     /// Sets whether the next print should clear the current line.
     pub fn set_needs_clear(&mut self, needs_clear: bool) {
         self.needs_clear = needs_clear;
@@ -129,31 +126,44 @@ impl Shell {
 
     /// Returns the width of the terminal in spaces, if any.
     pub fn err_width(&self) -> Option<usize> {
-        match self.err {
-            ShellOut::Stream { tty: true, .. } => imp::stderr_width(),
+        match self.output {
+            ShellOut::Stream {
+                stderr_tty: true, ..
+            } => imp::stderr_width(),
             _ => None,
         }
     }
 
     /// Returns `true` if stderr is a tty.
     pub fn is_err_tty(&self) -> bool {
-        match self.err {
-            ShellOut::Stream { tty, .. } => tty,
+        match self.output {
+            ShellOut::Stream { stderr_tty, .. } => stderr_tty,
             _ => false,
         }
     }
 
-    /// Gets a reference to the underlying writer.
+    /// Gets a reference to the underlying stdout writer.
+    pub fn out(&mut self) -> &mut dyn Write {
+        if self.needs_clear {
+            self.err_erase_line();
+        }
+        self.output.stdout()
+    }
+
+    /// Gets a reference to the underlying stderr writer.
     pub fn err(&mut self) -> &mut dyn Write {
         if self.needs_clear {
             self.err_erase_line();
         }
-        self.err.as_write()
+        self.output.stderr()
     }
 
     /// Erase from cursor to end of line.
     pub fn err_erase_line(&mut self) {
-        if let ShellOut::Stream { tty: true, .. } = self.err {
+        if let ShellOut::Stream {
+            stderr_tty: true, ..
+        } = self.output
+        {
             imp::err_erase_line(self);
             self.needs_clear = false;
         }
@@ -216,7 +226,8 @@ impl Shell {
         if self.needs_clear {
             self.err_erase_line();
         }
-        self.err.print(&"error", Some(&message), Red, false)
+        self.output
+            .message_stderr(&"error", Some(&message), Red, false)
     }
 
     /// Prints an amber 'warning' message.
@@ -245,10 +256,11 @@ impl Shell {
     /// Updates the color choice (always, never, or auto) from a string..
     pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> {
         if let ShellOut::Stream {
-            ref mut stream,
+            ref mut stdout,
+            ref mut stderr,
             ref mut color_choice,
             ..
-        } = self.err
+        } = self.output
         {
             let cfg = match color {
                 Some("always") => ColorChoice::Always,
@@ -263,7 +275,9 @@ impl Shell {
                 ),
             };
             *color_choice = cfg;
-            *stream = StandardStream::stderr(cfg.to_termcolor_color_choice());
+            let choice = cfg.to_termcolor_color_choice();
+            *stdout = StandardStream::stdout(choice);
+            *stderr = StandardStream::stderr(choice);
         }
         Ok(())
     }
@@ -273,17 +287,17 @@ impl Shell {
     /// If we are not using a color stream, this will always return `Never`, even if the color
     /// choice has been set to something else.
     pub fn color_choice(&self) -> ColorChoice {
-        match self.err {
+        match self.output {
             ShellOut::Stream { color_choice, .. } => color_choice,
             ShellOut::Write(_) => ColorChoice::Never,
         }
     }
 
     /// Whether the shell supports color.
-    pub fn supports_color(&self) -> bool {
-        match &self.err {
+    pub fn err_supports_color(&self) -> bool {
+        match &self.output {
             ShellOut::Write(_) => false,
-            ShellOut::Stream { stream, .. } => stream.supports_color(),
+            ShellOut::Stream { stderr, .. } => stderr.supports_color(),
         }
     }
 
@@ -302,6 +316,11 @@ impl Shell {
         self.err().write_all(message)?;
         Ok(())
     }
+
+    pub fn print_json<T: serde::ser::Serialize>(&mut self, obj: &T) {
+        let encoded = serde_json::to_string(&obj).unwrap();
+        drop(writeln!(self.out(), "{}", encoded));
+    }
 }
 
 impl Default for Shell {
@@ -314,7 +333,7 @@ impl ShellOut {
     /// Prints out a message with a status. The status comes first, and is bold plus the given
     /// color. The status can be justified, in which case the max width that will right align is
     /// 12 chars.
-    fn print(
+    fn message_stderr(
         &mut self,
         status: &dyn fmt::Display,
         message: Option<&dyn fmt::Display>,
@@ -322,20 +341,20 @@ impl ShellOut {
         justified: bool,
     ) -> CargoResult<()> {
         match *self {
-            ShellOut::Stream { ref mut stream, .. } => {
-                stream.reset()?;
-                stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
+            ShellOut::Stream { ref mut stderr, .. } => {
+                stderr.reset()?;
+                stderr.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
                 if justified {
-                    write!(stream, "{:>12}", status)?;
+                    write!(stderr, "{:>12}", status)?;
                 } else {
-                    write!(stream, "{}", status)?;
-                    stream.set_color(ColorSpec::new().set_bold(true))?;
-                    write!(stream, ":")?;
+                    write!(stderr, "{}", status)?;
+                    stderr.set_color(ColorSpec::new().set_bold(true))?;
+                    write!(stderr, ":")?;
                 }
-                stream.reset()?;
+                stderr.reset()?;
                 match message {
-                    Some(message) => writeln!(stream, " {}", message)?,
-                    None => write!(stream, " ")?,
+                    Some(message) => writeln!(stderr, " {}", message)?,
+                    None => write!(stderr, " ")?,
                 }
             }
             ShellOut::Write(ref mut w) => {
@@ -353,10 +372,18 @@ impl ShellOut {
         Ok(())
     }
 
-    /// Gets this object as a `io::Write`.
-    fn as_write(&mut self) -> &mut dyn Write {
+    /// Gets stdout as a `io::Write`.
+    fn stdout(&mut self) -> &mut dyn Write {
+        match *self {
+            ShellOut::Stream { ref mut stdout, .. } => stdout,
+            ShellOut::Write(ref mut w) => w,
+        }
+    }
+
+    /// Gets stderr as a `io::Write`.
+    fn stderr(&mut self) -> &mut dyn Write {
         match *self {
-            ShellOut::Stream { ref mut stream, .. } => stream,
+            ShellOut::Stream { ref mut stderr, .. } => stderr,
             ShellOut::Write(ref mut w) => w,
         }
     }
@@ -404,7 +431,7 @@ mod imp {
         // This is the "EL - Erase in Line" sequence. It clears from the cursor
         // to the end of line.
         // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
-        let _ = shell.err.as_write().write_all(b"\x1B[K");
+        let _ = shell.output.stderr().write_all(b"\x1B[K");
     }
 }
 
index b386ee7f558b19382867249307fffc41efca2c69..19cbd49c1011a4a7cbad7f2b45ab8df2a4d41781 100644 (file)
@@ -34,7 +34,6 @@ use crate::core::shell::Verbosity::Verbose;
 use crate::core::Shell;
 use anyhow::Error;
 use log::debug;
-use serde::ser;
 use std::fmt;
 
 pub use crate::util::errors::{InternalError, VerboseError};
@@ -93,11 +92,6 @@ impl fmt::Display for VersionInfo {
     }
 }
 
-pub fn print_json<T: ser::Serialize>(obj: &T) {
-    let encoded = serde_json::to_string(&obj).unwrap();
-    println!("{}", encoded);
-}
-
 pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
     debug!("exit_with_error; err={:?}", err);
     if let Some(ref err) = err.error {
index 0406a56063dba7a6a14acdee41c70fb692e2a543..e450f82f3dee51d4a5893aa25f65538c960e942b 100644 (file)
@@ -9,11 +9,11 @@ use tempfile::Builder as TempFileBuilder;
 use crate::core::compiler::Freshness;
 use crate::core::compiler::{CompileKind, DefaultExecutor, Executor};
 use crate::core::{Edition, Package, PackageId, Source, SourceId, Workspace};
-use crate::ops;
 use crate::ops::common_for_install_and_uninstall::*;
 use crate::sources::{GitSource, SourceConfigMap};
 use crate::util::errors::{CargoResult, CargoResultExt};
 use crate::util::{paths, Config, Filesystem};
+use crate::{drop_println, ops};
 
 struct Transaction {
     bins: Vec<PathBuf>,
@@ -531,9 +531,9 @@ pub fn install_list(dst: Option<&str>, config: &Config) -> CargoResult<()> {
     let root = resolve_root(dst, config)?;
     let tracker = InstallTracker::load(config, &root)?;
     for (k, v) in tracker.all_installed_bins() {
-        println!("{}:", k);
+        drop_println!(config, "{}:", k);
         for bin in v {
-            println!("    {}", bin);
+            drop_println!(config, "    {}", bin);
         }
     }
     Ok(())
index b84e6b3caf137ee975a83d0ef8974f76fc10512e..4b59afdae1e3baf68697865522ff3fab9a29f3c6 100644 (file)
@@ -15,12 +15,12 @@ use tar::{Archive, Builder, EntryType, Header};
 use crate::core::compiler::{BuildConfig, CompileMode, DefaultExecutor, Executor};
 use crate::core::{Feature, Shell, Verbosity, Workspace};
 use crate::core::{Package, PackageId, PackageSet, Resolve, Source, SourceId};
-use crate::ops;
 use crate::sources::PathSource;
 use crate::util::errors::{CargoResult, CargoResultExt};
 use crate::util::paths;
 use crate::util::toml::TomlManifest;
 use crate::util::{self, restricted_names, Config, FileLock};
+use crate::{drop_println, ops};
 
 pub struct PackageOpts<'cfg> {
     pub config: &'cfg Config,
@@ -102,7 +102,7 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
 
     if opts.list {
         for ar_file in ar_files {
-            println!("{}", ar_file.rel_str);
+            drop_println!(config, "{}", ar_file.rel_str);
         }
         return Ok(None);
     }
index a20a22c15d17805297cede92f22bbd0cbb275741..6e0e4b89ef6e833998b31c8d0f97fd196ea134e7 100644 (file)
@@ -23,7 +23,7 @@ use crate::util::errors::{CargoResult, CargoResultExt};
 use crate::util::important_paths::find_root_manifest_for_wd;
 use crate::util::IntoUrl;
 use crate::util::{paths, validate_package_name};
-use crate::version;
+use crate::{drop_print, drop_println, version};
 
 /// Registry settings loaded from config files.
 ///
@@ -660,7 +660,8 @@ pub fn registry_login(
     let token = match token {
         Some(token) => token,
         None => {
-            println!(
+            drop_println!(
+                config,
                 "please visit {}/me and paste the API Token below",
                 registry.host()
             );
@@ -751,11 +752,11 @@ pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
             .list_owners(&name)
             .chain_err(|| format!("failed to list owners of crate {}", name))?;
         for owner in owners.iter() {
-            print!("{}", owner.login);
+            drop_print!(config, "{}", owner.login);
             match (owner.name.as_ref(), owner.email.as_ref()) {
-                (Some(name), Some(email)) => println!(" ({} <{}>)", name, email),
-                (Some(s), None) | (None, Some(s)) => println!(" ({})", s),
-                (None, None) => println!(),
+                (Some(name), Some(email)) => drop_println!(config, " ({} <{}>)", name, email),
+                (Some(s), None) | (None, Some(s)) => drop_println!(config, " ({})", s),
+                (None, None) => drop_println!(config),
             }
         }
     }
@@ -876,12 +877,13 @@ pub fn search(
             }
             None => name,
         };
-        println!("{}", line);
+        drop_println!(config, "{}", line);
     }
 
     let search_max_limit = 100;
     if total_crates > limit && limit < search_max_limit {
-        println!(
+        drop_println!(
+            config,
             "... and {} crates more (use --limit N to see more)",
             total_crates - limit
         );
@@ -894,7 +896,12 @@ pub fn search(
         } else {
             String::new()
         };
-        println!("... and {} crates more{}", total_crates - limit, extra);
+        drop_println!(
+            config,
+            "... and {} crates more{}",
+            total_crates - limit,
+            extra
+        );
     }
 
     Ok(())
index 53ba973143c8fe2d5df13842cb759bbed75f0e26..1919cee854b97ac720237b7162243dc6b386c18b 100644 (file)
@@ -6,7 +6,8 @@ use crate::core::dependency::DepKind;
 use crate::core::resolver::{HasDevUnits, ResolveOpts};
 use crate::core::{Package, PackageId, PackageIdSpec, Workspace};
 use crate::ops::{self, Packages};
-use crate::util::CargoResult;
+use crate::util::{CargoResult, Config};
+use crate::{drop_print, drop_println};
 use anyhow::{bail, Context};
 use graph::Graph;
 use std::collections::{HashMap, HashSet};
@@ -200,12 +201,17 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
         graph.invert();
     }
 
-    print(opts, root_indexes, &graph)?;
+    print(ws.config(), opts, root_indexes, &graph)?;
     Ok(())
 }
 
 /// Prints a tree for each given root.
-fn print(opts: &TreeOptions, roots: Vec<usize>, graph: &Graph<'_>) -> CargoResult<()> {
+fn print(
+    config: &Config,
+    opts: &TreeOptions,
+    roots: Vec<usize>,
+    graph: &Graph<'_>,
+) -> CargoResult<()> {
     let format = Pattern::new(&opts.format)
         .with_context(|| format!("tree format `{}` not valid", opts.format))?;
 
@@ -220,7 +226,7 @@ fn print(opts: &TreeOptions, roots: Vec<usize>, graph: &Graph<'_>) -> CargoResul
 
     for (i, root_index) in roots.into_iter().enumerate() {
         if i != 0 {
-            println!();
+            drop_println!(config);
         }
 
         // A stack of bools used to determine where | symbols should appear
@@ -231,6 +237,7 @@ fn print(opts: &TreeOptions, roots: Vec<usize>, graph: &Graph<'_>) -> CargoResul
         let mut print_stack = vec![];
 
         print_node(
+            config,
             graph,
             root_index,
             &format,
@@ -248,6 +255,7 @@ fn print(opts: &TreeOptions, roots: Vec<usize>, graph: &Graph<'_>) -> CargoResul
 
 /// Prints a package and all of its dependencies.
 fn print_node<'a>(
+    config: &Config,
     graph: &'a Graph<'_>,
     node_index: usize,
     format: &Pattern,
@@ -261,12 +269,12 @@ fn print_node<'a>(
     let new = no_dedupe || visited_deps.insert(node_index);
 
     match prefix {
-        Prefix::Depth => print!("{}", levels_continue.len()),
+        Prefix::Depth => drop_print!(config, "{}", levels_continue.len()),
         Prefix::Indent => {
             if let Some((last_continues, rest)) = levels_continue.split_last() {
                 for continues in rest {
                     let c = if *continues { symbols.down } else { " " };
-                    print!("{}   ", c);
+                    drop_print!(config, "{}   ", c);
                 }
 
                 let c = if *last_continues {
@@ -274,7 +282,7 @@ fn print_node<'a>(
                 } else {
                     symbols.ell
                 };
-                print!("{0}{1}{1} ", c, symbols.right);
+                drop_print!(config, "{0}{1}{1} ", c, symbols.right);
             }
         }
         Prefix::None => {}
@@ -290,7 +298,7 @@ fn print_node<'a>(
     } else {
         " (*)"
     };
-    println!("{}{}", format.display(graph, node_index), star);
+    drop_println!(config, "{}{}", format.display(graph, node_index), star);
 
     if !new || in_cycle {
         return;
@@ -304,6 +312,7 @@ fn print_node<'a>(
         EdgeKind::Feature,
     ] {
         print_dependencies(
+            config,
             graph,
             node_index,
             format,
@@ -321,6 +330,7 @@ fn print_node<'a>(
 
 /// Prints all the dependencies of a package for the given dependency kind.
 fn print_dependencies<'a>(
+    config: &Config,
     graph: &'a Graph<'_>,
     node_index: usize,
     format: &Pattern,
@@ -348,10 +358,10 @@ fn print_dependencies<'a>(
         if let Some(name) = name {
             for continues in &**levels_continue {
                 let c = if *continues { symbols.down } else { " " };
-                print!("{}   ", c);
+                drop_print!(config, "{}   ", c);
             }
 
-            println!("{}", name);
+            drop_println!(config, "{}", name);
         }
     }
 
@@ -359,6 +369,7 @@ fn print_dependencies<'a>(
     while let Some(dependency) = it.next() {
         levels_continue.push(it.peek().is_some());
         print_node(
+            config,
             graph,
             *dependency,
             format,
index 7779233346173844467df0b9992bedfe65930ea6..2c446fa16397e20e415132f636aba3ca81b38031 100644 (file)
@@ -19,20 +19,23 @@ pub struct VendorOptions<'a> {
 }
 
 pub fn vendor(ws: &Workspace<'_>, opts: &VendorOptions<'_>) -> CargoResult<()> {
+    let config = ws.config();
     let mut extra_workspaces = Vec::new();
     for extra in opts.extra.iter() {
-        let extra = ws.config().cwd().join(extra);
-        let ws = Workspace::new(&extra, ws.config())?;
+        let extra = config.cwd().join(extra);
+        let ws = Workspace::new(&extra, config)?;
         extra_workspaces.push(ws);
     }
     let workspaces = extra_workspaces.iter().chain(Some(ws)).collect::<Vec<_>>();
     let vendor_config =
-        sync(ws.config(), &workspaces, opts).chain_err(|| "failed to sync".to_string())?;
-
-    let shell = ws.config().shell();
-    if shell.verbosity() != Verbosity::Quiet {
-        eprint!("To use vendored sources, add this to your .cargo/config for this project:\n\n");
-        print!("{}", &toml::to_string(&vendor_config).unwrap());
+        sync(config, &workspaces, opts).chain_err(|| "failed to sync".to_string())?;
+
+    if config.shell().verbosity() != Verbosity::Quiet {
+        crate::drop_eprint!(
+            config,
+            "To use vendored sources, add this to your .cargo/config for this project:\n\n"
+        );
+        crate::drop_print!(config, "{}", &toml::to_string(&vendor_config).unwrap());
     }
 
     Ok(())
index 51cba89f797eca568aa2b30a39f2307274d98550..603f2581f950999d0d8d31de3a0a0982d0057815 100644 (file)
@@ -1744,3 +1744,45 @@ impl StringList {
         &self.0
     }
 }
+
+#[macro_export]
+macro_rules! __shell_print {
+    ($config:expr, $which:ident, $newline:literal, $($arg:tt)*) => ({
+        let mut shell = $config.shell();
+        let out = shell.$which();
+        drop(out.write_fmt(format_args!($($arg)*)));
+        if $newline {
+            drop(out.write_all(b"\n"));
+        }
+    });
+}
+
+#[macro_export]
+macro_rules! drop_println {
+    ($config:expr) => ( $crate::drop_print!($config, "\n") );
+    ($config:expr, $($arg:tt)*) => (
+        $crate::__shell_print!($config, out, true, $($arg)*)
+    );
+}
+
+#[macro_export]
+macro_rules! drop_eprintln {
+    ($config:expr) => ( $crate::drop_eprint!($config, "\n") );
+    ($config:expr, $($arg:tt)*) => (
+        $crate::__shell_print!($config, err, true, $($arg)*)
+    );
+}
+
+#[macro_export]
+macro_rules! drop_print {
+    ($config:expr, $($arg:tt)*) => (
+        $crate::__shell_print!($config, out, false, $($arg)*)
+    );
+}
+
+#[macro_export]
+macro_rules! drop_eprint {
+    ($config:expr, $($arg:tt)*) => (
+        $crate::__shell_print!($config, err, false, $($arg)*)
+    );
+}
index 80cff54754fee67c42a46fe8e9a05d7041a2536b..fa4e05a74d256a5d41c7a347b97d87e52b5e79a2 100644 (file)
@@ -2,7 +2,9 @@
 
 use std::env;
 use std::fs::{self, File};
+use std::io::Read;
 use std::path::{Path, PathBuf};
+use std::process::Stdio;
 use std::str;
 
 use cargo_test_support::cargo_process;
@@ -371,3 +373,24 @@ fn z_flags_help() {
         .with_stdout_contains("    -Z unstable-options -- Allow the usage of unstable options")
         .run();
 }
+
+#[cargo_test]
+fn closed_output_ok() {
+    // Checks that closed output doesn't cause an error.
+    let mut p = cargo_process("--list").build_command();
+    p.stdout(Stdio::piped()).stderr(Stdio::piped());
+    let mut child = p.spawn().unwrap();
+    // Close stdout
+    drop(child.stdout.take());
+    // Read stderr
+    let mut s = String::new();
+    child
+        .stderr
+        .as_mut()
+        .unwrap()
+        .read_to_string(&mut s)
+        .unwrap();
+    let status = child.wait().unwrap();
+    assert!(status.success());
+    assert!(s.is_empty(), s);
+}
index 8f3f87260ed30fdba3c571d7f53b69c44acdd767..298f08dc83f2d9ffecf2d8dd7b6a7169ebf4828a 100644 (file)
@@ -23,6 +23,10 @@ fn simple_list() {
                 "id": 70,
                 "login": "github:rust-lang:core",
                 "name": "Core"
+            },
+            {
+                "id": 123,
+                "login": "octocat"
             }
         ]
     }"#;
@@ -43,7 +47,14 @@ fn simple_list() {
         .file("src/main.rs", "fn main() {}")
         .build();
 
-    p.cargo("owner -l --token sekrit").run();
+    p.cargo("owner -l --token sekrit")
+        .with_stdout(
+            "\
+github:rust-lang:core (Core)
+octocat
+",
+        )
+        .run();
 }
 
 #[cargo_test]
index 75ef3985d97bbb37a0546f21ac088371e18c96ac..4b4eb8a01f8420ab3817e3bb9a7cec4720805ff3 100644 (file)
@@ -35,9 +35,45 @@ fn write_crates(dest: &Path) {
             "repository": "https://github.com/nick29581/libhoare",
             "updated_at": "2014-11-20T21:49:21Z",
             "versions": null
-        }],
+        },
+        {
+            "id": "postgres",
+            "name": "postgres",
+            "updated_at": "2020-05-01T23:17:54.335921+00:00",
+            "versions": null,
+            "keywords": null,
+            "categories": null,
+            "badges": [
+                {
+                    "badge_type": "circle-ci",
+                    "attributes": {
+                        "repository": "sfackler/rust-postgres",
+                        "branch": null
+                    }
+                }
+            ],
+            "created_at": "2014-11-24T02:34:44.756689+00:00",
+            "downloads": 535491,
+            "recent_downloads": 88321,
+            "max_version": "0.17.3",
+            "newest_version": "0.17.3",
+            "description": "A native, synchronous PostgreSQL client",
+            "homepage": null,
+            "documentation": null,
+            "repository": "https://github.com/sfackler/rust-postgres",
+            "links": {
+                "version_downloads": "/api/v1/crates/postgres/downloads",
+                "versions": "/api/v1/crates/postgres/versions",
+                "owners": "/api/v1/crates/postgres/owners",
+                "owner_team": "/api/v1/crates/postgres/owner_team",
+                "owner_user": "/api/v1/crates/postgres/owner_user",
+                "reverse_dependencies": "/api/v1/crates/postgres/reverse_dependencies"
+            },
+            "exact_match": true
+        }
+        ],
         "meta": {
-            "total": 1
+            "total": 2
         }
     }"#;
 
@@ -56,6 +92,11 @@ fn write_crates(dest: &Path) {
     }
 }
 
+const SEARCH_RESULTS: &str = "\
+hoare = \"0.1.1\"        # Design by contract style assertions for Rust
+postgres = \"0.17.3\"    # A native, synchronous PostgreSQL client
+";
+
 fn setup() {
     let cargo_home = paths::root().join(".cargo");
     fs::create_dir_all(cargo_home).unwrap();
@@ -114,7 +155,7 @@ fn not_update() {
     drop(lock);
 
     cargo_process("search postgres")
-        .with_stdout_contains("hoare = \"0.1.1\"    # Design by contract style assertions for Rust")
+        .with_stdout_contains(SEARCH_RESULTS)
         .with_stderr("") // without "Updating ... index"
         .run();
 }
@@ -125,7 +166,7 @@ fn replace_default() {
     set_cargo_config();
 
     cargo_process("search postgres")
-        .with_stdout_contains("hoare = \"0.1.1\"    # Design by contract style assertions for Rust")
+        .with_stdout_contains(SEARCH_RESULTS)
         .with_stderr_contains("[..]Updating [..] index")
         .run();
 }
@@ -136,7 +177,7 @@ fn simple() {
 
     cargo_process("search postgres --index")
         .arg(registry_url().to_string())
-        .with_stdout_contains("hoare = \"0.1.1\"    # Design by contract style assertions for Rust")
+        .with_stdout_contains(SEARCH_RESULTS)
         .run();
 }
 
@@ -162,7 +203,7 @@ about this warning.
 [UPDATING] `[CWD]/registry` index
 ",
         )
-        .with_stdout_contains("hoare = \"0.1.1\"    # Design by contract style assertions for Rust")
+        .with_stdout_contains(SEARCH_RESULTS)
         .run();
 }
 
@@ -190,7 +231,7 @@ about this warning.
 [UPDATING] `[CWD]/registry` index
 ",
         )
-        .with_stdout_contains("hoare = \"0.1.1\"    # Design by contract style assertions for Rust")
+        .with_stdout_contains(SEARCH_RESULTS)
         .run();
 }
 
@@ -200,7 +241,7 @@ fn multiple_query_params() {
 
     cargo_process("search postgres sql --index")
         .arg(registry_url().to_string())
-        .with_stdout_contains("hoare = \"0.1.1\"    # Design by contract style assertions for Rust")
+        .with_stdout_contains(SEARCH_RESULTS)
         .run();
 }