diff --git a/crates/cargo-test-support/src/lib.rs b/crates/cargo-test-support/src/lib.rs index f3e0a54901d..ef69f852e3e 100644 --- a/crates/cargo-test-support/src/lib.rs +++ b/crates/cargo-test-support/src/lib.rs @@ -1679,6 +1679,7 @@ thread_local!( pub static RUSTC: Rustc = Rustc::new( PathBuf::from("rustc"), None, + None, Path::new("should be path to rustup rustc, but we don't care in tests"), None, ).unwrap() diff --git a/crates/cargo-test-support/src/paths.rs b/crates/cargo-test-support/src/paths.rs index 6618ef03172..c5067625a59 100644 --- a/crates/cargo-test-support/src/paths.rs +++ b/crates/cargo-test-support/src/paths.rs @@ -1,3 +1,4 @@ +use crate::{basic_manifest, project}; use filetime::{self, FileTime}; use lazy_static::lazy_static; use std::cell::RefCell; @@ -264,3 +265,24 @@ pub fn sysroot() -> String { let sysroot = String::from_utf8(output.stdout).unwrap(); 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::>(); + 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") +} diff --git a/src/bin/cargo/commands/clippy.rs b/src/bin/cargo/commands/clippy.rs deleted file mode 100644 index d395a9d1c90..00000000000 --- a/src/bin/cargo/commands/clippy.rs +++ /dev/null @@ -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::>()); - } - - compile_opts.build_config.primary_unit_rustc = Some(wrapper); - - ops::compile(&ws, &compile_opts)?; - Ok(()) -} diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index 7bd7079e194..3825c3cc612 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -72,15 +72,6 @@ pub fn cli() -> App { .long("allow-staged") .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( "\ 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. 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 { opts.filter = CompileFilter::Only { 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_staged: args.is_present("allow-staged"), broken_code: args.is_present("broken-code"), - clippy_args, }, )?; Ok(()) diff --git a/src/bin/cargo/commands/mod.rs b/src/bin/cargo/commands/mod.rs index 16e02774db3..7d4fe68c6e2 100644 --- a/src/bin/cargo/commands/mod.rs +++ b/src/bin/cargo/commands/mod.rs @@ -6,7 +6,6 @@ pub fn builtin() -> Vec { build::cli(), check::cli(), clean::cli(), - clippy::cli(), doc::cli(), fetch::cli(), fix::cli(), @@ -43,7 +42,6 @@ pub fn builtin_exec(cmd: &str) -> Option) -> Cli "build" => build::exec, "check" => check::exec, "clean" => clean::exec, - "clippy-preview" => clippy::exec, "doc" => doc::exec, "fetch" => fetch::exec, "fix" => fix::exec, @@ -80,7 +78,6 @@ pub mod bench; pub mod build; pub mod check; pub mod clean; -pub mod clippy; pub mod doc; pub mod fetch; pub mod fix; diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs index c5c37e611b6..26050e5db2b 100644 --- a/src/cargo/core/compiler/build_config.rs +++ b/src/cargo/core/compiler/build_config.rs @@ -22,7 +22,7 @@ pub struct BuildConfig { pub force_rebuild: bool, /// Output a build plan to stdout instead of actually compiling. 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, pub rustfix_diagnostic_server: RefCell>, } diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 1ac7f5be172..58a6c39d2da 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -73,8 +73,14 @@ pub struct Compilation<'cfg> { pub target: String, config: &'cfg Config, + + /// Rustc process to be used by default rustc_process: ProcessBuilder, - primary_unit_rustc_process: Option, + /// 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, target_runner: Option<(PathBuf, Vec)>, } @@ -85,13 +91,14 @@ impl<'cfg> Compilation<'cfg> { default_kind: CompileKind, ) -> CargoResult> { let mut rustc = bcx.rustc().process(); - - let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone(); + let mut primary_rustc_process = bcx.build_config.primary_unit_rustc.clone(); + let mut rustc_workspace_wrapper_process = bcx.rustc().workspace_process(); if bcx.config.extra_verbose() { 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(); } } @@ -120,7 +127,8 @@ impl<'cfg> Compilation<'cfg> { rustdocflags: HashMap::new(), config: bcx.config, rustc_process: rustc, - primary_unit_rustc_process, + rustc_workspace_wrapper_process, + primary_rustc_process, host: bcx.host_triple().to_string(), target: bcx.target_data.short_name(&default_kind).to_string(), target_runner: target_runner(bcx, default_kind)?, @@ -128,11 +136,16 @@ impl<'cfg> Compilation<'cfg> { } /// See `process`. - pub fn rustc_process(&self, pkg: &Package, is_primary: bool) -> CargoResult { - let rustc = if is_primary { - self.primary_unit_rustc_process - .clone() - .unwrap_or_else(|| self.rustc_process.clone()) + pub fn rustc_process( + &self, + pkg: &Package, + is_primary: bool, + is_workspace: bool, + ) -> CargoResult { + 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 { self.rustc_process.clone() }; diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 515ebb274fe..8df63c4ce28 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -616,11 +616,11 @@ fn compute_metadata<'a, 'cfg>( 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 // artifacts are separate from the `check` ones. - if let Some(proc) = &cx.bcx.build_config.primary_unit_rustc { - proc.get_program().hash(&mut hasher); + if let Some(path) = &cx.bcx.rustc().workspace_wrapper { + path.hash(&mut hasher); } } diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 3fca458069e..48f134dbb71 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -1100,22 +1100,12 @@ fn calculate_normal<'a, 'cfg>( // 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 // change. - let mut extra_flags = if unit.mode.is_doc() { + let extra_flags = if unit.mode.is_doc() { cx.bcx.rustdocflags_args(unit) } else { cx.bcx.rustflags_args(unit) } .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))); // Include metadata since it is exposed as environment variables. diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index d269ea7eb0e..1505e2f09b4 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -538,8 +538,11 @@ fn prepare_rustc<'a, 'cfg>( unit: &Unit<'a>, ) -> CargoResult { 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 { let client = cx.new_jobserver()?; base.inherit_jobserver(&client); diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 1ae80ac2d8a..c53cca17b1b 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -53,9 +53,10 @@ use rustfix::{self, CodeFix}; use crate::core::Workspace; use crate::ops::{self, CompileOptions}; +use crate::util; use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer}; use crate::util::errors::CargoResult; -use crate::util::{self, paths}; +use crate::util::ProcessBuilder; use crate::util::{existing_vcs_repo, LockServer, LockServerClient}; 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 EDITION_ENV: &str = "__CARGO_FIX_EDITION"; const IDIOMS_ENV: &str = "__CARGO_FIX_IDIOMS"; -const CLIPPY_FIX_ARGS: &str = "__CARGO_FIX_CLIPPY_ARGS"; pub struct FixOptions<'a> { pub edition: bool, @@ -74,7 +74,6 @@ pub struct FixOptions<'a> { pub allow_no_vcs: bool, pub allow_staged: bool, pub broken_code: bool, - pub clippy_args: Option>, } 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"); } - 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 .compile_opts .build_config @@ -222,12 +208,17 @@ pub fn fix_maybe_exec_rustc() -> CargoResult { let args = FixArgs::get(); trace!("cargo-fix as rustc got file {:?}", args.file); + 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(); if let Some(path) = &args.file { 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. @@ -239,7 +230,7 @@ pub fn fix_maybe_exec_rustc() -> CargoResult { // new rustc, and otherwise we capture the output to hide it in the scenario // that we have to back it all out. if !fixes.files.is_empty() { - let mut cmd = Command::new(&rustc); + let mut cmd = rustc.build_command(); args.apply(&mut cmd); cmd.arg("--error-format=json"); let output = cmd.output().context("failed to spawn rustc")?; @@ -279,7 +270,7 @@ pub fn fix_maybe_exec_rustc() -> CargoResult { // - If the fix failed, show the original warnings and suggestions. // - If `--broken-code`, show the error messages. // - If the fix succeeded, show any remaining warnings. - let mut cmd = Command::new(&rustc); + let mut cmd = rustc.build_command(); args.apply(&mut cmd); for arg in args.format_args { // Add any json/error format arguments that Cargo wants. This allows @@ -302,7 +293,7 @@ struct FixedFile { fn rustfix_crate( lock_addr: &str, - rustc: &Path, + rustc: &ProcessBuilder, filename: &Path, args: &FixArgs, ) -> Result { @@ -402,7 +393,7 @@ fn rustfix_crate( /// and any errors encountered while fixing files. fn rustfix_and_fix( fixes: &mut FixedCrate, - rustc: &Path, + rustc: &ProcessBuilder, filename: &Path, args: &FixArgs, ) -> Result<(), Error> { @@ -410,12 +401,15 @@ fn rustfix_and_fix( // TODO: implement a way to specify this. let only = HashSet::new(); - let mut cmd = Command::new(rustc); + let mut cmd = rustc.build_command(); cmd.arg("--error-format=json"); args.apply(&mut cmd); - let output = cmd - .output() - .with_context(|| format!("failed to execute `{}`", rustc.display()))?; + let output = cmd.output().with_context(|| { + format!( + "failed to execute `{}`", + rustc.get_program().to_string_lossy() + ) + })?; // 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 @@ -491,7 +485,7 @@ fn rustfix_and_fix( // 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 // going. - let code = match paths::read(file.as_ref()) { + let code = match util::paths::read(file.as_ref()) { Ok(s) => s, Err(e) => { warn!("failed to read `{}`: {}", file, e); @@ -591,7 +585,6 @@ struct FixArgs { enabled_edition: Option, other: Vec, rustc: Option, - clippy_args: Vec, format_args: Vec, } @@ -611,12 +604,7 @@ impl FixArgs { fn get() -> FixArgs { let mut ret = FixArgs::default(); - if let Ok(clippy_args) = env::var(CLIPPY_FIX_ARGS) { - 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); - } + ret.rustc = env::args_os().nth(1).map(PathBuf::from); for arg in env::args_os().skip(2) { let path = PathBuf::from(arg); @@ -654,10 +642,6 @@ impl FixArgs { cmd.arg(path); } - if !self.clippy_args.is_empty() { - cmd.args(&self.clippy_args); - } - cmd.args(&self.other).arg("--cap-lints=warn"); if let Some(edition) = &self.enabled_edition { cmd.arg("--edition").arg(edition); diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 66b4e8afb43..e32a82eda09 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -314,9 +314,19 @@ impl Config { .into_path_unlocked() }); 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( self.get_tool("rustc", &self.build_config()?.rustc), wrapper, + rustc_workspace_wrapper, &self .home() .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)] #[serde(rename_all = "kebab-case")] pub struct CargoHttpConfig { @@ -1714,6 +1715,7 @@ pub struct CargoBuildConfig { pub rustflags: Option, pub rustdocflags: Option, pub rustc_wrapper: Option, + pub rustc_workspace_wrapper: Option, pub rustc: Option, pub rustdoc: Option, pub out_dir: Option, diff --git a/src/cargo/util/process_builder.rs b/src/cargo/util/process_builder.rs index 0639ead36c3..953f8ae58af 100644 --- a/src/cargo/util/process_builder.rs +++ b/src/cargo/util/process_builder.rs @@ -6,6 +6,7 @@ use std::collections::BTreeMap; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt; +use std::iter::once; use std::path::Path; use std::process::{Command, Output, Stdio}; @@ -326,6 +327,37 @@ impl ProcessBuilder { } 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>) -> 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`. diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs index 0a3e6e46276..f9655fbd825 100644 --- a/src/cargo/util/rustc.rs +++ b/src/cargo/util/rustc.rs @@ -20,7 +20,9 @@ pub struct Rustc { pub path: PathBuf, /// An optional program that will be passed the path of the rust exe as its first argument, and /// rustc args following this. - pub wrapper: Option, + pub wrapper: Option, + /// An optional wrapper to be used in addition to `rustc.wrapper` for workspace crates + pub workspace_wrapper: Option, /// Verbose version information (the output of `rustc -vV`) pub verbose_version: String, /// The host triple (arch-platform-OS), this comes from verbose_version. @@ -37,6 +39,7 @@ impl Rustc { pub fn new( path: PathBuf, wrapper: Option, + workspace_wrapper: Option, rustup_rustc: &Path, cache_location: Option, ) -> CargoResult { @@ -64,7 +67,8 @@ impl Rustc { Ok(Rustc { path, - wrapper: wrapper.map(util::process), + wrapper, + workspace_wrapper, verbose_version, host, 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`. - pub fn process_with(&self, path: impl AsRef) -> ProcessBuilder { - match self.wrapper { - 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()), - } + pub fn process(&self) -> ProcessBuilder { + util::process(self.path.as_path()).wrapped(self.wrapper.as_ref()) } /// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`. - pub fn process(&self) -> ProcessBuilder { - self.process_with(&self.path) + pub fn workspace_process(&self) -> ProcessBuilder { + util::process(self.path.as_path()) + .wrapped(self.workspace_wrapper.as_ref()) + .wrapped(self.wrapper.as_ref()) } pub fn process_no_wrapper(&self) -> ProcessBuilder { @@ -95,10 +94,6 @@ impl Rustc { pub fn cached_output(&self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> { 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 diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index a5a80f45f59..aca629ebdf0 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -3700,6 +3700,42 @@ fn rustc_wrapper_from_path() { .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] fn cdylib_not_lifted() { let p = project() diff --git a/tests/testsuite/cache_messages.rs b/tests/testsuite/cache_messages.rs index bc5f365b90a..0503306b943 100644 --- a/tests/testsuite/cache_messages.rs +++ b/tests/testsuite/cache_messages.rs @@ -1,8 +1,7 @@ //! Tests for caching compiler diagnostics. use cargo_test_support::{ - basic_manifest, command_is_available, is_coarse_mtime, process, project, registry::Package, - sleep_ms, + basic_manifest, is_coarse_mtime, process, project, registry::Package, sleep_ms, }; use std::path::Path; @@ -274,56 +273,6 @@ fn fix() { 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] fn very_verbose() { // Handle cap-lints in dependencies. @@ -496,3 +445,49 @@ fn caching_large_output() { )) .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(); +} diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index 27f37788f5f..aecf66105c6 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -752,6 +752,15 @@ fn does_not_use_empty_rustc_wrapper() { 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] fn error_from_deep_recursion() -> Result<(), fmt::Error> { let mut big_macro = String::new(); @@ -772,3 +781,124 @@ fn error_from_deep_recursion() -> Result<(), fmt::Error> { 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(); +} diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index 178379a1e12..d8f28ae22b0 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -3,7 +3,8 @@ use std::fs::File; 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; @@ -1068,11 +1069,8 @@ fn doesnt_rebuild_dependencies() { } #[cargo_test] +#[cfg(unix)] fn does_not_crash_with_rustc_wrapper() { - // We don't have /usr/bin/env on Windows. - if cfg!(windows) { - return; - } let p = project() .file( "Cargo.toml", @@ -1090,6 +1088,49 @@ fn does_not_crash_with_rustc_wrapper() { .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] fn only_warn_for_relevant_crates() { let p = project() @@ -1251,47 +1292,6 @@ fn fix_in_existing_repo_weird_ignore() { 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::::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::::new(); - let _ = v.iter_mut().filter(|a| a.is_empty()); - } - " - ); -} - #[cargo_test] fn fix_color_message() { // Check that color appears in diagnostics. diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index b6cd7f643fb..d01d30fdebd 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -28,7 +28,6 @@ mod cargo_targets; mod cfg; mod check; mod clean; -mod clippy; mod collisions; mod concurrent; mod config;