about summary refs log tree commit diff
path: root/compiler/rustc_codegen_gcc/build_system/src
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume.gomez@huawei.com>2024-07-10 12:44:23 +0200
committerGuillaume Gomez <guillaume.gomez@huawei.com>2024-07-10 12:44:23 +0200
commit7cbe50e2098c35fda06433cd36bbced941607317 (patch)
tree5f93154e463e7258902781d746195519e20a9fc6 /compiler/rustc_codegen_gcc/build_system/src
parent649feb9c1a3c56650a4b6fa638b23103cbcd0dcd (diff)
parent98ed962c7d3eebe12c97588e61245273d265e72f (diff)
downloadrust-7cbe50e2098c35fda06433cd36bbced941607317.tar.gz
rust-7cbe50e2098c35fda06433cd36bbced941607317.zip
Merge commit '98ed962c7d3eebe12c97588e61245273d265e72f' into master
Diffstat (limited to 'compiler/rustc_codegen_gcc/build_system/src')
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/build.rs120
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/cargo.rs114
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/clean.rs16
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/config.rs107
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/fmt.rs35
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/main.rs36
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/prepare.rs157
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs125
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/test.rs640
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/utils.rs133
10 files changed, 778 insertions, 705 deletions
diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs
index c81b02e2183..d465ab7e506 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/build.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs
@@ -1,5 +1,7 @@
 use crate::config::{Channel, ConfigInfo};
-use crate::utils::{run_command, run_command_with_output_and_env, walk_dir};
+use crate::utils::{
+    copy_file, create_dir, get_sysroot_dir, run_command, run_command_with_output_and_env, walk_dir,
+};
 use std::collections::HashMap;
 use std::ffi::OsStr;
 use std::fs;
@@ -9,12 +11,14 @@ use std::path::Path;
 struct BuildArg {
     flags: Vec<String>,
     config_info: ConfigInfo,
+    build_sysroot: bool,
 }
 
 impl BuildArg {
+    /// Creates a new `BuildArg` instance by parsing command-line arguments.
     fn new() -> Result<Option<Self>, String> {
         let mut build_arg = Self::default();
-        // We skip binary name and the `build` command.
+        // Skip binary name and the `build` command.
         let mut args = std::env::args().skip(2);
 
         while let Some(arg) = args.next() {
@@ -29,6 +33,9 @@ impl BuildArg {
                         );
                     }
                 }
+                "--sysroot" => {
+                    build_arg.build_sysroot = true;
+                }
                 "--help" => {
                     Self::usage();
                     return Ok(None);
@@ -48,20 +55,20 @@ impl BuildArg {
             r#"
 `build` command help:
 
-    --features [arg]       : Add a new feature [arg]"#
+    --features [arg]       : Add a new feature [arg]
+    --sysroot              : Build with sysroot"#
         );
         ConfigInfo::show_usage();
         println!("    --help                 : Show this help");
     }
 }
 
-pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
-    let start_dir = Path::new("build_sysroot");
+fn cleanup_sysroot_previous_build(start_dir: &Path) {
     // Cleanup for previous run
     // Clean target dir except for build scripts and incremental cache
     let _ = walk_dir(
         start_dir.join("target"),
-        |dir: &Path| {
+        &mut |dir: &Path| {
             for top in &["debug", "release"] {
                 let _ = fs::remove_dir_all(dir.join(top).join("build"));
                 let _ = fs::remove_dir_all(dir.join(top).join("deps"));
@@ -70,7 +77,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
 
                 let _ = walk_dir(
                     dir.join(top),
-                    |sub_dir: &Path| {
+                    &mut |sub_dir: &Path| {
                         if sub_dir
                             .file_name()
                             .map(|filename| filename.to_str().unwrap().starts_with("libsysroot"))
@@ -80,7 +87,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
                         }
                         Ok(())
                     },
-                    |file: &Path| {
+                    &mut |file: &Path| {
                         if file
                             .file_name()
                             .map(|filename| filename.to_str().unwrap().starts_with("libsysroot"))
@@ -90,16 +97,39 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
                         }
                         Ok(())
                     },
+                    false,
                 );
             }
             Ok(())
         },
-        |_| Ok(()),
+        &mut |_| Ok(()),
+        false,
     );
 
     let _ = fs::remove_file(start_dir.join("Cargo.lock"));
     let _ = fs::remove_file(start_dir.join("test_target/Cargo.lock"));
     let _ = fs::remove_dir_all(start_dir.join("sysroot"));
+}
+
+pub fn create_build_sysroot_content(start_dir: &Path) -> Result<(), String> {
+    if !start_dir.is_dir() {
+        create_dir(start_dir)?;
+    }
+    copy_file("build_system/build_sysroot/Cargo.toml", &start_dir.join("Cargo.toml"))?;
+    copy_file("build_system/build_sysroot/Cargo.lock", &start_dir.join("Cargo.lock"))?;
+
+    let src_dir = start_dir.join("src");
+    if !src_dir.is_dir() {
+        create_dir(&src_dir)?;
+    }
+    copy_file("build_system/build_sysroot/lib.rs", &start_dir.join("src/lib.rs"))
+}
+
+pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
+    let start_dir = get_sysroot_dir();
+
+    cleanup_sysroot_previous_build(&start_dir);
+    create_build_sysroot_content(&start_dir)?;
 
     // Builds libs
     let mut rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
@@ -110,7 +140,6 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
     if config.no_default_features {
         rustflags.push_str(" -Csymbol-mangling-version=v0");
     }
-    let mut env = env.clone();
 
     let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"build", &"--target", &config.target];
 
@@ -127,46 +156,33 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
         "debug"
     };
 
+    if let Ok(cg_rustflags) = std::env::var("CG_RUSTFLAGS") {
+        rustflags.push(' ');
+        rustflags.push_str(&cg_rustflags);
+    }
+
+    let mut env = env.clone();
     env.insert("RUSTFLAGS".to_string(), rustflags);
-    run_command_with_output_and_env(&args, Some(start_dir), Some(&env))?;
+    run_command_with_output_and_env(&args, Some(&start_dir), Some(&env))?;
 
     // Copy files to sysroot
     let sysroot_path = start_dir.join(format!("sysroot/lib/rustlib/{}/lib/", config.target_triple));
-    fs::create_dir_all(&sysroot_path).map_err(|error| {
-        format!(
-            "Failed to create directory `{}`: {:?}",
-            sysroot_path.display(),
-            error
-        )
-    })?;
-    let copier = |dir_to_copy: &Path| {
+    create_dir(&sysroot_path)?;
+    let mut copier = |dir_to_copy: &Path| {
         // FIXME: should not use shell command!
         run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ())
     };
     walk_dir(
         start_dir.join(&format!("target/{}/{}/deps", config.target_triple, channel)),
-        copier,
-        copier,
+        &mut copier.clone(),
+        &mut copier,
+        false,
     )?;
 
     // Copy the source files to the sysroot (Rust for Linux needs this).
     let sysroot_src_path = start_dir.join("sysroot/lib/rustlib/src/rust");
-    fs::create_dir_all(&sysroot_src_path).map_err(|error| {
-        format!(
-            "Failed to create directory `{}`: {:?}",
-            sysroot_src_path.display(),
-            error
-        )
-    })?;
-    run_command(
-        &[
-            &"cp",
-            &"-r",
-            &start_dir.join("sysroot_src/library/"),
-            &sysroot_src_path,
-        ],
-        None,
-    )?;
+    create_dir(&sysroot_src_path)?;
+    run_command(&[&"cp", &"-r", &start_dir.join("sysroot_src/library/"), &sysroot_src_path], None)?;
 
     Ok(())
 }
@@ -174,20 +190,11 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
 fn build_codegen(args: &mut BuildArg) -> Result<(), String> {
     let mut env = HashMap::new();
 
-    env.insert(
-        "LD_LIBRARY_PATH".to_string(),
-        args.config_info.gcc_path.clone(),
-    );
-    env.insert(
-        "LIBRARY_PATH".to_string(),
-        args.config_info.gcc_path.clone(),
-    );
+    env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
+    env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
 
     if args.config_info.no_default_features {
-        env.insert(
-            "RUSTFLAGS".to_string(),
-            "-Csymbol-mangling-version=v0".to_string(),
-        );
+        env.insert("RUSTFLAGS".to_string(), "-Csymbol-mangling-version=v0".to_string());
     }
 
     let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"rustc"];
@@ -212,18 +219,15 @@ fn build_codegen(args: &mut BuildArg) -> Result<(), String> {
     // We voluntarily ignore the error.
     let _ = fs::remove_dir_all("target/out");
     let gccjit_target = "target/out/gccjit";
-    fs::create_dir_all(gccjit_target).map_err(|error| {
-        format!(
-            "Failed to create directory `{}`: {:?}",
-            gccjit_target, error
-        )
-    })?;
-
-    println!("[BUILD] sysroot");
-    build_sysroot(&env, &args.config_info)?;
+    create_dir(gccjit_target)?;
+    if args.build_sysroot {
+        println!("[BUILD] sysroot");
+        build_sysroot(&env, &args.config_info)?;
+    }
     Ok(())
 }
 
+/// Executes the build process.
 pub fn run() -> Result<(), String> {
     let mut args = match BuildArg::new()? {
         Some(args) => args,
diff --git a/compiler/rustc_codegen_gcc/build_system/src/cargo.rs b/compiler/rustc_codegen_gcc/build_system/src/cargo.rs
deleted file mode 100644
index 1cfcdba6b1c..00000000000
--- a/compiler/rustc_codegen_gcc/build_system/src/cargo.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-use crate::config::ConfigInfo;
-use crate::utils::{
-    get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
-    rustc_version_info,
-};
-
-use std::collections::HashMap;
-use std::ffi::OsStr;
-use std::path::PathBuf;
-
-fn args() -> Result<Option<Vec<String>>, String> {
-    // We skip the binary and the "cargo" option.
-    if let Some("--help") = std::env::args().skip(2).next().as_deref() {
-        usage();
-        return Ok(None);
-    }
-    let args = std::env::args().skip(2).collect::<Vec<_>>();
-    if args.is_empty() {
-        return Err(
-            "Expected at least one argument for `cargo` subcommand, found none".to_string(),
-        );
-    }
-    Ok(Some(args))
-}
-
-fn usage() {
-    println!(
-        r#"
-`cargo` command help:
-
-    [args]     : Arguments to be passed to the cargo command
-    --help     : Show this help
-"#
-    )
-}
-
-pub fn run() -> Result<(), String> {
-    let args = match args()? {
-        Some(a) => a,
-        None => return Ok(()),
-    };
-
-    // We first need to go to the original location to ensure that the config setup will go as
-    // expected.
-    let current_dir = std::env::current_dir()
-        .and_then(|path| path.canonicalize())
-        .map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
-    let current_exe = std::env::current_exe()
-        .and_then(|path| path.canonicalize())
-        .map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
-    let mut parent_dir = current_exe
-        .components()
-        .map(|comp| comp.as_os_str())
-        .collect::<Vec<_>>();
-    // We run this script from "build_system/target/release/y", so we need to remove these elements.
-    for to_remove in &["y", "release", "target", "build_system"] {
-        if parent_dir
-            .last()
-            .map(|part| part == to_remove)
-            .unwrap_or(false)
-        {
-            parent_dir.pop();
-        } else {
-            return Err(format!(
-                "Build script not executed from `build_system/target/release/y` (in path {})",
-                current_exe.display(),
-            ));
-        }
-    }
-    let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
-    std::env::set_current_dir(&parent_dir).map_err(|error| {
-        format!(
-            "Failed to go to `{}` folder: {:?}",
-            parent_dir.display(),
-            error
-        )
-    })?;
-
-    let mut env: HashMap<String, String> = std::env::vars().collect();
-    ConfigInfo::default().setup(&mut env, false)?;
-    let toolchain = get_toolchain()?;
-
-    let toolchain_version = rustc_toolchain_version_info(&toolchain)?;
-    let default_version = rustc_version_info(None)?;
-    if toolchain_version != default_version {
-        println!(
-            "rustc_codegen_gcc is built for {} but the default rustc version is {}.",
-            toolchain_version.short, default_version.short,
-        );
-        println!("Using {}.", toolchain_version.short);
-    }
-
-    // We go back to the original folder since we now have set up everything we needed.
-    std::env::set_current_dir(&current_dir).map_err(|error| {
-        format!(
-            "Failed to go back to `{}` folder: {:?}",
-            current_dir.display(),
-            error
-        )
-    })?;
-
-    let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
-    env.insert("RUSTDOCFLAGS".to_string(), rustflags);
-    let toolchain = format!("+{}", toolchain);
-    let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &toolchain];
-    for arg in &args {
-        command.push(arg);
-    }
-    if run_command_with_output_and_env_no_err(&command, None, Some(&env)).is_err() {
-        std::process::exit(1);
-    }
-
-    Ok(())
-}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/clean.rs b/compiler/rustc_codegen_gcc/build_system/src/clean.rs
index cd8e691a0ed..55f55acf73e 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/clean.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/clean.rs
@@ -1,4 +1,4 @@
-use crate::utils::{remove_file, run_command};
+use crate::utils::{get_sysroot_dir, remove_file, run_command};
 
 use std::fs::remove_dir_all;
 use std::path::Path;
@@ -42,11 +42,12 @@ fn usage() {
 }
 
 fn clean_all() -> Result<(), String> {
+    let build_sysroot = get_sysroot_dir();
     let dirs_to_remove = [
-        "target",
-        "build_sysroot/sysroot",
-        "build_sysroot/sysroot_src",
-        "build_sysroot/target",
+        "target".into(),
+        build_sysroot.join("sysroot"),
+        build_sysroot.join("sysroot_src"),
+        build_sysroot.join("target"),
     ];
     for dir in dirs_to_remove {
         let _ = remove_dir_all(dir);
@@ -56,10 +57,11 @@ fn clean_all() -> Result<(), String> {
         let _ = remove_dir_all(Path::new(crate::BUILD_DIR).join(dir));
     }
 
-    let files_to_remove = ["build_sysroot/Cargo.lock", "perf.data", "perf.data.old"];
+    let files_to_remove =
+        [build_sysroot.join("Cargo.lock"), "perf.data".into(), "perf.data.old".into()];
 
     for file in files_to_remove {
-        let _ = remove_file(file);
+        let _ = remove_file(&file);
     }
 
     println!("Successfully ran `clean all`");
diff --git a/compiler/rustc_codegen_gcc/build_system/src/config.rs b/compiler/rustc_codegen_gcc/build_system/src/config.rs
index 34c92a3485e..965aedd8be8 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/config.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/config.rs
@@ -1,5 +1,6 @@
 use crate::utils::{
-    create_symlink, get_os_name, run_command_with_output, rustc_version_info, split_args,
+    create_dir, create_symlink, get_os_name, get_sysroot_dir, run_command_with_output,
+    rustc_version_info, split_args,
 };
 use std::collections::HashMap;
 use std::env as std_env;
@@ -26,11 +27,7 @@ impl Channel {
 }
 
 fn failed_config_parsing(config_file: &Path, err: &str) -> Result<ConfigFile, String> {
-    Err(format!(
-        "Failed to parse `{}`: {}",
-        config_file.display(),
-        err
-    ))
+    Err(format!("Failed to parse `{}`: {}", config_file.display(), err))
 }
 
 #[derive(Default)]
@@ -48,11 +45,7 @@ impl ConfigFile {
             )
         })?;
         let toml = Toml::parse(&content).map_err(|err| {
-            format!(
-                "Error occurred around `{}`: {:?}",
-                &content[err.start..=err.end],
-                err.kind
-            )
+            format!("Error occurred around `{}`: {:?}", &content[err.start..=err.end], err.kind)
         })?;
         let mut config = Self::default();
         for (key, value) in toml.iter() {
@@ -181,11 +174,7 @@ impl ConfigInfo {
             },
             "--use-backend" => match args.next() {
                 Some(backend) if !backend.is_empty() => self.backend = Some(backend),
-                _ => {
-                    return Err(
-                        "Expected an argument after `--use-backend`, found nothing".into()
-                    )
-                }
+                _ => return Err("Expected an argument after `--use-backend`, found nothing".into()),
             },
             "--no-default-features" => self.no_default_features = true,
             _ => return Ok(false),
@@ -228,20 +217,10 @@ impl ConfigInfo {
 
         let output_dir = output_dir.join(&commit);
         if !output_dir.is_dir() {
-            std::fs::create_dir_all(&output_dir).map_err(|err| {
-                format!(
-                    "failed to create folder `{}`: {:?}",
-                    output_dir.display(),
-                    err,
-                )
-            })?;
+            create_dir(&output_dir)?;
         }
         let output_dir = output_dir.canonicalize().map_err(|err| {
-            format!(
-                "Failed to get absolute path of `{}`: {:?}",
-                output_dir.display(),
-                err
-            )
+            format!("Failed to get absolute path of `{}`: {:?}", output_dir.display(), err)
         })?;
 
         let libgccjit_so_name = "libgccjit.so";
@@ -252,13 +231,7 @@ impl ConfigInfo {
             let tempfile = output_dir.join(&tempfile_name);
             let is_in_ci = std::env::var("GITHUB_ACTIONS").is_ok();
 
-            let url = format!(
-                "https://github.com/antoyo/gcc/releases/download/master-{}/libgccjit.so",
-                commit,
-            );
-
-            println!("Downloading `{}`...", url);
-            download_gccjit(url, &output_dir, tempfile_name, !is_in_ci)?;
+            download_gccjit(&commit, &output_dir, tempfile_name, !is_in_ci)?;
 
             let libgccjit_so = output_dir.join(libgccjit_so_name);
             // If we reach this point, it means the file was correctly downloaded, so let's
@@ -275,10 +248,7 @@ impl ConfigInfo {
             println!("Downloaded libgccjit.so version {} successfully!", commit);
             // We need to create a link named `libgccjit.so.0` because that's what the linker is
             // looking for.
-            create_symlink(
-                &libgccjit_so,
-                output_dir.join(&format!("{}.0", libgccjit_so_name)),
-            )?;
+            create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?;
         }
 
         self.gcc_path = output_dir.display().to_string();
@@ -298,10 +268,7 @@ impl ConfigInfo {
             Some(config_file) => config_file.into(),
             None => self.compute_path("config.toml"),
         };
-        let ConfigFile {
-            gcc_path,
-            download_gccjit,
-        } = ConfigFile::new(&config_file)?;
+        let ConfigFile { gcc_path, download_gccjit } = ConfigFile::new(&config_file)?;
 
         if let Some(true) = download_gccjit {
             self.download_gccjit_if_needed()?;
@@ -310,10 +277,7 @@ impl ConfigInfo {
         self.gcc_path = match gcc_path {
             Some(path) => path,
             None => {
-                return Err(format!(
-                    "missing `gcc-path` value from `{}`",
-                    config_file.display(),
-                ))
+                return Err(format!("missing `gcc-path` value from `{}`", config_file.display(),))
             }
         };
         Ok(())
@@ -393,15 +357,16 @@ impl ConfigInfo {
             .join(&format!("librustc_codegen_gcc.{}", self.dylib_ext))
             .display()
             .to_string();
-        self.sysroot_path = current_dir
-            .join("build_sysroot/sysroot")
-            .display()
-            .to_string();
+        self.sysroot_path =
+            current_dir.join(&get_sysroot_dir()).join("sysroot").display().to_string();
         if let Some(backend) = &self.backend {
+            // This option is only used in the rust compiler testsuite. The sysroot is handled
+            // by its build system directly so no need to set it ourselves.
             rustflags.push(format!("-Zcodegen-backend={}", backend));
         } else {
             rustflags.extend_from_slice(&[
-                "--sysroot".to_string(), self.sysroot_path.clone(),
+                "--sysroot".to_string(),
+                self.sysroot_path.clone(),
                 format!("-Zcodegen-backend={}", self.cg_backend_path),
             ]);
         }
@@ -422,13 +387,6 @@ impl ConfigInfo {
             rustflags.push("-Csymbol-mangling-version=v0".to_string());
         }
 
-        rustflags.push("-Cdebuginfo=2".to_string());
-
-        // Since we don't support ThinLTO, disable LTO completely when not trying to do LTO.
-        // TODO(antoyo): remove when we can handle ThinLTO.
-        if !env.contains_key(&"FAT_LTO".to_string()) {
-            rustflags.push("-Clto=off".to_string());
-        }
         // FIXME(antoyo): remove once the atomic shim is gone
         if os_name == "Darwin" {
             rustflags.extend_from_slice(&[
@@ -440,10 +398,9 @@ impl ConfigInfo {
         // display metadata load errors
         env.insert("RUSTC_LOG".to_string(), "warn".to_string());
 
-        let sysroot = current_dir.join(&format!(
-            "build_sysroot/sysroot/lib/rustlib/{}/lib",
-            self.target_triple,
-        ));
+        let sysroot = current_dir
+            .join(&get_sysroot_dir())
+            .join(&format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
         let ld_library_path = format!(
             "{target}:{sysroot}:{gcc_path}",
             target = self.cargo_target_dir,
@@ -501,11 +458,27 @@ impl ConfigInfo {
 }
 
 fn download_gccjit(
-    url: String,
+    commit: &str,
     output_dir: &Path,
     tempfile_name: String,
     with_progress_bar: bool,
 ) -> Result<(), String> {
+    let url = if std::env::consts::OS == "linux" && std::env::consts::ARCH == "x86_64" {
+        format!("https://github.com/rust-lang/gcc/releases/download/master-{}/libgccjit.so", commit)
+    } else {
+        eprintln!(
+            "\
+Pre-compiled libgccjit.so not available for this os or architecture.
+Please compile it yourself and update the `config.toml` file
+to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
+        );
+        return Err(String::from(
+            "no appropriate pre-compiled libgccjit.so available for download",
+        ));
+    };
+
+    println!("Downloading `{}`...", url);
+
     // Try curl. If that fails and we are on windows, fallback to PowerShell.
     let mut ret = run_command_with_output(
         &[
@@ -521,11 +494,7 @@ fn download_gccjit(
             &"--retry",
             &"3",
             &"-SRfL",
-            if with_progress_bar {
-                &"--progress-bar"
-            } else {
-                &"-s"
-            },
+            if with_progress_bar { &"--progress-bar" } else { &"-s" },
             &url.as_str(),
         ],
         Some(&output_dir),
diff --git a/compiler/rustc_codegen_gcc/build_system/src/fmt.rs b/compiler/rustc_codegen_gcc/build_system/src/fmt.rs
new file mode 100644
index 00000000000..43644ba19b3
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/fmt.rs
@@ -0,0 +1,35 @@
+use crate::utils::run_command_with_output;
+use std::ffi::OsStr;
+use std::path::Path;
+
+fn show_usage() {
+    println!(
+        r#"
+`fmt` command help:
+
+    --check                : Pass `--check` argument to `cargo fmt` commands
+    --help                 : Show this help"#
+    );
+}
+
+pub fn run() -> Result<(), String> {
+    let mut check = false;
+    // We skip binary name and the `info` command.
+    let mut args = std::env::args().skip(2);
+    while let Some(arg) = args.next() {
+        match arg.as_str() {
+            "--help" => {
+                show_usage();
+                return Ok(());
+            }
+            "--check" => check = true,
+            _ => return Err(format!("Unknown option {}", arg)),
+        }
+    }
+
+    let cmd: &[&dyn AsRef<OsStr>] =
+        if check { &[&"cargo", &"fmt", &"--check"] } else { &[&"cargo", &"fmt"] };
+
+    run_command_with_output(cmd, Some(&Path::new(".")))?;
+    run_command_with_output(cmd, Some(&Path::new("build_system")))
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs
index 48ffbc7a907..d678fd75344 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/main.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs
@@ -2,12 +2,13 @@ use std::env;
 use std::process;
 
 mod build;
-mod cargo;
 mod clean;
 mod clone_gcc;
 mod config;
+mod fmt;
 mod info;
 mod prepare;
+mod rust_tools;
 mod rustc_info;
 mod test;
 mod utils;
@@ -26,16 +27,23 @@ macro_rules! arg_error {
 fn usage() {
     println!(
         "\
-Available commands for build_system:
+rustc_codegen_gcc build system
 
-    cargo     : Run cargo command
-    clean     : Run clean command
-    prepare   : Run prepare command
-    build     : Run build command
-    test      : Run test command
-    info      : Run info command
-    clone-gcc : Run clone-gcc command
-    --help    : Show this message"
+Usage: build_system [command] [options]
+
+Options:
+        --help    : Displays this help message.
+
+Commands:
+        cargo     : Executes a cargo command. 
+        rustc     : Compiles the program using the GCC compiler.
+        clean     : Cleans the build directory, removing all compiled files and artifacts.
+        prepare   : Prepares the environment for building, including fetching dependencies and setting up configurations.
+        build     : Compiles the project. 
+        test      : Runs tests for the project.
+        info      : Displays information about the build environment and project configuration.
+        clone-gcc : Clones the GCC compiler from a specified source.
+        fmt       : Runs rustfmt"
     );
 }
 
@@ -45,8 +53,10 @@ pub enum Command {
     CloneGcc,
     Prepare,
     Build,
+    Rustc,
     Test,
     Info,
+    Fmt,
 }
 
 fn main() {
@@ -56,12 +66,14 @@ fn main() {
 
     let command = match env::args().nth(1).as_deref() {
         Some("cargo") => Command::Cargo,
+        Some("rustc") => Command::Rustc,
         Some("clean") => Command::Clean,
         Some("prepare") => Command::Prepare,
         Some("build") => Command::Build,
         Some("test") => Command::Test,
         Some("info") => Command::Info,
         Some("clone-gcc") => Command::CloneGcc,
+        Some("fmt") => Command::Fmt,
         Some("--help") => {
             usage();
             process::exit(0);
@@ -75,13 +87,15 @@ fn main() {
     };
 
     if let Err(e) = match command {
-        Command::Cargo => cargo::run(),
+        Command::Cargo => rust_tools::run_cargo(),
+        Command::Rustc => rust_tools::run_rustc(),
         Command::Clean => clean::run(),
         Command::Prepare => prepare::run(),
         Command::Build => build::run(),
         Command::Test => test::run(),
         Command::Info => info::run(),
         Command::CloneGcc => clone_gcc::run(),
+        Command::Fmt => fmt::run(),
     } {
         eprintln!("Command failed to run: {e}");
         process::exit(1);
diff --git a/compiler/rustc_codegen_gcc/build_system/src/prepare.rs b/compiler/rustc_codegen_gcc/build_system/src/prepare.rs
index 821c793c7e5..00aa632165e 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/prepare.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/prepare.rs
@@ -1,58 +1,58 @@
 use crate::rustc_info::get_rustc_path;
 use crate::utils::{
-    cargo_install, git_clone_root_dir, remove_file, run_command, run_command_with_output, walk_dir,
+    cargo_install, create_dir, get_sysroot_dir, git_clone_root_dir, remove_file, run_command,
+    run_command_with_output, walk_dir,
 };
 
 use std::fs;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 
 fn prepare_libcore(
     sysroot_path: &Path,
     libgccjit12_patches: bool,
     cross_compile: bool,
+    sysroot_source: Option<String>,
 ) -> Result<(), String> {
-    let rustc_path = match get_rustc_path() {
-        Some(path) => path,
-        None => return Err("`rustc` path not found".to_string()),
-    };
+    let rustlib_dir: PathBuf;
 
-    let parent = match rustc_path.parent() {
-        Some(path) => path,
-        None => return Err(format!("No parent for `{}`", rustc_path.display())),
-    };
+    if let Some(path) = sysroot_source {
+        rustlib_dir = Path::new(&path)
+            .canonicalize()
+            .map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
+        if !rustlib_dir.is_dir() {
+            return Err(format!("Custom sysroot path {:?} not found", rustlib_dir));
+        }
+    } else {
+        let rustc_path = match get_rustc_path() {
+            Some(path) => path,
+            None => return Err("`rustc` path not found".to_string()),
+        };
+
+        let parent = match rustc_path.parent() {
+            Some(path) => path,
+            None => return Err(format!("No parent for `{}`", rustc_path.display())),
+        };
 
-    let rustlib_dir = parent
-        .join("../lib/rustlib/src/rust")
-        .canonicalize()
-        .map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
-    if !rustlib_dir.is_dir() {
-        return Err("Please install `rust-src` component".to_string());
+        rustlib_dir = parent
+            .join("../lib/rustlib/src/rust")
+            .canonicalize()
+            .map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
+        if !rustlib_dir.is_dir() {
+            return Err("Please install `rust-src` component".to_string());
+        }
     }
 
     let sysroot_dir = sysroot_path.join("sysroot_src");
     if sysroot_dir.is_dir() {
         if let Err(error) = fs::remove_dir_all(&sysroot_dir) {
-            return Err(format!(
-                "Failed to remove `{}`: {:?}",
-                sysroot_dir.display(),
-                error,
-            ));
+            return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
         }
     }
 
     let sysroot_library_dir = sysroot_dir.join("library");
-    fs::create_dir_all(&sysroot_library_dir).map_err(|error| {
-        format!(
-            "Failed to create folder `{}`: {:?}",
-            sysroot_library_dir.display(),
-            error,
-        )
-    })?;
+    create_dir(&sysroot_library_dir)?;
 
-    run_command(
-        &[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir],
-        None,
-    )?;
+    run_command(&[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir], None)?;
 
     println!("[GIT] init (cwd): `{}`", sysroot_dir.display());
     run_command(&[&"git", &"init"], Some(&sysroot_dir))?;
@@ -63,70 +63,52 @@ fn prepare_libcore(
     // This is needed on systems where nothing is configured.
     // git really needs something here, or it will fail.
     // Even using --author is not enough.
-    run_command(
-        &[&"git", &"config", &"user.email", &"none@example.com"],
-        Some(&sysroot_dir),
-    )?;
-    run_command(
-        &[&"git", &"config", &"user.name", &"None"],
-        Some(&sysroot_dir),
-    )?;
-    run_command(
-        &[&"git", &"config", &"core.autocrlf", &"false"],
-        Some(&sysroot_dir),
-    )?;
-    run_command(
-        &[&"git", &"config", &"commit.gpgSign", &"false"],
-        Some(&sysroot_dir),
-    )?;
-    run_command(
-        &[&"git", &"commit", &"-m", &"Initial commit", &"-q"],
-        Some(&sysroot_dir),
-    )?;
+    run_command(&[&"git", &"config", &"user.email", &"none@example.com"], Some(&sysroot_dir))?;
+    run_command(&[&"git", &"config", &"user.name", &"None"], Some(&sysroot_dir))?;
+    run_command(&[&"git", &"config", &"core.autocrlf", &"false"], Some(&sysroot_dir))?;
+    run_command(&[&"git", &"config", &"commit.gpgSign", &"false"], Some(&sysroot_dir))?;
+    run_command(&[&"git", &"commit", &"-m", &"Initial commit", &"-q"], Some(&sysroot_dir))?;
 
     let mut patches = Vec::new();
     walk_dir(
         "patches",
-        |_| Ok(()),
-        |file_path: &Path| {
+        &mut |_| Ok(()),
+        &mut |file_path: &Path| {
             patches.push(file_path.to_path_buf());
             Ok(())
         },
+        false,
     )?;
     if cross_compile {
         walk_dir(
             "patches/cross_patches",
-            |_| Ok(()),
-            |file_path: &Path| {
+            &mut |_| Ok(()),
+            &mut |file_path: &Path| {
                 patches.push(file_path.to_path_buf());
                 Ok(())
             },
+            false,
         )?;
     }
     if libgccjit12_patches {
         walk_dir(
             "patches/libgccjit12",
-            |_| Ok(()),
-            |file_path: &Path| {
+            &mut |_| Ok(()),
+            &mut |file_path: &Path| {
                 patches.push(file_path.to_path_buf());
                 Ok(())
             },
+            false,
         )?;
     }
     patches.sort();
     for file_path in patches {
         println!("[GIT] apply `{}`", file_path.display());
-        let path = Path::new("../..").join(file_path);
+        let path = Path::new("../../..").join(file_path);
         run_command_with_output(&[&"git", &"apply", &path], Some(&sysroot_dir))?;
         run_command_with_output(&[&"git", &"add", &"-A"], Some(&sysroot_dir))?;
         run_command_with_output(
-            &[
-                &"git",
-                &"commit",
-                &"--no-gpg-sign",
-                &"-m",
-                &format!("Patch {}", path.display()),
-            ],
+            &[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
             Some(&sysroot_dir),
         )?;
     }
@@ -145,13 +127,7 @@ fn prepare_rand() -> Result<(), String> {
     run_command_with_output(&[&"git", &"apply", &path], Some(rand_dir))?;
     run_command_with_output(&[&"git", &"add", &"-A"], Some(rand_dir))?;
     run_command_with_output(
-        &[
-            &"git",
-            &"commit",
-            &"--no-gpg-sign",
-            &"-m",
-            &format!("Patch {}", path.display()),
-        ],
+        &[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
         Some(rand_dir),
     )?;
 
@@ -165,10 +141,7 @@ fn build_raytracer(repo_dir: &Path) -> Result<(), String> {
     if mv_target.is_file() {
         remove_file(&mv_target)?;
     }
-    run_command(
-        &[&"mv", &"target/debug/main", &"raytracer_cg_llvm"],
-        Some(repo_dir),
-    )?;
+    run_command(&[&"mv", &"target/debug/main", &"raytracer_cg_llvm"], Some(repo_dir))?;
     Ok(())
 }
 
@@ -193,6 +166,7 @@ struct PrepareArg {
     cross_compile: bool,
     only_libcore: bool,
     libgccjit12_patches: bool,
+    sysroot_source: Option<String>,
 }
 
 impl PrepareArg {
@@ -200,12 +174,23 @@ impl PrepareArg {
         let mut only_libcore = false;
         let mut cross_compile = false;
         let mut libgccjit12_patches = false;
+        let mut sysroot_source = None;
 
-        for arg in std::env::args().skip(2) {
+        let mut args = std::env::args().skip(2);
+        while let Some(arg) = args.next() {
             match arg.as_str() {
                 "--only-libcore" => only_libcore = true,
                 "--cross" => cross_compile = true,
                 "--libgccjit12-patches" => libgccjit12_patches = true,
+                "--sysroot-source" => {
+                    if let Some(path) = args.next() {
+                        sysroot_source = Some(path);
+                    } else {
+                        return Err(
+                            "Expected a value after `--sysroot-source`, found nothing".to_string()
+                        );
+                    }
+                }
                 "--help" => {
                     Self::usage();
                     return Ok(None);
@@ -213,11 +198,7 @@ impl PrepareArg {
                 a => return Err(format!("Unknown argument `{a}`")),
             }
         }
-        Ok(Some(Self {
-            cross_compile,
-            only_libcore,
-            libgccjit12_patches,
-        }))
+        Ok(Some(Self { cross_compile, only_libcore, libgccjit12_patches, sysroot_source }))
     }
 
     fn usage() {
@@ -228,6 +209,7 @@ impl PrepareArg {
     --only-libcore           : Only setup libcore and don't clone other repositories
     --cross                  : Apply the patches needed to do cross-compilation
     --libgccjit12-patches    : Apply patches needed for libgccjit12
+    --sysroot-source         : Specify custom path for sysroot source
     --help                   : Show this help"#
         )
     }
@@ -238,8 +220,13 @@ pub fn run() -> Result<(), String> {
         Some(a) => a,
         None => return Ok(()),
     };
-    let sysroot_path = Path::new("build_sysroot");
-    prepare_libcore(sysroot_path, args.libgccjit12_patches, args.cross_compile)?;
+    let sysroot_path = get_sysroot_dir();
+    prepare_libcore(
+        &sysroot_path,
+        args.libgccjit12_patches,
+        args.cross_compile,
+        args.sysroot_source,
+    )?;
 
     if !args.only_libcore {
         cargo_install("hyperfine")?;
diff --git a/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs b/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs
new file mode 100644
index 00000000000..242fa7ef949
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs
@@ -0,0 +1,125 @@
+use crate::config::ConfigInfo;
+use crate::utils::{
+    get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
+    rustc_version_info,
+};
+
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::path::PathBuf;
+
+fn args(command: &str) -> Result<Option<Vec<String>>, String> {
+    // We skip the binary and the "cargo"/"rustc" option.
+    if let Some("--help") = std::env::args().skip(2).next().as_deref() {
+        usage(command);
+        return Ok(None);
+    }
+    let args = std::env::args().skip(2).collect::<Vec<_>>();
+    if args.is_empty() {
+        return Err(format!(
+            "Expected at least one argument for `{}` subcommand, found none",
+            command
+        ));
+    }
+    Ok(Some(args))
+}
+
+fn usage(command: &str) {
+    println!(
+        r#"
+`{}` command help:
+
+    [args]     : Arguments to be passed to the cargo command
+    --help     : Show this help
+"#,
+        command,
+    )
+}
+
+struct RustcTools {
+    env: HashMap<String, String>,
+    args: Vec<String>,
+    toolchain: String,
+    config: ConfigInfo,
+}
+
+impl RustcTools {
+    fn new(command: &str) -> Result<Option<Self>, String> {
+        let Some(args) = args(command)? else { return Ok(None) };
+
+        // We first need to go to the original location to ensure that the config setup will go as
+        // expected.
+        let current_dir = std::env::current_dir()
+            .and_then(|path| path.canonicalize())
+            .map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
+        let current_exe = std::env::current_exe()
+            .and_then(|path| path.canonicalize())
+            .map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
+        let mut parent_dir =
+            current_exe.components().map(|comp| comp.as_os_str()).collect::<Vec<_>>();
+        // We run this script from "build_system/target/release/y", so we need to remove these elements.
+        for to_remove in &["y", "release", "target", "build_system"] {
+            if parent_dir.last().map(|part| part == to_remove).unwrap_or(false) {
+                parent_dir.pop();
+            } else {
+                return Err(format!(
+                    "Build script not executed from `build_system/target/release/y` (in path {})",
+                    current_exe.display(),
+                ));
+            }
+        }
+        let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
+        std::env::set_current_dir(&parent_dir).map_err(|error| {
+            format!("Failed to go to `{}` folder: {:?}", parent_dir.display(), error)
+        })?;
+
+        let mut env: HashMap<String, String> = std::env::vars().collect();
+        let mut config = ConfigInfo::default();
+        config.setup(&mut env, false)?;
+        let toolchain = get_toolchain()?;
+
+        let toolchain_version = rustc_toolchain_version_info(&toolchain)?;
+        let default_version = rustc_version_info(None)?;
+        if toolchain_version != default_version {
+            println!(
+                "rustc_codegen_gcc is built for {} but the default rustc version is {}.",
+                toolchain_version.short, default_version.short,
+            );
+            println!("Using {}.", toolchain_version.short);
+        }
+
+        // We go back to the original folder since we now have set up everything we needed.
+        std::env::set_current_dir(&current_dir).map_err(|error| {
+            format!("Failed to go back to `{}` folder: {:?}", current_dir.display(), error)
+        })?;
+        let toolchain = format!("+{}", toolchain);
+        Ok(Some(Self { toolchain, args, env, config }))
+    }
+}
+
+pub fn run_cargo() -> Result<(), String> {
+    let Some(mut tools) = RustcTools::new("cargo")? else { return Ok(()) };
+    let rustflags = tools.env.get("RUSTFLAGS").cloned().unwrap_or_default();
+    tools.env.insert("RUSTDOCFLAGS".to_string(), rustflags);
+    let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &tools.toolchain];
+    for arg in &tools.args {
+        command.push(arg);
+    }
+    if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
+        std::process::exit(1);
+    }
+
+    Ok(())
+}
+
+pub fn run_rustc() -> Result<(), String> {
+    let Some(tools) = RustcTools::new("rustc")? else { return Ok(()) };
+    let mut command = tools.config.rustc_command_vec();
+    for arg in &tools.args {
+        command.push(arg);
+    }
+    if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
+        std::process::exit(1);
+    }
+    Ok(())
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs
index 0895dc6bff7..8d088a3aac3 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/test.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs
@@ -1,13 +1,14 @@
 use crate::build;
 use crate::config::{Channel, ConfigInfo};
 use crate::utils::{
-    get_toolchain, git_clone, git_clone_root_dir, remove_file, run_command, run_command_with_env,
-    run_command_with_output_and_env, rustc_version_info, split_args, walk_dir,
+    create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file,
+    run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info,
+    split_args, walk_dir,
 };
 
-use std::collections::{BTreeSet, HashMap};
+use std::collections::HashMap;
 use std::ffi::OsStr;
-use std::fs::{create_dir_all, remove_dir_all, File};
+use std::fs::{remove_dir_all, File};
 use std::io::{BufRead, BufReader};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
@@ -19,46 +20,27 @@ type Runners = HashMap<&'static str, (&'static str, Runner)>;
 fn get_runners() -> Runners {
     let mut runners = HashMap::new();
 
+    runners.insert("--test-rustc", ("Run all rustc tests", test_rustc as Runner));
+    runners
+        .insert("--test-successful-rustc", ("Run successful rustc tests", test_successful_rustc));
     runners.insert(
-        "--test-rustc",
-        ("Run all rustc tests", test_rustc as Runner),
-    );
-    runners.insert(
-        "--test-successful-rustc",
-        ("Run successful rustc tests", test_successful_rustc),
-    );
-    runners.insert(
-        "--test-failing-rustc",
-        ("Run failing rustc tests", test_failing_rustc),
-    );
-    runners.insert(
-        "--projects",
-        ("Run the tests of popular crates", test_projects),
+        "--test-failing-ui-pattern-tests",
+        ("Run failing ui pattern tests", test_failing_ui_pattern_tests),
     );
+    runners.insert("--test-failing-rustc", ("Run failing rustc tests", test_failing_rustc));
+    runners.insert("--projects", ("Run the tests of popular crates", test_projects));
     runners.insert("--test-libcore", ("Run libcore tests", test_libcore));
     runners.insert("--clean", ("Empty cargo target directory", clean));
     runners.insert("--build-sysroot", ("Build sysroot", build_sysroot));
     runners.insert("--std-tests", ("Run std tests", std_tests));
     runners.insert("--asm-tests", ("Run asm tests", asm_tests));
-    runners.insert(
-        "--extended-tests",
-        ("Run extended sysroot tests", extended_sysroot_tests),
-    );
-    runners.insert(
-        "--extended-rand-tests",
-        ("Run extended rand tests", extended_rand_tests),
-    );
+    runners.insert("--extended-tests", ("Run extended sysroot tests", extended_sysroot_tests));
+    runners.insert("--extended-rand-tests", ("Run extended rand tests", extended_rand_tests));
     runners.insert(
         "--extended-regex-example-tests",
-        (
-            "Run extended regex example tests",
-            extended_regex_example_tests,
-        ),
-    );
-    runners.insert(
-        "--extended-regex-tests",
-        ("Run extended regex tests", extended_regex_tests),
+        ("Run extended regex example tests", extended_regex_example_tests),
     );
+    runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests));
     runners.insert("--mini-tests", ("Run mini tests", mini_tests));
 
     runners
@@ -71,15 +53,9 @@ fn get_number_after_arg(
     match args.next() {
         Some(nb) if !nb.is_empty() => match usize::from_str(&nb) {
             Ok(nb) => Ok(nb),
-            Err(_) => Err(format!(
-                "Expected a number after `{}`, found `{}`",
-                option, nb
-            )),
+            Err(_) => Err(format!("Expected a number after `{}`, found `{}`", option, nb)),
         },
-        _ => Err(format!(
-            "Expected a number after `{}`, found nothing",
-            option
-        )),
+        _ => Err(format!("Expected a number after `{}`, found nothing", option)),
     }
 }
 
@@ -110,7 +86,7 @@ fn show_usage() {
 struct TestArg {
     build_only: bool,
     use_system_gcc: bool,
-    runners: BTreeSet<String>,
+    runners: Vec<String>,
     flags: Vec<String>,
     nb_parts: Option<usize>,
     current_part: Option<usize>,
@@ -130,9 +106,7 @@ impl TestArg {
             match arg.as_str() {
                 "--features" => match args.next() {
                     Some(feature) if !feature.is_empty() => {
-                        test_arg
-                            .flags
-                            .extend_from_slice(&["--features".into(), feature]);
+                        test_arg.flags.extend_from_slice(&["--features".into(), feature]);
                     }
                     _ => {
                         return Err("Expected an argument after `--features`, found nothing".into())
@@ -157,8 +131,10 @@ impl TestArg {
                     show_usage();
                     return Ok(None);
                 }
-                x if runners.contains_key(x) => {
-                    test_arg.runners.insert(x.into());
+                x if runners.contains_key(x)
+                    && !test_arg.runners.iter().any(|runner| runner == x) =>
+                {
+                    test_arg.runners.push(x.into());
                 }
                 arg => {
                     if !test_arg.config_info.parse_argument(arg, &mut args)? {
@@ -211,8 +187,7 @@ fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> {
 fn clean(_env: &Env, args: &TestArg) -> Result<(), String> {
     let _ = std::fs::remove_dir_all(&args.config_info.cargo_target_dir);
     let path = Path::new(&args.config_info.cargo_target_dir).join("gccjit");
-    std::fs::create_dir_all(&path)
-        .map_err(|error| format!("failed to create folder `{}`: {:?}", path.display(), error))
+    create_dir(&path)
 }
 
 fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
@@ -304,13 +279,8 @@ fn maybe_run_command_in_vm(
     let sudo_command: &[&dyn AsRef<OsStr>] = &[&"sudo", &"cp", &exe, &vm_exe_path];
     run_command_with_env(sudo_command, None, Some(env))?;
 
-    let mut vm_command: Vec<&dyn AsRef<OsStr>> = vec![
-        &"sudo",
-        &"chroot",
-        &vm_dir,
-        &"qemu-m68k-static",
-        &inside_vm_exe_path,
-    ];
+    let mut vm_command: Vec<&dyn AsRef<OsStr>> =
+        vec![&"sudo", &"chroot", &vm_dir, &"qemu-m68k-static", &inside_vm_exe_path];
     vm_command.extend_from_slice(command);
     run_command_with_output_and_env(&vm_command, Some(&vm_parent_dir), Some(env))?;
     Ok(())
@@ -399,11 +369,7 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
     }
     run_command_with_env(&command, None, Some(env))?;
     maybe_run_command_in_vm(
-        &[
-            &cargo_target_dir.join("std_example"),
-            &"--target",
-            &args.config_info.target_triple,
-        ],
+        &[&cargo_target_dir.join("std_example"), &"--target", &args.config_info.target_triple],
         env,
         args,
     )?;
@@ -427,11 +393,7 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
         command.push(test_flag);
     }
     run_command_with_env(&command, None, Some(env))?;
-    maybe_run_command_in_vm(
-        &[&cargo_target_dir.join("subslice-patterns-const-eval")],
-        env,
-        args,
-    )?;
+    maybe_run_command_in_vm(&[&cargo_target_dir.join("subslice-patterns-const-eval")], env, args)?;
 
     // FIXME: create a function "display_if_not_quiet" or something along the line.
     println!("[AOT] track-caller-attribute");
@@ -447,11 +409,7 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
         command.push(test_flag);
     }
     run_command_with_env(&command, None, Some(env))?;
-    maybe_run_command_in_vm(
-        &[&cargo_target_dir.join("track-caller-attribute")],
-        env,
-        args,
-    )?;
+    maybe_run_command_in_vm(&[&cargo_target_dir.join("track-caller-attribute")], env, args)?;
 
     // FIXME: create a function "display_if_not_quiet" or something along the line.
     println!("[AOT] mod_bench");
@@ -477,11 +435,7 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
     );
     let rust_dir_path = Path::new(crate::BUILD_DIR).join("rust");
     // If the repository was already cloned, command will fail, so doesn't matter.
-    let _ = git_clone(
-        "https://github.com/rust-lang/rust.git",
-        Some(&rust_dir_path),
-        false,
-    );
+    let _ = git_clone("https://github.com/rust-lang/rust.git", Some(&rust_dir_path), false);
     let rust_dir: Option<&Path> = Some(&rust_dir_path);
     run_command(&[&"git", &"checkout", &"--", &"tests/"], rust_dir)?;
     run_command_with_output_and_env(&[&"git", &"fetch"], rust_dir, Some(env))?;
@@ -511,12 +465,8 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
         }
     })?;
     let rustc = String::from_utf8(
-        run_command_with_env(
-            &[&"rustup", &toolchain, &"which", &"rustc"],
-            rust_dir,
-            Some(env),
-        )?
-        .stdout,
+        run_command_with_env(&[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env))?
+            .stdout,
     )
     .map_err(|error| format!("Failed to retrieve rustc path: {:?}", error))
     .and_then(|rustc| {
@@ -573,13 +523,7 @@ download-ci-llvm = false
             llvm_filecheck = llvm_filecheck.trim(),
         ),
     )
-    .map_err(|error| {
-        format!(
-            "Failed to write into `{}`: {:?}",
-            file_path.display(),
-            error
-        )
-    })?;
+    .map_err(|error| format!("Failed to write into `{}`: {:?}", file_path.display(), error))?;
     Ok(rust_dir_path)
 }
 
@@ -591,21 +535,19 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
 
     env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
 
-    let extra = if args.is_using_gcc_master_branch() {
-        ""
-    } else {
-        " -Csymbol-mangling-version=v0"
-    };
+    let extra =
+        if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" };
 
     let rustc_args = &format!(
         r#"-Zpanic-abort-tests \
             -Zcodegen-backend="{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}" \
-            --sysroot "{pwd}/build_sysroot/sysroot" -Cpanic=abort{extra}"#,
+            --sysroot "{sysroot_dir}" -Cpanic=abort{extra}"#,
         pwd = std::env::current_dir()
             .map_err(|error| format!("`current_dir` failed: {:?}", error))?
             .display(),
         channel = args.config_info.channel.as_str(),
         dylib_ext = args.config_info.dylib_ext,
+        sysroot_dir = args.config_info.sysroot_path,
         extra = extra,
     );
 
@@ -703,20 +645,23 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> {
         //"https://github.com/rust-lang/cargo", // TODO: very slow, only run on master?
     ];
 
+    let mut env = env.clone();
+    let rustflags =
+        format!("{} --cap-lints allow", env.get("RUSTFLAGS").cloned().unwrap_or_default());
+    env.insert("RUSTFLAGS".to_string(), rustflags);
     let run_tests = |projects_path, iter: &mut dyn Iterator<Item = &&str>| -> Result<(), String> {
         for project in iter {
             let clone_result = git_clone_root_dir(project, projects_path, true)?;
             let repo_path = Path::new(&clone_result.repo_dir);
-            run_cargo_command(&[&"build", &"--release"], Some(repo_path), env, args)?;
-            run_cargo_command(&[&"test"], Some(repo_path), env, args)?;
+            run_cargo_command(&[&"build", &"--release"], Some(repo_path), &env, args)?;
+            run_cargo_command(&[&"test"], Some(repo_path), &env, args)?;
         }
 
         Ok(())
     };
 
     let projects_path = Path::new("projects");
-    create_dir_all(projects_path)
-        .map_err(|err| format!("Failed to create directory `projects`: {}", err))?;
+    create_dir(projects_path)?;
 
     let nb_parts = args.nb_parts.unwrap_or(0);
     if nb_parts > 0 {
@@ -737,9 +682,9 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> {
 fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> {
     // FIXME: create a function "display_if_not_quiet" or something along the line.
     println!("[TEST] libcore");
-    let path = Path::new("build_sysroot/sysroot_src/library/core/tests");
+    let path = get_sysroot_dir().join("sysroot_src/library/core/tests");
     let _ = remove_dir_all(path.join("target"));
-    run_cargo_command(&[&"test"], Some(path), env, args)?;
+    run_cargo_command(&[&"test"], Some(&path), env, args)?;
     Ok(())
 }
 
@@ -763,10 +708,8 @@ fn extended_rand_tests(env: &Env, args: &TestArg) -> Result<(), String> {
     }
     let mut env = env.clone();
     // newer aho_corasick versions throw a deprecation warning
-    let rustflags = format!(
-        "{} --cap-lints warn",
-        env.get("RUSTFLAGS").cloned().unwrap_or_default()
-    );
+    let rustflags =
+        format!("{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default());
     env.insert("RUSTFLAGS".to_string(), rustflags);
 
     let path = Path::new(crate::BUILD_DIR).join("rand");
@@ -788,18 +731,11 @@ fn extended_regex_example_tests(env: &Env, args: &TestArg) -> Result<(), String>
     println!("[TEST] rust-lang/regex example shootout-regex-dna");
     let mut env = env.clone();
     // newer aho_corasick versions throw a deprecation warning
-    let rustflags = format!(
-        "{} --cap-lints warn",
-        env.get("RUSTFLAGS").cloned().unwrap_or_default()
-    );
+    let rustflags =
+        format!("{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default());
     env.insert("RUSTFLAGS".to_string(), rustflags);
     // Make sure `[codegen mono items] start` doesn't poison the diff
-    run_cargo_command(
-        &[&"build", &"--example", &"shootout-regex-dna"],
-        Some(&path),
-        &env,
-        args,
-    )?;
+    run_cargo_command(&[&"build", &"--example", &"shootout-regex-dna"], Some(&path), &env, args)?;
 
     run_cargo_command_with_callback(
         &[&"run", &"--example", &"shootout-regex-dna"],
@@ -810,10 +746,8 @@ fn extended_regex_example_tests(env: &Env, args: &TestArg) -> Result<(), String>
             // FIXME: rewrite this with `child.stdin.write_all()` because
             // `examples/regexdna-input.txt` is very small.
             let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"bash", &"-c"];
-            let cargo_args = cargo_command
-                .iter()
-                .map(|s| s.as_ref().to_str().unwrap())
-                .collect::<Vec<_>>();
+            let cargo_args =
+                cargo_command.iter().map(|s| s.as_ref().to_str().unwrap()).collect::<Vec<_>>();
             let bash_command = format!(
                 "cat examples/regexdna-input.txt | {} | grep -v 'Spawned thread' > res.txt",
                 cargo_args.join(" "),
@@ -841,10 +775,8 @@ fn extended_regex_tests(env: &Env, args: &TestArg) -> Result<(), String> {
     println!("[TEST] rust-lang/regex tests");
     let mut env = env.clone();
     // newer aho_corasick versions throw a deprecation warning
-    let rustflags = format!(
-        "{} --cap-lints warn",
-        env.get("RUSTFLAGS").cloned().unwrap_or_default()
-    );
+    let rustflags =
+        format!("{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default());
     env.insert("RUSTFLAGS".to_string(), rustflags);
     let path = Path::new(crate::BUILD_DIR).join("regex");
     run_cargo_command(
@@ -884,7 +816,7 @@ fn extended_sysroot_tests(env: &Env, args: &TestArg) -> Result<(), String> {
     Ok(())
 }
 
-fn should_not_remove_test(file: &str) -> bool {
+fn valid_ui_error_pattern_test(file: &str) -> bool {
     // contains //~ERROR, but shouldn't be removed
     [
         "issues/auxiliary/issue-3136-a.rs",
@@ -899,7 +831,8 @@ fn should_not_remove_test(file: &str) -> bool {
     .any(|to_ignore| file.ends_with(to_ignore))
 }
 
-fn should_remove_test(file_path: &Path) -> Result<bool, String> {
+#[rustfmt::skip]
+fn contains_ui_error_patterns(file_path: &Path) -> Result<bool, String> {
     // Tests generating errors.
     let file = File::open(file_path)
         .map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?;
@@ -916,8 +849,8 @@ fn should_remove_test(file_path: &Path) -> Result<bool, String> {
             "//~",
             "thread",
         ]
-        .iter()
-        .any(|check| line.contains(check))
+            .iter()
+            .any(|check| line.contains(check))
         {
             return Ok(true);
         }
@@ -925,17 +858,27 @@ fn should_remove_test(file_path: &Path) -> Result<bool, String> {
             return Ok(true);
         }
     }
-    if file_path
-        .display()
-        .to_string()
-        .contains("ambiguous-4-extern.rs")
-    {
+    if file_path.display().to_string().contains("ambiguous-4-extern.rs") {
         eprintln!("nothing found for {file_path:?}");
     }
     Ok(false)
 }
 
-fn test_rustc_inner<F>(env: &Env, args: &TestArg, prepare_files_callback: F) -> Result<(), String>
+// # Parameters
+//
+// * `env`: An environment variable that provides context for the function.
+// * `args`: The arguments passed to the test. This could include things like the flags, config etc.
+// * `prepare_files_callback`: A callback function that prepares the files needed for the test. Its used to remove/retain tests giving Error to run various rust test suits.
+// * `run_error_pattern_test`: A boolean that determines whether to run only error pattern tests.
+// * `test_type`: A string that indicates the type of the test being run.
+//
+fn test_rustc_inner<F>(
+    env: &Env,
+    args: &TestArg,
+    prepare_files_callback: F,
+    run_error_pattern_test: bool,
+    test_type: &str,
+) -> Result<(), String>
 where
     F: Fn(&Path) -> Result<bool, String>,
 {
@@ -944,139 +887,138 @@ where
     let mut env = env.clone();
     let rust_path = setup_rustc(&mut env, args)?;
 
-    walk_dir(
-        rust_path.join("tests/ui"),
-        |dir| {
-            let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or("");
-            if [
-                "abi",
-                "extern",
-                "unsized-locals",
-                "proc-macro",
-                "threads-sendsync",
-                "borrowck",
-                "test-attrs",
-            ]
-            .iter()
-            .any(|name| *name == dir_name)
-            {
-                std::fs::remove_dir_all(dir).map_err(|error| {
-                    format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
-                })?;
-            }
-            Ok(())
-        },
-        |_| Ok(()),
-    )?;
-
-    // These two functions are used to remove files that are known to not be working currently
-    // with the GCC backend to reduce noise.
-    fn dir_handling(dir: &Path) -> Result<(), String> {
-        if dir
-            .file_name()
-            .map(|name| name == "auxiliary")
-            .unwrap_or(true)
-        {
-            return Ok(());
-        }
-        walk_dir(dir, dir_handling, file_handling)
-    }
-    fn file_handling(file_path: &Path) -> Result<(), String> {
-        if !file_path
-            .extension()
-            .map(|extension| extension == "rs")
-            .unwrap_or(false)
-        {
-            return Ok(());
-        }
-        let path_str = file_path.display().to_string().replace("\\", "/");
-        if should_not_remove_test(&path_str) {
-            return Ok(());
-        } else if should_remove_test(file_path)? {
-            return remove_file(&file_path);
-        }
-        Ok(())
+    if !prepare_files_callback(&rust_path)? {
+        // FIXME: create a function "display_if_not_quiet" or something along the line.
+        println!("Keeping all {} tests", test_type);
     }
 
-    remove_file(&rust_path.join("tests/ui/consts/const_cmp_type_id.rs"))?;
-    remove_file(&rust_path.join("tests/ui/consts/issue-73976-monomorphic.rs"))?;
-    // this test is oom-killed in the CI.
-    remove_file(&rust_path.join("tests/ui/consts/issue-miri-1910.rs"))?;
-    // Tests generating errors.
-    remove_file(&rust_path.join("tests/ui/consts/issue-94675.rs"))?;
-    remove_file(&rust_path.join("tests/ui/mir/mir_heavy_promoted.rs"))?;
-    remove_file(&rust_path.join("tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs"))?;
-    remove_file(&rust_path.join("tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs"))?;
+    if test_type == "ui" {
+        if run_error_pattern_test {
+            // After we removed the error tests that are known to panic with rustc_codegen_gcc, we now remove the passing tests since this runs the error tests.
+            walk_dir(
+                rust_path.join("tests/ui"),
+                &mut |_dir| Ok(()),
+                &mut |file_path| {
+                    if contains_ui_error_patterns(file_path)? {
+                        Ok(())
+                    } else {
+                        remove_file(file_path).map_err(|e| e.to_string())
+                    }
+                },
+                true,
+            )?;
+        } else {
+            walk_dir(
+                rust_path.join("tests/ui"),
+                &mut |dir| {
+                    let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or("");
+                    if [
+                        "abi",
+                        "extern",
+                        "unsized-locals",
+                        "proc-macro",
+                        "threads-sendsync",
+                        "borrowck",
+                        "test-attrs",
+                    ]
+                    .iter()
+                    .any(|name| *name == dir_name)
+                    {
+                        std::fs::remove_dir_all(dir).map_err(|error| {
+                            format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
+                        })?;
+                    }
+                    Ok(())
+                },
+                &mut |_| Ok(()),
+                false,
+            )?;
 
-    walk_dir(rust_path.join("tests/ui"), dir_handling, file_handling)?;
+            // These two functions are used to remove files that are known to not be working currently
+            // with the GCC backend to reduce noise.
+            fn dir_handling(dir: &Path) -> Result<(), String> {
+                if dir.file_name().map(|name| name == "auxiliary").unwrap_or(true) {
+                    return Ok(());
+                }
 
-    if !prepare_files_callback(&rust_path)? {
-        // FIXME: create a function "display_if_not_quiet" or something along the line.
-        println!("Keeping all UI tests");
-    }
+                walk_dir(dir, &mut dir_handling, &mut file_handling, false)
+            }
+            fn file_handling(file_path: &Path) -> Result<(), String> {
+                if !file_path.extension().map(|extension| extension == "rs").unwrap_or(false) {
+                    return Ok(());
+                }
+                let path_str = file_path.display().to_string().replace("\\", "/");
+                if valid_ui_error_pattern_test(&path_str) {
+                    return Ok(());
+                } else if contains_ui_error_patterns(file_path)? {
+                    return remove_file(&file_path);
+                }
+                Ok(())
+            }
 
-    let nb_parts = args.nb_parts.unwrap_or(0);
-    if nb_parts > 0 {
-        let current_part = args.current_part.unwrap();
-        // FIXME: create a function "display_if_not_quiet" or something along the line.
-        println!(
-            "Splitting ui_test into {} parts (and running part {})",
-            nb_parts, current_part
-        );
-        let out = String::from_utf8(
-            run_command(
-                &[
-                    &"find",
-                    &"tests/ui",
-                    &"-type",
-                    &"f",
-                    &"-name",
-                    &"*.rs",
-                    &"-not",
-                    &"-path",
-                    &"*/auxiliary/*",
-                ],
-                Some(&rust_path),
-            )?
-            .stdout,
-        )
-        .map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
-        let mut files = out
-            .split('\n')
-            .map(|line| line.trim())
-            .filter(|line| !line.is_empty())
-            .collect::<Vec<_>>();
-        // To ensure it'll be always the same sub files, we sort the content.
-        files.sort();
-        // We increment the number of tests by one because if this is an odd number, we would skip
-        // one test.
-        let count = files.len() / nb_parts + 1;
-        let start = current_part * count;
-        // We remove the files we don't want to test.
-        for path in files.iter().skip(start).take(count) {
-            remove_file(&rust_path.join(path))?;
+            walk_dir(rust_path.join("tests/ui"), &mut dir_handling, &mut file_handling, false)?;
+        }
+        let nb_parts = args.nb_parts.unwrap_or(0);
+        if nb_parts > 0 {
+            let current_part = args.current_part.unwrap();
+            // FIXME: create a function "display_if_not_quiet" or something along the line.
+            println!(
+                "Splitting ui_test into {} parts (and running part {})",
+                nb_parts, current_part
+            );
+            let out = String::from_utf8(
+                run_command(
+                    &[
+                        &"find",
+                        &"tests/ui",
+                        &"-type",
+                        &"f",
+                        &"-name",
+                        &"*.rs",
+                        &"-not",
+                        &"-path",
+                        &"*/auxiliary/*",
+                    ],
+                    Some(&rust_path),
+                )?
+                .stdout,
+            )
+            .map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
+            let mut files = out
+                .split('\n')
+                .map(|line| line.trim())
+                .filter(|line| !line.is_empty())
+                .collect::<Vec<_>>();
+            // To ensure it'll be always the same sub files, we sort the content.
+            files.sort();
+            // We increment the number of tests by one because if this is an odd number, we would skip
+            // one test.
+            let count = files.len() / nb_parts + 1;
+            // We remove the files we don't want to test.
+            let start = current_part * count;
+            for path in files.iter().skip(start).take(count) {
+                remove_file(&rust_path.join(path))?;
+            }
         }
     }
 
     // FIXME: create a function "display_if_not_quiet" or something along the line.
-    println!("[TEST] rustc test suite");
+    println!("[TEST] rustc {} test suite", test_type);
     env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
 
-    let extra = if args.is_using_gcc_master_branch() {
-        ""
-    } else {
-        " -Csymbol-mangling-version=v0"
-    };
+    let extra =
+        if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" };
 
     let rustc_args = format!(
-        "{} -Zcodegen-backend={} --sysroot {}{}",
-        env.get("TEST_FLAGS").unwrap_or(&String::new()),
-        args.config_info.cg_backend_path,
-        args.config_info.sysroot_path,
-        extra,
+        "{test_flags} -Zcodegen-backend={backend} --sysroot {sysroot}{extra}",
+        test_flags = env.get("TEST_FLAGS").unwrap_or(&String::new()),
+        backend = args.config_info.cg_backend_path,
+        sysroot = args.config_info.sysroot_path,
+        extra = extra,
     );
 
     env.get_mut("RUSTFLAGS").unwrap().clear();
+
     run_command_with_output_and_env(
         &[
             &"./x.py",
@@ -1085,7 +1027,7 @@ where
             &"always",
             &"--stage",
             &"0",
-            &"tests/ui",
+            &format!("tests/{}", test_type),
             &"--rustc-args",
             &rustc_args,
         ],
@@ -1096,68 +1038,162 @@ where
 }
 
 fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
-    test_rustc_inner(env, args, |_| Ok(false))
+    //test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
+    test_rustc_inner(env, args, |_| Ok(false), false, "ui")
 }
 
 fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
-    test_rustc_inner(env, args, |rust_path| {
-        // Removing all tests.
-        run_command(
-            &[
-                &"find",
-                &"tests/ui",
-                &"-type",
-                &"f",
-                &"-name",
-                &"*.rs",
-                &"-not",
-                &"-path",
-                &"*/auxiliary/*",
-                &"-delete",
-            ],
-            Some(rust_path),
-        )?;
+    let result1 = Ok(());
+    /*test_rustc_inner(
+        env,
+        args,
+        retain_files_callback("tests/failing-run-make-tests.txt", "run-make"),
+        false,
+        "run-make",
+    )*/
+
+    let result2 = test_rustc_inner(
+        env,
+        args,
+        retain_files_callback("tests/failing-ui-tests.txt", "ui"),
+        false,
+        "ui",
+    );
+
+    result1.and(result2)
+}
+
+fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
+    test_rustc_inner(
+        env,
+        args,
+        remove_files_callback("tests/failing-ui-tests.txt", "ui"),
+        false,
+        "ui",
+    )?;
+    Ok(())
+    /*test_rustc_inner(
+        env,
+        args,
+        remove_files_callback("tests/failing-run-make-tests.txt", "run-make"),
+        false,
+        "run-make",
+    )*/
+}
+
+fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String> {
+    test_rustc_inner(
+        env,
+        args,
+        remove_files_callback("tests/failing-ice-tests.txt", "ui"),
+        true,
+        "ui",
+    )
+}
+
+fn retain_files_callback<'a>(
+    file_path: &'a str,
+    test_type: &'a str,
+) -> impl Fn(&Path) -> Result<bool, String> + 'a {
+    move |rust_path| {
+        let files = std::fs::read_to_string(file_path).unwrap_or_default();
+        let first_file_name = files.lines().next().unwrap_or("");
+        // If the first line ends with a `/`, we treat all lines in the file as a directory.
+        if first_file_name.ends_with('/') {
+            // Treat as directory
+            // Removing all tests.
+            run_command(
+                &[
+                    &"find",
+                    &format!("tests/{}", test_type),
+                    &"-mindepth",
+                    &"1",
+                    &"-type",
+                    &"d",
+                    &"-exec",
+                    &"rm",
+                    &"-rf",
+                    &"{}",
+                    &"+",
+                ],
+                Some(rust_path),
+            )?;
+        } else {
+            // Treat as file
+            // Removing all tests.
+            run_command(
+                &[
+                    &"find",
+                    &format!("tests/{}", test_type),
+                    &"-type",
+                    &"f",
+                    &"-name",
+                    &"*.rs",
+                    &"-not",
+                    &"-path",
+                    &"*/auxiliary/*",
+                    &"-delete",
+                ],
+                Some(rust_path),
+            )?;
+        }
+
         // Putting back only the failing ones.
-        let path = "tests/failing-ui-tests.txt";
-        if let Ok(files) = std::fs::read_to_string(path) {
-            for file in files
-                .split('\n')
-                .map(|line| line.trim())
-                .filter(|line| !line.is_empty())
-            {
+        if let Ok(files) = std::fs::read_to_string(&file_path) {
+            for file in files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) {
                 run_command(&[&"git", &"checkout", &"--", &file], Some(&rust_path))?;
             }
         } else {
             println!(
-                "Failed to read `{}`, not putting back failing ui tests",
-                path
+                "Failed to read `{}`, not putting back failing {} tests",
+                file_path, test_type
             );
         }
+
         Ok(true)
-    })
+    }
 }
 
-fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
-    test_rustc_inner(env, args, |rust_path| {
-        // Removing the failing tests.
-        let path = "tests/failing-ui-tests.txt";
-        if let Ok(files) = std::fs::read_to_string(path) {
-            for file in files
-                .split('\n')
-                .map(|line| line.trim())
-                .filter(|line| !line.is_empty())
-            {
-                let path = rust_path.join(file);
-                remove_file(&path)?;
+fn remove_files_callback<'a>(
+    file_path: &'a str,
+    test_type: &'a str,
+) -> impl Fn(&Path) -> Result<bool, String> + 'a {
+    move |rust_path| {
+        let files = std::fs::read_to_string(file_path).unwrap_or_default();
+        let first_file_name = files.lines().next().unwrap_or("");
+        // If the first line ends with a `/`, we treat all lines in the file as a directory.
+        if first_file_name.ends_with('/') {
+            // Removing the failing tests.
+            if let Ok(files) = std::fs::read_to_string(file_path) {
+                for file in
+                    files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty())
+                {
+                    let path = rust_path.join(file);
+                    if let Err(e) = std::fs::remove_dir_all(&path) {
+                        println!("Failed to remove directory `{}`: {}", path.display(), e);
+                    }
+                }
+            } else {
+                println!(
+                    "Failed to read `{}`, not putting back failing {} tests",
+                    file_path, test_type
+                );
             }
         } else {
-            println!(
-                "Failed to read `{}`, not putting back failing ui tests",
-                path
-            );
+            // Removing the failing tests.
+            if let Ok(files) = std::fs::read_to_string(file_path) {
+                for file in
+                    files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty())
+                {
+                    let path = rust_path.join(file);
+                    remove_file(&path)?;
+                }
+            } else {
+                println!("Failed to read `{}`, not putting back failing ui tests", file_path);
+            }
         }
         Ok(true)
-    })
+    }
 }
 
 fn run_all(env: &Env, args: &TestArg) -> Result<(), String> {
@@ -1181,14 +1217,8 @@ pub fn run() -> Result<(), String> {
 
     if !args.use_system_gcc {
         args.config_info.setup_gcc_path()?;
-        env.insert(
-            "LIBRARY_PATH".to_string(),
-            args.config_info.gcc_path.clone(),
-        );
-        env.insert(
-            "LD_LIBRARY_PATH".to_string(),
-            args.config_info.gcc_path.clone(),
-        );
+        env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
+        env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
     }
 
     build_if_no_backend(&env, &args)?;
diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs
index d9c13fd143d..3bba8df6c65 100644
--- a/compiler/rustc_codegen_gcc/build_system/src/utils.rs
+++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs
@@ -1,10 +1,42 @@
 use std::collections::HashMap;
+#[cfg(unix)]
+use std::ffi::c_int;
 use std::ffi::OsStr;
 use std::fmt::Debug;
 use std::fs;
+#[cfg(unix)]
+use std::os::unix::process::ExitStatusExt;
 use std::path::{Path, PathBuf};
 use std::process::{Command, ExitStatus, Output};
 
+#[cfg(unix)]
+extern "C" {
+    fn raise(signal: c_int) -> c_int;
+}
+
+fn exec_command(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+    env: Option<&HashMap<String, String>>,
+) -> Result<ExitStatus, String> {
+    let status = get_command_inner(input, cwd, env)
+        .spawn()
+        .map_err(|e| command_error(input, &cwd, e))?
+        .wait()
+        .map_err(|e| command_error(input, &cwd, e))?;
+    #[cfg(unix)]
+    {
+        if let Some(signal) = status.signal() {
+            unsafe {
+                raise(signal as _);
+            }
+            // In case the signal didn't kill the current process.
+            return Err(command_error(input, &cwd, format!("Process received signal {}", signal)));
+        }
+    }
+    Ok(status)
+}
+
 fn get_command_inner(
     input: &[&dyn AsRef<OsStr>],
     cwd: Option<&Path>,
@@ -37,13 +69,8 @@ fn check_exit_status(
     }
     let mut error = format!(
         "Command `{}`{} exited with status {:?}",
-        input
-            .iter()
-            .map(|s| s.as_ref().to_str().unwrap())
-            .collect::<Vec<_>>()
-            .join(" "),
-        cwd.map(|cwd| format!(" (running in folder `{}`)", cwd.display()))
-            .unwrap_or_default(),
+        input.iter().map(|s| s.as_ref().to_str().unwrap()).collect::<Vec<_>>().join(" "),
+        cwd.map(|cwd| format!(" (running in folder `{}`)", cwd.display())).unwrap_or_default(),
         exit_status.code()
     );
     let input = input.iter().map(|i| i.as_ref()).collect::<Vec<&OsStr>>();
@@ -68,11 +95,7 @@ fn check_exit_status(
 fn command_error<D: Debug>(input: &[&dyn AsRef<OsStr>], cwd: &Option<&Path>, error: D) -> String {
     format!(
         "Command `{}`{} failed to run: {error:?}",
-        input
-            .iter()
-            .map(|s| s.as_ref().to_str().unwrap())
-            .collect::<Vec<_>>()
-            .join(" "),
+        input.iter().map(|s| s.as_ref().to_str().unwrap()).collect::<Vec<_>>().join(" "),
         cwd.as_ref()
             .map(|cwd| format!(" (running in folder `{}`)", cwd.display(),))
             .unwrap_or_default(),
@@ -88,9 +111,8 @@ pub fn run_command_with_env(
     cwd: Option<&Path>,
     env: Option<&HashMap<String, String>>,
 ) -> Result<Output, String> {
-    let output = get_command_inner(input, cwd, env)
-        .output()
-        .map_err(|e| command_error(input, &cwd, e))?;
+    let output =
+        get_command_inner(input, cwd, env).output().map_err(|e| command_error(input, &cwd, e))?;
     check_exit_status(input, cwd, output.status, Some(&output), true)?;
     Ok(output)
 }
@@ -99,11 +121,7 @@ pub fn run_command_with_output(
     input: &[&dyn AsRef<OsStr>],
     cwd: Option<&Path>,
 ) -> Result<(), String> {
-    let exit_status = get_command_inner(input, cwd, None)
-        .spawn()
-        .map_err(|e| command_error(input, &cwd, e))?
-        .wait()
-        .map_err(|e| command_error(input, &cwd, e))?;
+    let exit_status = exec_command(input, cwd, None)?;
     check_exit_status(input, cwd, exit_status, None, true)?;
     Ok(())
 }
@@ -113,11 +131,7 @@ pub fn run_command_with_output_and_env(
     cwd: Option<&Path>,
     env: Option<&HashMap<String, String>>,
 ) -> Result<(), String> {
-    let exit_status = get_command_inner(input, cwd, env)
-        .spawn()
-        .map_err(|e| command_error(input, &cwd, e))?
-        .wait()
-        .map_err(|e| command_error(input, &cwd, e))?;
+    let exit_status = exec_command(input, cwd, env)?;
     check_exit_status(input, cwd, exit_status, None, true)?;
     Ok(())
 }
@@ -127,11 +141,7 @@ pub fn run_command_with_output_and_env_no_err(
     cwd: Option<&Path>,
     env: Option<&HashMap<String, String>>,
 ) -> Result<(), String> {
-    let exit_status = get_command_inner(input, cwd, env)
-        .spawn()
-        .map_err(|e| command_error(input, &cwd, e))?
-        .wait()
-        .map_err(|e| command_error(input, &cwd, e))?;
+    let exit_status = exec_command(input, cwd, env)?;
     check_exit_status(input, cwd, exit_status, None, false)?;
     Ok(())
 }
@@ -164,10 +174,7 @@ pub fn cargo_install(to_install: &str) -> Result<(), String> {
 
 pub fn get_os_name() -> Result<String, String> {
     let output = run_command(&[&"uname"], None)?;
-    let name = std::str::from_utf8(&output.stdout)
-        .unwrap_or("")
-        .trim()
-        .to_string();
+    let name = std::str::from_utf8(&output.stdout).unwrap_or("").trim().to_string();
     if !name.is_empty() {
         Ok(name)
     } else {
@@ -274,11 +281,7 @@ fn git_clone_inner(
         command.push(&"1");
     }
     run_command_with_output(&command, None)?;
-    Ok(CloneResult {
-        ran_clone: true,
-        repo_name,
-        repo_dir: dest.display().to_string(),
-    })
+    Ok(CloneResult { ran_clone: true, repo_name, repo_dir: dest.display().to_string() })
 }
 
 fn get_repo_name(url: &str) -> String {
@@ -307,6 +310,25 @@ pub fn git_clone(
     git_clone_inner(to_clone, dest, shallow_clone, repo_name)
 }
 
+pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<(), String> {
+    fs::create_dir_all(&path).map_err(|error| {
+        format!("Failed to create directory `{}`: {:?}", path.as_ref().display(), error)
+    })
+}
+
+pub fn copy_file<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<(), String> {
+    fs::copy(&from, &to)
+        .map_err(|error| {
+            format!(
+                "Failed to copy file `{}` into `{}`: {:?}",
+                from.as_ref().display(),
+                to.as_ref().display(),
+                error
+            )
+        })
+        .map(|_| ())
+}
+
 /// This function differs from `git_clone` in how it handles *where* the repository will be cloned.
 /// In `git_clone`, it is cloned in the provided path. In this function, the path you provide is
 /// the parent folder. So if you pass "a" as folder and try to clone "b.git", it will be cloned into
@@ -318,15 +340,15 @@ pub fn git_clone_root_dir(
 ) -> Result<CloneResult, String> {
     let repo_name = get_repo_name(to_clone);
 
-    git_clone_inner(
-        to_clone,
-        &dest_parent_dir.join(&repo_name),
-        shallow_clone,
-        repo_name,
-    )
+    git_clone_inner(to_clone, &dest_parent_dir.join(&repo_name), shallow_clone, repo_name)
 }
 
-pub fn walk_dir<P, D, F>(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String>
+pub fn walk_dir<P, D, F>(
+    dir: P,
+    dir_cb: &mut D,
+    file_cb: &mut F,
+    recursive: bool,
+) -> Result<(), String>
 where
     P: AsRef<Path>,
     D: FnMut(&Path) -> Result<(), String>,
@@ -341,6 +363,9 @@ where
         let entry_path = entry.path();
         if entry_path.is_dir() {
             dir_cb(&entry_path)?;
+            if recursive {
+                walk_dir(entry_path, dir_cb, file_cb, recursive)?; // Recursive call
+            }
         } else {
             file_cb(&entry_path)?;
         }
@@ -383,11 +408,7 @@ pub fn split_args(args: &str) -> Result<Vec<String>, String> {
                 }
             }
             if !found_end {
-                return Err(format!(
-                    "Didn't find `{}` at the end of `{}`",
-                    end,
-                    &args[start..]
-                ));
+                return Err(format!("Didn't find `{}` at the end of `{}`", end, &args[start..]));
             }
         } else if c == '\\' {
             // We skip the escaped character.
@@ -403,11 +424,7 @@ pub fn split_args(args: &str) -> Result<Vec<String>, String> {
 
 pub fn remove_file<P: AsRef<Path> + ?Sized>(file_path: &P) -> Result<(), String> {
     std::fs::remove_file(file_path).map_err(|error| {
-        format!(
-            "Failed to remove `{}`: {:?}",
-            file_path.as_ref().display(),
-            error
-        )
+        format!("Failed to remove `{}`: {:?}", file_path.as_ref().display(), error)
     })
 }
 
@@ -427,6 +444,10 @@ pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> R
     })
 }
 
+pub fn get_sysroot_dir() -> PathBuf {
+    Path::new(crate::BUILD_DIR).join("build_sysroot")
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;