@@ -2,13 +2,15 @@ use std::env;
22use std:: path:: PathBuf ;
33
44use crate :: core:: build_steps:: compile;
5- use crate :: core:: build_steps:: tool:: { self , Tool } ;
6- use crate :: core:: builder:: { self , Builder , Compiler , Kind , ShouldRun , Step } ;
5+ use crate :: core:: build_steps:: tool:: { self , SourceType , Tool } ;
6+ use crate :: core:: builder:: {
7+ self , Builder , Compiler , Kind , RunConfig , ShouldRun , Step , crate_description,
8+ } ;
79use crate :: core:: config:: TargetSelection ;
810use crate :: utils:: exec:: { BootstrapCommand , command} ;
911use crate :: utils:: helpers:: { self , dylib_path, dylib_path_var, t} ;
1012use crate :: utils:: render_tests:: add_flags_and_try_run_tests;
11- use crate :: { DocTests , envify} ;
13+ use crate :: { DocTests , Mode , envify} ;
1214
1315/// Given a `cargo test` subcommand, add the appropriate flags and run it.
1416///
@@ -175,3 +177,150 @@ impl Step for RemoteCopyLibs {
175177 }
176178 }
177179}
180+
181+ // FIXME(#137178): `Crate` is very confusing, probably need to be split into two steps?
182+
183+ /// Runs `cargo test` for standard library crates.
184+ ///
185+ /// (Also used internally to run `cargo test` for compiler crates.)
186+ ///
187+ /// FIXME(Zalathar): Try to split this into two separate steps: a user-visible step for testing
188+ /// standard library crates, and an internal step used for both library crates and compiler crates.
189+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
190+ pub struct Crate {
191+ pub compiler : Compiler ,
192+ pub target : TargetSelection ,
193+ pub mode : Mode ,
194+ pub crates : Vec < String > ,
195+ }
196+
197+ impl Step for Crate {
198+ type Output = ( ) ;
199+ const DEFAULT : bool = true ;
200+
201+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
202+ run. crate_or_deps ( "sysroot" ) . crate_or_deps ( "coretests" )
203+ }
204+
205+ fn make_run ( run : RunConfig < ' _ > ) {
206+ let builder = run. builder ;
207+ let host = run. build_triple ( ) ;
208+ let compiler = builder. compiler_for ( builder. top_stage , host, host) ;
209+ let crates = run
210+ . paths
211+ . iter ( )
212+ . map ( |p| builder. crate_paths [ & p. assert_single_path ( ) . path ] . clone ( ) )
213+ . collect ( ) ;
214+
215+ builder. ensure ( Crate { compiler, target : run. target , mode : Mode :: Std , crates } ) ;
216+ }
217+
218+ /// Runs all unit tests plus documentation tests for a given crate defined
219+ /// by a `Cargo.toml` (single manifest)
220+ ///
221+ /// This is what runs tests for crates like the standard library, compiler, etc.
222+ /// It essentially is the driver for running `cargo test`.
223+ ///
224+ /// Currently this runs all tests for a DAG by passing a bunch of `-p foo`
225+ /// arguments, and those arguments are discovered from `cargo metadata`.
226+ fn run ( self , builder : & Builder < ' _ > ) {
227+ let compiler = self . compiler ;
228+ let target = self . target ;
229+ let mode = self . mode ;
230+
231+ // Prepare sysroot
232+ // See [field@compile::Std::force_recompile].
233+ builder. ensure ( compile:: Std :: new ( compiler, compiler. host ) . force_recompile ( true ) ) ;
234+
235+ // If we're not doing a full bootstrap but we're testing a stage2
236+ // version of libstd, then what we're actually testing is the libstd
237+ // produced in stage1. Reflect that here by updating the compiler that
238+ // we're working with automatically.
239+ let compiler = builder. compiler_for ( compiler. stage , compiler. host , target) ;
240+
241+ let mut cargo = if builder. kind == Kind :: Miri {
242+ if builder. top_stage == 0 {
243+ eprintln ! ( "ERROR: `x.py miri` requires stage 1 or higher" ) ;
244+ std:: process:: exit ( 1 ) ;
245+ }
246+
247+ // Build `cargo miri test` command
248+ // (Implicitly prepares target sysroot)
249+ let mut cargo = builder:: Cargo :: new (
250+ builder,
251+ compiler,
252+ mode,
253+ SourceType :: InTree ,
254+ target,
255+ Kind :: MiriTest ,
256+ ) ;
257+ // This hack helps bootstrap run standard library tests in Miri. The issue is as
258+ // follows: when running `cargo miri test` on libcore, cargo builds a local copy of core
259+ // and makes it a dependency of the integration test crate. This copy duplicates all the
260+ // lang items, so the build fails. (Regular testing avoids this because the sysroot is a
261+ // literal copy of what `cargo build` produces, but since Miri builds its own sysroot
262+ // this does not work for us.) So we need to make it so that the locally built libcore
263+ // contains all the items from `core`, but does not re-define them -- we want to replace
264+ // the entire crate but a re-export of the sysroot crate. We do this by swapping out the
265+ // source file: if `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a
266+ // `lib.rs` file, and a `lib.miri.rs` file exists in the same folder, we build that
267+ // instead. But crucially we only do that for the library, not the test builds.
268+ cargo. env ( "MIRI_REPLACE_LIBRS_IF_NOT_TEST" , "1" ) ;
269+ // std needs to be built with `-Zforce-unstable-if-unmarked`. For some reason the builder
270+ // does not set this directly, but relies on the rustc wrapper to set it, and we are not using
271+ // the wrapper -- hence we have to set it ourselves.
272+ cargo. rustflag ( "-Zforce-unstable-if-unmarked" ) ;
273+ cargo
274+ } else {
275+ // Also prepare a sysroot for the target.
276+ if !builder. is_builder_target ( target) {
277+ builder. ensure ( compile:: Std :: new ( compiler, target) . force_recompile ( true ) ) ;
278+ builder. ensure ( RemoteCopyLibs { compiler, target } ) ;
279+ }
280+
281+ // Build `cargo test` command
282+ builder:: Cargo :: new ( builder, compiler, mode, SourceType :: InTree , target, builder. kind )
283+ } ;
284+
285+ match mode {
286+ Mode :: Std => {
287+ if builder. kind == Kind :: Miri {
288+ // We can't use `std_cargo` as that uses `optimized-compiler-builtins` which
289+ // needs host tools for the given target. This is similar to what `compile::Std`
290+ // does when `is_for_mir_opt_tests` is true. There's probably a chance for
291+ // de-duplication here... `std_cargo` should support a mode that avoids needing
292+ // host tools.
293+ cargo
294+ . arg ( "--manifest-path" )
295+ . arg ( builder. src . join ( "library/sysroot/Cargo.toml" ) ) ;
296+ } else {
297+ compile:: std_cargo ( builder, target, compiler. stage , & mut cargo) ;
298+ // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`,
299+ // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`.
300+ // Override it.
301+ if builder. download_rustc ( ) && compiler. stage > 0 {
302+ let sysroot = builder
303+ . out
304+ . join ( compiler. host )
305+ . join ( format ! ( "stage{}-test-sysroot" , compiler. stage) ) ;
306+ cargo. env ( "RUSTC_SYSROOT" , sysroot) ;
307+ }
308+ }
309+ }
310+ Mode :: Rustc => {
311+ compile:: rustc_cargo ( builder, & mut cargo, target, & compiler, & self . crates ) ;
312+ }
313+ _ => panic ! ( "can only test libraries" ) ,
314+ } ;
315+
316+ run_cargo_test (
317+ cargo,
318+ & [ ] ,
319+ & self . crates ,
320+ & self . crates [ 0 ] ,
321+ & * crate_description ( & self . crates ) ,
322+ target,
323+ builder,
324+ ) ;
325+ }
326+ }
0 commit comments