@@ -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>)`.
864876async 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