@@ -18,7 +18,9 @@ use crate::core::build_steps::{check, clean, compile, dist, doc, install, run, s
1818use  crate :: core:: config:: flags:: { Color ,  Subcommand } ; 
1919use  crate :: core:: config:: { DryRun ,  SplitDebuginfo ,  TargetSelection } ; 
2020use  crate :: utils:: cache:: { Cache ,  Interned ,  INTERNER } ; 
21- use  crate :: utils:: helpers:: { self ,  add_dylib_path,  add_link_lib_path,  exe,  libdir,  output,  t} ; 
21+ use  crate :: utils:: helpers:: { 
22+     self ,  add_dylib_path,  add_link_lib_path,  dylib_path,  dylib_path_var,  exe,  libdir,  output,  t, 
23+ } ; 
2224use  crate :: Crate ; 
2325use  crate :: EXTRA_CHECK_CFGS ; 
2426use  crate :: { Build ,  CLang ,  DocTests ,  GitRepo ,  Mode } ; 
@@ -1151,6 +1153,43 @@ impl<'a> Builder<'a> {
11511153        self . ensure ( tool:: Rustdoc  {  compiler } ) 
11521154    } 
11531155
1156+     pub  fn  cargo_clippy_cmd ( & self ,  run_compiler :  Compiler )  -> Command  { 
1157+         let  initial_sysroot_bin = self . initial_rustc . parent ( ) . unwrap ( ) ; 
1158+         // Set PATH to include the sysroot bin dir so clippy can find cargo. 
1159+         let  path = t ! ( env:: join_paths( 
1160+             // The sysroot comes first in PATH to avoid using rustup's cargo. 
1161+             std:: iter:: once( PathBuf :: from( initial_sysroot_bin) ) 
1162+                 . chain( env:: split_paths( & t!( env:: var( "PATH" ) ) ) ) 
1163+         ) ) ; 
1164+ 
1165+         if  run_compiler. stage  == 0  { 
1166+             // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy. 
1167+             let  cargo_clippy = self . build . config . download_clippy ( ) ; 
1168+             let  mut  cmd = Command :: new ( cargo_clippy) ; 
1169+             cmd. env ( "PATH" ,  & path) ; 
1170+             return  cmd; 
1171+         } 
1172+ 
1173+         let  build_compiler = self . compiler ( run_compiler. stage  - 1 ,  self . build . build ) ; 
1174+         self . ensure ( tool:: Clippy  { 
1175+             compiler :  build_compiler, 
1176+             target :  self . build . build , 
1177+             extra_features :  vec ! [ ] , 
1178+         } ) ; 
1179+         let  cargo_clippy = self . ensure ( tool:: CargoClippy  { 
1180+             compiler :  build_compiler, 
1181+             target :  self . build . build , 
1182+             extra_features :  vec ! [ ] , 
1183+         } ) ; 
1184+         let  mut  dylib_path = dylib_path ( ) ; 
1185+         dylib_path. insert ( 0 ,  self . sysroot ( run_compiler) . join ( "lib" ) ) ; 
1186+ 
1187+         let  mut  cmd = Command :: new ( cargo_clippy. unwrap ( ) ) ; 
1188+         cmd. env ( dylib_path_var ( ) ,  env:: join_paths ( & dylib_path) . unwrap ( ) ) ; 
1189+         cmd. env ( "PATH" ,  path) ; 
1190+         cmd
1191+     } 
1192+ 
11541193    pub  fn  rustdoc_cmd ( & self ,  compiler :  Compiler )  -> Command  { 
11551194        let  mut  cmd = Command :: new ( & self . bootstrap_out . join ( "rustdoc" ) ) ; 
11561195        cmd. env ( "RUSTC_STAGE" ,  compiler. stage . to_string ( ) ) 
@@ -1201,7 +1240,12 @@ impl<'a> Builder<'a> {
12011240        target :  TargetSelection , 
12021241        cmd :  & str , 
12031242    )  -> Command  { 
1204-         let  mut  cargo = Command :: new ( & self . initial_cargo ) ; 
1243+         let  mut  cargo = if  cmd == "clippy"  { 
1244+             self . cargo_clippy_cmd ( compiler) 
1245+         }  else  { 
1246+             Command :: new ( & self . initial_cargo ) 
1247+         } ; 
1248+ 
12051249        // Run cargo from the source root so it can find .cargo/config. 
12061250        // This matters when using vendoring and the working directory is outside the repository. 
12071251        cargo. current_dir ( & self . src ) ; 
@@ -1325,6 +1369,23 @@ impl<'a> Builder<'a> {
13251369            compiler. stage 
13261370        } ; 
13271371
1372+         // We synthetically interpret a stage0 compiler used to build tools as a 
1373+         // "raw" compiler in that it's the exact snapshot we download. Normally 
1374+         // the stage0 build means it uses libraries build by the stage0 
1375+         // compiler, but for tools we just use the precompiled libraries that 
1376+         // we've downloaded 
1377+         let  use_snapshot = mode == Mode :: ToolBootstrap ; 
1378+         assert ! ( !use_snapshot || stage == 0  || self . local_rebuild) ; 
1379+ 
1380+         let  maybe_sysroot = self . sysroot ( compiler) ; 
1381+         let  sysroot = if  use_snapshot {  self . rustc_snapshot_sysroot ( )  }  else  {  & maybe_sysroot } ; 
1382+         let  libdir = self . rustc_libdir ( compiler) ; 
1383+ 
1384+         let  sysroot_str = sysroot. as_os_str ( ) . to_str ( ) . expect ( "sysroot should be UTF-8" ) ; 
1385+         if  !matches ! ( self . config. dry_run,  DryRun :: SelfCheck )  { 
1386+             self . verbose_than ( 0 ,  & format ! ( "using sysroot {sysroot_str}" ) ) ; 
1387+         } 
1388+ 
13281389        let  mut  rustflags = Rustflags :: new ( target) ; 
13291390        if  stage != 0  { 
13301391            if  let  Ok ( s)  = env:: var ( "CARGOFLAGS_NOT_BOOTSTRAP" )  { 
@@ -1336,41 +1397,16 @@ impl<'a> Builder<'a> {
13361397                cargo. args ( s. split_whitespace ( ) ) ; 
13371398            } 
13381399            rustflags. env ( "RUSTFLAGS_BOOTSTRAP" ) ; 
1339-             if  cmd == "clippy"  { 
1340-                 // clippy overwrites sysroot if we pass it to cargo. 
1341-                 // Pass it directly to clippy instead. 
1342-                 // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, 
1343-                 // so it has no way of knowing the sysroot. 
1344-                 rustflags. arg ( "--sysroot" ) ; 
1345-                 rustflags. arg ( 
1346-                     self . sysroot ( compiler) 
1347-                         . as_os_str ( ) 
1348-                         . to_str ( ) 
1349-                         . expect ( "sysroot must be valid UTF-8" ) , 
1350-                 ) ; 
1351-                 // Only run clippy on a very limited subset of crates (in particular, not build scripts). 
1352-                 cargo. arg ( "-Zunstable-options" ) ; 
1353-                 // Explicitly does *not* set `--cfg=bootstrap`, since we're using a nightly clippy. 
1354-                 let  host_version = Command :: new ( "rustc" ) . arg ( "--version" ) . output ( ) . map_err ( |_| ( ) ) ; 
1355-                 let  output = host_version. and_then ( |output| { 
1356-                     if  output. status . success ( )  { 
1357-                         Ok ( output) 
1358-                     }  else  { 
1359-                         Err ( ( ) ) 
1360-                     } 
1361-                 } ) . unwrap_or_else ( |_| { 
1362-                     eprintln ! ( 
1363-                         "ERROR: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component" 
1364-                     ) ; 
1365-                     eprintln ! ( "HELP: try `rustup component add clippy`" ) ; 
1366-                     crate :: exit!( 1 ) ; 
1367-                 } ) ; 
1368-                 if  !t ! ( std:: str :: from_utf8( & output. stdout) ) . contains ( "nightly" )  { 
1369-                     rustflags. arg ( "--cfg=bootstrap" ) ; 
1370-                 } 
1371-             }  else  { 
1372-                 rustflags. arg ( "--cfg=bootstrap" ) ; 
1373-             } 
1400+             rustflags. arg ( "--cfg=bootstrap" ) ; 
1401+         } 
1402+ 
1403+         if  cmd == "clippy"  { 
1404+             // clippy overwrites sysroot if we pass it to cargo. 
1405+             // Pass it directly to clippy instead. 
1406+             // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, 
1407+             // so it has no way of knowing the sysroot. 
1408+             rustflags. arg ( "--sysroot" ) ; 
1409+             rustflags. arg ( sysroot_str) ; 
13741410        } 
13751411
13761412        let  use_new_symbol_mangling = match  self . config . rust_new_symbol_mangling  { 
@@ -1565,18 +1601,6 @@ impl<'a> Builder<'a> {
15651601
15661602        let  want_rustdoc = self . doc_tests  != DocTests :: No ; 
15671603
1568-         // We synthetically interpret a stage0 compiler used to build tools as a 
1569-         // "raw" compiler in that it's the exact snapshot we download. Normally 
1570-         // the stage0 build means it uses libraries build by the stage0 
1571-         // compiler, but for tools we just use the precompiled libraries that 
1572-         // we've downloaded 
1573-         let  use_snapshot = mode == Mode :: ToolBootstrap ; 
1574-         assert ! ( !use_snapshot || stage == 0  || self . local_rebuild) ; 
1575- 
1576-         let  maybe_sysroot = self . sysroot ( compiler) ; 
1577-         let  sysroot = if  use_snapshot {  self . rustc_snapshot_sysroot ( )  }  else  {  & maybe_sysroot } ; 
1578-         let  libdir = self . rustc_libdir ( compiler) ; 
1579- 
15801604        // Clear the output directory if the real rustc we're using has changed; 
15811605        // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc. 
15821606        // 
@@ -1612,10 +1636,19 @@ impl<'a> Builder<'a> {
16121636            ) 
16131637            . env ( "RUSTC_ERROR_METADATA_DST" ,  self . extended_error_dir ( ) ) 
16141638            . env ( "RUSTC_BREAK_ON_ICE" ,  "1" ) ; 
1615-         // Clippy support is a hack and uses the default `cargo-clippy` in path. 
1616-         // Don't override RUSTC so that the `cargo-clippy` in path will be run. 
1617-         if  cmd != "clippy"  { 
1618-             cargo. env ( "RUSTC" ,  self . bootstrap_out . join ( "rustc" ) ) ; 
1639+ 
1640+         // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree 
1641+         // sysroot depending on whether we're building build scripts. 
1642+         // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not 
1643+         // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs. 
1644+         cargo. env ( "RUSTC_WRAPPER" ,  self . bootstrap_out . join ( "rustc" ) ) ; 
1645+         // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:( 
1646+         cargo. env ( "RUSTC" ,  self . bootstrap_out . join ( "rustc" ) ) ; 
1647+ 
1648+         // Someone might have set some previous rustc wrapper (e.g. 
1649+         // sccache) before bootstrap overrode it. Respect that variable. 
1650+         if  let  Some ( existing_wrapper)  = env:: var_os ( "RUSTC_WRAPPER" )  { 
1651+             cargo. env ( "RUSTC_WRAPPER_REAL" ,  existing_wrapper) ; 
16191652        } 
16201653
16211654        // Dealing with rpath here is a little special, so let's go into some 
0 commit comments