Skip to content

Commit 3d85426

Browse files
Tom Faysylvestre
authored andcommitted
handle more rustc wrapping scenarios
the wrapped rustc may not just be "rustc", it may have an extension or more path components
1 parent 9b18ed5 commit 3d85426

1 file changed

Lines changed: 65 additions & 42 deletions

File tree

src/compiler/compiler.rs

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,18 @@ pub async fn write_temp_file(
860860
.context("failed to write temporary file")
861861
}
862862

863+
/// Returns true if the given path looks like a program known to have
864+
/// a rustc compatible interface.
865+
fn is_rustc_like<P: AsRef<Path>>(p: P) -> bool {
866+
matches!(
867+
p.as_ref()
868+
.file_stem()
869+
.map(|s| s.to_string_lossy().to_lowercase())
870+
.as_deref(),
871+
Some("rustc") | Some("clippy-driver")
872+
)
873+
}
874+
863875
/// If `executable` is a known compiler, return `Some(Box<Compiler>)`.
864876
async fn detect_compiler<T>(
865877
creator: T,
@@ -876,58 +888,58 @@ where
876888
trace!("detect_compiler: {}", executable.display());
877889

878890
// First, see if this looks like rustc.
879-
let filename = match executable.file_stem() {
880-
None => bail!("could not determine compiler kind"),
881-
Some(f) => f,
882-
};
883-
let filename = filename.to_string_lossy().to_lowercase();
884-
885-
// Detect the secnario where cargo is used with sccache as a RUSTC_WRAPPER and another tool as a
886-
// RUSTC_WORKSPACE_WRAPPER. In that case "rustc" will be the first argument rather than the command.
887-
// As a guardrail against false positives, also check for the CARGO environment variable which
888-
// cargo sets for any process it runs.
889-
// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads
890-
let using_rustc_workspace_wrapper = env.iter().any(|(k, _)| k == OsStr::new("CARGO"))
891-
&& filename != "rustc"
892-
&& args.iter().next().map(|arg1| arg1.as_os_str()) == Some(OsStr::new("rustc"));
893-
894-
let rustc_vv =
895-
if filename == "rustc" || filename == "clippy-driver" || using_rustc_workspace_wrapper {
896-
// Sanity check that it's really rustc.
897-
let executable = executable.to_path_buf();
898-
let mut child = creator.clone().new_command_sync(executable);
899-
// rustc workspace wrappers expect rustc as the first argument
900-
if using_rustc_workspace_wrapper {
901-
child.arg("rustc");
891+
892+
let rustc_executable = if is_rustc_like(executable) {
893+
Some(executable.to_path_buf())
894+
} else if env.iter().any(|(k, _)| k == OsStr::new("CARGO")) {
895+
// If not, detect the scenario where cargo is configured to wrap rustc with something other than sccache.
896+
// This happens when sccache is used as a RUSTC_WRAPPER and another tool is used as a
897+
// RUSTC_WORKSPACE_WRAPPER. In that case rustc will be the first argument rather than the command.
898+
//
899+
// The check for the CARGO env acts as a guardrail against false positives.
900+
// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads
901+
args.iter().next().and_then(|arg1| {
902+
if is_rustc_like(arg1) {
903+
Some(PathBuf::from(arg1))
904+
} else {
905+
None
902906
}
907+
})
908+
} else {
909+
None
910+
};
903911

904-
child.env_clear().envs(ref_env(env)).args(&["-vV"]);
912+
let rustc_vv = if let Some(rustc_executable) = &rustc_executable {
913+
// Sanity check that it's really rustc.
914+
let mut child = creator.clone().new_command_sync(executable);
915+
// We're wrapping rustc if the executable doesn't match the detected rustc_executable. In this case the wrapper
916+
// expects rustc as the first argument.
917+
if rustc_executable != executable {
918+
child.arg(rustc_executable);
919+
}
905920

906-
run_input_output(child, None).await.map(|output| {
907-
if let Ok(stdout) = String::from_utf8(output.stdout.clone()) {
908-
if stdout.starts_with("rustc ") {
909-
return Some(Ok(stdout));
910-
}
921+
child.env_clear().envs(ref_env(env)).args(&["-vV"]);
922+
923+
run_input_output(child, None).await.map(|output| {
924+
if let Ok(stdout) = String::from_utf8(output.stdout.clone()) {
925+
if stdout.starts_with("rustc ") {
926+
return Some(Ok(stdout));
911927
}
912-
Some(Err(ProcessError(output)))
913-
})?
914-
} else {
915-
None
916-
};
928+
}
929+
Some(Err(ProcessError(output)))
930+
})?
931+
} else {
932+
None
933+
};
917934

918935
let creator1 = creator.clone();
919-
let executable = executable.to_owned();
920936
let pool = pool.clone();
921937
let cwd = cwd.to_owned();
922938
match rustc_vv {
923939
Some(Ok(rustc_verbose_version)) => {
924940
debug!("Found rustc");
925941

926-
let executable = if using_rustc_workspace_wrapper {
927-
PathBuf::from("rustc")
928-
} else {
929-
executable.to_owned()
930-
};
942+
let executable = rustc_executable.unwrap();
931943
let executable2 = executable.clone();
932944

933945
let proxy = RustupProxy::find_proxy_executable::<T>(
@@ -997,6 +1009,7 @@ where
9971009
}
9981010
Some(Err(e)) => Err(e).context("Failed to launch subprocess for compiler determination"),
9991011
None => {
1012+
let executable = executable.to_owned();
10001013
let cc = detect_c_compiler(creator, executable, env.to_vec(), pool).await;
10011014
cc.map(|c| (c, None))
10021015
}
@@ -1284,7 +1297,7 @@ mod test {
12841297
// Windows uses bin, everything else uses lib. Just create both.
12851298
fs::create_dir(f.tempdir.path().join("lib")).unwrap();
12861299
fs::create_dir(f.tempdir.path().join("bin")).unwrap();
1287-
let rustc = f.mk_bin("rustc").unwrap();
1300+
let rustc = f.mk_bin("rustc.exe").unwrap();
12881301
let creator = new_creator();
12891302
let runtime = single_threaded_runtime();
12901303
let pool = runtime.handle();
@@ -1296,6 +1309,15 @@ mod test {
12961309
assert_eq!(CompilerKind::Rust, c.kind());
12971310
}
12981311

1312+
#[test]
1313+
fn test_is_rustc_like() {
1314+
assert!(is_rustc_like("rustc"));
1315+
assert!(is_rustc_like("rustc.exe"));
1316+
assert!(is_rustc_like("/path/to/rustc.exe"));
1317+
assert!(is_rustc_like("/path/to/clippy-driver.exe"));
1318+
assert!(!is_rustc_like("rust"));
1319+
}
1320+
12991321
fn populate_rustc_command_mock(
13001322
creator: &Arc<std::sync::Mutex<MockCommandCreator>>,
13011323
f: &TestFixture,
@@ -1338,7 +1360,8 @@ LLVM version: 6.0",
13381360
creator,
13391361
&rustc,
13401362
f.tempdir.path(),
1341-
&[OsString::from("rustc")],
1363+
// Specifying an extension tests the ignoring
1364+
&[OsString::from("rustc.exe")],
13421365
&[(OsString::from("CARGO"), OsString::from("CARGO"))],
13431366
pool,
13441367
None,

0 commit comments

Comments
 (0)