From 6694fdb6775deaf88db3cf0cc7c8b706bb61870a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20Kr=C3=BCger?=
Date: Fri, 1 May 2020 00:48:38 +0200
Subject: [PATCH 01/24] clippy fixes
---
crates/cargo-test-support/src/registry.rs | 2 +-
src/bin/cargo/commands/tree.rs | 2 +-
.../core/compiler/build_context/target_info.rs | 2 +-
src/cargo/core/compiler/compile_kind.rs | 2 +-
src/cargo/core/compiler/standard_lib.rs | 4 ++--
src/cargo/core/compiler/unit.rs | 2 +-
src/cargo/core/compiler/unit_graph.rs | 2 +-
src/cargo/core/manifest.rs | 14 +++++++-------
src/cargo/core/registry.rs | 2 +-
src/cargo/core/resolver/conflict_cache.rs | 2 +-
src/cargo/core/resolver/encode.rs | 6 +++---
src/cargo/lib.rs | 2 +-
src/cargo/ops/cargo_fetch.rs | 2 +-
src/cargo/ops/cargo_output_metadata.rs | 2 +-
src/cargo/ops/registry.rs | 2 +-
tests/testsuite/multitarget.rs | 2 +-
16 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs
index cf6cff8d2c2..6d3db585a34 100644
--- a/crates/cargo-test-support/src/registry.rs
+++ b/crates/cargo-test-support/src/registry.rs
@@ -442,7 +442,7 @@ impl Package {
} else {
registry_path.join(&file)
};
- let prev = fs::read_to_string(&dst).unwrap_or(String::new());
+ let prev = fs::read_to_string(&dst).unwrap_or_default();
t!(fs::create_dir_all(dst.parent().unwrap()));
t!(fs::write(&dst, prev + &line[..] + "\n"));
diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs
index b98ee5f7aad..9cfff98fe48 100644
--- a/src/bin/cargo/commands/tree.rs
+++ b/src/bin/cargo/commands/tree.rs
@@ -206,7 +206,7 @@ fn parse_edge_kinds(config: &Config, args: &ArgMatches<'_>) -> CargoResult 1 && !config.cli_unstable().multitarget {
bail!("specifying multiple `--target` flags requires `-Zmultitarget`")
}
- if targets.len() != 0 {
+ if !targets.is_empty() {
return Ok(targets
.iter()
.map(|value| Ok(CompileKind::Target(CompileTarget::new(value)?)))
diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs
index 0ae0b651546..c1c7f875c65 100644
--- a/src/cargo/core/compiler/standard_lib.rs
+++ b/src/cargo/core/compiler/standard_lib.rs
@@ -161,7 +161,7 @@ pub fn generate_std_roots(
let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev);
for kind in kinds {
- let list = ret.entry(*kind).or_insert(Vec::new());
+ let list = ret.entry(*kind).or_insert_with(Vec::new);
list.push(interner.intern(
pkg,
lib,
@@ -173,7 +173,7 @@ pub fn generate_std_roots(
));
}
}
- return Ok(ret);
+ Ok(ret)
}
fn detect_sysroot_src_path(target_data: &RustcTargetData) -> CargoResult {
diff --git a/src/cargo/core/compiler/unit.rs b/src/cargo/core/compiler/unit.rs
index ebbba387983..84565a6517e 100644
--- a/src/cargo/core/compiler/unit.rs
+++ b/src/cargo/core/compiler/unit.rs
@@ -204,6 +204,6 @@ impl UnitInterner {
}
let item = Rc::new(item.clone());
me.cache.insert(item.clone());
- return item;
+ item
}
}
diff --git a/src/cargo/core/compiler/unit_graph.rs b/src/cargo/core/compiler/unit_graph.rs
index 2db9015058e..350c040abad 100644
--- a/src/cargo/core/compiler/unit_graph.rs
+++ b/src/cargo/core/compiler/unit_graph.rs
@@ -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)?;
- write!(lock, "\n")?;
+ writeln!(lock)?;
Ok(())
}
diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs
index c5c15a4a8b0..646708e5901 100644
--- a/src/cargo/core/manifest.rs
+++ b/src/cargo/core/manifest.rs
@@ -669,7 +669,7 @@ impl Target {
.set_name(name)
.set_doctest(true)
.set_doc(true);
- return target;
+ target
}
pub fn bin_target(
@@ -684,7 +684,7 @@ impl Target {
.set_name(name)
.set_required_features(required_features)
.set_doc(true);
- return target;
+ target
}
/// Builds a `Target` corresponding to the `build = "build.rs"` entry.
@@ -696,7 +696,7 @@ impl Target {
.set_for_host(true)
.set_benched(false)
.set_tested(false);
- return target;
+ target
}
pub fn metabuild_target(name: &str) -> Target {
@@ -707,7 +707,7 @@ impl Target {
.set_for_host(true)
.set_benched(false)
.set_tested(false);
- return target;
+ target
}
pub fn example_target(
@@ -733,7 +733,7 @@ impl Target {
.set_required_features(required_features)
.set_tested(false)
.set_benched(false);
- return target;
+ target
}
pub fn test_target(
@@ -748,7 +748,7 @@ impl Target {
.set_name(name)
.set_required_features(required_features)
.set_benched(false);
- return target;
+ target
}
pub fn bench_target(
@@ -763,7 +763,7 @@ impl Target {
.set_name(name)
.set_required_features(required_features)
.set_tested(false);
- return target;
+ target
}
pub fn name(&self) -> &str {
diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs
index 5b45a4bc501..8a05b4510d6 100644
--- a/src/cargo/core/registry.rs
+++ b/src/cargo/core/registry.rs
@@ -622,7 +622,7 @@ fn lock(
// Lock the summary's ID if possible
let summary = match pair {
- Some((precise, _)) => summary.override_id(precise.clone()),
+ Some((precise, _)) => summary.override_id(*precise),
None => summary,
};
summary.map_dependencies(|dep| {
diff --git a/src/cargo/core/resolver/conflict_cache.rs b/src/cargo/core/resolver/conflict_cache.rs
index c59e4117810..e0e64b96a5d 100644
--- a/src/cargo/core/resolver/conflict_cache.rs
+++ b/src/cargo/core/resolver/conflict_cache.rs
@@ -213,7 +213,7 @@ impl ConflictCache {
for c in con.keys() {
self.dep_from_pid
- .entry(c.clone())
+ .entry(*c)
.or_insert_with(HashSet::new)
.insert(dep.clone());
}
diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs
index 5fb4631952f..4d711498235 100644
--- a/src/cargo/core/resolver/encode.rs
+++ b/src/cargo/core/resolver/encode.rs
@@ -260,7 +260,7 @@ impl EncodableResolve {
let mut g = Graph::new();
for &(ref id, _) in live_pkgs.values() {
- g.add(id.clone());
+ g.add(*id);
}
for &(ref id, pkg) in live_pkgs.values() {
@@ -271,7 +271,7 @@ impl EncodableResolve {
for edge in deps.iter() {
if let Some(to_depend_on) = lookup_id(edge) {
- g.link(id.clone(), to_depend_on);
+ g.link(*id, to_depend_on);
}
}
}
@@ -282,7 +282,7 @@ impl EncodableResolve {
if let Some(ref replace) = pkg.replace {
assert!(pkg.dependencies.is_none());
if let Some(replace_id) = lookup_id(replace) {
- replacements.insert(id.clone(), replace_id);
+ replacements.insert(*id, replace_id);
}
}
}
diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs
index a5c94f73fa8..b386ee7f558 100644
--- a/src/cargo/lib.rs
+++ b/src/cargo/lib.rs
@@ -144,7 +144,7 @@ pub fn display_error(err: &Error, shell: &mut Shell) {
/// and context.
pub fn display_warning_with_error(warning: &str, err: &Error, shell: &mut Shell) {
drop(shell.warn(warning));
- drop(writeln!(shell.err(), ""));
+ drop(writeln!(shell.err()));
_display_error(err, shell, false);
}
diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs
index a21c9443eb0..1e0d855d0d1 100644
--- a/src/cargo/ops/cargo_fetch.rs
+++ b/src/cargo/ops/cargo_fetch.rs
@@ -39,7 +39,7 @@ pub fn fetch<'a>(
deps.iter().any(|d| {
// If no target was specified then all dependencies are
// fetched.
- if options.targets.len() == 0 {
+ if options.targets.is_empty() {
return true;
}
diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs
index d10a6a4f3c9..e6261d3a927 100644
--- a/src/cargo/ops/cargo_output_metadata.rs
+++ b/src/cargo/ops/cargo_output_metadata.rs
@@ -176,7 +176,7 @@ fn build_resolve_graph_r(
let deps: Vec = resolve
.deps(pkg_id)
.filter(|(_dep_id, deps)| {
- if requested_kinds == &[CompileKind::Host] {
+ if requested_kinds == [CompileKind::Host] {
true
} else {
requested_kinds.iter().any(|kind| {
diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs
index 4d47bc45342..570e6f522bd 100644
--- a/src/cargo/ops/registry.rs
+++ b/src/cargo/ops/registry.rs
@@ -378,7 +378,7 @@ fn registry(
token: token_config,
index: index_config,
} = registry_configuration(config, registry.clone())?;
- let opt_index = index_config.as_ref().or(index.as_ref());
+ let opt_index = index_config.as_ref().or_else(|| index.as_ref());
let sid = get_source_id(config, opt_index, registry.as_ref())?;
if !sid.is_remote_registry() {
bail!(
diff --git a/tests/testsuite/multitarget.rs b/tests/testsuite/multitarget.rs
index 1235642cb56..a72203c5779 100644
--- a/tests/testsuite/multitarget.rs
+++ b/tests/testsuite/multitarget.rs
@@ -35,7 +35,7 @@ fn simple_build() {
.masquerade_as_nightly_cargo()
.run();
- assert!(p.target_bin(&t1, "foo").is_file());
+ assert!(p.target_bin(t1, "foo").is_file());
assert!(p.target_bin(&t2, "foo").is_file());
}
From e94facea1544112562d3b2aacefd996dabb6fa7f Mon Sep 17 00:00:00 2001
From: Rain
Date: Fri, 1 May 2020 17:02:44 -0700
Subject: [PATCH 02/24] features: allow activated_features_unverified to
communicate not-present
I'm currently writing [`cargo-guppy`](https://github.com/facebookincubator/cargo-guppy), a toolkit for analyzing Rust dependency graphs. As part of that I'm writing some tests to compare guppy to `cargo`, to ensure that it produces the same results for the subset of analyses `cargo` can do.
While writing tests I found it useful to distinguish between packages that weren't present at all and packages that were activated but with no features. This commit adds that functionality to the `_unverified` method.
---
src/cargo/core/resolver/features.rs | 20 +++++++++-----------
src/cargo/ops/cargo_clean.rs | 5 +++--
src/cargo/ops/cargo_compile.rs | 5 ++++-
3 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs
index acbdec424b3..cea01d113a4 100644
--- a/src/cargo/core/resolver/features.rs
+++ b/src/cargo/core/resolver/features.rs
@@ -208,36 +208,34 @@ impl ResolvedFeatures {
pkg_id: PackageId,
features_for: FeaturesFor,
) -> Vec {
- self.activated_features_int(pkg_id, features_for, true)
+ self.activated_features_int(pkg_id, features_for)
+ .expect("activated_features for invalid package")
}
- /// Variant of `activated_features` that returns an empty Vec if this is
+ /// Variant of `activated_features` that returns `None` if this is
/// not a valid pkg_id/is_build combination. Used in places which do
/// not know which packages are activated (like `cargo clean`).
pub fn activated_features_unverified(
&self,
pkg_id: PackageId,
features_for: FeaturesFor,
- ) -> Vec {
- self.activated_features_int(pkg_id, features_for, false)
+ ) -> Option> {
+ self.activated_features_int(pkg_id, features_for).ok()
}
fn activated_features_int(
&self,
pkg_id: PackageId,
features_for: FeaturesFor,
- verify: bool,
- ) -> Vec {
+ ) -> CargoResult> {
if let Some(legacy) = &self.legacy {
- legacy.get(&pkg_id).map_or_else(Vec::new, |v| v.clone())
+ Ok(legacy.get(&pkg_id).map_or_else(Vec::new, |v| v.clone()))
} else {
let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep;
if let Some(fs) = self.activated_features.get(&(pkg_id, is_build)) {
- fs.iter().cloned().collect()
- } else if verify {
- panic!("features did not find {:?} {:?}", pkg_id, is_build)
+ Ok(fs.iter().cloned().collect())
} else {
- Vec::new()
+ anyhow::bail!("features did not find {:?} {:?}", pkg_id, is_build)
}
}
}
diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs
index beb9b03cd39..0a4166940e3 100644
--- a/src/cargo/ops/cargo_clean.rs
+++ b/src/cargo/ops/cargo_clean.rs
@@ -129,8 +129,9 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
// Use unverified here since this is being more
// exhaustive than what is actually needed.
let features_for = unit_for.map_to_features_for();
- let features =
- features.activated_features_unverified(pkg.package_id(), features_for);
+ let features = features
+ .activated_features_unverified(pkg.package_id(), features_for)
+ .unwrap_or_default();
units.push(interner.intern(
pkg, target, profile, *kind, *mode, features, /*is_std*/ false,
));
diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs
index 29e3abf018e..762da60b02b 100644
--- a/src/cargo/ops/cargo_compile.rs
+++ b/src/cargo/ops/cargo_compile.rs
@@ -978,7 +978,10 @@ pub fn resolve_all_features(
.proc_macro();
for dep in deps {
let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
- for feature in resolved_features.activated_features_unverified(dep_id, features_for) {
+ for feature in resolved_features
+ .activated_features_unverified(dep_id, features_for)
+ .unwrap_or_default()
+ {
features.insert(format!("{}/{}", dep.name_in_toml(), feature));
}
}
From 4fd483507a3e1471635a178a0eeddb7752b301bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kornel=20Lesin=CC=81ski?=
Date: Mon, 27 Apr 2020 20:17:36 +0100
Subject: [PATCH 03/24] Hint git-fetch-with-cli on git errors
---
src/cargo/sources/git/utils.rs | 39 ++++++++++++++--
tests/testsuite/git_auth.rs | 82 ++++++++++++++++++++++++++++++++++
2 files changed, 118 insertions(+), 3 deletions(-)
diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs
index 08efd065b4e..1d28d36ccfa 100644
--- a/src/cargo/sources/git/utils.rs
+++ b/src/cargo/sources/git/utils.rs
@@ -4,7 +4,7 @@ use crate::util::paths;
use crate::util::process_builder::process;
use crate::util::{network, Config, IntoUrl, Progress};
use curl::easy::{Easy, List};
-use git2::{self, ObjectType};
+use git2::{self, ErrorClass, ObjectType};
use log::{debug, info};
use serde::ser;
use serde::Serialize;
@@ -97,10 +97,43 @@ impl GitRemote {
reference: &GitReference,
cargo_config: &Config,
) -> CargoResult<(GitDatabase, GitRevision)> {
+ let format_error = |e: anyhow::Error, operation| {
+ let may_be_libgit_fault = e
+ .chain()
+ .filter_map(|e| e.downcast_ref::())
+ .any(|e| match e.class() {
+ ErrorClass::Net
+ | ErrorClass::Ssl
+ | ErrorClass::Submodule
+ | ErrorClass::FetchHead
+ | ErrorClass::Ssh
+ | ErrorClass::Callback
+ | ErrorClass::Http => true,
+ _ => false,
+ });
+ let uses_cli = cargo_config
+ .net_config()
+ .ok()
+ .and_then(|config| config.git_fetch_with_cli)
+ .unwrap_or(false);
+ let msg = if !uses_cli && may_be_libgit_fault {
+ format!(
+ r"failed to {} into: {}
+ If your environment requires git authentication or proxying, try enabling `git-fetch-with-cli`
+ https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli",
+ operation,
+ into.display()
+ )
+ } else {
+ format!("failed to {} into: {}", operation, into.display())
+ };
+ e.context(msg)
+ };
+
let mut repo_and_rev = None;
if let Ok(mut repo) = git2::Repository::open(into) {
self.fetch_into(&mut repo, cargo_config)
- .chain_err(|| format!("failed to fetch into {}", into.display()))?;
+ .map_err(|e| format_error(e, "fetch"))?;
if let Ok(rev) = reference.resolve(&repo) {
repo_and_rev = Some((repo, rev));
}
@@ -110,7 +143,7 @@ impl GitRemote {
None => {
let repo = self
.clone_into(into, cargo_config)
- .chain_err(|| format!("failed to clone into: {}", into.display()))?;
+ .map_err(|e| format_error(e, "clone"))?;
let rev = reference.resolve(&repo)?;
(repo, rev)
}
diff --git a/tests/testsuite/git_auth.rs b/tests/testsuite/git_auth.rs
index 258014cf653..5aea1a9c5c1 100644
--- a/tests/testsuite/git_auth.rs
+++ b/tests/testsuite/git_auth.rs
@@ -265,3 +265,85 @@ Caused by:
.run();
t.join().ok().unwrap();
}
+
+#[cargo_test]
+fn net_err_suggests_fetch_with_cli() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies]
+ foo = { git = "ssh://needs-proxy.invalid/git" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build -v")
+ .with_status(101)
+ .with_stderr(
+ "\
+[UPDATING] git repository `ssh://needs-proxy.invalid/git`
+warning: spurious network error[..]
+warning: spurious network error[..]
+[ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 [..]`
+
+Caused by:
+ failed to load source for dependency `foo`
+
+Caused by:
+ Unable to update ssh://needs-proxy.invalid/git
+
+Caused by:
+ failed to clone into: [..]
+ If your environment requires git authentication or proxying, try enabling `git-fetch-with-cli`
+ https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
+
+Caused by:
+ failed to resolve address for needs-proxy.invalid[..]
+",
+ )
+ .run();
+
+ p.change_file(
+ ".cargo/config",
+ "
+ [net]
+ git-fetch-with-cli = true
+ ",
+ );
+
+ p.cargo("build -v")
+ .with_status(101)
+ .with_stderr(
+ "\
+[UPDATING] git repository `ssh://needs-proxy.invalid/git`
+[RUNNING] `git fetch[..]
+[ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 [..]`
+
+Caused by:
+ failed to load source for dependency `foo`
+
+Caused by:
+ Unable to update ssh://needs-proxy.invalid/git
+
+Caused by:
+ failed to fetch into: [..]
+
+Caused by:
+ [..]process didn't exit successfully[..]
+--- stderr
+ssh: Could not resolve hostname[..]
+fatal: [..]
+
+Please make sure you have the correct access rights
+and the repository exists.
+[..]",
+ )
+ .run();
+}
From 1840d1631d225495228c387ae13932a8cb3c5f1a Mon Sep 17 00:00:00 2001
From: Alex Crichton
Date: Mon, 4 May 2020 10:53:18 -0700
Subject: [PATCH 04/24] Rename bitcode-in-rlib flag to embed-bitcode
This flag changed names in nightly, so let's rename it here in Cargo to
get our CI passing and enable the same wins for avoiding bitcode.
---
src/cargo/core/compiler/build_context/target_info.rs | 12 ++++++------
src/cargo/core/compiler/mod.rs | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs
index 28d9589e1ef..8258f4d8853 100644
--- a/src/cargo/core/compiler/build_context/target_info.rs
+++ b/src/cargo/core/compiler/build_context/target_info.rs
@@ -41,7 +41,7 @@ pub struct TargetInfo {
/// Extra flags to pass to `rustdoc`, see `env_args`.
pub rustdocflags: Vec,
/// Remove this when it hits stable (1.44)
- pub supports_bitcode_in_rlib: Option,
+ pub supports_embed_bitcode: Option,
}
/// Kind of each file generated by a Unit, part of `FileType`.
@@ -111,10 +111,10 @@ impl TargetInfo {
.args(&rustflags)
.env_remove("RUSTC_LOG");
- let mut bitcode_in_rlib_test = process.clone();
- bitcode_in_rlib_test.arg("-Cbitcode-in-rlib");
- let supports_bitcode_in_rlib = match kind {
- CompileKind::Host => Some(rustc.cached_output(&bitcode_in_rlib_test).is_ok()),
+ let mut embed_bitcode_test = process.clone();
+ embed_bitcode_test.arg("-Cembed-bitcode");
+ let supports_embed_bitcode = match kind {
+ CompileKind::Host => Some(rustc.cached_output(&embed_bitcode_test).is_ok()),
_ => None,
};
@@ -202,7 +202,7 @@ impl TargetInfo {
"RUSTDOCFLAGS",
)?,
cfg,
- supports_bitcode_in_rlib,
+ supports_embed_bitcode,
})
}
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index cce5301cd48..c5b09373e07 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -815,10 +815,10 @@ fn build_base_args(
.bcx
.target_data
.info(CompileKind::Host)
- .supports_bitcode_in_rlib
+ .supports_embed_bitcode
.unwrap()
{
- cmd.arg("-Cbitcode-in-rlib=no");
+ cmd.arg("-Cembed-bitcode=no");
}
}
}
From e97b36ab336440ddc641b23eaa84d49f70d0bc8c Mon Sep 17 00:00:00 2001
From: Bastian Kauschke
Date: Mon, 4 May 2020 21:12:02 +0200
Subject: [PATCH 05/24] double negation
---
src/cargo/util/toml/mod.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs
index b0295a7245e..77e0dbb8385 100644
--- a/src/cargo/util/toml/mod.rs
+++ b/src/cargo/util/toml/mod.rs
@@ -78,7 +78,7 @@ fn do_read_manifest(
let (mut manifest, paths) =
TomlManifest::to_real_manifest(&manifest, source_id, package_root, config)?;
add_unused(manifest.warnings_mut());
- if !manifest.targets().iter().any(|t| !t.is_custom_build()) {
+ if manifest.targets().iter().all(|t| t.is_custom_build()) {
bail!(
"no targets specified in the manifest\n \
either src/lib.rs, src/main.rs, a [lib] section, or \
From e2219254695b290ff1ef9d8c2015939af9a9ab1f Mon Sep 17 00:00:00 2001
From: Alex Crichton
Date: Thu, 30 Apr 2020 12:24:50 -0700
Subject: [PATCH 06/24] Don't force rustc to do codegen for LTO builds
This commit updates Cargo's implementation of LTO builds to do less work
and hopefully be faster when doing a cold build. Additionaly this should
save space on disk! The general idea is that the compiler does not need
object files if it's only going to perform LTO with some artifacts. In
this case all rustc needs to do is load bitcode from dependencies. This
means that if you're doing an LTO build generating object code for
intermediate dependencies is just wasted time!
Here Cargo is updated with more intrusive knowledge about LTO. Cargo
will now analyze the dependency graph to figure out which crates are
being compiled with LTO, and then it will figure out which dependencies
only need to have bitcode in them. Pure-bitcode artifacts are emitted
with the `-Clinker-plugin-lto` flag. Some artifacts are still used in
multiple scenarios (such as those shared between build scripts and final
artifacts), so those are not compiled with `-Clinker-plugin-lto` since
the linker is not guaranteed to know how to perform LTO.
This functionality was recently implemented in rust-lang/rust#71528
where rustc is now capable of reading bitcode from `-Clinker-plugin-lto`
rlibs. Previously rustc would only read its own format of bitcode, but
this has now been extended! This support is now on nightly, hence this
PR.
---
.../compiler/build_context/target_info.rs | 2 +-
src/cargo/core/compiler/context/mod.rs | 8 +
src/cargo/core/compiler/fingerprint.rs | 8 +-
src/cargo/core/compiler/lto.rs | 116 ++++++++
src/cargo/core/compiler/mod.rs | 41 +--
tests/testsuite/lto.rs | 254 ++++++++++++++++++
6 files changed, 410 insertions(+), 19 deletions(-)
create mode 100644 src/cargo/core/compiler/lto.rs
diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs
index 8d9274c0bda..17ea6c6411e 100644
--- a/src/cargo/core/compiler/build_context/target_info.rs
+++ b/src/cargo/core/compiler/build_context/target_info.rs
@@ -40,7 +40,7 @@ pub struct TargetInfo {
pub rustflags: Vec,
/// Extra flags to pass to `rustdoc`, see `env_args`.
pub rustdocflags: Vec,
- /// Remove this when it hits stable (1.44)
+ /// Remove this when it hits stable (1.45)
pub supports_embed_bitcode: Option,
}
diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs
index 0855fc09ce7..ae5c1382947 100644
--- a/src/cargo/core/compiler/context/mod.rs
+++ b/src/cargo/core/compiler/context/mod.rs
@@ -16,6 +16,7 @@ use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts};
use super::fingerprint::Fingerprint;
use super::job_queue::JobQueue;
use super::layout::Layout;
+use super::lto::Lto;
use super::unit_graph::UnitDep;
use super::{BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor};
@@ -72,6 +73,11 @@ pub struct Context<'a, 'cfg> {
/// jobserver clients for each Unit (which eventually becomes a rustc
/// process).
pub rustc_clients: HashMap,
+
+ /// Map of the LTO-status of each unit. This indicates what sort of
+ /// compilation is happening (only object, only bitcode, both, etc), and is
+ /// precalculated early on.
+ pub lto: HashMap,
}
impl<'a, 'cfg> Context<'a, 'cfg> {
@@ -111,6 +117,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
rmeta_required: HashSet::new(),
rustc_clients: HashMap::new(),
pipelining,
+ lto: HashMap::new(),
})
}
@@ -123,6 +130,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
self.prepare_units()?;
self.prepare()?;
custom_build::build_map(&mut self)?;
+ super::lto::generate(&mut self)?;
self.check_collistions()?;
for unit in &self.bcx.roots {
diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs
index 89263cc7c9b..598abdec8e1 100644
--- a/src/cargo/core/compiler/fingerprint.rs
+++ b/src/cargo/core/compiler/fingerprint.rs
@@ -71,6 +71,7 @@
//! -C incremental=… flag | ✓ |
//! mtime of sources | ✓[^3] |
//! RUSTFLAGS/RUSTDOCFLAGS | ✓ |
+//! LTO flags | ✓ |
//! is_std | | ✓
//!
//! [^1]: Build script and bin dependencies are not included.
@@ -1241,7 +1242,12 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult),
+
+ /// This rustc invocation only needs to produce bitcode, there's no need to
+ /// produce object files, so we can pass `-Clinker-plugin-lto`
+ OnlyBitcode,
+
+ /// This rustc invocation needs to embed bitcode in object files. This means
+ /// that object files may be used for a normal link, and the crate may be
+ /// loaded for LTO later, so both are required.
+ EmbedBitcode,
+
+ /// Nothing related to LTO is required of this compilation.
+ None,
+}
+
+pub fn generate(cx: &mut Context<'_, '_>) -> CargoResult<()> {
+ let mut map = HashMap::new();
+ for unit in cx.bcx.roots.iter() {
+ calculate(cx, &mut map, unit, false)?;
+ }
+ cx.lto = map;
+ Ok(())
+}
+
+fn calculate(
+ cx: &Context<'_, '_>,
+ map: &mut HashMap,
+ unit: &Unit,
+ require_bitcode: bool,
+) -> CargoResult<()> {
+ let (lto, require_bitcode_for_deps) = if unit.target.for_host() {
+ // Disable LTO for host builds since we only really want to perform LTO
+ // for the final binary, and LTO on plugins/build scripts/proc macros is
+ // largely not desired.
+ (Lto::None, false)
+ } else if unit.target.can_lto() {
+ // Otherwise if this target can perform LTO then we're going to read the
+ // LTO value out of the profile.
+ assert!(!require_bitcode); // can't depend on binaries/staticlib/etc
+ match unit.profile.lto {
+ profiles::Lto::Named(s) => match s.as_str() {
+ "n" | "no" | "off" => (Lto::Run(Some(s)), false),
+ _ => (Lto::Run(Some(s)), true),
+ },
+ profiles::Lto::Bool(true) => (Lto::Run(None), true),
+ profiles::Lto::Bool(false) => (Lto::None, false),
+ }
+ } else if require_bitcode {
+ // Otherwise we're a dependency of something, an rlib. This means that
+ // if our parent required bitcode of some kind then we need to generate
+ // bitcode.
+ (Lto::OnlyBitcode, true)
+ } else {
+ (Lto::None, false)
+ };
+
+ match map.entry(unit.clone()) {
+ // If we haven't seen this unit before then insert our value and keep
+ // going.
+ Entry::Vacant(v) => {
+ v.insert(lto);
+ }
+
+ Entry::Occupied(mut v) => {
+ let result = match (lto, v.get()) {
+ // Targets which execute LTO cannot be depended on, so these
+ // units should only show up once in the dependency graph, so we
+ // should never hit this case.
+ (Lto::Run(_), _) | (_, Lto::Run(_)) => {
+ unreachable!("lto-able targets shouldn't show up twice")
+ }
+
+ // If we calculated the same thing as before then we can bail
+ // out quickly.
+ (Lto::OnlyBitcode, Lto::OnlyBitcode) | (Lto::None, Lto::None) => return Ok(()),
+
+ // This is where the trickiness happens. This unit needs
+ // bitcode and the previously calculated value for this unit
+ // says it didn't need bitcode (or vice versa). This means that
+ // we're a shared dependency between some targets which require
+ // LTO and some which don't. This means that instead of being
+ // either only-objects or only-bitcode we have to embed both in
+ // rlibs (used for different compilations), so we switch to
+ // embedding bitcode.
+ (Lto::OnlyBitcode, Lto::None)
+ | (Lto::OnlyBitcode, Lto::EmbedBitcode)
+ | (Lto::None, Lto::OnlyBitcode)
+ | (Lto::None, Lto::EmbedBitcode) => Lto::EmbedBitcode,
+
+ // Currently this variant is never calculated above, so no need
+ // to handle this case.
+ (Lto::EmbedBitcode, _) => unreachable!(),
+ };
+ // No need to recurse if we calculated the same value as before.
+ if result == *v.get() {
+ return Ok(());
+ }
+ v.insert(result);
+ }
+ }
+
+ for dep in cx.unit_deps(unit) {
+ calculate(cx, map, &dep.unit, require_bitcode_for_deps)?;
+ }
+ Ok(())
+}
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index c5b09373e07..dd2dbea39ea 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -10,6 +10,7 @@ mod job;
mod job_queue;
mod layout;
mod links;
+mod lto;
mod output_depinfo;
pub mod standard_lib;
mod timings;
@@ -42,7 +43,7 @@ use self::output_depinfo::output_depinfo;
use self::unit_graph::UnitDep;
pub use crate::core::compiler::unit::{Unit, UnitInterner};
use crate::core::manifest::TargetSourcePath;
-use crate::core::profiles::{Lto, PanicStrategy, Profile};
+use crate::core::profiles::{PanicStrategy, Profile};
use crate::core::{Edition, Feature, InternedString, PackageId, Target};
use crate::util::errors::{self, CargoResult, CargoResultExt, ProcessError, VerboseError};
use crate::util::machine_message::Message;
@@ -740,7 +741,6 @@ fn build_base_args(
let bcx = cx.bcx;
let Profile {
ref opt_level,
- ref lto,
codegen_units,
debuginfo,
debug_assertions,
@@ -793,24 +793,31 @@ fn build_base_args(
cmd.arg("-C").arg(format!("panic={}", panic));
}
- // Disable LTO for host builds as prefer_dynamic and it are mutually
- // exclusive.
- let lto_possible = unit.target.can_lto() && !unit.target.for_host();
- match lto {
- Lto::Bool(true) => {
- if lto_possible {
- cmd.args(&["-C", "lto"]);
- }
+ match cx.lto[&unit] {
+ lto::Lto::Run(None) => {
+ cmd.arg("-C").arg("lto");
+ }
+ lto::Lto::Run(Some(s)) => {
+ cmd.arg("-C").arg(format!("lto={}", s));
}
- Lto::Named(s) => {
- if lto_possible {
- cmd.arg("-C").arg(format!("lto={}", s));
+ lto::Lto::EmbedBitcode => {} // this is rustc's default
+ lto::Lto::OnlyBitcode => {
+ // Note that this compiler flag, like the one below, is just an
+ // optimization in terms of build time. If we don't pass it then
+ // both object code and bitcode will show up. This is lagely just
+ // compat until the feature lands on stable and we can remove the
+ // conditional branch.
+ if cx
+ .bcx
+ .target_data
+ .info(CompileKind::Host)
+ .supports_embed_bitcode
+ .unwrap()
+ {
+ cmd.arg("-Clinker-plugin-lto");
}
}
- // If LTO isn't being enabled then there's no need for bitcode to be
- // present in the intermediate artifacts, so shave off some build time
- // by removing it.
- Lto::Bool(false) => {
+ lto::Lto::None => {
if cx
.bcx
.target_data
diff --git a/tests/testsuite/lto.rs b/tests/testsuite/lto.rs
index 4d77b2186e2..a671e27e04d 100644
--- a/tests/testsuite/lto.rs
+++ b/tests/testsuite/lto.rs
@@ -3,6 +3,10 @@ use cargo_test_support::registry::Package;
#[cargo_test]
fn with_deps() {
+ if !cargo_test_support::is_nightly() {
+ return;
+ }
+
Package::new("bar", "0.0.1").publish();
let p = project()
@@ -22,5 +26,255 @@ fn with_deps() {
)
.file("src/main.rs", "extern crate bar; fn main() {}")
.build();
+ p.cargo("build -v --release")
+ .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-Clinker-plugin-lto[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
+ .run();
+}
+
+#[cargo_test]
+fn shared_deps() {
+ if !cargo_test_support::is_nightly() {
+ return;
+ }
+
+ Package::new("bar", "0.0.1").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [dependencies]
+ bar = "*"
+
+ [build-dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("build.rs", "extern crate bar; fn main() {}")
+ .file("src/main.rs", "extern crate bar; fn main() {}")
+ .build();
+ p.cargo("build -v --release")
+ .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
+ .run();
+}
+
+#[cargo_test]
+fn build_dep_not_ltod() {
+ if !cargo_test_support::is_nightly() {
+ return;
+ }
+
+ Package::new("bar", "0.0.1").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [build-dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("build.rs", "extern crate bar; fn main() {}")
+ .file("src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("build -v --release")
+ .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-Cembed-bitcode=no[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
+ .run();
+}
+
+#[cargo_test]
+fn complicated() {
+ if !cargo_test_support::is_nightly() {
+ return;
+ }
+
+ Package::new("dep-shared", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-normal2", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-normal", "0.0.1")
+ .dep("dep-shared", "*")
+ .dep("dep-normal2", "*")
+ .file(
+ "src/lib.rs",
+ "
+ pub fn foo() {
+ dep_shared::foo();
+ dep_normal2::foo();
+ }
+ ",
+ )
+ .publish();
+ Package::new("dep-build2", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-build", "0.0.1")
+ .dep("dep-shared", "*")
+ .dep("dep-build2", "*")
+ .file(
+ "src/lib.rs",
+ "
+ pub fn foo() {
+ dep_shared::foo();
+ dep_build2::foo();
+ }
+ ",
+ )
+ .publish();
+ Package::new("dep-proc-macro2", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-proc-macro", "0.0.1")
+ .proc_macro(true)
+ .dep("dep-shared", "*")
+ .dep("dep-proc-macro2", "*")
+ .file(
+ "src/lib.rs",
+ "
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[proc_macro_attribute]
+ pub fn foo(_: TokenStream, a: TokenStream) -> TokenStream {
+ dep_shared::foo();
+ dep_proc_macro2::foo();
+ a
+ }
+ ",
+ )
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [lib]
+ crate-type = ['cdylib', 'staticlib']
+
+ [dependencies]
+ dep-normal = "*"
+ dep-proc-macro = "*"
+
+ [build-dependencies]
+ dep-build = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("build.rs", "fn main() { dep_build::foo() }")
+ .file(
+ "src/main.rs",
+ "#[dep_proc_macro::foo] fn main() { dep_normal::foo() }",
+ )
+ .file(
+ "src/lib.rs",
+ "#[dep_proc_macro::foo] pub fn foo() { dep_normal::foo() }",
+ )
+ .build();
+ p.cargo("build -v --release")
+ // normal deps and their transitive dependencies do not need object
+ // code, so they should have linker-plugin-lto specified
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_normal2 [..]-Clinker-plugin-lto[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_normal [..]-Clinker-plugin-lto[..]`")
+ // build dependencies and their transitive deps don't need any bitcode,
+ // so embedding should be turned off
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_build2 [..]-Cembed-bitcode=no[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_build [..]-Cembed-bitcode=no[..]`")
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name build_script_build [..]-Cembed-bitcode=no[..]`",
+ )
+ // proc macro deps are the same as build deps here
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name dep_proc_macro2 [..]-Cembed-bitcode=no[..]`",
+ )
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name dep_proc_macro [..]-Cembed-bitcode=no[..]`",
+ )
+ .with_stderr_contains("[..]`rustc[..]--crate-name test [..]--crate-type bin[..]-C lto[..]`")
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name test [..]--crate-type cdylib[..]-C lto[..]`",
+ )
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_shared [..]`")
+ .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C lto[..]")
+ .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-Clinker-plugin-lto[..]")
+ .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-Cembed-bitcode[..]")
+ .run();
+}
+
+#[cargo_test]
+fn off_in_manifest_works() {
+ if !cargo_test_support::is_nightly() {
+ return;
+ }
+
+ Package::new("bar", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = "off"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::foo() }")
+ .build();
+ p.cargo("build -v --release").run();
+}
+
+#[cargo_test]
+fn between_builds() {
+ if !cargo_test_support::is_nightly() {
+ return;
+ }
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("src/lib.rs", "pub fn foo() {}")
+ .file("src/main.rs", "fn main() { test::foo() }")
+ .build();
+ p.cargo("build -v --release --lib").run();
p.cargo("build -v --release").run();
}
From 7a06be149c114bffb7c65732eca09f3060afd96f Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Sun, 26 Apr 2020 13:20:44 -0700
Subject: [PATCH 07/24] Add CrateType to replace LibKind.
---
.../compiler/build_context/target_info.rs | 40 +++---
src/cargo/core/compiler/compilation.rs | 2 +-
.../compiler/context/compilation_files.rs | 25 ++--
src/cargo/core/compiler/custom_build.rs | 2 +-
src/cargo/core/compiler/mod.rs | 14 ++-
src/cargo/core/compiler/unit.rs | 8 +-
src/cargo/core/compiler/unit_dependencies.rs | 4 +-
src/cargo/core/manifest.rs | 118 ++++--------------
src/cargo/core/mod.rs | 2 +-
src/cargo/ops/cargo_compile.rs | 4 +-
src/cargo/util/toml/mod.rs | 2 +-
src/cargo/util/toml/targets.rs | 13 +-
12 files changed, 89 insertions(+), 145 deletions(-)
diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs
index 17ea6c6411e..698a9e4080f 100644
--- a/src/cargo/core/compiler/build_context/target_info.rs
+++ b/src/cargo/core/compiler/build_context/target_info.rs
@@ -1,4 +1,4 @@
-use crate::core::compiler::{BuildOutput, CompileKind, CompileTarget};
+use crate::core::compiler::{BuildOutput, CompileKind, CompileTarget, CrateType};
use crate::core::{Dependency, TargetKind, Workspace};
use crate::util::config::{Config, StringList, TargetConfig};
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder, Rustc};
@@ -25,7 +25,7 @@ pub struct TargetInfo {
/// `Some((prefix, suffix))`, for example `libcargo.so` would be
/// `Some(("lib", ".so")). The value is `None` if the crate type is not
/// supported.
- crate_types: RefCell>>,
+ crate_types: RefCell>>,
/// `cfg` information extracted from `rustc --print=cfg`.
cfg: Vec,
/// Path to the sysroot.
@@ -123,10 +123,16 @@ impl TargetInfo {
}
let crate_type_process = process.clone();
- const KNOWN_CRATE_TYPES: &[&str] =
- &["bin", "rlib", "dylib", "cdylib", "staticlib", "proc-macro"];
+ const KNOWN_CRATE_TYPES: &[CrateType] = &[
+ CrateType::Bin,
+ CrateType::Rlib,
+ CrateType::Dylib,
+ CrateType::Cdylib,
+ CrateType::Staticlib,
+ CrateType::ProcMacro,
+ ];
for crate_type in KNOWN_CRATE_TYPES.iter() {
- process.arg("--crate-type").arg(crate_type);
+ process.arg("--crate-type").arg(crate_type.as_str());
}
process.arg("--print=sysroot");
@@ -140,7 +146,7 @@ impl TargetInfo {
let mut map = HashMap::new();
for crate_type in KNOWN_CRATE_TYPES {
let out = parse_crate_type(crate_type, &process, &output, &error, &mut lines)?;
- map.insert(crate_type.to_string(), out);
+ map.insert(crate_type.clone(), out);
}
let line = match lines.next() {
@@ -228,13 +234,19 @@ impl TargetInfo {
/// Returns `None` if the target does not support the given crate type.
pub fn file_types(
&self,
- crate_type: &str,
+ crate_type: &CrateType,
flavor: FileFlavor,
kind: &TargetKind,
target_triple: &str,
) -> CargoResult
+
+
+
+
Note: The
+#[bench] attribute
+is currently unstable and only available on the
+nightly channel.
+There are some packages available on
+crates.io that may help with
+running benchmarks on the stable channel, such as
+Criterion.
+
+
+
diff --git a/src/etc/man/cargo-bench.1 b/src/etc/man/cargo-bench.1
index 708d2066a8b..442f164d893 100644
--- a/src/etc/man/cargo-bench.1
+++ b/src/etc/man/cargo-bench.1
@@ -2,12 +2,12 @@
.\" Title: cargo-bench
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.10
-.\" Date: 2020-02-06
+.\" Date: 2020-05-08
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
-.TH "CARGO\-BENCH" "1" "2020-02-06" "\ \&" "\ \&"
+.TH "CARGO\-BENCH" "1" "2020-05-08" "\ \&" "\ \&"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -59,6 +59,22 @@ the test harness to tell it to run only benchmarks.
The libtest harness may be disabled by setting \fBharness = false\fP in the target
manifest settings, in which case your code will need to provide its own \fBmain\fP
function to handle running benchmarks.
+.RS 3
+.ll -.6i
+.sp
+\fBNote\fP: The
+\c
+.URL "https://doc.rust\-lang.org/nightly/unstable\-book/library\-features/test.html" "\fB#[bench]\fP attribute"
+is currently unstable and only available on the
+.URL "https://doc.rust\-lang.org/book/appendix\-07\-nightly\-rust.html" "nightly channel" "."
+There are some packages available on
+.URL "https://crates.io/keywords/benchmark" "crates.io" " "
+that may help with
+running benchmarks on the stable channel, such as
+.URL "https://crates.io/crates/criterion" "Criterion" "."
+.br
+.RE
+.ll
.SH "OPTIONS"
.SS "Benchmark Options"
.sp
From 63ffc6ae883f366ae008148c8e6fe93bbbd3a3b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20Kr=C3=BCger?=
Date: Fri, 8 May 2020 23:16:30 +0200
Subject: [PATCH 17/24] more clippy fixes
---
src/cargo/core/compiler/build_context/target_info.rs | 6 +++---
src/cargo/core/compiler/mod.rs | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs
index d3bab3365bf..ab3dc1c26a9 100644
--- a/src/cargo/core/compiler/build_context/target_info.rs
+++ b/src/cargo/core/compiler/build_context/target_info.rs
@@ -375,7 +375,7 @@ impl TargetInfo {
suffix,
prefix: prefix.clone(),
flavor: FileFlavor::DebugInfo,
- crate_type: Some(crate_type.clone()),
+ crate_type: Some(crate_type),
// macOS tools like lldb use all sorts of magic to locate
// dSYM files. See https://lldb.llvm.org/use/symbols.html
// for some details. It seems like a `.dSYM` located next
@@ -389,7 +389,7 @@ impl TargetInfo {
suffix: ".pdb".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::DebugInfo,
- crate_type: Some(crate_type.clone()),
+ crate_type: Some(crate_type),
// The absolute path to the pdb file is embedded in the
// executable. If the exe/pdb pair is moved to another
// machine, then debuggers will look in the same directory
@@ -466,7 +466,7 @@ impl TargetInfo {
} else {
FileFlavor::Normal
};
- let file_types = self.file_types(&crate_type, flavor, target_triple)?;
+ let file_types = self.file_types(crate_type, flavor, target_triple)?;
match file_types {
Some(types) => {
result.extend(types);
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index 93922ea7e0e..aaa7c7ef591 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -777,7 +777,7 @@ fn build_base_args(
cmd.arg("-C").arg(format!("panic={}", panic));
}
- match cx.lto[&unit] {
+ match cx.lto[unit] {
lto::Lto::Run(None) => {
cmd.arg("-C").arg("lto");
}
From 67b10f745d10c1de90c307a31910a0bb2fe533f8 Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Mon, 11 May 2020 13:45:18 -0700
Subject: [PATCH 18/24] Move SipHasher to an isolated module.
---
.../compiler/context/compilation_files.rs | 8 +++----
src/cargo/core/compiler/context/mod.rs | 1 -
src/cargo/util/hasher.rs | 24 +++++++++++++++++++
src/cargo/util/hex.rs | 9 ++++---
src/cargo/util/mod.rs | 2 ++
src/cargo/util/rustc.rs | 10 ++++----
6 files changed, 38 insertions(+), 16 deletions(-)
create mode 100644 src/cargo/util/hasher.rs
diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs
index aef7e69dc28..04d75fe0ac5 100644
--- a/src/cargo/core/compiler/context/compilation_files.rs
+++ b/src/cargo/core/compiler/context/compilation_files.rs
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::env;
use std::fmt;
-use std::hash::{Hash, Hasher, SipHasher};
+use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::sync::Arc;
@@ -11,7 +11,7 @@ use log::info;
use super::{BuildContext, CompileKind, Context, FileFlavor, Layout};
use crate::core::compiler::{CompileMode, CompileTarget, CrateType, FileType, Unit};
use crate::core::{Target, TargetKind, Workspace};
-use crate::util::{self, CargoResult};
+use crate::util::{self, CargoResult, StableHasher};
/// The `Metadata` is a hash used to make unique file names for each unit in a
/// build. It is also use for symbol mangling.
@@ -481,7 +481,7 @@ fn compute_metadata(
if !should_use_metadata(bcx, unit) {
return None;
}
- let mut hasher = SipHasher::new();
+ let mut hasher = StableHasher::new();
// This is a generic version number that can be changed to make
// backwards-incompatible changes to any file structures in the output
@@ -556,7 +556,7 @@ fn compute_metadata(
Some(Metadata(hasher.finish()))
}
-fn hash_rustc_version(bcx: &BuildContext<'_, '_>, hasher: &mut SipHasher) {
+fn hash_rustc_version(bcx: &BuildContext<'_, '_>, hasher: &mut StableHasher) {
let vers = &bcx.rustc().version;
if vers.pre.is_empty() || bcx.config.cli_unstable().separate_nightlies {
// For stable, keep the artifacts separate. This helps if someone is
diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs
index 60f2b8af34d..fbba329c2e1 100644
--- a/src/cargo/core/compiler/context/mod.rs
+++ b/src/cargo/core/compiler/context/mod.rs
@@ -1,4 +1,3 @@
-#![allow(deprecated)]
use std::collections::{BTreeSet, HashMap, HashSet};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
diff --git a/src/cargo/util/hasher.rs b/src/cargo/util/hasher.rs
new file mode 100644
index 00000000000..01e15ae2c04
--- /dev/null
+++ b/src/cargo/util/hasher.rs
@@ -0,0 +1,24 @@
+//! Implementation of a hasher that produces the same values across releases.
+//!
+//! The hasher should be fast and have a low chance of collisions (but is not
+//! sufficient for cryptographic purposes).
+#![allow(deprecated)]
+
+use std::hash::{Hasher, SipHasher};
+
+pub struct StableHasher(SipHasher);
+
+impl StableHasher {
+ pub fn new() -> StableHasher {
+ StableHasher(SipHasher::new())
+ }
+}
+
+impl Hasher for StableHasher {
+ fn finish(&self) -> u64 {
+ self.0.finish()
+ }
+ fn write(&mut self, bytes: &[u8]) {
+ self.0.write(bytes)
+ }
+}
diff --git a/src/cargo/util/hex.rs b/src/cargo/util/hex.rs
index baeb34781ec..0263e7a2d9a 100644
--- a/src/cargo/util/hex.rs
+++ b/src/cargo/util/hex.rs
@@ -1,7 +1,6 @@
-#![allow(deprecated)]
-
+use super::StableHasher;
use std::fs::File;
-use std::hash::{Hash, Hasher, SipHasher};
+use std::hash::{Hash, Hasher};
use std::io::Read;
pub fn to_hex(num: u64) -> String {
@@ -18,13 +17,13 @@ pub fn to_hex(num: u64) -> String {
}
pub fn hash_u64(hashable: H) -> u64 {
- let mut hasher = SipHasher::new();
+ let mut hasher = StableHasher::new();
hashable.hash(&mut hasher);
hasher.finish()
}
pub fn hash_u64_file(mut file: &File) -> std::io::Result {
- let mut hasher = SipHasher::new_with_keys(0, 0);
+ let mut hasher = StableHasher::new();
let mut buf = [0; 64 * 1024];
loop {
let n = file.read(&mut buf)?;
diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs
index 45e44ba61cf..7f2ba2697e4 100644
--- a/src/cargo/util/mod.rs
+++ b/src/cargo/util/mod.rs
@@ -9,6 +9,7 @@ pub use self::errors::{CargoResult, CargoResultExt, CliResult, Test};
pub use self::errors::{CargoTestError, CliError, ProcessError};
pub use self::flock::{FileLock, Filesystem};
pub use self::graph::Graph;
+pub use self::hasher::StableHasher;
pub use self::hex::{hash_u64, short_hash, to_hex};
pub use self::into_url::IntoUrl;
pub use self::into_url_with_base::IntoUrlWithBase;
@@ -39,6 +40,7 @@ pub mod diagnostic_server;
pub mod errors;
mod flock;
pub mod graph;
+mod hasher;
pub mod hex;
pub mod important_paths;
pub mod into_url;
diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs
index eb2176b7b9e..2ab08fec6cc 100644
--- a/src/cargo/util/rustc.rs
+++ b/src/cargo/util/rustc.rs
@@ -1,8 +1,6 @@
-#![allow(deprecated)] // for SipHasher
-
use std::collections::hash_map::{Entry, HashMap};
use std::env;
-use std::hash::{Hash, Hasher, SipHasher};
+use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
@@ -11,7 +9,7 @@ use serde::{Deserialize, Serialize};
use crate::core::InternedString;
use crate::util::paths;
-use crate::util::{self, profile, CargoResult, CargoResultExt, ProcessBuilder};
+use crate::util::{self, profile, CargoResult, CargoResultExt, ProcessBuilder, StableHasher};
/// Information on the `rustc` executable
#[derive(Debug)]
@@ -222,7 +220,7 @@ impl Drop for Cache {
}
fn rustc_fingerprint(path: &Path, rustup_rustc: &Path) -> CargoResult {
- let mut hasher = SipHasher::new();
+ let mut hasher = StableHasher::new();
let path = paths::resolve_executable(path)?;
path.hash(&mut hasher);
@@ -266,7 +264,7 @@ fn rustc_fingerprint(path: &Path, rustup_rustc: &Path) -> CargoResult {
}
fn process_fingerprint(cmd: &ProcessBuilder) -> u64 {
- let mut hasher = SipHasher::new();
+ let mut hasher = StableHasher::new();
cmd.get_args().hash(&mut hasher);
let mut env = cmd.get_envs().iter().collect::>();
env.sort_unstable();
From ce86e866e9802a961d43a7b38d52464ea711f752 Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Mon, 11 May 2020 13:07:00 -0700
Subject: [PATCH 19/24] Add context to some fs errors.
---
src/cargo/core/compiler/mod.rs | 4 ++--
src/cargo/core/compiler/output_depinfo.rs | 3 +--
src/cargo/core/compiler/timings.rs | 21 +++++++++++++----
src/cargo/ops/cargo_install.rs | 4 +---
src/cargo/ops/cargo_new.rs | 9 ++++----
src/cargo/ops/fix.rs | 8 +++----
src/cargo/ops/vendor.rs | 3 +--
src/cargo/sources/git/utils.rs | 3 +--
src/cargo/sources/registry/local.rs | 2 +-
src/cargo/sources/registry/mod.rs | 3 ++-
src/cargo/sources/registry/remote.rs | 5 ++--
src/cargo/util/config/mod.rs | 6 +++--
src/cargo/util/flock.rs | 3 +--
src/cargo/util/paths.rs | 28 +++++++++++++++++++++--
src/cargo/util/sha256.rs | 4 ++--
15 files changed, 69 insertions(+), 37 deletions(-)
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index aaa7c7ef591..8a22e4aa050 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -1144,7 +1144,7 @@ fn on_stderr_line(
// Check if caching is enabled.
if let Some((path, cell)) = &mut options.cache_cell {
// Cache the output, which will be replayed later when Fresh.
- let f = cell.try_borrow_mut_with(|| File::create(path))?;
+ let f = cell.try_borrow_mut_with(|| paths::create(path))?;
debug_assert!(!line.contains('\n'));
f.write_all(line.as_bytes())?;
f.write_all(&[b'\n'])?;
@@ -1332,7 +1332,7 @@ fn replay_output_cache(
// We sometimes have gigabytes of output from the compiler, so avoid
// loading it all into memory at once, as that can cause OOM where
// otherwise there would be none.
- let file = fs::File::open(&path)?;
+ let file = paths::open(&path)?;
let mut reader = std::io::BufReader::new(file);
let mut line = String::new();
loop {
diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs
index 3a9fac2c6d4..d017a81f700 100644
--- a/src/cargo/core/compiler/output_depinfo.rs
+++ b/src/cargo/core/compiler/output_depinfo.rs
@@ -23,7 +23,6 @@
//! be detected via changes to `Cargo.lock`.
use std::collections::{BTreeSet, HashSet};
-use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
@@ -148,7 +147,7 @@ pub fn output_depinfo(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<()>
}
// Otherwise write it all out
- let mut outfile = BufWriter::new(File::create(output_path)?);
+ let mut outfile = BufWriter::new(paths::create(output_path)?);
write!(outfile, "{}:", target_fn)?;
for dep in &deps {
write!(outfile, " {}", dep)?;
diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs
index 4704eb12813..20b4c7f600b 100644
--- a/src/cargo/core/compiler/timings.rs
+++ b/src/cargo/core/compiler/timings.rs
@@ -10,7 +10,6 @@ use crate::util::cpu::State;
use crate::util::machine_message::{self, Message};
use crate::util::{paths, CargoResult, Config};
use std::collections::HashMap;
-use std::fs::File;
use std::io::{BufWriter, Write};
use std::time::{Duration, Instant, SystemTime};
@@ -122,6 +121,17 @@ impl<'cfg> Timings<'cfg> {
.collect();
let start_str = humantime::format_rfc3339_seconds(SystemTime::now()).to_string();
let profile = bcx.build_config.requested_profile.to_string();
+ let last_cpu_state = if enabled {
+ match State::current() {
+ Ok(state) => Some(state),
+ Err(e) => {
+ log::info!("failed to get CPU state, CPU tracking disabled: {:?}", e);
+ None
+ }
+ }
+ } else {
+ None
+ };
Timings {
config: bcx.config,
@@ -138,7 +148,7 @@ impl<'cfg> Timings<'cfg> {
unit_times: Vec::new(),
active: HashMap::new(),
concurrency: Vec::new(),
- last_cpu_state: if enabled { State::current().ok() } else { None },
+ last_cpu_state,
last_cpu_recording: Instant::now(),
cpu_usage: Vec::new(),
}
@@ -287,7 +297,10 @@ impl<'cfg> Timings<'cfg> {
}
let current = match State::current() {
Ok(s) => s,
- Err(_) => return,
+ Err(e) => {
+ log::info!("failed to get CPU state: {:?}", e);
+ return;
+ }
};
let pct_idle = current.idle_since(prev);
*prev = current;
@@ -323,7 +336,7 @@ impl<'cfg> Timings<'cfg> {
let duration = d_as_f64(self.start.elapsed());
let timestamp = self.start_str.replace(&['-', ':'][..], "");
let filename = format!("cargo-timing-{}.html", timestamp);
- let mut f = BufWriter::new(File::create(&filename)?);
+ let mut f = BufWriter::new(paths::create(&filename)?);
let roots: Vec<&str> = self
.root_targets
.iter()
diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs
index fd343f85465..0406a56063d 100644
--- a/src/cargo/ops/cargo_install.rs
+++ b/src/cargo/ops/cargo_install.rs
@@ -374,9 +374,7 @@ fn install_one(
if !source_id.is_path() && fs::rename(src, &dst).is_ok() {
continue;
}
- fs::copy(src, &dst).chain_err(|| {
- format_err!("failed to copy `{}` to `{}`", src.display(), dst.display())
- })?;
+ paths::copy(src, &dst)?;
}
let (to_replace, to_install): (Vec<&str>, Vec<&str>) = binaries
diff --git a/src/cargo/ops/cargo_new.rs b/src/cargo/ops/cargo_new.rs
index bb6262c5f2e..fbd2a1637b3 100644
--- a/src/cargo/ops/cargo_new.rs
+++ b/src/cargo/ops/cargo_new.rs
@@ -9,7 +9,6 @@ use serde::Deserialize;
use std::collections::BTreeMap;
use std::env;
use std::fmt;
-use std::fs;
use std::io::{BufRead, BufReader, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;
@@ -562,10 +561,10 @@ fn write_ignore_file(
VersionControl::NoVcs => return Ok("".to_string()),
};
- let ignore: String = match fs::File::open(&fp_ignore) {
- Err(why) => match why.kind() {
- ErrorKind::NotFound => list.format_new(vcs),
- _ => return Err(anyhow::format_err!("{}", why)),
+ let ignore: String = match paths::open(&fp_ignore) {
+ Err(err) => match err.downcast_ref::() {
+ Some(io_err) if io_err.kind() == ErrorKind::NotFound => list.format_new(vcs),
+ _ => return Err(err),
},
Ok(file) => list.format_existing(BufReader::new(file), vcs),
};
diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs
index 1983e9387a8..b5702cb8afb 100644
--- a/src/cargo/ops/fix.rs
+++ b/src/cargo/ops/fix.rs
@@ -41,7 +41,6 @@
use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::OsString;
-use std::fs;
use std::path::{Path, PathBuf};
use std::process::{self, Command, ExitStatus};
use std::str;
@@ -55,7 +54,7 @@ use crate::core::Workspace;
use crate::ops::{self, CompileOptions};
use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer};
use crate::util::errors::CargoResult;
-use crate::util::{self, Config, ProcessBuilder};
+use crate::util::{self, paths, Config, ProcessBuilder};
use crate::util::{existing_vcs_repo, LockServer, LockServerClient};
const FIX_ENV: &str = "__CARGO_FIX_PLZ";
@@ -256,8 +255,7 @@ pub fn fix_maybe_exec_rustc() -> CargoResult {
if !output.status.success() {
if env::var_os(BROKEN_CODE_ENV).is_none() {
for (path, file) in fixes.files.iter() {
- fs::write(path, &file.original_code)
- .with_context(|| format!("failed to write file `{}`", path))?;
+ paths::write(path, &file.original_code)?;
}
}
log_failed_fix(&output.stderr)?;
@@ -517,7 +515,7 @@ fn rustfix_and_fix(
}
}
let new_code = fixed.finish()?;
- fs::write(&file, new_code).with_context(|| format!("failed to write file `{}`", file))?;
+ paths::write(&file, new_code)?;
}
Ok(())
diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs
index b3edd243c29..77792333461 100644
--- a/src/cargo/ops/vendor.rs
+++ b/src/cargo/ops/vendor.rs
@@ -330,8 +330,7 @@ fn cp_sources(
paths::create_dir_all(dst.parent().unwrap())?;
- fs::copy(&p, &dst)
- .chain_err(|| format!("failed to copy `{}` to `{}`", p.display(), dst.display()))?;
+ paths::copy(&p, &dst)?;
let cksum = Sha256::new().update_path(dst)?.finish_hex();
cksums.insert(relative.to_str().unwrap().replace("\\", "/"), cksum);
}
diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs
index 1d28d36ccfa..f8190a2ddae 100644
--- a/src/cargo/sources/git/utils.rs
+++ b/src/cargo/sources/git/utils.rs
@@ -10,7 +10,6 @@ use serde::ser;
use serde::Serialize;
use std::env;
use std::fmt;
-use std::fs::File;
use std::path::{Path, PathBuf};
use std::process::Command;
use url::Url;
@@ -363,7 +362,7 @@ impl<'a> GitCheckout<'a> {
info!("reset {} to {}", self.repo.path().display(), self.revision);
let object = self.repo.find_object(self.revision.0, None)?;
reset(&self.repo, &object, config)?;
- File::create(ok_file)?;
+ paths::create(ok_file)?;
Ok(())
}
diff --git a/src/cargo/sources/registry/local.rs b/src/cargo/sources/registry/local.rs
index c3ded744a96..d9c1221b04c 100644
--- a/src/cargo/sources/registry/local.rs
+++ b/src/cargo/sources/registry/local.rs
@@ -85,7 +85,7 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> {
// crate files here never change in that we're not the one writing them,
// so it's not our responsibility to synchronize access to them.
let path = self.root.join(&crate_file).into_path_unlocked();
- let mut crate_file = File::open(&path)?;
+ let mut crate_file = paths::open(&path)?;
// If we've already got an unpacked version of this crate, then skip the
// checksum below as it is in theory already verified.
diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs
index 4c340026cbf..d49b46318e9 100644
--- a/src/cargo/sources/registry/mod.rs
+++ b/src/cargo/sources/registry/mod.rs
@@ -468,7 +468,8 @@ impl<'cfg> RegistrySource<'cfg> {
.create(true)
.read(true)
.write(true)
- .open(&path)?;
+ .open(&path)
+ .chain_err(|| format!("failed to open `{}`", path.display()))?;
let gz = GzDecoder::new(tarball);
let mut tar = Archive::new(gz);
diff --git a/src/cargo/sources/registry/remote.rs b/src/cargo/sources/registry/remote.rs
index 52bb4ca7e4f..6dc4db00cc0 100644
--- a/src/cargo/sources/registry/remote.rs
+++ b/src/cargo/sources/registry/remote.rs
@@ -225,7 +225,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
// Create a dummy file to record the mtime for when we updated the
// index.
- File::create(&path.join(LAST_UPDATED_FILE))?;
+ paths::create(&path.join(LAST_UPDATED_FILE))?;
Ok(())
}
@@ -283,7 +283,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
.create(true)
.read(true)
.write(true)
- .open(&path)?;
+ .open(&path)
+ .chain_err(|| format!("failed to open `{}`", path.display()))?;
let meta = dst.metadata()?;
if meta.len() > 0 {
return Ok(dst);
diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs
index 260d011c851..51cba89f797 100644
--- a/src/cargo/util/config/mod.rs
+++ b/src/cargo/util/config/mod.rs
@@ -1622,9 +1622,11 @@ pub fn save_credentials(cfg: &Config, token: String, registry: Option) -
let contents = toml.to_string();
file.seek(SeekFrom::Start(0))?;
- file.write_all(contents.as_bytes())?;
+ file.write_all(contents.as_bytes())
+ .chain_err(|| format!("failed to write to `{}`", file.path().display()))?;
file.file().set_len(contents.len() as u64)?;
- set_permissions(file.file(), 0o600)?;
+ set_permissions(file.file(), 0o600)
+ .chain_err(|| format!("failed to set permissions of `{}`", file.path().display()))?;
return Ok(());
diff --git a/src/cargo/util/flock.rs b/src/cargo/util/flock.rs
index d31fa2ba8cd..2d732bb7ae3 100644
--- a/src/cargo/util/flock.rs
+++ b/src/cargo/util/flock.rs
@@ -148,8 +148,7 @@ impl Filesystem {
/// Handles errors where other Cargo processes are also attempting to
/// concurrently create this directory.
pub fn create_dir(&self) -> CargoResult<()> {
- paths::create_dir_all(&self.root)?;
- Ok(())
+ paths::create_dir_all(&self.root)
}
/// Returns an adaptor that can be used to print the path of this
diff --git a/src/cargo/util/paths.rs b/src/cargo/util/paths.rs
index 7b907128e13..c5a09fdae05 100644
--- a/src/cargo/util/paths.rs
+++ b/src/cargo/util/paths.rs
@@ -1,6 +1,6 @@
use std::env;
use std::ffi::{OsStr, OsString};
-use std::fs::{self, OpenOptions};
+use std::fs::{self, File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::iter;
@@ -162,6 +162,18 @@ pub fn append(path: &Path, contents: &[u8]) -> CargoResult<()> {
Ok(())
}
+/// Creates a new file.
+pub fn create>(path: P) -> CargoResult {
+ let path = path.as_ref();
+ File::create(path).chain_err(|| format!("failed to create file `{}`", path.display()))
+}
+
+/// Opens an existing file.
+pub fn open>(path: P) -> CargoResult {
+ let path = path.as_ref();
+ File::open(path).chain_err(|| format!("failed to open file `{}`", path.display()))
+}
+
pub fn mtime(path: &Path) -> CargoResult {
let meta = fs::metadata(path).chain_err(|| format!("failed to stat `{}`", path.display()))?;
Ok(FileTime::from_last_modification_time(&meta))
@@ -265,7 +277,11 @@ pub fn remove_dir_all>(p: P) -> CargoResult<()> {
}
fn _remove_dir_all(p: &Path) -> CargoResult<()> {
- if p.symlink_metadata()?.file_type().is_symlink() {
+ if p.symlink_metadata()
+ .chain_err(|| format!("could not get metadata for `{}` to remove", p.display()))?
+ .file_type()
+ .is_symlink()
+ {
return remove_file(p);
}
let entries = p
@@ -394,6 +410,14 @@ fn _link_or_copy(src: &Path, dst: &Path) -> CargoResult<()> {
Ok(())
}
+/// Copies a file from one location to another.
+pub fn copy, Q: AsRef>(from: P, to: Q) -> CargoResult {
+ let from = from.as_ref();
+ let to = to.as_ref();
+ fs::copy(from, to)
+ .chain_err(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))
+}
+
/// Changes the filesystem mtime (and atime if possible) for the given file.
///
/// This intentionally does not return an error, as this is sometimes not
diff --git a/src/cargo/util/sha256.rs b/src/cargo/util/sha256.rs
index f1b80594cfb..36bb2cef6d7 100644
--- a/src/cargo/util/sha256.rs
+++ b/src/cargo/util/sha256.rs
@@ -1,4 +1,4 @@
-use crate::util::{CargoResult, CargoResultExt};
+use crate::util::{paths, CargoResult, CargoResultExt};
use crypto_hash::{Algorithm, Hasher};
use std::fs::File;
use std::io::{self, Read, Write};
@@ -30,7 +30,7 @@ impl Sha256 {
pub fn update_path>(&mut self, path: P) -> CargoResult<&mut Sha256> {
let path = path.as_ref();
- let file = File::open(path)?;
+ let file = paths::open(path)?;
self.update_file(&file)
.chain_err(|| format!("failed to read `{}`", path.display()))?;
Ok(self)
From 09084a365f612192fa026754b448f5b0be231890 Mon Sep 17 00:00:00 2001
From: Jan-Erik Rediger
Date: Tue, 12 May 2020 16:45:55 +0200
Subject: [PATCH 20/24] Expand error message to explain that a string was found
With `opt-level = "3"` this previously said:
must be an integer, `z`, or `s`, but found: 3 for ...
The error message doesn't make that super clear.
This should now be a bit more clear.
Fixes #8234
---
src/cargo/util/toml/mod.rs | 2 +-
tests/testsuite/config.rs | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs
index c8cceecde87..c7858deffb1 100644
--- a/src/cargo/util/toml/mod.rs
+++ b/src/cargo/util/toml/mod.rs
@@ -321,7 +321,7 @@ impl<'de> de::Deserialize<'de> for TomlOptLevel {
} else {
Err(E::custom(format!(
"must be an integer, `z`, or `s`, \
- but found: {}",
+ but found the string: \"{}\"",
value
)))
}
diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs
index 46dbca69d50..4cbea32f54a 100644
--- a/tests/testsuite/config.rs
+++ b/tests/testsuite/config.rs
@@ -574,7 +574,7 @@ opt-level = 'foo'
error in [..]/.cargo/config: could not load config key `profile.dev.opt-level`
Caused by:
- must be an integer, `z`, or `s`, but found: foo",
+ must be an integer, `z`, or `s`, but found the string: \"foo\"",
);
let config = ConfigBuilder::new()
@@ -587,7 +587,7 @@ Caused by:
error in environment variable `CARGO_PROFILE_DEV_OPT_LEVEL`: could not load config key `profile.dev.opt-level`
Caused by:
- must be an integer, `z`, or `s`, but found: asdf",
+ must be an integer, `z`, or `s`, but found the string: \"asdf\"",
);
}
From 7274307af4eecd221c152ad9affd0a7eeaffab44 Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Tue, 12 May 2020 08:03:35 -0700
Subject: [PATCH 21/24] Ignore broken console output in some situations.
---
src/bin/cargo/cli.rs | 21 ++--
src/bin/cargo/commands/locate_project.rs | 4 +-
src/bin/cargo/commands/metadata.rs | 4 +-
src/bin/cargo/commands/pkgid.rs | 2 +-
src/bin/cargo/commands/read_manifest.rs | 4 +-
src/bin/cargo/commands/tree.rs | 2 +-
src/bin/cargo/commands/verify_project.rs | 14 +--
src/bin/cargo/commands/version.rs | 7 +-
src/cargo/core/compiler/job_queue.rs | 4 +-
src/cargo/core/compiler/mod.rs | 4 +-
src/cargo/core/compiler/timings.rs | 2 +-
src/cargo/core/compiler/unit_graph.rs | 2 +-
src/cargo/core/manifest.rs | 2 +-
src/cargo/core/resolver/features.rs | 8 +-
src/cargo/core/shell.rs | 129 ++++++++++++++---------
src/cargo/lib.rs | 6 --
src/cargo/ops/cargo_install.rs | 6 +-
src/cargo/ops/cargo_package.rs | 4 +-
src/cargo/ops/registry.rs | 25 +++--
src/cargo/ops/tree/mod.rs | 31 ++++--
src/cargo/ops/vendor.rs | 19 ++--
src/cargo/util/config/mod.rs | 42 ++++++++
tests/testsuite/cargo_command.rs | 23 ++++
tests/testsuite/owner.rs | 13 ++-
tests/testsuite/search.rs | 57 ++++++++--
25 files changed, 294 insertions(+), 141 deletions(-)
diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs
index 7fc1da57a86..23ed0ca53a3 100644
--- a/src/bin/cargo/cli.rs
+++ b/src/bin/cargo/cli.rs
@@ -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);
}
}
}
diff --git a/src/bin/cargo/commands/locate_project.rs b/src/bin/cargo/commands/locate_project.rs
index df0c424aa4a..5897de108b3 100644
--- a/src/bin/cargo/commands/locate_project.rs
+++ b/src/bin/cargo/commands/locate_project.rs
@@ -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(())
}
diff --git a/src/bin/cargo/commands/metadata.rs b/src/bin/cargo/commands/metadata.rs
index 1130f074e8f..616df735379 100644
--- a/src/bin/cargo/commands/metadata.rs
+++ b/src/bin/cargo/commands/metadata.rs
@@ -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(())
}
diff --git a/src/bin/cargo/commands/pkgid.rs b/src/bin/cargo/commands/pkgid.rs
index 57be0d11877..453c95a184d 100644
--- a/src/bin/cargo/commands/pkgid.rs
+++ b/src/bin/cargo/commands/pkgid.rs
@@ -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(())
}
diff --git a/src/bin/cargo/commands/read_manifest.rs b/src/bin/cargo/commands/read_manifest.rs
index fe2528b18aa..96cba1e082a 100644
--- a/src/bin/cargo/commands/read_manifest.rs
+++ b/src/bin/cargo/commands/read_manifest.rs
@@ -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(())
}
diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs
index 9cfff98fe48..7eeac6cb8e7 100644
--- a/src/bin/cargo/commands/tree.rs
+++ b/src/bin/cargo/commands/tree.rs
@@ -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") {
diff --git a/src/bin/cargo/commands/verify_project.rs b/src/bin/cargo/commands/verify_project.rs
index fe2b42aebed..ea5ac117803 100644
--- a/src/bin/cargo/commands/verify_project.rs
+++ b/src/bin/cargo/commands/verify_project.rs
@@ -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(())
}
diff --git a/src/bin/cargo/commands/version.rs b/src/bin/cargo/commands/version.rs
index 81c6838e7ab..73172826150 100644
--- a/src/bin/cargo/commands/version.rs
+++ b/src/bin/cargo/commands/version.rs
@@ -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(())
}
diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs
index 7a103b13d21..a1f2bba16d3 100644
--- a/src/cargo/core/compiler/job_queue.rs
+++ b/src/cargo/core/compiler/job_queue.rs
@@ -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 {
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index 8a22e4aa050..f662495a1a1 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -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));
diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs
index 20b4c7f600b..8302475defd 100644
--- a/src/cargo/core/compiler/timings.rs
+++ b/src/cargo/core/compiler/timings.rs
@@ -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);
}
diff --git a/src/cargo/core/compiler/unit_graph.rs b/src/cargo/core/compiler/unit_graph.rs
index 350c040abad..d242f6b0497 100644
--- a/src/cargo/core/compiler/unit_graph.rs
+++ b/src/cargo/core/compiler/unit_graph.rs
@@ -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(())
}
diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs
index 994603aca57..221b769295b 100644
--- a/src/cargo/core/manifest.rs
+++ b/src/cargo/core/manifest.rs
@@ -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);
}
}
}
diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs
index cea01d113a4..8f71007f610 100644
--- a/src/cargo/core/resolver/features.rs
+++ b/src/cargo/core/resolver/features.rs
@@ -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;
}
diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs
index eae8a70cc8d..a3e72b5261e 100644
--- a/src/cargo/core/shell.rs
+++ b/src/cargo/core/shell.rs
@@ -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),
/// 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) -> 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 {
- 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(&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");
}
}
diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs
index b386ee7f558..19cbd49c101 100644
--- a/src/cargo/lib.rs
+++ b/src/cargo/lib.rs
@@ -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(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 {
diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs
index 0406a56063d..e450f82f3de 100644
--- a/src/cargo/ops/cargo_install.rs
+++ b/src/cargo/ops/cargo_install.rs
@@ -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,
@@ -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(())
diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs
index b84e6b3caf1..4b59afdae1e 100644
--- a/src/cargo/ops/cargo_package.rs
+++ b/src/cargo/ops/cargo_package.rs
@@ -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
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(())
diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs
index 53ba973143c..1919cee854b 100644
--- a/src/cargo/ops/tree/mod.rs
+++ b/src/cargo/ops/tree/mod.rs
@@ -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, graph: &Graph<'_>) -> CargoResult<()> {
+fn print(
+ config: &Config,
+ opts: &TreeOptions,
+ roots: Vec,
+ 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, 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, 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, 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,
diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs
index 77792333461..2c446fa1639 100644
--- a/src/cargo/ops/vendor.rs
+++ b/src/cargo/ops/vendor.rs
@@ -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::>();
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(())
diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs
index 51cba89f797..603f2581f95 100644
--- a/src/cargo/util/config/mod.rs
+++ b/src/cargo/util/config/mod.rs
@@ -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)*)
+ );
+}
diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs
index 80cff54754f..fa4e05a74d2 100644
--- a/tests/testsuite/cargo_command.rs
+++ b/tests/testsuite/cargo_command.rs
@@ -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);
+}
diff --git a/tests/testsuite/owner.rs b/tests/testsuite/owner.rs
index 8f3f87260ed..298f08dc83f 100644
--- a/tests/testsuite/owner.rs
+++ b/tests/testsuite/owner.rs
@@ -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]
diff --git a/tests/testsuite/search.rs b/tests/testsuite/search.rs
index 75ef3985d97..4b4eb8a01f8 100644
--- a/tests/testsuite/search.rs
+++ b/tests/testsuite/search.rs
@@ -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();
}
From 62711bd385dd8a3803f8b7dcb16231ab2d012e3a Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Wed, 13 May 2020 16:08:04 -0700
Subject: [PATCH 22/24] Forbid certain macros in the codebase.
---
src/cargo/core/compiler/build_plan.rs | 6 +-
src/cargo/core/compiler/context/mod.rs | 2 +-
src/cargo/ops/fix.rs | 7 +-
tests/internal.rs | 93 ++++++++++++++++++++++++++
4 files changed, 103 insertions(+), 5 deletions(-)
create mode 100644 tests/internal.rs
diff --git a/src/cargo/core/compiler/build_plan.rs b/src/cargo/core/compiler/build_plan.rs
index a01fa19821b..b76e8028716 100644
--- a/src/cargo/core/compiler/build_plan.rs
+++ b/src/cargo/core/compiler/build_plan.rs
@@ -14,7 +14,7 @@ use serde::Serialize;
use super::context::OutputFile;
use super::{CompileKind, CompileMode, Context, Unit};
use crate::core::TargetKind;
-use crate::util::{internal, CargoResult, ProcessBuilder};
+use crate::util::{internal, CargoResult, Config, ProcessBuilder};
#[derive(Debug, Serialize)]
struct Invocation {
@@ -146,9 +146,9 @@ impl BuildPlan {
self.plan.inputs = inputs;
}
- pub fn output_plan(self) {
+ pub fn output_plan(self, config: &Config) {
let encoded = serde_json::to_string(&self.plan).unwrap();
- println!("{}", encoded);
+ crate::drop_println!(config, "{}", encoded);
}
}
diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs
index fbba329c2e1..602aba2cda8 100644
--- a/src/cargo/core/compiler/context/mod.rs
+++ b/src/cargo/core/compiler/context/mod.rs
@@ -157,7 +157,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
if build_plan {
plan.set_inputs(self.build_plan_inputs()?);
- plan.output_plan();
+ plan.output_plan(self.bcx.config);
}
// Collect the result of the build into `self.compilation`.
diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs
index b5702cb8afb..ca21dcfdfd6 100644
--- a/src/cargo/ops/fix.rs
+++ b/src/cargo/ops/fix.rs
@@ -41,6 +41,7 @@
use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::OsString;
+use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{self, Command, ExitStatus};
use std::str;
@@ -526,7 +527,11 @@ fn exit_with(status: ExitStatus) -> ! {
{
use std::os::unix::prelude::*;
if let Some(signal) = status.signal() {
- eprintln!("child failed with signal `{}`", signal);
+ drop(writeln!(
+ std::io::stderr().lock(),
+ "child failed with signal `{}`",
+ signal
+ ));
process::exit(2);
}
}
diff --git a/tests/internal.rs b/tests/internal.rs
new file mode 100644
index 00000000000..2631d787381
--- /dev/null
+++ b/tests/internal.rs
@@ -0,0 +1,93 @@
+//! Tests for internal code checks.
+use std::fs;
+
+#[test]
+fn check_forbidden_code() {
+ // Do not use certain macros, functions, etc.
+ if !cargo::util::is_ci() {
+ // Only check these on CI, otherwise it could be annoying.
+ return;
+ }
+ let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
+ for entry in walkdir::WalkDir::new(path)
+ .into_iter()
+ .filter_map(|e| e.ok())
+ {
+ let path = entry.path();
+ if !entry
+ .file_name()
+ .to_str()
+ .map(|s| s.ends_with(".rs"))
+ .unwrap_or(false)
+ {
+ continue;
+ }
+ let c = fs::read_to_string(path).unwrap();
+ for (line_index, line) in c.lines().enumerate() {
+ if line_has_print(line) {
+ if entry.file_name().to_str().unwrap() == "cargo_new.rs" && line.contains("Hello") {
+ // An exception.
+ continue;
+ }
+ panic!(
+ "found print macro in {}:{}\n\n{}\n\n\
+ print! macros should not be used in Cargo because they can panic.\n\
+ Use one of the drop_print macros instead.\n\
+ ",
+ path.display(),
+ line_index,
+ line
+ );
+ }
+ if line_has_macro(line, "dbg") {
+ panic!(
+ "found dbg! macro in {}:{}\n\n{}\n\n\
+ dbg! should not be used outside of debugging.",
+ path.display(),
+ line_index,
+ line
+ );
+ }
+ }
+ }
+}
+
+fn line_has_print(line: &str) -> bool {
+ line_has_macro(line, "print")
+ || line_has_macro(line, "eprint")
+ || line_has_macro(line, "println")
+ || line_has_macro(line, "eprintln")
+}
+
+#[test]
+fn line_has_print_works() {
+ assert!(line_has_print("print!"));
+ assert!(line_has_print("println!"));
+ assert!(line_has_print("eprint!"));
+ assert!(line_has_print("eprintln!"));
+ assert!(line_has_print("(print!(\"hi!\"))"));
+ assert!(!line_has_print("print"));
+ assert!(!line_has_print("i like to print things"));
+ assert!(!line_has_print("drop_print!"));
+ assert!(!line_has_print("drop_println!"));
+ assert!(!line_has_print("drop_eprint!"));
+ assert!(!line_has_print("drop_eprintln!"));
+}
+
+fn line_has_macro(line: &str, mac: &str) -> bool {
+ for (i, _) in line.match_indices(mac) {
+ if line.get(i + mac.len()..i + mac.len() + 1) != Some("!") {
+ continue;
+ }
+ if i == 0 {
+ return true;
+ }
+ // Check for identifier boundary start.
+ let prev1 = line.get(i - 1..i).unwrap().chars().next().unwrap();
+ if prev1.is_alphanumeric() || prev1 == '_' {
+ continue;
+ }
+ return true;
+ }
+ false
+}
From 5a096da9d7035c8d35c4050742835317933662b6 Mon Sep 17 00:00:00 2001
From: Eric Huss
Date: Wed, 13 May 2020 16:24:20 -0700
Subject: [PATCH 23/24] Fix windows.
---
src/cargo/core/shell.rs | 6 +++---
src/cargo/ops/fix.rs | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs
index a3e72b5261e..e1198f17689 100644
--- a/src/cargo/core/shell.rs
+++ b/src/cargo/core/shell.rs
@@ -308,8 +308,8 @@ impl Shell {
}
#[cfg(windows)]
{
- if let ShellOut::Stream { stream, .. } = &mut self.err {
- ::fwdansi::write_ansi(stream, message)?;
+ if let ShellOut::Stream { stderr, .. } = &mut self.output {
+ ::fwdansi::write_ansi(stderr, message)?;
return Ok(());
}
}
@@ -495,6 +495,6 @@ mod imp {
fn default_err_erase_line(shell: &mut Shell) {
if let Some(max_width) = imp::stderr_width() {
let blank = " ".repeat(max_width);
- drop(write!(shell.err.as_write(), "{}\r", blank));
+ drop(write!(shell.output.stderr(), "{}\r", blank));
}
}
diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs
index ca21dcfdfd6..7c4834dba59 100644
--- a/src/cargo/ops/fix.rs
+++ b/src/cargo/ops/fix.rs
@@ -41,7 +41,6 @@
use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::OsString;
-use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{self, Command, ExitStatus};
use std::str;
@@ -525,6 +524,7 @@ fn rustfix_and_fix(
fn exit_with(status: ExitStatus) -> ! {
#[cfg(unix)]
{
+ use std::io::Write;
use std::os::unix::prelude::*;
if let Some(signal) = status.signal() {
drop(writeln!(
From d6a1428609f468ad633972b0bcd0b2f1c67dec37 Mon Sep 17 00:00:00 2001
From: Julian Wollersberger
<24991778+Julian-Wollersberger@users.noreply.github.com>
Date: Sun, 3 May 2020 19:02:52 +0200
Subject: [PATCH 24/24] Rephrased error message for disallowed sections in
virtual workspace
---
src/cargo/util/toml/mod.rs | 26 +++++++++++++-------------
tests/testsuite/workspaces.rs | 2 +-
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs
index c7858deffb1..246bd37f145 100644
--- a/src/cargo/util/toml/mod.rs
+++ b/src/cargo/util/toml/mod.rs
@@ -1320,43 +1320,43 @@ impl TomlManifest {
config: &Config,
) -> CargoResult<(VirtualManifest, Vec)> {
if me.project.is_some() {
- bail!("virtual manifests do not define [project]");
+ bail!("this virtual manifest specifies a [project] section, which is not allowed");
}
if me.package.is_some() {
- bail!("virtual manifests do not define [package]");
+ bail!("this virtual manifest specifies a [package] section, which is not allowed");
}
if me.lib.is_some() {
- bail!("virtual manifests do not specify [lib]");
+ bail!("this virtual manifest specifies a [lib] section, which is not allowed");
}
if me.bin.is_some() {
- bail!("virtual manifests do not specify [[bin]]");
+ bail!("this virtual manifest specifies a [[bin]] section, which is not allowed");
}
if me.example.is_some() {
- bail!("virtual manifests do not specify [[example]]");
+ bail!("this virtual manifest specifies a [[example]] section, which is not allowed");
}
if me.test.is_some() {
- bail!("virtual manifests do not specify [[test]]");
+ bail!("this virtual manifest specifies a [[test]] section, which is not allowed");
}
if me.bench.is_some() {
- bail!("virtual manifests do not specify [[bench]]");
+ bail!("this virtual manifest specifies a [[bench]] section, which is not allowed");
}
if me.dependencies.is_some() {
- bail!("virtual manifests do not specify [dependencies]");
+ bail!("this virtual manifest specifies a [dependencies] section, which is not allowed");
}
if me.dev_dependencies.is_some() || me.dev_dependencies2.is_some() {
- bail!("virtual manifests do not specify [dev-dependencies]");
+ bail!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
}
if me.build_dependencies.is_some() || me.build_dependencies2.is_some() {
- bail!("virtual manifests do not specify [build-dependencies]");
+ bail!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
}
if me.features.is_some() {
- bail!("virtual manifests do not specify [features]");
+ bail!("this virtual manifest specifies a [features] section, which is not allowed");
}
if me.target.is_some() {
- bail!("virtual manifests do not specify [target]");
+ bail!("this virtual manifest specifies a [target] section, which is not allowed");
}
if me.badges.is_some() {
- bail!("virtual manifests do not specify [badges]");
+ bail!("this virtual manifest specifies a [badges] section, which is not allowed");
}
let mut nested_paths = Vec::new();
diff --git a/tests/testsuite/workspaces.rs b/tests/testsuite/workspaces.rs
index 0dfb43b6d0a..42ba045a6ab 100644
--- a/tests/testsuite/workspaces.rs
+++ b/tests/testsuite/workspaces.rs
@@ -2117,7 +2117,7 @@ fn ws_err_unused() {
[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
Caused by:
- virtual manifests do not specify {}
+ this virtual manifest specifies a {} section, which is not allowed
",
key
))