about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--Cargo.toml1
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs81
-rw-r--r--src/tools/compiletest/src/lib.rs35
-rw-r--r--src/tools/compiletest/src/runtest.rs243
-rw-r--r--src/tools/run-make-support/Cargo.toml6
-rw-r--r--src/tools/run-make-support/src/lib.rs151
-rw-r--r--tests/run-make/CURRENT_RUSTC_VERSION/Makefile6
-rw-r--r--tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs27
-rw-r--r--tests/run-make/a-b-a-linker-guard/Makefile16
-rw-r--r--tests/run-make/a-b-a-linker-guard/rmake.rs45
11 files changed, 583 insertions, 32 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 47977a8850d..1344d4152d9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3275,6 +3275,10 @@ dependencies = [
 ]
 
 [[package]]
+name = "run_make_support"
+version = "0.0.0"
+
+[[package]]
 name = "rust-demangler"
 version = "0.0.1"
 dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index 2ea16c22666..5847a817e76 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ members = [
   "src/tools/clippy",
   "src/tools/clippy/clippy_dev",
   "src/tools/compiletest",
+  "src/tools/run-make-support",
   "src/tools/error_index_generator",
   "src/tools/linkchecker",
   "src/tools/lint-docs",
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index a2c6e79d5e2..4a4497e57db 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1327,6 +1327,52 @@ macro_rules! coverage_test_alias {
     };
 }
 
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
+pub struct RunMakeSupport {
+    pub compiler: Compiler,
+    pub target: TargetSelection,
+}
+
+impl Step for RunMakeSupport {
+    type Output = PathBuf;
+    const DEFAULT: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.never()
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+        run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() });
+    }
+
+    fn run(self, builder: &Builder<'_>) -> PathBuf {
+        builder.ensure(compile::Std::new(self.compiler, self.target));
+
+        let cargo = tool::prepare_tool_cargo(
+            builder,
+            self.compiler,
+            Mode::ToolStd,
+            self.target,
+            "build",
+            "src/tools/run-make-support",
+            SourceType::InTree,
+            &[],
+        );
+
+        let mut cargo = Command::from(cargo);
+        builder.run(&mut cargo);
+
+        let lib_name = "librun_make_support.rlib";
+        let lib = builder.tools_dir(self.compiler).join(&lib_name);
+
+        let cargo_out =
+            builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(&lib_name);
+        builder.copy(&cargo_out, &lib);
+        lib
+    }
+}
+
 default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" });
 
 default_test!(RunPassValgrind {
@@ -1361,7 +1407,40 @@ host_test!(RustdocJson { path: "tests/rustdoc-json", mode: "rustdoc-json", suite
 
 host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" });
 
-default_test!(RunMake { path: "tests/run-make", mode: "run-make", suite: "run-make" });
+// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake`
+// tests.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct RunMake {
+    pub compiler: Compiler,
+    pub target: TargetSelection,
+}
+
+impl Step for RunMake {
+    type Output = ();
+    const DEFAULT: bool = true;
+    const ONLY_HOSTS: bool = false;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.suite_path("tests/run-make")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+        run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() });
+        run.builder.ensure(RunMake { compiler, target: run.target });
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        builder.ensure(Compiletest {
+            compiler: self.compiler,
+            target: self.target,
+            mode: "run-make",
+            suite: "run-make",
+            path: "tests/run-make",
+            compare_mode: None,
+        });
+    }
+}
 
 host_test!(RunMakeFullDeps {
     path: "tests/run-make-fulldeps",
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 57a82eb37ed..b32a5a4bf1a 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -655,13 +655,21 @@ fn collect_tests_from_dir(
         return Ok(());
     }
 
-    if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
-        let paths = TestPaths {
-            file: dir.to_path_buf(),
-            relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
-        };
-        tests.extend(make_test(config, cache, &paths, inputs, poisoned));
-        return Ok(());
+    if config.mode == Mode::RunMake {
+        if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
+            return Err(io::Error::other(
+                "run-make tests cannot have both `Makefile` and `rmake.rs`",
+            ));
+        }
+
+        if dir.join("Makefile").exists() || dir.join("rmake.rs").exists() {
+            let paths = TestPaths {
+                file: dir.to_path_buf(),
+                relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
+            };
+            tests.extend(make_test(config, cache, &paths, inputs, poisoned));
+            return Ok(());
+        }
     }
 
     // If we find a test foo/bar.rs, we have to build the
@@ -733,8 +741,17 @@ fn make_test(
     poisoned: &mut bool,
 ) -> Vec<test::TestDescAndFn> {
     let test_path = if config.mode == Mode::RunMake {
-        // Parse directives in the Makefile
-        testpaths.file.join("Makefile")
+        if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
+            panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
+        }
+
+        if testpaths.file.join("rmake.rs").exists() {
+            // Parse directives in rmake.rs.
+            testpaths.file.join("rmake.rs")
+        } else {
+            // Parse directives in the Makefile.
+            testpaths.file.join("Makefile")
+        }
     } else {
         PathBuf::from(&testpaths.file)
     };
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index f1ba9ab4767..a942aa9dc90 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -3570,6 +3570,17 @@ impl<'test> TestCx<'test> {
     }
 
     fn run_rmake_test(&self) {
+        let test_dir = &self.testpaths.file;
+        if test_dir.join("rmake.rs").exists() {
+            self.run_rmake_v2_test();
+        } else if test_dir.join("Makefile").exists() {
+            self.run_rmake_legacy_test();
+        } else {
+            self.fatal("failed to find either `rmake.rs` or `Makefile`")
+        }
+    }
+
+    fn run_rmake_legacy_test(&self) {
         let cwd = env::current_dir().unwrap();
         let src_root = self.config.src_base.parent().unwrap().parent().unwrap();
         let src_root = cwd.join(&src_root);
@@ -3737,6 +3748,238 @@ impl<'test> TestCx<'test> {
         fs::remove_dir(path)
     }
 
+    fn run_rmake_v2_test(&self) {
+        // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
+        // (`rmake.rs`) to run the actual tests. The support library is already built as a tool
+        // dylib and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`.
+        //
+        // 1. We need to build the recipe `rmake.rs` and link in the support library.
+        // 2. We need to run the recipe to build and run the tests.
+        let cwd = env::current_dir().unwrap();
+        let src_root = self.config.src_base.parent().unwrap().parent().unwrap();
+        let src_root = cwd.join(&src_root);
+        let build_root = self.config.build_base.parent().unwrap().parent().unwrap();
+        let build_root = cwd.join(&build_root);
+
+        let tmpdir = cwd.join(self.output_base_name());
+        if tmpdir.exists() {
+            self.aggressive_rm_rf(&tmpdir).unwrap();
+        }
+        create_dir_all(&tmpdir).unwrap();
+
+        // HACK: assume stageN-target, we only want stageN.
+        let stage = self.config.stage_id.split('-').next().unwrap();
+
+        // First, we construct the path to the built support library.
+        let mut support_lib_path = PathBuf::new();
+        support_lib_path.push(&build_root);
+        support_lib_path.push(format!("{}-tools-bin", stage));
+        support_lib_path.push("librun_make_support.rlib");
+
+        let mut stage_std_path = PathBuf::new();
+        stage_std_path.push(&build_root);
+        stage_std_path.push(&stage);
+        stage_std_path.push("lib");
+
+        // Then, we need to build the recipe `rmake.rs` and link in the support library.
+        let recipe_bin =
+            tmpdir.join(if self.config.target.contains("windows") { "rmake.exe" } else { "rmake" });
+
+        let mut support_lib_deps = PathBuf::new();
+        support_lib_deps.push(&build_root);
+        support_lib_deps.push(format!("{}-tools", stage));
+        support_lib_deps.push(&self.config.host);
+        support_lib_deps.push("release");
+        support_lib_deps.push("deps");
+
+        let mut support_lib_deps_deps = PathBuf::new();
+        support_lib_deps_deps.push(&build_root);
+        support_lib_deps_deps.push(format!("{}-tools", stage));
+        support_lib_deps_deps.push("release");
+        support_lib_deps_deps.push("deps");
+
+        debug!(?support_lib_deps);
+        debug!(?support_lib_deps_deps);
+
+        let res = self.cmd2procres(
+            Command::new(&self.config.rustc_path)
+                .arg("-o")
+                .arg(&recipe_bin)
+                .arg(format!(
+                    "-Ldependency={}",
+                    &support_lib_path.parent().unwrap().to_string_lossy()
+                ))
+                .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy()))
+                .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy()))
+                .arg("--extern")
+                .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
+                .arg(&self.testpaths.file.join("rmake.rs"))
+                .env("TARGET", &self.config.target)
+                .env("PYTHON", &self.config.python)
+                .env("S", &src_root)
+                .env("RUST_BUILD_STAGE", &self.config.stage_id)
+                .env("RUSTC", cwd.join(&self.config.rustc_path))
+                .env("TMPDIR", &tmpdir)
+                .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
+                .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
+                .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
+                .env("LLVM_COMPONENTS", &self.config.llvm_components)
+                // We for sure don't want these tests to run in parallel, so make
+                // sure they don't have access to these vars if we run via `make`
+                // at the top level
+                .env_remove("MAKEFLAGS")
+                .env_remove("MFLAGS")
+                .env_remove("CARGO_MAKEFLAGS"),
+        );
+        if !res.status.success() {
+            self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res);
+        }
+
+        // Finally, we need to run the recipe binary to build and run the actual tests.
+        debug!(?recipe_bin);
+
+        let mut dylib_env_paths = String::new();
+        dylib_env_paths.push_str(&env::var(dylib_env_var()).unwrap());
+        dylib_env_paths.push(':');
+        dylib_env_paths.push_str(&support_lib_path.parent().unwrap().to_string_lossy());
+        dylib_env_paths.push(':');
+        dylib_env_paths.push_str(
+            &stage_std_path.join("rustlib").join(&self.config.host).join("lib").to_string_lossy(),
+        );
+
+        let mut target_rpath_env_path = String::new();
+        target_rpath_env_path.push_str(&tmpdir.to_string_lossy());
+        target_rpath_env_path.push(':');
+        target_rpath_env_path.push_str(&dylib_env_paths);
+
+        let mut cmd = Command::new(&recipe_bin);
+        cmd.current_dir(&self.testpaths.file)
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
+            .env("TARGET_RPATH_ENV", &target_rpath_env_path)
+            .env(dylib_env_var(), &dylib_env_paths)
+            .env("TARGET", &self.config.target)
+            .env("PYTHON", &self.config.python)
+            .env("S", &src_root)
+            .env("RUST_BUILD_STAGE", &self.config.stage_id)
+            .env("RUSTC", cwd.join(&self.config.rustc_path))
+            .env("TMPDIR", &tmpdir)
+            .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
+            .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
+            .env("LLVM_COMPONENTS", &self.config.llvm_components)
+            // We for sure don't want these tests to run in parallel, so make
+            // sure they don't have access to these vars if we run via `make`
+            // at the top level
+            .env_remove("MAKEFLAGS")
+            .env_remove("MFLAGS")
+            .env_remove("CARGO_MAKEFLAGS");
+
+        if let Some(ref rustdoc) = self.config.rustdoc_path {
+            cmd.env("RUSTDOC", cwd.join(rustdoc));
+        }
+
+        if let Some(ref rust_demangler) = self.config.rust_demangler_path {
+            cmd.env("RUST_DEMANGLER", cwd.join(rust_demangler));
+        }
+
+        if let Some(ref node) = self.config.nodejs {
+            cmd.env("NODE", node);
+        }
+
+        if let Some(ref linker) = self.config.target_linker {
+            cmd.env("RUSTC_LINKER", linker);
+        }
+
+        if let Some(ref clang) = self.config.run_clang_based_tests_with {
+            cmd.env("CLANG", clang);
+        }
+
+        if let Some(ref filecheck) = self.config.llvm_filecheck {
+            cmd.env("LLVM_FILECHECK", filecheck);
+        }
+
+        if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
+            cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
+        }
+
+        if let Some(ref remote_test_client) = self.config.remote_test_client {
+            cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
+        }
+
+        // We don't want RUSTFLAGS set from the outside to interfere with
+        // compiler flags set in the test cases:
+        cmd.env_remove("RUSTFLAGS");
+
+        // Use dynamic musl for tests because static doesn't allow creating dylibs
+        if self.config.host.contains("musl") {
+            cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
+        }
+
+        if self.config.bless {
+            cmd.env("RUSTC_BLESS_TEST", "--bless");
+            // Assume this option is active if the environment variable is "defined", with _any_ value.
+            // As an example, a `Makefile` can use this option by:
+            //
+            //   ifdef RUSTC_BLESS_TEST
+            //       cp "$(TMPDIR)"/actual_something.ext expected_something.ext
+            //   else
+            //       $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
+            //   endif
+        }
+
+        if self.config.target.contains("msvc") && self.config.cc != "" {
+            // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
+            // and that `lib.exe` lives next to it.
+            let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
+
+            // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
+            // a path and instead passes `C:\msys64\foo`, so convert all
+            // `/`-arguments to MSVC here to `-` arguments.
+            let cflags = self
+                .config
+                .cflags
+                .split(' ')
+                .map(|s| s.replace("/", "-"))
+                .collect::<Vec<_>>()
+                .join(" ");
+            let cxxflags = self
+                .config
+                .cxxflags
+                .split(' ')
+                .map(|s| s.replace("/", "-"))
+                .collect::<Vec<_>>()
+                .join(" ");
+
+            cmd.env("IS_MSVC", "1")
+                .env("IS_WINDOWS", "1")
+                .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
+                .env("CC", format!("'{}' {}", self.config.cc, cflags))
+                .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags));
+        } else {
+            cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
+                .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags))
+                .env("AR", &self.config.ar);
+
+            if self.config.target.contains("windows") {
+                cmd.env("IS_WINDOWS", "1");
+            }
+        }
+
+        let (Output { stdout, stderr, status }, truncated) =
+            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`"));
+        if !status.success() {
+            let res = ProcRes {
+                status,
+                stdout: String::from_utf8_lossy(&stdout).into_owned(),
+                stderr: String::from_utf8_lossy(&stderr).into_owned(),
+                truncated,
+                cmdline: format!("{:?}", cmd),
+            };
+            self.fatal_proc_rec("rmake recipe failed to complete", &res);
+        }
+    }
+
     fn run_js_doc_test(&self) {
         if let Some(nodejs) = &self.config.nodejs {
             let out_dir = self.output_base_dir();
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
new file mode 100644
index 00000000000..178deae6499
--- /dev/null
+++ b/src/tools/run-make-support/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "run_make_support"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
new file mode 100644
index 00000000000..820218732ce
--- /dev/null
+++ b/src/tools/run-make-support/src/lib.rs
@@ -0,0 +1,151 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Output};
+
+fn setup_common_build_cmd() -> Command {
+    let rustc = env::var("RUSTC").unwrap();
+    let mut cmd = Command::new(rustc);
+    cmd.arg("--out-dir")
+        .arg(env::var("TMPDIR").unwrap())
+        .arg("-L")
+        .arg(env::var("TMPDIR").unwrap());
+    cmd
+}
+
+fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! {
+    eprintln!("command failed at line {caller_line_number}");
+    eprintln!("{cmd}");
+    eprintln!("output status: `{}`", output.status);
+    eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap());
+    eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap());
+    std::process::exit(1)
+}
+
+pub fn rustc() -> RustcInvocationBuilder {
+    RustcInvocationBuilder::new()
+}
+
+pub fn aux_build() -> AuxBuildInvocationBuilder {
+    AuxBuildInvocationBuilder::new()
+}
+
+#[derive(Debug)]
+pub struct RustcInvocationBuilder {
+    cmd: Command,
+}
+
+impl RustcInvocationBuilder {
+    fn new() -> Self {
+        let cmd = setup_common_build_cmd();
+        Self { cmd }
+    }
+
+    pub fn arg(&mut self, arg: &str) -> &mut RustcInvocationBuilder {
+        self.cmd.arg(arg);
+        self
+    }
+
+    #[track_caller]
+    pub fn run(&mut self) -> Output {
+        let caller_location = std::panic::Location::caller();
+        let caller_line_number = caller_location.line();
+
+        let output = self.cmd.output().unwrap();
+        if !output.status.success() {
+            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
+        }
+        output
+    }
+}
+
+#[derive(Debug)]
+pub struct AuxBuildInvocationBuilder {
+    cmd: Command,
+}
+
+impl AuxBuildInvocationBuilder {
+    fn new() -> Self {
+        let mut cmd = setup_common_build_cmd();
+        cmd.arg("--crate-type=lib");
+        Self { cmd }
+    }
+
+    pub fn arg(&mut self, arg: &str) -> &mut AuxBuildInvocationBuilder {
+        self.cmd.arg(arg);
+        self
+    }
+
+    #[track_caller]
+    pub fn run(&mut self) -> Output {
+        let caller_location = std::panic::Location::caller();
+        let caller_line_number = caller_location.line();
+
+        let output = self.cmd.output().unwrap();
+        if !output.status.success() {
+            handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
+        }
+        output
+    }
+}
+
+fn run_common(bin_name: &str) -> (Command, Output) {
+    let target = env::var("TARGET").unwrap();
+
+    let bin_name =
+        if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() };
+
+    let mut bin_path = PathBuf::new();
+    bin_path.push(env::var("TMPDIR").unwrap());
+    bin_path.push(&bin_name);
+    let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap();
+    let mut cmd = Command::new(bin_path);
+    cmd.env(&ld_lib_path_envvar, {
+        let mut paths = vec![];
+        paths.push(PathBuf::from(env::var("TMPDIR").unwrap()));
+        for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) {
+            paths.push(p.to_path_buf());
+        }
+        for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) {
+            paths.push(p.to_path_buf());
+        }
+        env::join_paths(paths.iter()).unwrap()
+    });
+
+    if target.contains("windows") {
+        let mut paths = vec![];
+        for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) {
+            paths.push(p.to_path_buf());
+        }
+        paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf());
+        cmd.env("PATH", env::join_paths(paths.iter()).unwrap());
+    }
+
+    let output = cmd.output().unwrap();
+    (cmd, output)
+}
+
+/// Run a built binary and make sure it succeeds.
+#[track_caller]
+pub fn run(bin_name: &str) -> Output {
+    let caller_location = std::panic::Location::caller();
+    let caller_line_number = caller_location.line();
+
+    let (cmd, output) = run_common(bin_name);
+    if !output.status.success() {
+        handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
+    }
+    output
+}
+
+/// Run a built binary and make sure it fails.
+#[track_caller]
+pub fn run_fail(bin_name: &str) -> Output {
+    let caller_location = std::panic::Location::caller();
+    let caller_line_number = caller_location.line();
+
+    let (cmd, output) = run_common(bin_name);
+    if output.status.success() {
+        handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number);
+    }
+    output
+}
diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/Makefile b/tests/run-make/CURRENT_RUSTC_VERSION/Makefile
deleted file mode 100644
index 7940dae207b..00000000000
--- a/tests/run-make/CURRENT_RUSTC_VERSION/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-include ../tools.mk
-
-all:
-	$(RUSTC) --emit=metadata --crate-type lib stable.rs
-	$(RUSTC) --emit=metadata --extern stable=$(TMPDIR)/libstable.rmeta main.rs 2>&1 >/dev/null \
-		| $(CGREP) -e "stable since $$(cat $(S)/src/version)(-[a-zA-Z]+)?"
diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs
new file mode 100644
index 00000000000..586f4e4095f
--- /dev/null
+++ b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs
@@ -0,0 +1,27 @@
+// ignore-tidy-linelength
+
+extern crate run_make_support;
+
+use std::path::PathBuf;
+
+use run_make_support::{aux_build, rustc};
+
+fn main() {
+    aux_build()
+        .arg("--emit=metadata")
+        .arg("stable.rs")
+        .run();
+    let mut stable_path = PathBuf::from(env!("TMPDIR"));
+    stable_path.push("libstable.rmeta");
+    let output = rustc()
+        .arg("--emit=metadata")
+        .arg("--extern")
+        .arg(&format!("stable={}", &stable_path.to_string_lossy()))
+        .arg("main.rs")
+        .run();
+
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    let version = include_str!(concat!(env!("S"), "/src/version"));
+    let expected_string = format!("stable since {}", version.trim());
+    assert!(stderr.contains(&expected_string));
+}
diff --git a/tests/run-make/a-b-a-linker-guard/Makefile b/tests/run-make/a-b-a-linker-guard/Makefile
deleted file mode 100644
index 43282eae09c..00000000000
--- a/tests/run-make/a-b-a-linker-guard/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-# Test that if we build `b` against a version of `a` that has one set
-# of types, it will not run with a dylib that has a different set of
-# types.
-
-# NOTE(eddyb) this test only works with the `legacy` mangling,
-# and will probably get removed once `legacy` is gone.
-
-all:
-	$(RUSTC) a.rs --cfg x -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy
-	$(RUSTC) b.rs -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy
-	$(call RUN,b)
-	$(RUSTC) a.rs --cfg y -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=legacy
-	$(call FAIL,b)
diff --git a/tests/run-make/a-b-a-linker-guard/rmake.rs b/tests/run-make/a-b-a-linker-guard/rmake.rs
new file mode 100644
index 00000000000..ef4813e1214
--- /dev/null
+++ b/tests/run-make/a-b-a-linker-guard/rmake.rs
@@ -0,0 +1,45 @@
+// ignore-tidy-linelength
+
+extern crate run_make_support;
+
+use run_make_support::{run, run_fail, rustc};
+
+fn main() {
+    rustc()
+        .arg("a.rs")
+        .arg("--cfg")
+        .arg("x")
+        .arg("-C")
+        .arg("prefer-dynamic")
+        .arg("-Z")
+        .arg("unstable-options")
+        .arg("-C")
+        .arg("symbol-mangling-version=legacy")
+        .run();
+
+    rustc()
+       .arg("b.rs")
+       .arg("-C")
+       .arg("prefer-dynamic")
+       .arg("-Z")
+       .arg("unstable-options")
+       .arg("-C")
+       .arg("symbol-mangling-version=legacy")
+       .run();
+
+    run("b");
+
+    rustc()
+        .arg("a.rs")
+        .arg("--cfg")
+        .arg("y")
+        .arg("-C")
+        .arg("prefer-dynamic")
+        .arg("-Z")
+        .arg("unstable-options")
+        .arg("-C")
+        .arg("symbol-mangling-version=legacy")
+        .run();
+
+    run_fail("b");
+}