Skip to content

Commit a6803b9

Browse files
committed
add 'x.py miri', and make it work for 'library/{core,alloc,std}'
1 parent b390f2f commit a6803b9

File tree

14 files changed

+422
-30
lines changed

14 files changed

+422
-30
lines changed

library/alloc/benches/vec_deque_append.rs

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ const WARMUP_N: usize = 100;
55
const BENCH_N: usize = 1000;
66

77
fn main() {
8+
if cfg!(miri) {
9+
// Don't benchmark Miri...
10+
// (Due to bootstrap quirks, this gets picked up by `x.py miri library/alloc --no-doc`.)
11+
return;
12+
}
813
let a: VecDeque<i32> = (0..VECDEQUE_LEN).collect();
914
let b: VecDeque<i32> = (0..VECDEQUE_LEN).collect();
1015

library/alloc/src/lib.miri.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about.
2+
#![no_std]
3+
extern crate alloc as realalloc;
4+
pub use realalloc::*;

library/core/src/lib.miri.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about.
2+
#![no_std]
3+
extern crate core as realcore;
4+
pub use realcore::*;

library/std/src/lib.miri.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about.
2+
#![no_std]
3+
extern crate std as realstd;
4+
pub use realstd::*;

src/bootstrap/src/core/build_steps/test.rs

+55-16
Original file line numberDiff line numberDiff line change
@@ -2536,9 +2536,14 @@ fn prepare_cargo_test(
25362536
//
25372537
// Note that to run the compiler we need to run with the *host* libraries,
25382538
// but our wrapper scripts arrange for that to be the case anyway.
2539-
let mut dylib_path = dylib_path();
2540-
dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target)));
2541-
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
2539+
//
2540+
// We skip everything on Miri as then this overwrites the libdir set up
2541+
// by `Cargo::new` and that actually makes things go wrong.
2542+
if builder.kind != Kind::Miri {
2543+
let mut dylib_path = dylib_path();
2544+
dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target)));
2545+
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
2546+
}
25422547

25432548
if builder.remote_tested(target) {
25442549
cargo.env(
@@ -2594,28 +2599,62 @@ impl Step for Crate {
25942599
let target = self.target;
25952600
let mode = self.mode;
25962601

2602+
// Prepare sysroot
25972603
// See [field@compile::Std::force_recompile].
25982604
builder.ensure(compile::Std::force_recompile(compiler, compiler.host));
25992605

2600-
if builder.config.build != target {
2601-
builder.ensure(compile::Std::force_recompile(compiler, target));
2602-
builder.ensure(RemoteCopyLibs { compiler, target });
2603-
}
2604-
26052606
// If we're not doing a full bootstrap but we're testing a stage2
26062607
// version of libstd, then what we're actually testing is the libstd
26072608
// produced in stage1. Reflect that here by updating the compiler that
26082609
// we're working with automatically.
26092610
let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
26102611

2611-
let mut cargo = builder::Cargo::new(
2612-
builder,
2613-
compiler,
2614-
mode,
2615-
SourceType::InTree,
2616-
target,
2617-
builder.kind.as_str(),
2618-
);
2612+
let mut cargo = if builder.kind == Kind::Miri {
2613+
if builder.top_stage == 0 {
2614+
eprintln!("ERROR: `x.py miri` requires stage 1 or higher");
2615+
std::process::exit(1);
2616+
}
2617+
2618+
// Build `cargo miri test` command
2619+
// (Implicitly prepares target sysroot)
2620+
let mut cargo = builder::Cargo::new(
2621+
builder,
2622+
compiler,
2623+
mode,
2624+
SourceType::InTree,
2625+
target,
2626+
"miri-test",
2627+
);
2628+
// This hack helps bootstrap run standard library tests in Miri. The issue is as
2629+
// follows: when running `cargo miri test` on libcore, cargo builds a local copy of core
2630+
// and makes it a dependency of the integration test crate. This copy duplicates all the
2631+
// lang items, so the build fails. (Regular testing avoids this because the sysroot is a
2632+
// literal copy of what `cargo build` produces, but since Miri builds its own sysroot
2633+
// this does not work for us.) So we need to make it so that the locally built libcore
2634+
// contains all the items from `core`, but does not re-define them -- we want to replace
2635+
// the entire crate but a re-export of the sysroot crate. We do this by swapping out the
2636+
// source file: if `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a
2637+
// `lib.rs` file, and a `lib.miri.rs` file exists in the same folder, we build that
2638+
// instead. But crucially we only do that for the library, not the test builds.
2639+
cargo.env("MIRI_REPLACE_LIBRS_IF_NOT_TEST", "1");
2640+
cargo
2641+
} else {
2642+
// Also prepare a sysroot for the target.
2643+
if builder.config.build != target {
2644+
builder.ensure(compile::Std::force_recompile(compiler, target));
2645+
builder.ensure(RemoteCopyLibs { compiler, target });
2646+
}
2647+
2648+
// Build `cargo test` command
2649+
builder::Cargo::new(
2650+
builder,
2651+
compiler,
2652+
mode,
2653+
SourceType::InTree,
2654+
target,
2655+
builder.kind.as_str(),
2656+
)
2657+
};
26192658

26202659
match mode {
26212660
Mode::Std => {

src/bootstrap/src/core/builder.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ pub enum Kind {
631631
Format,
632632
#[value(alias = "t")]
633633
Test,
634+
Miri,
634635
Bench,
635636
#[value(alias = "d")]
636637
Doc,
@@ -673,6 +674,7 @@ impl Kind {
673674
Kind::Fix => "fix",
674675
Kind::Format => "fmt",
675676
Kind::Test => "test",
677+
Kind::Miri => "miri",
676678
Kind::Bench => "bench",
677679
Kind::Doc => "doc",
678680
Kind::Clean => "clean",
@@ -822,6 +824,7 @@ impl<'a> Builder<'a> {
822824
// Run run-make last, since these won't pass without make on Windows
823825
test::RunMake,
824826
),
827+
Kind::Miri => describe!(test::Crate),
825828
Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
826829
Kind::Doc => describe!(
827830
doc::UnstableBook,
@@ -970,6 +973,7 @@ impl<'a> Builder<'a> {
970973
Subcommand::Fix => (Kind::Fix, &paths[..]),
971974
Subcommand::Doc { .. } => (Kind::Doc, &paths[..]),
972975
Subcommand::Test { .. } => (Kind::Test, &paths[..]),
976+
Subcommand::Miri { .. } => (Kind::Miri, &paths[..]),
973977
Subcommand::Bench { .. } => (Kind::Bench, &paths[..]),
974978
Subcommand::Dist => (Kind::Dist, &paths[..]),
975979
Subcommand::Install => (Kind::Install, &paths[..]),
@@ -1309,7 +1313,11 @@ impl<'a> Builder<'a> {
13091313
if cmd == "clippy" {
13101314
cargo = self.cargo_clippy_cmd(compiler);
13111315
cargo.arg(cmd);
1312-
} else if let Some(subcmd) = cmd.strip_prefix("miri-") {
1316+
} else if let Some(subcmd) = cmd.strip_prefix("miri") {
1317+
// Command must be "miri-X".
1318+
let subcmd = subcmd
1319+
.strip_prefix("-")
1320+
.unwrap_or_else(|| panic!("expected `miri-$subcommand`, but got {}", cmd));
13131321
cargo = self.cargo_miri_cmd(compiler);
13141322
cargo.arg("miri").arg(subcmd);
13151323
} else {

src/bootstrap/src/core/config/config.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -2022,7 +2022,7 @@ impl Config {
20222022
Subcommand::Build { .. } => {
20232023
flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
20242024
}
2025-
Subcommand::Test { .. } => {
2025+
Subcommand::Test { .. } | Subcommand::Miri { .. } => {
20262026
flags.stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
20272027
}
20282028
Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2),
@@ -2044,6 +2044,7 @@ impl Config {
20442044
if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None {
20452045
match config.cmd {
20462046
Subcommand::Test { .. }
2047+
| Subcommand::Miri { .. }
20472048
| Subcommand::Doc { .. }
20482049
| Subcommand::Build { .. }
20492050
| Subcommand::Bench { .. }
@@ -2099,7 +2100,9 @@ impl Config {
20992100

21002101
pub(crate) fn test_args(&self) -> Vec<&str> {
21012102
let mut test_args = match self.cmd {
2102-
Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
2103+
Subcommand::Test { ref test_args, .. }
2104+
| Subcommand::Bench { ref test_args, .. }
2105+
| Subcommand::Miri { ref test_args, .. } => {
21032106
test_args.iter().flat_map(|s| s.split_whitespace()).collect()
21042107
}
21052108
_ => vec![],

src/bootstrap/src/core/config/flags.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,25 @@ pub enum Subcommand {
382382
/// `/<build_base>/rustfix_missing_coverage.txt`
383383
rustfix_coverage: bool,
384384
},
385+
/// Build and run some test suites *in Miri*
386+
Miri {
387+
#[arg(long)]
388+
/// run all tests regardless of failure
389+
no_fail_fast: bool,
390+
#[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
391+
/// extra arguments to be passed for the test tool being used
392+
/// (e.g. libtest, compiletest or rustdoc)
393+
test_args: Vec<String>,
394+
/// extra options to pass the compiler when running tests
395+
#[arg(long, value_name = "ARGS", allow_hyphen_values(true))]
396+
rustc_args: Vec<String>,
397+
#[arg(long)]
398+
/// do not run doc tests
399+
no_doc: bool,
400+
#[arg(long)]
401+
/// only run doc tests
402+
doc: bool,
403+
},
385404
/// Build and run some benchmarks
386405
Bench {
387406
#[arg(long, allow_hyphen_values(true))]
@@ -453,6 +472,7 @@ impl Subcommand {
453472
Subcommand::Fix { .. } => Kind::Fix,
454473
Subcommand::Format { .. } => Kind::Format,
455474
Subcommand::Test { .. } => Kind::Test,
475+
Subcommand::Miri { .. } => Kind::Miri,
456476
Subcommand::Clean { .. } => Kind::Clean,
457477
Subcommand::Dist { .. } => Kind::Dist,
458478
Subcommand::Install { .. } => Kind::Install,
@@ -464,7 +484,7 @@ impl Subcommand {
464484

465485
pub fn rustc_args(&self) -> Vec<&str> {
466486
match *self {
467-
Subcommand::Test { ref rustc_args, .. } => {
487+
Subcommand::Test { ref rustc_args, .. } | Subcommand::Miri { ref rustc_args, .. } => {
468488
rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
469489
}
470490
_ => vec![],
@@ -473,14 +493,16 @@ impl Subcommand {
473493

474494
pub fn fail_fast(&self) -> bool {
475495
match *self {
476-
Subcommand::Test { no_fail_fast, .. } => !no_fail_fast,
496+
Subcommand::Test { no_fail_fast, .. } | Subcommand::Miri { no_fail_fast, .. } => {
497+
!no_fail_fast
498+
}
477499
_ => false,
478500
}
479501
}
480502

481503
pub fn doc_tests(&self) -> DocTests {
482504
match *self {
483-
Subcommand::Test { doc, no_doc, .. } => {
505+
Subcommand::Test { doc, no_doc, .. } | Subcommand::Miri { no_doc, doc, .. } => {
484506
if doc {
485507
DocTests::Only
486508
} else if no_doc {

src/etc/completions/x.py.fish

+40
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "fix" -d 'Run cargo fix'
3939
complete -c x.py -n "__fish_use_subcommand" -f -a "fmt" -d 'Run rustfmt'
4040
complete -c x.py -n "__fish_use_subcommand" -f -a "doc" -d 'Build documentation'
4141
complete -c x.py -n "__fish_use_subcommand" -f -a "test" -d 'Build and run some test suites'
42+
complete -c x.py -n "__fish_use_subcommand" -f -a "miri" -d 'Build and run some test suites *in Miri*'
4243
complete -c x.py -n "__fish_use_subcommand" -f -a "bench" -d 'Build and run some benchmarks'
4344
complete -c x.py -n "__fish_use_subcommand" -f -a "clean" -d 'Clean out build directories'
4445
complete -c x.py -n "__fish_use_subcommand" -f -a "dist" -d 'Build distribution artifacts'
@@ -308,6 +309,45 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-profile-generate
308309
complete -c x.py -n "__fish_seen_subcommand_from test" -l enable-bolt-settings -d 'Enable BOLT link flags'
309310
complete -c x.py -n "__fish_seen_subcommand_from test" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
310311
complete -c x.py -n "__fish_seen_subcommand_from test" -s h -l help -d 'Print help (see more with \'--help\')'
312+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
313+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-args -d 'extra options to pass the compiler when running tests' -r
314+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l config -d 'TOML configuration file for build' -r -F
315+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
316+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l build -d 'build target of the stage0 compiler' -r -f
317+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l host -d 'host targets to build' -r -f
318+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l target -d 'target targets to build' -r -f
319+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l exclude -d 'build paths to exclude' -r -F
320+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l skip -d 'build paths to skip' -r -F
321+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-error-format -r -f
322+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)"
323+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f
324+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
325+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
326+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
327+
complete -c x.py -n "__fish_seen_subcommand_from miri" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
328+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
329+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l error-format -d 'rustc error format' -r -f
330+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
331+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
332+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
333+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
334+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
335+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r
336+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l set -d 'override options in config.toml' -r -f
337+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l no-fail-fast -d 'run all tests regardless of failure'
338+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l no-doc -d 'do not run doc tests'
339+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l doc -d 'only run doc tests'
340+
complete -c x.py -n "__fish_seen_subcommand_from miri" -s v -l verbose -d 'use verbose output (-vv for very verbose)'
341+
complete -c x.py -n "__fish_seen_subcommand_from miri" -s i -l incremental -d 'use incremental compilation'
342+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l include-default-paths -d 'include default paths in addition to the provided ones'
343+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l dry-run -d 'dry run; don\'t build anything'
344+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
345+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l json-output -d 'use message-format=json'
346+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
347+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
348+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l enable-bolt-settings -d 'Enable BOLT link flags'
349+
complete -c x.py -n "__fish_seen_subcommand_from miri" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
350+
complete -c x.py -n "__fish_seen_subcommand_from miri" -s h -l help -d 'Print help (see more with \'--help\')'
311351
complete -c x.py -n "__fish_seen_subcommand_from bench" -l test-args -r
312352
complete -c x.py -n "__fish_seen_subcommand_from bench" -l config -d 'TOML configuration file for build' -r -F
313353
complete -c x.py -n "__fish_seen_subcommand_from bench" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"

0 commit comments

Comments
 (0)