Auto merge of #7533 - yaahc:clippy-banishment, r=ehuss

Close the front door for clippy but open the back
This commit is contained in:
bors 2020-03-14 20:50:15 +00:00
commit 7302186d7b
19 changed files with 390 additions and 301 deletions

View File

@ -1679,6 +1679,7 @@ thread_local!(
pub static RUSTC: Rustc = Rustc::new( pub static RUSTC: Rustc = Rustc::new(
PathBuf::from("rustc"), PathBuf::from("rustc"),
None, None,
None,
Path::new("should be path to rustup rustc, but we don't care in tests"), Path::new("should be path to rustup rustc, but we don't care in tests"),
None, None,
).unwrap() ).unwrap()

View File

@ -1,3 +1,4 @@
use crate::{basic_manifest, project};
use filetime::{self, FileTime}; use filetime::{self, FileTime};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::cell::RefCell; use std::cell::RefCell;
@ -264,3 +265,24 @@ pub fn sysroot() -> String {
let sysroot = String::from_utf8(output.stdout).unwrap(); let sysroot = String::from_utf8(output.stdout).unwrap();
sysroot.trim().to_string() sysroot.trim().to_string()
} }
pub fn echo_wrapper() -> std::path::PathBuf {
let p = project()
.at("rustc-echo-wrapper")
.file("Cargo.toml", &basic_manifest("rustc-echo-wrapper", "1.0.0"))
.file(
"src/main.rs",
r#"
fn main() {
let args = std::env::args().collect::<Vec<_>>();
eprintln!("WRAPPER CALLED: {}", args[1..].join(" "));
let status = std::process::Command::new(&args[1])
.args(&args[2..]).status().unwrap();
std::process::exit(status.code().unwrap_or(1));
}
"#,
)
.build();
p.cargo("build").run();
p.bin("rustc-echo-wrapper")
}

View File

@ -1,85 +0,0 @@
use crate::command_prelude::*;
use cargo::ops;
use cargo::util;
pub fn cli() -> App {
subcommand("clippy-preview")
.about("Checks a package to catch common mistakes and improve your Rust code.")
.arg(Arg::with_name("args").multiple(true))
.arg_package_spec(
"Package(s) to check",
"Check all packages in the workspace",
"Exclude packages from the check",
)
.arg_jobs()
.arg_targets_all(
"Check only this package's library",
"Check only the specified binary",
"Check all binaries",
"Check only the specified example",
"Check all examples",
"Check only the specified test target",
"Check all tests",
"Check only the specified bench target",
"Check all benches",
"Check all targets",
)
.arg_release("Check artifacts in release mode, with optimizations")
.arg_profile("Check artifacts with the specified profile")
.arg_features()
.arg_target_triple("Check for the target triple")
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.after_help(
"\
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package should be built. If it is not given, then the
current package is built. For more information on SPEC and its format, see the
`cargo help pkgid` command.
All packages in the workspace are checked if the `--workspace` flag is supplied. The
`--workspace` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--workspace` flag.
To allow or deny a lint from the command line you can use `cargo clippy --`
with:
-W --warn OPT Set lint warnings
-A --allow OPT Set lint allowed
-D --deny OPT Set lint denied
-F --forbid OPT Set lint forbidden
You can use tool lints to allow or deny lints from your code, eg.:
#[allow(clippy::needless_lifetimes)]
",
)
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;
let mode = CompileMode::Check { test: false };
let mut compile_opts =
args.compile_options(config, mode, Some(&ws), ProfileChecking::Checked)?;
if !config.cli_unstable().unstable_options {
return Err(anyhow::format_err!(
"`clippy-preview` is unstable, pass `-Z unstable-options` to enable it"
)
.into());
}
let mut wrapper = util::process(util::config::clippy_driver());
if let Some(clippy_args) = args.values_of("args") {
wrapper.args(&clippy_args.collect::<Vec<_>>());
}
compile_opts.build_config.primary_unit_rustc = Some(wrapper);
ops::compile(&ws, &compile_opts)?;
Ok(())
}

View File

@ -72,15 +72,6 @@ pub fn cli() -> App {
.long("allow-staged") .long("allow-staged")
.help("Fix code even if the working directory has staged changes"), .help("Fix code even if the working directory has staged changes"),
) )
.arg(
Arg::with_name("clippy")
.long("clippy")
.help("Get fix suggestions from clippy instead of rustc")
.hidden(true)
.multiple(true)
.min_values(0)
.number_of_values(1),
)
.after_help( .after_help(
"\ "\
This Cargo subcommand will automatically take rustc's suggestions from This Cargo subcommand will automatically take rustc's suggestions from
@ -134,21 +125,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
// code as we can. // code as we can.
let mut opts = args.compile_options(config, mode, Some(&ws), ProfileChecking::Unchecked)?; let mut opts = args.compile_options(config, mode, Some(&ws), ProfileChecking::Unchecked)?;
let use_clippy = args.is_present("clippy");
let clippy_args = args
.value_of("clippy")
.map(|s| s.split(' ').map(|s| s.to_string()).collect())
.or_else(|| Some(vec![]))
.filter(|_| use_clippy);
if use_clippy && !config.cli_unstable().unstable_options {
return Err(anyhow::format_err!(
"`cargo fix --clippy` is unstable, pass `-Z unstable-options` to enable it"
)
.into());
}
if let CompileFilter::Default { .. } = opts.filter { if let CompileFilter::Default { .. } = opts.filter {
opts.filter = CompileFilter::Only { opts.filter = CompileFilter::Only {
all_targets: true, all_targets: true,
@ -171,7 +147,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
allow_no_vcs: args.is_present("allow-no-vcs"), allow_no_vcs: args.is_present("allow-no-vcs"),
allow_staged: args.is_present("allow-staged"), allow_staged: args.is_present("allow-staged"),
broken_code: args.is_present("broken-code"), broken_code: args.is_present("broken-code"),
clippy_args,
}, },
)?; )?;
Ok(()) Ok(())

View File

@ -6,7 +6,6 @@ pub fn builtin() -> Vec<App> {
build::cli(), build::cli(),
check::cli(), check::cli(),
clean::cli(), clean::cli(),
clippy::cli(),
doc::cli(), doc::cli(),
fetch::cli(), fetch::cli(),
fix::cli(), fix::cli(),
@ -43,7 +42,6 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches<'_>) -> Cli
"build" => build::exec, "build" => build::exec,
"check" => check::exec, "check" => check::exec,
"clean" => clean::exec, "clean" => clean::exec,
"clippy-preview" => clippy::exec,
"doc" => doc::exec, "doc" => doc::exec,
"fetch" => fetch::exec, "fetch" => fetch::exec,
"fix" => fix::exec, "fix" => fix::exec,
@ -80,7 +78,6 @@ pub mod bench;
pub mod build; pub mod build;
pub mod check; pub mod check;
pub mod clean; pub mod clean;
pub mod clippy;
pub mod doc; pub mod doc;
pub mod fetch; pub mod fetch;
pub mod fix; pub mod fix;

View File

@ -22,7 +22,7 @@ pub struct BuildConfig {
pub force_rebuild: bool, pub force_rebuild: bool,
/// Output a build plan to stdout instead of actually compiling. /// Output a build plan to stdout instead of actually compiling.
pub build_plan: bool, pub build_plan: bool,
/// An optional override of the rustc path for primary units only /// An optional override of the rustc process for primary units
pub primary_unit_rustc: Option<ProcessBuilder>, pub primary_unit_rustc: Option<ProcessBuilder>,
pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>, pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>,
} }

View File

@ -73,8 +73,14 @@ pub struct Compilation<'cfg> {
pub target: String, pub target: String,
config: &'cfg Config, config: &'cfg Config,
/// Rustc process to be used by default
rustc_process: ProcessBuilder, rustc_process: ProcessBuilder,
primary_unit_rustc_process: Option<ProcessBuilder>, /// Rustc process to be used for workspace crates instead of rustc_process
rustc_workspace_wrapper_process: ProcessBuilder,
/// Optional rustc process to be used for primary crates instead of either rustc_process or
/// rustc_workspace_wrapper_process
primary_rustc_process: Option<ProcessBuilder>,
target_runner: Option<(PathBuf, Vec<String>)>, target_runner: Option<(PathBuf, Vec<String>)>,
} }
@ -85,13 +91,14 @@ impl<'cfg> Compilation<'cfg> {
default_kind: CompileKind, default_kind: CompileKind,
) -> CargoResult<Compilation<'cfg>> { ) -> CargoResult<Compilation<'cfg>> {
let mut rustc = bcx.rustc().process(); let mut rustc = bcx.rustc().process();
let mut primary_rustc_process = bcx.build_config.primary_unit_rustc.clone();
let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone(); let mut rustc_workspace_wrapper_process = bcx.rustc().workspace_process();
if bcx.config.extra_verbose() { if bcx.config.extra_verbose() {
rustc.display_env_vars(); rustc.display_env_vars();
rustc_workspace_wrapper_process.display_env_vars();
if let Some(rustc) = primary_unit_rustc_process.as_mut() { if let Some(rustc) = primary_rustc_process.as_mut() {
rustc.display_env_vars(); rustc.display_env_vars();
} }
} }
@ -120,7 +127,8 @@ impl<'cfg> Compilation<'cfg> {
rustdocflags: HashMap::new(), rustdocflags: HashMap::new(),
config: bcx.config, config: bcx.config,
rustc_process: rustc, rustc_process: rustc,
primary_unit_rustc_process, rustc_workspace_wrapper_process,
primary_rustc_process,
host: bcx.host_triple().to_string(), host: bcx.host_triple().to_string(),
target: bcx.target_data.short_name(&default_kind).to_string(), target: bcx.target_data.short_name(&default_kind).to_string(),
target_runner: target_runner(bcx, default_kind)?, target_runner: target_runner(bcx, default_kind)?,
@ -128,11 +136,16 @@ impl<'cfg> Compilation<'cfg> {
} }
/// See `process`. /// See `process`.
pub fn rustc_process(&self, pkg: &Package, is_primary: bool) -> CargoResult<ProcessBuilder> { pub fn rustc_process(
let rustc = if is_primary { &self,
self.primary_unit_rustc_process pkg: &Package,
.clone() is_primary: bool,
.unwrap_or_else(|| self.rustc_process.clone()) is_workspace: bool,
) -> CargoResult<ProcessBuilder> {
let rustc = if is_primary && self.primary_rustc_process.is_some() {
self.primary_rustc_process.clone().unwrap()
} else if is_workspace {
self.rustc_workspace_wrapper_process.clone()
} else { } else {
self.rustc_process.clone() self.rustc_process.clone()
}; };

View File

@ -616,11 +616,11 @@ fn compute_metadata<'a, 'cfg>(
bcx.rustc().verbose_version.hash(&mut hasher); bcx.rustc().verbose_version.hash(&mut hasher);
if cx.is_primary_package(unit) { if cx.bcx.ws.is_member(unit.pkg) {
// This is primarily here for clippy. This ensures that the clippy // This is primarily here for clippy. This ensures that the clippy
// artifacts are separate from the `check` ones. // artifacts are separate from the `check` ones.
if let Some(proc) = &cx.bcx.build_config.primary_unit_rustc { if let Some(path) = &cx.bcx.rustc().workspace_wrapper {
proc.get_program().hash(&mut hasher); path.hash(&mut hasher);
} }
} }

View File

@ -1100,22 +1100,12 @@ fn calculate_normal<'a, 'cfg>(
// Fill out a bunch more information that we'll be tracking typically // Fill out a bunch more information that we'll be tracking typically
// hashed to take up less space on disk as we just need to know when things // hashed to take up less space on disk as we just need to know when things
// change. // change.
let mut extra_flags = if unit.mode.is_doc() { let extra_flags = if unit.mode.is_doc() {
cx.bcx.rustdocflags_args(unit) cx.bcx.rustdocflags_args(unit)
} else { } else {
cx.bcx.rustflags_args(unit) cx.bcx.rustflags_args(unit)
} }
.to_vec(); .to_vec();
if cx.is_primary_package(unit) {
// This is primarily here for clippy arguments.
if let Some(proc) = &cx.bcx.build_config.primary_unit_rustc {
let args = proc
.get_args()
.iter()
.map(|s| s.to_string_lossy().to_string());
extra_flags.extend(args);
}
}
let profile_hash = util::hash_u64((&unit.profile, unit.mode, cx.bcx.extra_args_for(unit))); let profile_hash = util::hash_u64((&unit.profile, unit.mode, cx.bcx.extra_args_for(unit)));
// Include metadata since it is exposed as environment variables. // Include metadata since it is exposed as environment variables.

View File

@ -538,8 +538,11 @@ fn prepare_rustc<'a, 'cfg>(
unit: &Unit<'a>, unit: &Unit<'a>,
) -> CargoResult<ProcessBuilder> { ) -> CargoResult<ProcessBuilder> {
let is_primary = cx.is_primary_package(unit); let is_primary = cx.is_primary_package(unit);
let is_workspace = cx.bcx.ws.is_member(unit.pkg);
let mut base = cx.compilation.rustc_process(unit.pkg, is_primary)?; let mut base = cx
.compilation
.rustc_process(unit.pkg, is_primary, is_workspace)?;
if cx.bcx.config.cli_unstable().jobserver_per_rustc { if cx.bcx.config.cli_unstable().jobserver_per_rustc {
let client = cx.new_jobserver()?; let client = cx.new_jobserver()?;
base.inherit_jobserver(&client); base.inherit_jobserver(&client);

View File

@ -53,9 +53,10 @@ use rustfix::{self, CodeFix};
use crate::core::Workspace; use crate::core::Workspace;
use crate::ops::{self, CompileOptions}; use crate::ops::{self, CompileOptions};
use crate::util;
use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer}; use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer};
use crate::util::errors::CargoResult; use crate::util::errors::CargoResult;
use crate::util::{self, paths}; use crate::util::ProcessBuilder;
use crate::util::{existing_vcs_repo, LockServer, LockServerClient}; use crate::util::{existing_vcs_repo, LockServer, LockServerClient};
const FIX_ENV: &str = "__CARGO_FIX_PLZ"; const FIX_ENV: &str = "__CARGO_FIX_PLZ";
@ -63,7 +64,6 @@ const BROKEN_CODE_ENV: &str = "__CARGO_FIX_BROKEN_CODE";
const PREPARE_FOR_ENV: &str = "__CARGO_FIX_PREPARE_FOR"; const PREPARE_FOR_ENV: &str = "__CARGO_FIX_PREPARE_FOR";
const EDITION_ENV: &str = "__CARGO_FIX_EDITION"; const EDITION_ENV: &str = "__CARGO_FIX_EDITION";
const IDIOMS_ENV: &str = "__CARGO_FIX_IDIOMS"; const IDIOMS_ENV: &str = "__CARGO_FIX_IDIOMS";
const CLIPPY_FIX_ARGS: &str = "__CARGO_FIX_CLIPPY_ARGS";
pub struct FixOptions<'a> { pub struct FixOptions<'a> {
pub edition: bool, pub edition: bool,
@ -74,7 +74,6 @@ pub struct FixOptions<'a> {
pub allow_no_vcs: bool, pub allow_no_vcs: bool,
pub allow_staged: bool, pub allow_staged: bool,
pub broken_code: bool, pub broken_code: bool,
pub clippy_args: Option<Vec<String>>,
} }
pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions<'_>) -> CargoResult<()> { pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions<'_>) -> CargoResult<()> {
@ -101,19 +100,6 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions<'_>) -> CargoResult<()> {
wrapper.env(IDIOMS_ENV, "1"); wrapper.env(IDIOMS_ENV, "1");
} }
if opts.clippy_args.is_some() {
if let Err(e) = util::process("clippy-driver").arg("-V").exec_with_output() {
eprintln!("Warning: clippy-driver not found: {:?}", e);
}
let clippy_args = opts
.clippy_args
.as_ref()
.map_or_else(String::new, |args| serde_json::to_string(&args).unwrap());
wrapper.env(CLIPPY_FIX_ARGS, clippy_args);
}
*opts *opts
.compile_opts .compile_opts
.build_config .build_config
@ -222,12 +208,17 @@ pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
let args = FixArgs::get(); let args = FixArgs::get();
trace!("cargo-fix as rustc got file {:?}", args.file); trace!("cargo-fix as rustc got file {:?}", args.file);
let rustc = args.rustc.as_ref().expect("fix wrapper rustc was not set"); let rustc = args.rustc.as_ref().expect("fix wrapper rustc was not set");
let workspace_rustc = std::env::var("RUSTC_WORKSPACE_WRAPPER")
.map(PathBuf::from)
.ok();
let rustc = util::process(rustc).wrapped(workspace_rustc.as_ref());
let mut fixes = FixedCrate::default(); let mut fixes = FixedCrate::default();
if let Some(path) = &args.file { if let Some(path) = &args.file {
trace!("start rustfixing {:?}", path); trace!("start rustfixing {:?}", path);
fixes = rustfix_crate(&lock_addr, rustc.as_ref(), path, &args)?; fixes = rustfix_crate(&lock_addr, &rustc, path, &args)?;
} }
// Ok now we have our final goal of testing out the changes that we applied. // Ok now we have our final goal of testing out the changes that we applied.
@ -239,7 +230,7 @@ pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
// new rustc, and otherwise we capture the output to hide it in the scenario // new rustc, and otherwise we capture the output to hide it in the scenario
// that we have to back it all out. // that we have to back it all out.
if !fixes.files.is_empty() { if !fixes.files.is_empty() {
let mut cmd = Command::new(&rustc); let mut cmd = rustc.build_command();
args.apply(&mut cmd); args.apply(&mut cmd);
cmd.arg("--error-format=json"); cmd.arg("--error-format=json");
let output = cmd.output().context("failed to spawn rustc")?; let output = cmd.output().context("failed to spawn rustc")?;
@ -279,7 +270,7 @@ pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
// - If the fix failed, show the original warnings and suggestions. // - If the fix failed, show the original warnings and suggestions.
// - If `--broken-code`, show the error messages. // - If `--broken-code`, show the error messages.
// - If the fix succeeded, show any remaining warnings. // - If the fix succeeded, show any remaining warnings.
let mut cmd = Command::new(&rustc); let mut cmd = rustc.build_command();
args.apply(&mut cmd); args.apply(&mut cmd);
for arg in args.format_args { for arg in args.format_args {
// Add any json/error format arguments that Cargo wants. This allows // Add any json/error format arguments that Cargo wants. This allows
@ -302,7 +293,7 @@ struct FixedFile {
fn rustfix_crate( fn rustfix_crate(
lock_addr: &str, lock_addr: &str,
rustc: &Path, rustc: &ProcessBuilder,
filename: &Path, filename: &Path,
args: &FixArgs, args: &FixArgs,
) -> Result<FixedCrate, Error> { ) -> Result<FixedCrate, Error> {
@ -402,7 +393,7 @@ fn rustfix_crate(
/// and any errors encountered while fixing files. /// and any errors encountered while fixing files.
fn rustfix_and_fix( fn rustfix_and_fix(
fixes: &mut FixedCrate, fixes: &mut FixedCrate,
rustc: &Path, rustc: &ProcessBuilder,
filename: &Path, filename: &Path,
args: &FixArgs, args: &FixArgs,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -410,12 +401,15 @@ fn rustfix_and_fix(
// TODO: implement a way to specify this. // TODO: implement a way to specify this.
let only = HashSet::new(); let only = HashSet::new();
let mut cmd = Command::new(rustc); let mut cmd = rustc.build_command();
cmd.arg("--error-format=json"); cmd.arg("--error-format=json");
args.apply(&mut cmd); args.apply(&mut cmd);
let output = cmd let output = cmd.output().with_context(|| {
.output() format!(
.with_context(|| format!("failed to execute `{}`", rustc.display()))?; "failed to execute `{}`",
rustc.get_program().to_string_lossy()
)
})?;
// If rustc didn't succeed for whatever reasons then we're very likely to be // If rustc didn't succeed for whatever reasons then we're very likely to be
// looking at otherwise broken code. Let's not make things accidentally // looking at otherwise broken code. Let's not make things accidentally
@ -491,7 +485,7 @@ fn rustfix_and_fix(
// Attempt to read the source code for this file. If this fails then // Attempt to read the source code for this file. If this fails then
// that'd be pretty surprising, so log a message and otherwise keep // that'd be pretty surprising, so log a message and otherwise keep
// going. // going.
let code = match paths::read(file.as_ref()) { let code = match util::paths::read(file.as_ref()) {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
warn!("failed to read `{}`: {}", file, e); warn!("failed to read `{}`: {}", file, e);
@ -591,7 +585,6 @@ struct FixArgs {
enabled_edition: Option<String>, enabled_edition: Option<String>,
other: Vec<OsString>, other: Vec<OsString>,
rustc: Option<PathBuf>, rustc: Option<PathBuf>,
clippy_args: Vec<String>,
format_args: Vec<String>, format_args: Vec<String>,
} }
@ -611,12 +604,7 @@ impl FixArgs {
fn get() -> FixArgs { fn get() -> FixArgs {
let mut ret = FixArgs::default(); let mut ret = FixArgs::default();
if let Ok(clippy_args) = env::var(CLIPPY_FIX_ARGS) { ret.rustc = env::args_os().nth(1).map(PathBuf::from);
ret.clippy_args = serde_json::from_str(&clippy_args).unwrap();
ret.rustc = Some(util::config::clippy_driver());
} else {
ret.rustc = env::args_os().nth(1).map(PathBuf::from);
}
for arg in env::args_os().skip(2) { for arg in env::args_os().skip(2) {
let path = PathBuf::from(arg); let path = PathBuf::from(arg);
@ -654,10 +642,6 @@ impl FixArgs {
cmd.arg(path); cmd.arg(path);
} }
if !self.clippy_args.is_empty() {
cmd.args(&self.clippy_args);
}
cmd.args(&self.other).arg("--cap-lints=warn"); cmd.args(&self.other).arg("--cap-lints=warn");
if let Some(edition) = &self.enabled_edition { if let Some(edition) = &self.enabled_edition {
cmd.arg("--edition").arg(edition); cmd.arg("--edition").arg(edition);

View File

@ -314,9 +314,19 @@ impl Config {
.into_path_unlocked() .into_path_unlocked()
}); });
let wrapper = self.maybe_get_tool("rustc_wrapper", &self.build_config()?.rustc_wrapper); let wrapper = self.maybe_get_tool("rustc_wrapper", &self.build_config()?.rustc_wrapper);
let rustc_workspace_wrapper = self.maybe_get_tool(
"rustc_workspace_wrapper",
&self.build_config()?.rustc_workspace_wrapper,
);
if !self.cli_unstable().unstable_options && rustc_workspace_wrapper.is_some() {
bail!("Usage of `RUSTC_WORKSPACE_WRAPPER` requires `-Z unstable-options`")
}
Rustc::new( Rustc::new(
self.get_tool("rustc", &self.build_config()?.rustc), self.get_tool("rustc", &self.build_config()?.rustc),
wrapper, wrapper,
rustc_workspace_wrapper,
&self &self
.home() .home()
.join("bin") .join("bin")
@ -1645,15 +1655,6 @@ impl Drop for PackageCacheLock<'_> {
} }
} }
/// returns path to clippy-driver binary
///
/// Allows override of the path via `CARGO_CLIPPY_DRIVER` env variable
pub fn clippy_driver() -> PathBuf {
env::var("CARGO_CLIPPY_DRIVER")
.unwrap_or_else(|_| "clippy-driver".into())
.into()
}
#[derive(Debug, Default, Deserialize, PartialEq)] #[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub struct CargoHttpConfig { pub struct CargoHttpConfig {
@ -1714,6 +1715,7 @@ pub struct CargoBuildConfig {
pub rustflags: Option<StringList>, pub rustflags: Option<StringList>,
pub rustdocflags: Option<StringList>, pub rustdocflags: Option<StringList>,
pub rustc_wrapper: Option<PathBuf>, pub rustc_wrapper: Option<PathBuf>,
pub rustc_workspace_wrapper: Option<PathBuf>,
pub rustc: Option<PathBuf>, pub rustc: Option<PathBuf>,
pub rustdoc: Option<PathBuf>, pub rustdoc: Option<PathBuf>,
pub out_dir: Option<ConfigRelativePath>, pub out_dir: Option<ConfigRelativePath>,

View File

@ -6,6 +6,7 @@ use std::collections::BTreeMap;
use std::env; use std::env;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fmt; use std::fmt;
use std::iter::once;
use std::path::Path; use std::path::Path;
use std::process::{Command, Output, Stdio}; use std::process::{Command, Output, Stdio};
@ -326,6 +327,37 @@ impl ProcessBuilder {
} }
command command
} }
/// Wraps an existing command with the provided wrapper, if it is present and valid.
///
/// # Examples
///
/// ```rust
/// use cargo::util::{ProcessBuilder, process};
/// // Running this would execute `rustc`
/// let cmd: ProcessBuilder = process("rustc");
///
/// // Running this will execute `sccache rustc`
/// let cmd = cmd.wrapped(Some("sccache"));
/// ```
pub fn wrapped(mut self, wrapper: Option<impl AsRef<OsStr>>) -> Self {
let wrapper = if let Some(wrapper) = wrapper.as_ref() {
wrapper.as_ref()
} else {
return self;
};
if wrapper.is_empty() {
return self;
}
let args = once(self.program).chain(self.args.into_iter()).collect();
self.program = wrapper.to_os_string();
self.args = args;
self
}
} }
/// A helper function to create a `ProcessBuilder`. /// A helper function to create a `ProcessBuilder`.

View File

@ -20,7 +20,9 @@ pub struct Rustc {
pub path: PathBuf, pub path: PathBuf,
/// An optional program that will be passed the path of the rust exe as its first argument, and /// An optional program that will be passed the path of the rust exe as its first argument, and
/// rustc args following this. /// rustc args following this.
pub wrapper: Option<ProcessBuilder>, pub wrapper: Option<PathBuf>,
/// An optional wrapper to be used in addition to `rustc.wrapper` for workspace crates
pub workspace_wrapper: Option<PathBuf>,
/// Verbose version information (the output of `rustc -vV`) /// Verbose version information (the output of `rustc -vV`)
pub verbose_version: String, pub verbose_version: String,
/// The host triple (arch-platform-OS), this comes from verbose_version. /// The host triple (arch-platform-OS), this comes from verbose_version.
@ -37,6 +39,7 @@ impl Rustc {
pub fn new( pub fn new(
path: PathBuf, path: PathBuf,
wrapper: Option<PathBuf>, wrapper: Option<PathBuf>,
workspace_wrapper: Option<PathBuf>,
rustup_rustc: &Path, rustup_rustc: &Path,
cache_location: Option<PathBuf>, cache_location: Option<PathBuf>,
) -> CargoResult<Rustc> { ) -> CargoResult<Rustc> {
@ -64,7 +67,8 @@ impl Rustc {
Ok(Rustc { Ok(Rustc {
path, path,
wrapper: wrapper.map(util::process), wrapper,
workspace_wrapper,
verbose_version, verbose_version,
host, host,
cache: Mutex::new(cache), cache: Mutex::new(cache),
@ -72,20 +76,15 @@ impl Rustc {
} }
/// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`. /// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`.
pub fn process_with(&self, path: impl AsRef<Path>) -> ProcessBuilder { pub fn process(&self) -> ProcessBuilder {
match self.wrapper { util::process(self.path.as_path()).wrapped(self.wrapper.as_ref())
Some(ref wrapper) if !wrapper.get_program().is_empty() => {
let mut cmd = wrapper.clone();
cmd.arg(path.as_ref());
cmd
}
_ => util::process(path.as_ref()),
}
} }
/// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`. /// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`.
pub fn process(&self) -> ProcessBuilder { pub fn workspace_process(&self) -> ProcessBuilder {
self.process_with(&self.path) util::process(self.path.as_path())
.wrapped(self.workspace_wrapper.as_ref())
.wrapped(self.wrapper.as_ref())
} }
pub fn process_no_wrapper(&self) -> ProcessBuilder { pub fn process_no_wrapper(&self) -> ProcessBuilder {
@ -95,10 +94,6 @@ impl Rustc {
pub fn cached_output(&self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> { pub fn cached_output(&self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> {
self.cache.lock().unwrap().cached_output(cmd) self.cache.lock().unwrap().cached_output(cmd)
} }
pub fn set_wrapper(&mut self, wrapper: ProcessBuilder) {
self.wrapper = Some(wrapper);
}
} }
/// It is a well known fact that `rustc` is not the fastest compiler in the /// It is a well known fact that `rustc` is not the fastest compiler in the

View File

@ -3700,6 +3700,42 @@ fn rustc_wrapper_from_path() {
.run(); .run();
} }
#[cargo_test]
// NOTE: we don't have `/usr/bin/env` on Windows.
#[cfg(not(windows))]
fn rustc_workspace_wrapper() {
let p = project().file("src/lib.rs", "").build();
p.cargo("build -v -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", "/usr/bin/env")
.masquerade_as_nightly_cargo()
.with_stderr_contains("[RUNNING] `/usr/bin/env rustc --crate-name foo [..]")
.run();
}
#[cargo_test]
#[cfg(not(windows))]
fn rustc_workspace_wrapper_relative() {
let p = project().file("src/lib.rs", "").build();
p.cargo("build -v -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", "./sccache")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains("[..]/foo/./sccache rustc[..]")
.run();
}
#[cargo_test]
#[cfg(not(windows))]
fn rustc_workspace_wrapper_from_path() {
let p = project().file("src/lib.rs", "").build();
p.cargo("build -v -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", "wannabe_sccache")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains("[..]`wannabe_sccache rustc [..]")
.run();
}
#[cargo_test] #[cargo_test]
fn cdylib_not_lifted() { fn cdylib_not_lifted() {
let p = project() let p = project()

View File

@ -1,8 +1,7 @@
//! Tests for caching compiler diagnostics. //! Tests for caching compiler diagnostics.
use cargo_test_support::{ use cargo_test_support::{
basic_manifest, command_is_available, is_coarse_mtime, process, project, registry::Package, basic_manifest, is_coarse_mtime, process, project, registry::Package, sleep_ms,
sleep_ms,
}; };
use std::path::Path; use std::path::Path;
@ -274,56 +273,6 @@ fn fix() {
assert_eq!(p.read_file("src/lib.rs"), "pub fn r#try() {}"); assert_eq!(p.read_file("src/lib.rs"), "pub fn r#try() {}");
} }
#[cargo_test]
fn clippy() {
if !command_is_available("clippy-driver") {
return;
}
// Caching clippy output.
// This is just a random clippy lint (assertions_on_constants) that
// hopefully won't change much in the future.
let p = project()
.file(
"src/lib.rs",
"pub fn f() { assert!(true); }\n\
fn unused_func() {}",
)
.build();
p.cargo("clippy-preview -Zunstable-options -v")
.masquerade_as_nightly_cargo()
.with_stderr_contains("[RUNNING] `clippy[..]")
.with_stderr_contains("[..]assert!(true)[..]")
.run();
// `check` should be separate from clippy.
p.cargo("check -v")
.with_stderr_contains(
"\
[CHECKING] foo [..]
[RUNNING] `rustc[..]
[WARNING] [..]unused_func[..]
",
)
.with_stderr_does_not_contain("[..]assert!(true)[..]")
.run();
// Again, reading from the cache.
p.cargo("clippy-preview -Zunstable-options -v")
.masquerade_as_nightly_cargo()
.with_stderr_contains("[FRESH] foo [..]")
.with_stderr_contains("[..]assert!(true)[..]")
.run();
// And `check` should also be fresh, reading from cache.
p.cargo("check -v")
.with_stderr_contains("[FRESH] foo [..]")
.with_stderr_contains("[WARNING] [..]unused_func[..]")
.with_stderr_does_not_contain("[..]assert!(true)[..]")
.run();
}
#[cargo_test] #[cargo_test]
fn very_verbose() { fn very_verbose() {
// Handle cap-lints in dependencies. // Handle cap-lints in dependencies.
@ -496,3 +445,49 @@ fn caching_large_output() {
)) ))
.run(); .run();
} }
#[cargo_test]
fn rustc_workspace_wrapper() {
use cargo_test_support::paths;
let p = project()
.file(
"src/lib.rs",
"pub fn f() { assert!(true); }\n\
fn unused_func() {}",
)
.build();
p.cargo("check -Zunstable-options -v")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]")
.run();
// Check without a wrapper should rebuild
p.cargo("check -v")
.with_stderr_contains(
"\
[CHECKING] foo [..]
[RUNNING] `rustc[..]
[WARNING] [..]unused_func[..]
",
)
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]")
.run();
// Again, reading from the cache.
p.cargo("check -Zunstable-options -v")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("[FRESH] foo [..]")
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]")
.run();
// And `check` should also be fresh, reading from cache.
p.cargo("check -v")
.with_stderr_contains("[FRESH] foo [..]")
.with_stderr_contains("[WARNING] [..]unused_func[..]")
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]")
.run();
}

View File

@ -747,6 +747,15 @@ fn does_not_use_empty_rustc_wrapper() {
p.cargo("check").env("RUSTC_WRAPPER", "").run(); p.cargo("check").env("RUSTC_WRAPPER", "").run();
} }
#[cargo_test]
fn does_not_use_empty_rustc_workspace_wrapper() {
let p = project().file("src/lib.rs", "").build();
p.cargo("check -Zunstable-options")
.masquerade_as_nightly_cargo()
.env("RUSTC_WORKSPACE_WRAPPER", "")
.run();
}
#[cargo_test] #[cargo_test]
fn error_from_deep_recursion() -> Result<(), fmt::Error> { fn error_from_deep_recursion() -> Result<(), fmt::Error> {
let mut big_macro = String::new(); let mut big_macro = String::new();
@ -767,3 +776,124 @@ fn error_from_deep_recursion() -> Result<(), fmt::Error> {
Ok(()) Ok(())
} }
#[cargo_test]
fn rustc_workspace_wrapper_affects_all_workspace_members() {
use cargo_test_support::paths;
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["bar", "baz"]
"#,
)
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
p.cargo("check -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]")
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name baz [..]")
.run();
}
#[cargo_test]
fn rustc_workspace_wrapper_includes_path_deps() {
use cargo_test_support::paths;
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"
authors = []
[workspace]
members = ["bar"]
[dependencies]
baz = { path = "baz" }
"#,
)
.file("src/lib.rs", "")
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
p.cargo("check --workspace -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo [..]")
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]")
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name baz [..]")
.run();
}
#[cargo_test]
fn rustc_workspace_wrapper_respects_primary_units() {
use cargo_test_support::paths;
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["bar", "baz"]
"#,
)
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn bar() {}")
.file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
.file("baz/src/lib.rs", "pub fn baz() {}")
.build();
p.cargo("check -p bar -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]")
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]")
.run();
}
#[cargo_test]
fn rustc_workspace_wrapper_excludes_published_deps() {
use cargo_test_support::paths;
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"
authors = []
[workspace]
members = ["bar"]
[dependencies]
baz = "1.0.0"
"#,
)
.file("src/lib.rs", "")
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn bar() {}")
.build();
Package::new("baz", "1.0.0").publish();
p.cargo("check --workspace -v -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo [..]")
.with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]")
.with_stderr_contains("[CHECKING] baz [..]")
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]")
.run();
}

View File

@ -3,7 +3,8 @@
use std::fs::File; use std::fs::File;
use cargo_test_support::git; use cargo_test_support::git;
use cargo_test_support::{basic_manifest, command_is_available, project}; use cargo_test_support::paths;
use cargo_test_support::{basic_manifest, project};
use std::io::Write; use std::io::Write;
@ -1068,11 +1069,8 @@ fn doesnt_rebuild_dependencies() {
} }
#[cargo_test] #[cargo_test]
#[cfg(unix)]
fn does_not_crash_with_rustc_wrapper() { fn does_not_crash_with_rustc_wrapper() {
// We don't have /usr/bin/env on Windows.
if cfg!(windows) {
return;
}
let p = project() let p = project()
.file( .file(
"Cargo.toml", "Cargo.toml",
@ -1090,6 +1088,49 @@ fn does_not_crash_with_rustc_wrapper() {
.run(); .run();
} }
#[cargo_test]
#[cfg(unix)]
fn does_not_crash_with_rustc_workspace_wrapper() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("fix --allow-no-vcs --verbose -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", "/usr/bin/env")
.masquerade_as_nightly_cargo()
.run();
}
#[cargo_test]
fn uses_workspace_wrapper_and_primary_wrapper_override() {
// We don't have /usr/bin/env on Windows.
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("fix --allow-no-vcs --verbose -Zunstable-options")
.env("RUSTC_WORKSPACE_WRAPPER", paths::echo_wrapper())
.masquerade_as_nightly_cargo()
.with_stderr_contains("WRAPPER CALLED: rustc src/lib.rs --crate-name foo [..]")
.run();
}
#[cargo_test] #[cargo_test]
fn only_warn_for_relevant_crates() { fn only_warn_for_relevant_crates() {
let p = project() let p = project()
@ -1251,47 +1292,6 @@ fn fix_in_existing_repo_weird_ignore() {
p.cargo("fix").cwd("src").run(); p.cargo("fix").cwd("src").run();
} }
#[cargo_test]
fn fix_with_clippy() {
if !command_is_available("clippy-driver") {
return;
}
let p = project()
.file(
"src/lib.rs",
"
pub fn foo() {
let mut v = Vec::<String>::new();
let _ = v.iter_mut().filter(|&ref a| a.is_empty());
}
",
)
.build();
let stderr = "\
[CHECKING] foo v0.0.1 ([..])
[FIXING] src/lib.rs (1 fix)
[FINISHED] [..]
";
p.cargo("fix -Zunstable-options --clippy --allow-no-vcs")
.masquerade_as_nightly_cargo()
.with_stderr(stderr)
.with_stdout("")
.run();
assert_eq!(
p.read_file("src/lib.rs"),
"
pub fn foo() {
let mut v = Vec::<String>::new();
let _ = v.iter_mut().filter(|a| a.is_empty());
}
"
);
}
#[cargo_test] #[cargo_test]
fn fix_color_message() { fn fix_color_message() {
// Check that color appears in diagnostics. // Check that color appears in diagnostics.

View File

@ -28,7 +28,6 @@ mod cargo_targets;
mod cfg; mod cfg;
mod check; mod check;
mod clean; mod clean;
mod clippy;
mod collisions; mod collisions;
mod concurrent; mod concurrent;
mod config; mod config;