3
3
use std:: env;
4
4
use std:: fs:: { self , File } ;
5
5
use std:: io:: BufReader ;
6
- use std:: path:: PathBuf ;
6
+ use std:: path:: { Path , PathBuf } ;
7
7
use std:: process:: Command ;
8
8
9
9
use rustc_version:: VersionMeta ;
@@ -412,9 +412,25 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
412
412
// Arguments are treated very differently depending on whether this crate is
413
413
// for interpretation by Miri, or for use by a build script / proc macro.
414
414
if target_crate {
415
- // Forward arguments, but remove "link" from "--emit" to make this a check-only build .
415
+ // Forward arguments, but patched .
416
416
let emit_flag = "--emit" ;
417
+ // This hack helps bootstrap run standard library tests in Miri. The issue is as follows:
418
+ // when running `cargo miri test` on libcore, cargo builds a local copy of core and makes it
419
+ // a dependency of the integration test crate. This copy duplicates all the lang items, so
420
+ // the build fails. (Regular testing avoids this because the sysroot is a literal copy of
421
+ // what `cargo build` produces, but since Miri builds its own sysroot this does not work for
422
+ // us.) So we need to make it so that the locally built libcore contains all the items from
423
+ // `core`, but does not re-define them -- we want to replace the entire crate but a
424
+ // re-export of the sysroot crate. We do this by swapping out the source file: if
425
+ // `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a `lib.rs` file, and a
426
+ // `lib.miri.rs` file exists in the same folder, we build that instead. But crucially we
427
+ // only do that for the library, not the unit test crate (which would be runnable) or
428
+ // rustdoc (which would have a different `phase`).
429
+ let replace_librs = env:: var_os ( "MIRI_REPLACE_LIBRS_IF_NOT_TEST" ) . is_some ( )
430
+ && !runnable_crate
431
+ && phase == RustcPhase :: Build ;
417
432
while let Some ( arg) = args. next ( ) {
433
+ // Patch `--emit`: remove "link" from "--emit" to make this a check-only build.
418
434
if let Some ( val) = arg. strip_prefix ( emit_flag) {
419
435
// Patch this argument. First, extract its value.
420
436
let val =
@@ -429,13 +445,36 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
429
445
}
430
446
}
431
447
cmd. arg ( format ! ( "{emit_flag}={}" , val. join( "," ) ) ) ;
432
- } else if arg == "--extern" {
433
- // Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
434
- // https://github.com/rust-lang/miri/issues/1705
448
+ continue ;
449
+ }
450
+ // Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
451
+ // https://github.com/rust-lang/miri/issues/1705
452
+ if arg == "--extern" {
435
453
forward_patched_extern_arg ( & mut args, & mut cmd) ;
436
- } else {
437
- cmd. arg ( arg) ;
454
+ continue ;
438
455
}
456
+ // If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a
457
+ // `lib.miri.rs` file exists, then build that instead. We only consider relative paths
458
+ // as cargo uses those for files in the workspace; dependencies from crates.io get
459
+ // absolute paths.
460
+ if replace_librs {
461
+ let path = Path :: new ( & arg) ;
462
+ if path. is_relative ( )
463
+ && path. file_name ( ) . is_some_and ( |f| f == "lib.rs" )
464
+ && path. is_file ( )
465
+ {
466
+ let miri_rs = Path :: new ( & arg) . with_extension ( "miri.rs" ) ;
467
+ if miri_rs. is_file ( ) {
468
+ if verbose > 0 {
469
+ eprintln ! ( "Performing REPLACE_LIBRS hack: {arg:?} -> {miri_rs:?}" ) ;
470
+ }
471
+ cmd. arg ( miri_rs) ;
472
+ continue ;
473
+ }
474
+ }
475
+ }
476
+ // Fallback: just propagate the argument.
477
+ cmd. arg ( arg) ;
439
478
}
440
479
441
480
// During setup, patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does).
0 commit comments