Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8757be4

Browse files
committed
Auto merge of rust-lang#127958 - jieyouxu:compiletest-rmake-cleanup, r=<try>
Cleanup rmake.rs setup in compiletest While debugging rmake.rs tests I realized that the rmake.rs setup itself in compiletest is very messy and confused. Now that I know some of the bootstrap steps and the rmake.rs tests themselves better, I realized there are cleanups that are possible: - Rework how `source_root` and `build_root` are calculated. They should now be less fragile then before. - Shuffle around path calculations to make them more logically grouped and closer to eventual use site(s). - Cleanup executable extension calculation with `std::env::consts::EXE_EXTENSION`. - Cleanup various dylib search path handling: renamed variables to better reflect their purpose, minimized mutability scope of said variables. - Prune useless env vars passed to both `rustc` and recipe binary commands. - Vastly improve the documentation for the setup of rmake.rs tests, including assumed bootstrap-provided build layouts, rmake.rs test layout, dylib search paths, intended purpose of passed env vars and the `COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0` stage0 sysroot special handling. This PR is best reviewed commit-by-commit. Fixes rust-lang#127920. r? bootstrap (or Kobzol, or Mark, or T-compiler) try-job: aarch64-apple try-job: armhf-gnu try-job: dist-x86_64-linux try-job: test-various try-job: x86_64-mingw try-job: x86_64-msvc try-job: x86_64-gnu-llvm-18
2 parents 11e5724 + ef2d0dd commit 8757be4

File tree

1 file changed

+213
-86
lines changed

1 file changed

+213
-86
lines changed

src/tools/compiletest/src/runtest.rs

Lines changed: 213 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -3433,29 +3433,54 @@ impl<'test> TestCx<'test> {
34333433

34343434
fn run_rmake_v2_test(&self) {
34353435
// For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
3436-
// (`rmake.rs`) to run the actual tests. The support library is already built as a tool
3437-
// dylib and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`.
3436+
// (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust
3437+
// library and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`.
34383438
//
3439-
// 1. We need to build the recipe `rmake.rs` and link in the support library.
3440-
// 2. We need to run the recipe to build and run the tests.
3441-
let cwd = env::current_dir().unwrap();
3442-
let src_root = self.config.src_base.parent().unwrap().parent().unwrap();
3443-
let src_root = cwd.join(&src_root);
3444-
let build_root = self.config.build_base.parent().unwrap().parent().unwrap();
3445-
let build_root = cwd.join(&build_root);
3439+
// 1. We need to build the recipe `rmake.rs` as a binary and link in the `run_make_support`
3440+
// library.
3441+
// 2. We need to run the recipe binary.
3442+
3443+
// So we assume the rust-lang/rust project setup looks like (our `.` is the top-level
3444+
// directory, irrelevant entries to our purposes omitted):
3445+
//
3446+
// ```
3447+
// . // <- `source_root`
3448+
// ├── build/ // <- `build_root`
3449+
// ├── compiler/
3450+
// ├── library/
3451+
// ├── src/
3452+
// │ └── tools/
3453+
// │ └── run_make_support/
3454+
// └── tests
3455+
// └── run-make/
3456+
// ```
3457+
3458+
// `source_root` is the top-level directory containing the rust-lang/rust checkout.
3459+
let source_root =
3460+
self.config.find_rust_src_root().expect("could not determine rust source root");
3461+
debug!(?source_root);
3462+
// `self.config.build_base` is actually the build base folder + "test" + test suite name, it
3463+
// looks like `build/<host_tuplet>/test/run-make`. But we want `build/<host_tuplet>/`. Note
3464+
// that the `build` directory does not need to be called `build`, nor does it need to be
3465+
// under `source_root`, so we must compute it based off of `self.config.build_base`.
3466+
debug!(?self.config.build_base);
3467+
let build_root =
3468+
self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf();
3469+
debug!(?build_root);
34463470

34473471
// We construct the following directory tree for each rmake.rs test:
34483472
// ```
3449-
// base_dir/
3473+
// <base_dir>/
34503474
// rmake.exe
34513475
// rmake_out/
34523476
// ```
3453-
// having the executable separate from the output artifacts directory allows the recipes to
3454-
// `remove_dir_all($TMPDIR)` without running into permission denied issues because
3455-
// the executable is not under the `rmake_out/` directory.
3477+
// having the recipe executable separate from the output artifacts directory allows the
3478+
// recipes to `remove_dir_all($TMPDIR)` without running into issues related trying to remove
3479+
// a currently running executable because the recipe executable is not under the
3480+
// `rmake_out/` directory.
34563481
//
34573482
// This setup intentionally diverges from legacy Makefile run-make tests.
3458-
let base_dir = cwd.join(self.output_base_name());
3483+
let base_dir = self.output_base_name();
34593484
if base_dir.exists() {
34603485
self.aggressive_rm_rf(&base_dir).unwrap();
34613486
}
@@ -3477,120 +3502,222 @@ impl<'test> TestCx<'test> {
34773502
}
34783503
}
34793504

3480-
// HACK: assume stageN-target, we only want stageN.
3505+
// `self.config.stage_id` looks like `stage1-<target_tuplet>`, but we only want
3506+
// the `stage1` part as that is what the output directories of bootstrap are prefixed with.
3507+
// Note that this *assumes* build layout from bootstrap is produced as:
3508+
//
3509+
// ```
3510+
// build/<target_tuplet>/ // <- this is `build_root`
3511+
// ├── stage0
3512+
// ├── stage0-bootstrap-tools
3513+
// ├── stage0-codegen
3514+
// ├── stage0-rustc
3515+
// ├── stage0-std
3516+
// ├── stage0-sysroot
3517+
// ├── stage0-tools
3518+
// ├── stage0-tools-bin
3519+
// ├── stage1
3520+
// ├── stage1-std
3521+
// ├── stage1-tools
3522+
// ├── stage1-tools-bin
3523+
// └── test
3524+
// ```
3525+
// FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so
3526+
// we don't have to hack out a `stageN`.
3527+
debug!(?self.config.stage_id);
34813528
let stage = self.config.stage_id.split('-').next().unwrap();
34823529

3483-
// First, we construct the path to the built support library.
3484-
let mut support_lib_path = PathBuf::new();
3485-
support_lib_path.push(&build_root);
3486-
support_lib_path.push(format!("{}-tools-bin", stage));
3487-
support_lib_path.push("librun_make_support.rlib");
3488-
3489-
let mut stage_std_path = PathBuf::new();
3490-
stage_std_path.push(&build_root);
3491-
stage_std_path.push(&stage);
3492-
stage_std_path.push("lib");
3493-
3494-
// Then, we need to build the recipe `rmake.rs` and link in the support library.
3495-
let recipe_bin = base_dir.join(if self.config.target.contains("windows") {
3496-
"rmake.exe"
3497-
} else {
3498-
"rmake"
3499-
});
3500-
3501-
let mut support_lib_deps = PathBuf::new();
3502-
support_lib_deps.push(&build_root);
3503-
support_lib_deps.push(format!("{}-tools", stage));
3504-
support_lib_deps.push(&self.config.host);
3505-
support_lib_deps.push("release");
3506-
support_lib_deps.push("deps");
3507-
3508-
let mut support_lib_deps_deps = PathBuf::new();
3509-
support_lib_deps_deps.push(&build_root);
3510-
support_lib_deps_deps.push(format!("{}-tools", stage));
3511-
support_lib_deps_deps.push("release");
3512-
support_lib_deps_deps.push("deps");
3530+
// In order to link in the support library as a rlib when compiling recipes, we need three
3531+
// paths:
3532+
// 1. Path of the built support library rlib itself.
3533+
// 2. Path of the built support library's dependencies directory.
3534+
// 3. Path of the built support library's dependencies' dependencies directory.
3535+
//
3536+
// The paths look like
3537+
//
3538+
// ```
3539+
// build/<target_tuplet>/
3540+
// ├── stageN-tools-bin/
3541+
// │ └── librun_make_support.rlib // <- support rlib itself
3542+
// ├── stageN-tools/
3543+
// │ ├── release/deps/ // <- deps of deps
3544+
// │ └── <host_tuplet>/release/deps/ // <- deps
3545+
// ```
3546+
//
3547+
// There almost certainly is a better way to do this, but this seems to work for now.
35133548

3549+
let support_lib_path = {
3550+
let mut p = build_root.clone();
3551+
p.push(format!("{}-tools-bin", stage));
3552+
p.push("librun_make_support.rlib");
3553+
p
3554+
};
3555+
debug!(?support_lib_path);
3556+
3557+
let support_lib_deps = {
3558+
let mut p = build_root.clone();
3559+
p.push(format!("{}-tools", stage));
3560+
p.push(&self.config.host);
3561+
p.push("release");
3562+
p.push("deps");
3563+
p
3564+
};
35143565
debug!(?support_lib_deps);
3566+
3567+
let support_lib_deps_deps = {
3568+
let mut p = build_root.clone();
3569+
p.push(format!("{}-tools", stage));
3570+
p.push("release");
3571+
p.push("deps");
3572+
p
3573+
};
35153574
debug!(?support_lib_deps_deps);
35163575

3517-
let orig_dylib_env_paths =
3576+
// To compile the recipe with rustc, we need to provide suitable dynamic library search
3577+
// paths to rustc. This includes both:
3578+
// 1. The "base" dylib search paths that was provided to compiletest, e.g. `LD_LIBRARY_PATH`
3579+
// on some linux distros.
3580+
// 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc.
3581+
3582+
let base_dylib_search_paths =
35183583
Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap()));
35193584

3520-
let mut host_dylib_env_paths = Vec::new();
3521-
host_dylib_env_paths.push(cwd.join(&self.config.compile_lib_path));
3522-
host_dylib_env_paths.extend(orig_dylib_env_paths.iter().cloned());
3523-
let host_dylib_env_paths = env::join_paths(host_dylib_env_paths).unwrap();
3585+
let host_dylib_search_paths = {
3586+
let mut paths = vec![self.config.compile_lib_path.clone()];
3587+
paths.extend(base_dylib_search_paths.iter().cloned());
3588+
paths
3589+
};
3590+
3591+
// Calculate the paths of the recipe binary. As previously discussed, this is placed at
3592+
// `<base_dir>/<bin_name>` with `bin_name` being `rmake` or `rmake.exe` dependending on
3593+
// platform.
3594+
let recipe_bin = {
3595+
let mut p = base_dir.join("rmake");
3596+
p.set_extension(env::consts::EXE_EXTENSION);
3597+
p
3598+
};
3599+
debug!(?recipe_bin);
35243600

3525-
let mut cmd = Command::new(&self.config.rustc_path);
3526-
cmd.arg("-o")
3601+
let mut rustc = Command::new(&self.config.rustc_path);
3602+
rustc
3603+
// Specify output path
3604+
.arg("-o")
35273605
.arg(&recipe_bin)
3606+
// Specify library search paths for `run_make_support`.
35283607
.arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy()))
35293608
.arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy()))
35303609
.arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy()))
3610+
// Provide `run_make_support` as extern prelude, so test writers don't need to write
3611+
// `extern run_make_support;`.
35313612
.arg("--extern")
35323613
.arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
3614+
// Default to Edition 2021.
35333615
.arg("--edition=2021")
3616+
// The recipe file itself.
35343617
.arg(&self.testpaths.file.join("rmake.rs"))
3535-
.env("TARGET", &self.config.target)
3536-
.env("PYTHON", &self.config.python)
3537-
.env("RUST_BUILD_STAGE", &self.config.stage_id)
3538-
.env("RUSTC", cwd.join(&self.config.rustc_path))
3539-
.env("LD_LIB_PATH_ENVVAR", dylib_env_var())
3540-
.env(dylib_env_var(), &host_dylib_env_paths)
3541-
.env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
3542-
.env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
3543-
.env("LLVM_COMPONENTS", &self.config.llvm_components);
3618+
// Provide necessary library search paths for rustc.
3619+
.env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap());
35443620

35453621
// In test code we want to be very pedantic about values being silently discarded that are
35463622
// annotated with `#[must_use]`.
3547-
cmd.arg("-Dunused_must_use");
3548-
3623+
rustc.arg("-Dunused_must_use");
3624+
3625+
// > `cg_clif` uses `COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0` for running the rustc
3626+
// > test suite. With the introduction of rmake.rs this broke. `librun_make_support.rlib` is
3627+
// > compiled using the bootstrap rustc wrapper which sets `--sysroot
3628+
// > build/aarch64-unknown-linux-gnu/stage0-sysroot`, but then compiletest will compile
3629+
// > `rmake.rs` using the sysroot of the bootstrap compiler causing it to not find the
3630+
// > `libstd.rlib` against which `librun_make_support.rlib` is compiled.
3631+
//
3632+
// The gist here is that we have to pass the proper stage0 sysroot if we want
3633+
//
3634+
// ```
3635+
// $ COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0
3636+
// ```
3637+
//
3638+
// to work correctly.
3639+
//
3640+
// See <https://github.com/rust-lang/rust/pull/122248> for more background.
35493641
if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() {
3550-
let mut stage0_sysroot = build_root.clone();
3551-
stage0_sysroot.push("stage0-sysroot");
3642+
let stage0_sysroot = {
3643+
let mut p = build_root.clone();
3644+
p.push("stage0-sysroot");
3645+
p
3646+
};
35523647
debug!(?stage0_sysroot);
3553-
debug!(exists = stage0_sysroot.exists());
35543648

3555-
cmd.arg("--sysroot").arg(&stage0_sysroot);
3649+
rustc.arg("--sysroot").arg(&stage0_sysroot);
35563650
}
35573651

3558-
let res = self.run_command_to_procres(&mut cmd);
3652+
// Now run rustc to build the recipe.
3653+
let res = self.run_command_to_procres(&mut rustc);
35593654
if !res.status.success() {
35603655
self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res);
35613656
}
35623657

3563-
// Finally, we need to run the recipe binary to build and run the actual tests.
3564-
debug!(?recipe_bin);
3658+
// To actually run the recipe, we have to provide the recipe with a bunch of information
3659+
// provided through env vars.
35653660

3566-
let mut dylib_env_paths = orig_dylib_env_paths.clone();
3567-
dylib_env_paths.push(support_lib_path.parent().unwrap().to_path_buf());
3568-
dylib_env_paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib"));
3569-
let dylib_env_paths = env::join_paths(dylib_env_paths).unwrap();
3661+
// Compute stage-specific standard library paths.
3662+
let stage_std_path = {
3663+
let mut p = build_root.clone();
3664+
p.push(&stage);
3665+
p.push("lib");
3666+
p
3667+
};
3668+
debug!(?stage_std_path);
3669+
3670+
// Compute dynamic library search paths for recipes.
3671+
let recipe_dylib_search_paths = {
3672+
let mut paths = base_dylib_search_paths.clone();
3673+
paths.push(support_lib_path.parent().unwrap().to_path_buf());
3674+
paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib"));
3675+
paths
3676+
};
3677+
debug!(?recipe_dylib_search_paths);
35703678

3571-
let mut target_rpath_env_path = Vec::new();
3572-
target_rpath_env_path.push(&rmake_out_dir);
3573-
target_rpath_env_path.extend(&orig_dylib_env_paths);
3574-
let target_rpath_env_path = env::join_paths(target_rpath_env_path).unwrap();
3679+
// Compute runtime library search paths for recipes. This is target-specific.
3680+
let target_runtime_dylib_search_paths = {
3681+
let mut paths = vec![rmake_out_dir.clone()];
3682+
paths.extend(base_dylib_search_paths.iter().cloned());
3683+
paths
3684+
};
35753685

3686+
// FIXME(jieyouxu): please rename `TARGET_RPATH_ENV`, `HOST_RPATH_DIR` and
3687+
// `TARGET_RPATH_DIR`, it is **extremely** confusing!
35763688
let mut cmd = Command::new(&recipe_bin);
35773689
cmd.current_dir(&rmake_out_dir)
35783690
.stdout(Stdio::piped())
35793691
.stderr(Stdio::piped())
3692+
// Provide the target-specific env var that is used to record dylib search paths. For
3693+
// example, this could be `LD_LIBRARY_PATH` on some linux distros but `PATH` on Windows.
35803694
.env("LD_LIB_PATH_ENVVAR", dylib_env_var())
3581-
.env("TARGET_RPATH_ENV", &target_rpath_env_path)
3582-
.env(dylib_env_var(), &dylib_env_paths)
3695+
// Provide the dylib search paths.
3696+
.env(dylib_env_var(), &env::join_paths(recipe_dylib_search_paths).unwrap())
3697+
// Provide runtime dylib search paths.
3698+
.env("TARGET_RPATH_ENV", &env::join_paths(target_runtime_dylib_search_paths).unwrap())
3699+
// Provide the target.
35833700
.env("TARGET", &self.config.target)
3701+
// Some tests unfortunately still need Python, so provide path to a Python interpreter.
35843702
.env("PYTHON", &self.config.python)
3585-
.env("SOURCE_ROOT", &src_root)
3586-
.env("RUST_BUILD_STAGE", &self.config.stage_id)
3587-
.env("RUSTC", cwd.join(&self.config.rustc_path))
3588-
.env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
3589-
.env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
3703+
// Provide path to checkout root. This is the top-level directory containing
3704+
// rust-lang/rust checkout.
3705+
.env("SOURCE_ROOT", &source_root)
3706+
// Provide path to stage-corresponding rustc.
3707+
.env("RUSTC", &self.config.rustc_path)
3708+
// Provide the directory to libraries that are needed to run the *compiler*. This is not
3709+
// to be confused with `TARGET_RPATH_ENV` or `TARGET_RPATH_DIR`. This is needed if the
3710+
// recipe wants to invoke rustc.
3711+
.env("HOST_RPATH_DIR", &self.config.compile_lib_path)
3712+
// Provide the directory to libraries that might be needed to run compiled binaries
3713+
// (further compiled by the recipe!).
3714+
.env("TARGET_RPATH_DIR", &self.config.run_lib_path)
3715+
// Provide which LLVM components are available (e.g. which LLVM components are provided
3716+
// through a specific CI runner).
35903717
.env("LLVM_COMPONENTS", &self.config.llvm_components);
35913718

35923719
if let Some(ref rustdoc) = self.config.rustdoc_path {
3593-
cmd.env("RUSTDOC", cwd.join(rustdoc));
3720+
cmd.env("RUSTDOC", source_root.join(rustdoc));
35943721
}
35953722

35963723
if let Some(ref node) = self.config.nodejs {

0 commit comments

Comments
 (0)