Ignore broken console output in some situations.

This commit is contained in:
Eric Huss 2020-05-12 08:03:35 -07:00
parent 55869de80a
commit 7274307af4
25 changed files with 294 additions and 141 deletions

View 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);
}
}
}

View 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(())
}

View 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(())
}

View 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(())
}

View 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(())
}

View 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") {

View 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) -> ! {
let mut h = HashMap::new();
h.insert(reason.to_string(), value.to_string());
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("invalid".to_string(), e.to_string());
config.shell().print_json(&h);
process::exit(1)
}
let mut h = HashMap::new();
h.insert("success".to_string(), "true".to_string());
print_json(&h);
config.shell().print_json(&h);
Ok(())
}

View 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(())
}

View 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 {

View 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));

View 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);
}

View 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(())
}

View 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);
}
}
}

View 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;
}

View 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 stream, .. } => stream,
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 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");
}
}

View 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 {

View 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(())

View 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);
}

View 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(())

View 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,

View 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())?;
sync(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());
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(())

View 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)*)
);
}

View 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);
}

View 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]

View 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();
}