about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2023-09-26 16:09:51 +0200
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2023-10-04 23:21:03 +0200
commiteedf1b6cb458c6a474bf2e9ccc29cbe9059f7764 (patch)
tree2631d61e0c41d8aa1a1ec12518125d99a49a6899
parent71febd94f455236ee6a6eb3d87fd881fad9081d6 (diff)
downloadrust-eedf1b6cb458c6a474bf2e9ccc29cbe9059f7764.tar.gz
rust-eedf1b6cb458c6a474bf2e9ccc29cbe9059f7764.zip
Migrate build.sh script to rust
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--.github/workflows/stdarch.yml2
-rw-r--r--Readme.md2
-rwxr-xr-xbuild.sh67
-rwxr-xr-xbuild_sysroot/build_sysroot.sh4
-rw-r--r--build_system/src/build.rs215
-rw-r--r--build_system/src/config.rs121
-rw-r--r--build_system/src/main.rs7
-rw-r--r--build_system/src/prepare.rs137
-rw-r--r--build_system/src/utils.rs113
-rw-r--r--config.sh2
12 files changed, 541 insertions, 133 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4702494f05c..f075c744e45 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -119,7 +119,7 @@ jobs:
     - name: Build
       run: |
         ./y.sh prepare --only-libcore
-        ${{ matrix.libgccjit_version.env_extra }} ./build.sh ${{ matrix.libgccjit_version.extra }}
+        ${{ matrix.libgccjit_version.env_extra }} ./y.sh build ${{ matrix.libgccjit_version.extra }}
         ${{ matrix.libgccjit_version.env_extra }} cargo test ${{ matrix.libgccjit_version.extra }}
         ./clean_all.sh
 
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 51fc5c76cdb..bd0415040e7 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -86,7 +86,7 @@ jobs:
     - name: Build
       run: |
         ./y.sh prepare --only-libcore
-        EMBED_LTO_BITCODE=1 ./build.sh --release --release-sysroot
+        EMBED_LTO_BITCODE=1 ./y.sh build --release --release-sysroot
         cargo test
         ./clean_all.sh
 
diff --git a/.github/workflows/stdarch.yml b/.github/workflows/stdarch.yml
index c44d8efe3c7..6c28326823c 100644
--- a/.github/workflows/stdarch.yml
+++ b/.github/workflows/stdarch.yml
@@ -100,7 +100,7 @@ jobs:
     - name: Build
       run: |
         ./y.sh prepare --only-libcore
-        ./build.sh --release --release-sysroot
+        ./y.sh build --release --release-sysroot
         cargo test
 
     - name: Clean
diff --git a/Readme.md b/Readme.md
index 060f7c0326d..de6cab120a4 100644
--- a/Readme.md
+++ b/Readme.md
@@ -66,7 +66,7 @@ Then you can run commands like this:
 
 ```bash
 $ ./y.sh prepare # download and patch sysroot src and install hyperfine for benchmarking
-$ LIBRARY_PATH=$(cat gcc_path) LD_LIBRARY_PATH=$(cat gcc_path) ./build.sh --release
+$ LIBRARY_PATH=$(cat gcc_path) LD_LIBRARY_PATH=$(cat gcc_path) ./y.sh build --release
 ```
 
 To run the tests:
diff --git a/build.sh b/build.sh
deleted file mode 100755
index ba0d0d04948..00000000000
--- a/build.sh
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env bash
-
-#set -x
-set -e
-
-codegen_channel=debug
-sysroot_channel=debug
-
-flags=
-
-while [[ $# -gt 0 ]]; do
-    case $1 in
-        --release)
-            codegen_channel=release
-            shift
-            ;;
-        --release-sysroot)
-            sysroot_channel=release
-            shift
-            ;;
-        --no-default-features)
-            flags="$flags --no-default-features"
-            shift
-            ;;
-        --features)
-            shift
-            flags="$flags --features $1"
-            shift
-            ;;
-        *)
-            echo "Unknown option $1"
-            exit 1
-            ;;
-    esac
-done
-
-if [ -f ./gcc_path ]; then
-    export GCC_PATH=$(cat gcc_path)
-else
-    echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
-    exit 1
-fi
-
-export LD_LIBRARY_PATH="$GCC_PATH"
-export LIBRARY_PATH="$GCC_PATH"
-
-if [[ "$codegen_channel" == "release" ]]; then
-    export CHANNEL='release'
-    CARGO_INCREMENTAL=1 cargo rustc --release $flags
-else
-    echo $LD_LIBRARY_PATH
-    export CHANNEL='debug'
-    cargo rustc $flags
-fi
-
-source config.sh
-
-rm -r target/out || true
-mkdir -p target/out/gccjit
-
-echo "[BUILD] sysroot"
-if [[ "$sysroot_channel" == "release" ]]; then
-    time ./build_sysroot/build_sysroot.sh --release
-else
-    time ./build_sysroot/build_sysroot.sh
-fi
-
diff --git a/build_sysroot/build_sysroot.sh b/build_sysroot/build_sysroot.sh
index 9d692d599f6..851e9895ce2 100755
--- a/build_sysroot/build_sysroot.sh
+++ b/build_sysroot/build_sysroot.sh
@@ -5,9 +5,9 @@
 set -e
 cd $(dirname "$0")
 
-pushd ../ >/dev/null
+pushd ../
 source ./config.sh
-popd >/dev/null
+popd
 
 # Cleanup for previous run
 #     v Clean target dir except for build scripts and incremental cache
diff --git a/build_system/src/build.rs b/build_system/src/build.rs
index 7384557d805..58c36412ea5 100644
--- a/build_system/src/build.rs
+++ b/build_system/src/build.rs
@@ -1,3 +1,218 @@
+use crate::config::set_config;
+use crate::utils::{get_gcc_path, run_command_with_env, run_command_with_output, walk_dir};
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::Path;
+
+#[derive(Default)]
+struct BuildArg {
+    codegen_release_channel: bool,
+    sysroot_release_channel: bool,
+    no_default_features: bool,
+    features: Vec<String>,
+    gcc_path: String,
+}
+
+impl BuildArg {
+    fn new() -> Result<Option<Self>, String> {
+        let gcc_path = get_gcc_path()?;
+        let mut build_arg = Self {
+            gcc_path,
+            ..Default::default()
+        };
+        let mut args = std::env::args().skip(2);
+
+        while let Some(arg) = args.next() {
+            match arg.as_str() {
+                "--release" => build_arg.codegen_release_channel = true,
+                "--release-sysroot" => build_arg.sysroot_release_channel = true,
+                "--no-default-features" => build_arg.no_default_features = true,
+                "--features" => {
+                    if let Some(arg) = args.next() {
+                        build_arg.features.push(arg.as_str().into());
+                    } else {
+                        return Err(format!(
+                            "Expected a value after `--features`, found nothing"
+                        ));
+                    }
+                }
+                "--help" => {
+                    Self::usage();
+                    return Ok(None);
+                }
+                a => return Err(format!("Unknown argument `{a}`")),
+            }
+        }
+        Ok(Some(build_arg))
+    }
+
+    fn usage() {
+        println!(
+            r#"
+`build` command help:
+
+    --release              : Build codegen in release mode
+    --release-sysroot      : Build sysroot in release mode
+    --no-default-features  : Add `--no-default-features` flag
+    --features [arg]       : Add a new feature [arg]
+    --help                 : Show this help
+"#
+        )
+    }
+}
+
+fn build_sysroot(
+    env: &mut HashMap<String, String>,
+    release_mode: bool,
+    target_triple: &str,
+) -> Result<(), String> {
+    std::env::set_current_dir("build_sysroot")
+        .map_err(|e| format!("Failed to go to `build_sysroot` directory: {e:?}"))?;
+    // Cleanup for previous run
+    //     v Clean target dir except for build scripts and incremental cache
+   let _e = walk_dir(
+        "target",
+        |dir: &Path| {
+            for top in &["debug", "release"] {
+                let _e = fs::remove_dir_all(dir.join(top).join("build"));
+                let _e = fs::remove_dir_all(dir.join(top).join("deps"));
+                let _e = fs::remove_dir_all(dir.join(top).join("examples"));
+                let _e = fs::remove_dir_all(dir.join(top).join("native"));
+
+                let _e = walk_dir(
+                    dir.join(top),
+                    |sub_dir: &Path| {
+                        if sub_dir
+                            .file_name()
+                            .map(|s| s.to_str().unwrap().starts_with("libsysroot"))
+                            .unwrap_or(false)
+                        {
+                            let _e = fs::remove_dir_all(sub_dir);
+                        }
+                        Ok(())
+                    },
+                    |file: &Path| {
+                        if file
+                            .file_name()
+                            .map(|s| s.to_str().unwrap().starts_with("libsysroot"))
+                            .unwrap_or(false)
+                        {
+                            let _e = fs::remove_file(file);
+                        }
+                        Ok(())
+                    },
+                );
+            }
+            Ok(())
+        },
+        |_| Ok(()),
+    );
+
+    let _e = fs::remove_file("Cargo.lock");
+    let _e = fs::remove_file("test_target/Cargo.lock");
+    let _e = fs::remove_dir_all("sysroot");
+
+    // Builds libs
+    let channel = if release_mode {
+        let rustflags = env
+            .get(&"RUSTFLAGS".to_owned())
+            .cloned()
+            .unwrap_or_default();
+        env.insert(
+            "RUSTFLAGS".to_owned(),
+            format!("{rustflags} -Zmir-opt-level=3"),
+        );
+        run_command_with_output(
+            &[
+                &"cargo",
+                &"build",
+                &"--target",
+                &target_triple,
+                &"--release",
+            ],
+            None,
+            Some(&env),
+        )?;
+        "release"
+    } else {
+        run_command_with_output(
+            &[
+                &"cargo",
+                &"build",
+                &"--target",
+                &target_triple,
+                &"--features",
+                &"compiler_builtins/c",
+            ],
+            None,
+            Some(env),
+        )?;
+        "debug"
+    };
+
+    // Copy files to sysroot
+    let sysroot_path = format!("sysroot/lib/rustlib/{target_triple}/lib/");
+    fs::create_dir_all(&sysroot_path)
+        .map_err(|e| format!("Failed to create directory `{sysroot_path}`: {e:?}"))?;
+    let copier = |d: &Path| run_command_with_output(&[&"cp", &"-r", &d, &sysroot_path], None, None);
+    walk_dir(
+        &format!("target/{target_triple}/{channel}/deps"),
+        copier,
+        copier,
+    )?;
+
+    Ok(())
+}
+
+fn build_codegen(args: &BuildArg) -> Result<(), String> {
+    let mut env = HashMap::new();
+
+    let current_dir =
+        std::env::current_dir().map_err(|e| format!("`current_dir` failed: {e:?}"))?;
+    env.insert(
+        "RUST_COMPILER_RT_ROOT".to_owned(),
+        format!("{}", current_dir.join("llvm/compiler-rt").display()),
+    );
+    env.insert("LD_LIBRARY_PATH".to_owned(), args.gcc_path.clone());
+    env.insert("LIBRARY_PATH".to_owned(), args.gcc_path.clone());
+
+    let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"rustc"];
+    if args.codegen_release_channel {
+        command.push(&"--release");
+        env.insert("CHANNEL".to_owned(), "release".to_owned());
+        env.insert("CARGO_INCREMENTAL".to_owned(), "1".to_owned());
+    } else {
+        env.insert("CHANNEL".to_owned(), "debug".to_owned());
+    }
+    let ref_features = args.features.iter().map(|s| s.as_str()).collect::<Vec<_>>();
+    for feature in &ref_features {
+        command.push(feature);
+    }
+    run_command_with_env(&command, None, Some(&env))?;
+
+    let config = set_config(&mut env, &[], Some(&args.gcc_path))?;
+
+    // We voluntarily ignore the error.
+    let _e = fs::remove_dir_all("target/out");
+    let gccjit_target = "target/out/gccjit";
+    fs::create_dir_all(gccjit_target)
+        .map_err(|e| format!("Failed to create directory `{gccjit_target}`: {e:?}"))?;
+
+    println!("[BUILD] sysroot");
+    build_sysroot(
+        &mut env,
+        args.sysroot_release_channel,
+        &config.target_triple,
+    )?;
+    Ok(())
+}
+
 pub fn run() -> Result<(), String> {
+    let args = match BuildArg::new()? {
+        Some(a) => a,
+        None => return Ok(()),
+    };
+    build_codegen(&args)?;
     Ok(())
 }
diff --git a/build_system/src/config.rs b/build_system/src/config.rs
new file mode 100644
index 00000000000..5160eb2ecae
--- /dev/null
+++ b/build_system/src/config.rs
@@ -0,0 +1,121 @@
+use crate::utils::{get_gcc_path, get_os_name, get_rustc_host_triple};
+use std::collections::HashMap;
+use std::env as std_env;
+
+pub struct ConfigInfo {
+    pub target_triple: String,
+    pub rustc_command: Vec<String>,
+    pub run_wrapper: Option<&'static str>,
+}
+
+// Returns the beginning for the command line of rustc.
+pub fn set_config(
+    env: &mut HashMap<String, String>,
+    test_flags: &[String],
+    gcc_path: Option<&str>,
+) -> Result<ConfigInfo, String> {
+    env.insert("CARGO_INCREMENTAL".to_owned(), "0".to_owned());
+
+    let gcc_path = match gcc_path {
+        Some(g) => g.to_owned(),
+        None => get_gcc_path()?,
+    };
+    env.insert("GCC_PATH".to_owned(), gcc_path.clone());
+
+    let os_name = get_os_name()?;
+    let dylib_ext = match os_name.as_str() {
+        "Linux" => "so",
+        "Darwin" => "dylib",
+        os => return Err(format!("unsupported OS `{os}`")),
+    };
+    let host_triple = get_rustc_host_triple()?;
+    let mut linker = None;
+    let mut target_triple = host_triple.as_str();
+    let mut run_wrapper = None;
+    // FIXME: handle this with a command line flag?
+    // let mut target_triple = "m68k-unknown-linux-gnu";
+
+    if host_triple != target_triple {
+        if target_triple == "m68k-unknown-linux-gnu" {
+            target_triple = "mips-unknown-linux-gnu";
+            linker = Some("-Clinker=m68k-linux-gcc");
+        } else if target_triple == "aarch64-unknown-linux-gnu" {
+            // We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
+            linker = Some("-Clinker=aarch64-linux-gnu-gcc");
+            run_wrapper = Some("qemu-aarch64 -L /usr/aarch64-linux-gnu");
+        } else {
+            return Err(format!("unknown non-native platform `{target_triple}`"));
+        }
+    }
+    // Since we don't support ThinLTO, disable LTO completely when not trying to do LTO.
+    // TODO(antoyo): remove when we can handle ThinLTO.
+    let disable_lto_lfags = "-Clto=off";
+    let current_dir = std_env::current_dir().map_err(|e| format!("`current_dir` failed: {e:?}"))?;
+    let cg_backend_path = current_dir
+        .join("target")
+        .join(if let Some(channel) = env.get(&"CHANNEL".to_owned()) {
+            channel.as_str()
+        } else {
+            "debug"
+        })
+        .join(&format!("librustc_codegen_gcc.{dylib_ext}"));
+    let sysroot_path = current_dir.join("build_sysroot/sysroot");
+    let mut rustflags = Vec::new();
+    if let Some(cg_rustflags) = env.get(&"CG_RUSTFLAGS".to_owned()) {
+        rustflags.push(cg_rustflags.clone());
+    }
+    if let Some(linker) = linker {
+        rustflags.push(linker.to_owned());
+    }
+    rustflags.extend_from_slice(&[
+        "-Csymbol-mangling-version=v0".to_owned(),
+        "-Cdebuginfo=2".to_owned(),
+        disable_lto_lfags.to_owned(),
+        format!("-Zcodegen-backend={}", cg_backend_path.display()),
+        "--sysroot".to_owned(),
+        format!("{}", sysroot_path.display()),
+    ]);
+    rustflags.extend_from_slice(test_flags);
+    // FIXME(antoyo): remove once the atomic shim is gone
+    if os_name == "Darwin" {
+        rustflags.extend_from_slice(&[
+            "-Clink-arg=-undefined".to_owned(),
+            "-Clink-arg=dynamic_lookup".to_owned(),
+        ]);
+    }
+    env.insert("RUSTFLAGS".to_owned(), rustflags.join(" "));
+    // display metadata load errors
+    env.insert("RUSTC_LOG".to_owned(), "warn".to_owned());
+
+    let ld_library_path = format!(
+        "{target}:{sysroot}:{gcc_path}",
+        target = current_dir.join("target/out").display(),
+        sysroot = current_dir
+            .join(&format!(
+                "build_sysroot/sysroot/lib/rustlib/{target_triple}/lib"
+            ),)
+            .display(),
+    );
+    env.insert("LD_LIBRARY_PATH".to_owned(), ld_library_path.clone());
+    env.insert("DYLD_LIBRARY_PATH".to_owned(), ld_library_path);
+
+    // NOTE: To avoid the -fno-inline errors, use /opt/gcc/bin/gcc instead of cc.
+    // To do so, add a symlink for cc to /opt/gcc/bin/gcc in our PATH.
+    // Another option would be to add the following Rust flag: -Clinker=/opt/gcc/bin/gcc
+    let path = std::env::var("PATH").unwrap_or_default();
+    env.insert("PATH".to_owned(), format!("/opt/gcc/bin:{path}"));
+
+    let mut rustc_command = vec!["rustc".to_owned()];
+    rustc_command.extend_from_slice(&rustflags);
+    rustc_command.extend_from_slice(&[
+        "-L".to_owned(),
+        "crate=target/out".to_owned(),
+        "--out-dir".to_owned(),
+        "target/out".to_owned(),
+    ]);
+    Ok(ConfigInfo {
+        target_triple: target_triple.to_owned(),
+        rustc_command,
+        run_wrapper,
+    })
+}
diff --git a/build_system/src/main.rs b/build_system/src/main.rs
index 16c4c3a9c62..332a14ff0a2 100644
--- a/build_system/src/main.rs
+++ b/build_system/src/main.rs
@@ -2,6 +2,7 @@ use std::env;
 use std::process;
 
 mod build;
+mod config;
 mod prepare;
 mod rustc_info;
 mod utils;
@@ -16,12 +17,14 @@ macro_rules! arg_error {
 }
 
 fn usage() {
-    println!("\
+    println!(
+        "\
 Available commands for build_system:
 
     prepare  : Run prepare command
     build    : Run build command
-    --help   : Show this message");
+    --help   : Show this message"
+    );
 }
 
 pub enum Command {
diff --git a/build_system/src/prepare.rs b/build_system/src/prepare.rs
index 9c31b5cb8b3..6274628378e 100644
--- a/build_system/src/prepare.rs
+++ b/build_system/src/prepare.rs
@@ -15,11 +15,10 @@ fn prepare_libcore(sysroot_path: &Path) -> Result<(), String> {
         None => return Err(format!("No parent for `{}`", rustc_path.display())),
     };
 
-    let rustlib_dir =
-        parent
-            .join("../lib/rustlib/src/rust")
-            .canonicalize()
-            .map_err(|e| format!("Failed to canonicalize path: {e:?}"))?;
+    let rustlib_dir = parent
+        .join("../lib/rustlib/src/rust")
+        .canonicalize()
+        .map_err(|e| format!("Failed to canonicalize path: {e:?}"))?;
     if !rustlib_dir.is_dir() {
         return Err("Please install `rust-src` component".to_owned());
     }
@@ -27,18 +26,26 @@ fn prepare_libcore(sysroot_path: &Path) -> Result<(), String> {
     let sysroot_dir = sysroot_path.join("sysroot_src");
     if sysroot_dir.is_dir() {
         if let Err(e) = fs::remove_dir_all(&sysroot_dir) {
-            return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), e));
+            return Err(format!(
+                "Failed to remove `{}`: {:?}",
+                sysroot_dir.display(),
+                e
+            ));
         }
     }
 
     let sysroot_library_dir = sysroot_dir.join("library");
-    fs::create_dir_all(&sysroot_library_dir)
-        .map_err(|e| format!(
+    fs::create_dir_all(&sysroot_library_dir).map_err(|e| {
+        format!(
             "Failed to create folder `{}`: {e:?}",
             sysroot_library_dir.display(),
-        ))?;
+        )
+    })?;
 
-    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))?;
@@ -49,26 +56,52 @@ fn prepare_libcore(sysroot_path: &Path) -> Result<(), String> {
     // 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| {
-        patches.push(file_path.to_path_buf());
-        Ok(())
-    })?;
+    walk_dir(
+        "patches",
+        |_| Ok(()),
+        |file_path: &Path| {
+            patches.push(file_path.to_path_buf());
+            Ok(())
+        },
+    )?;
     patches.sort();
     for file_path in patches {
         println!("[GIT] apply `{}`", file_path.display());
         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", &"apply", &path], Some(&sysroot_dir), None)?;
+        run_command_with_output(&[&"git", &"add", &"-A"], Some(&sysroot_dir), None)?;
         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),
+            None,
         )?;
     }
     println!("Successfully prepared libcore for building");
@@ -83,7 +116,10 @@ fn build_raytracer(repo_dir: &Path) -> Result<(), String> {
         std::fs::remove_file(&mv_target)
             .map_err(|e| format!("Failed to remove file `{}`: {e:?}", mv_target.display()))?;
     }
-    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(())
 }
 
@@ -99,16 +135,21 @@ where
     run_command(&[&"git", &"checkout", &"--", &"."], Some(&repo_path))?;
     run_command(&[&"git", &"checkout", &checkout_commit], Some(&repo_path))?;
     let filter = format!("-{}-", clone_result.repo_name);
-    walk_dir("crate_patches", |_| Ok(()), |file_path| {
-        let s = file_path.as_os_str().to_str().unwrap();
-        if s.contains(&filter) && s.ends_with(".patch") {
-            run_command_with_output(
-                &[&"git", &"am", &file_path.canonicalize().unwrap()],
-                Some(&repo_path),
-            )?;
-        }
-        Ok(())
-    })?;
+    walk_dir(
+        "crate_patches",
+        |_| Ok(()),
+        |file_path| {
+            let s = file_path.as_os_str().to_str().unwrap();
+            if s.contains(&filter) && s.ends_with(".patch") {
+                run_command_with_output(
+                    &[&"git", &"am", &file_path.canonicalize().unwrap()],
+                    Some(&repo_path),
+                    None,
+                )?;
+            }
+            Ok(())
+        },
+    )?;
     if let Some(extra) = extra {
         extra(&repo_path)?;
     }
@@ -128,23 +169,23 @@ impl PrepareArg {
                 "--only-libcore" => only_libcore = true,
                 "--help" => {
                     Self::usage();
-                    return Ok(None)
+                    return Ok(None);
                 }
                 a => return Err(format!("Unknown argument `{a}`")),
             }
         }
-        Ok(Some(Self {
-            only_libcore,
-        }))
+        Ok(Some(Self { only_libcore }))
     }
 
     fn usage() {
-        println!(r#"
+        println!(
+            r#"
 `prepare` command help:
 
     --only-libcore  : Only setup libcore and don't clone other repositories
     --help          : Show this help
-"#)
+"#
+        )
     }
 }
 
@@ -160,9 +201,21 @@ pub fn run() -> Result<(), String> {
         cargo_install("hyperfine")?;
 
         let to_clone = &[
-            ("https://github.com/rust-random/rand.git", "0f933f9c7176e53b2a3c7952ded484e1783f0bf1", None),
-            ("https://github.com/rust-lang/regex.git", "341f207c1071f7290e3f228c710817c280c8dca1", None),
-            ("https://github.com/ebobby/simple-raytracer", "804a7a21b9e673a482797aa289a18ed480e4d813", Some(build_raytracer)),
+            (
+                "https://github.com/rust-random/rand.git",
+                "0f933f9c7176e53b2a3c7952ded484e1783f0bf1",
+                None,
+            ),
+            (
+                "https://github.com/rust-lang/regex.git",
+                "341f207c1071f7290e3f228c710817c280c8dca1",
+                None,
+            ),
+            (
+                "https://github.com/ebobby/simple-raytracer",
+                "804a7a21b9e673a482797aa289a18ed480e4d813",
+                Some(build_raytracer),
+            ),
         ];
 
         for (repo_url, checkout_commit, cb) in to_clone {
diff --git a/build_system/src/utils.rs b/build_system/src/utils.rs
index c350864dbd2..1724e275595 100644
--- a/build_system/src/utils.rs
+++ b/build_system/src/utils.rs
@@ -1,10 +1,15 @@
+use std::collections::HashMap;
 use std::ffi::OsStr;
 use std::fmt::Debug;
 use std::fs;
 use std::path::Path;
 use std::process::{Command, ExitStatus, Output};
 
-fn get_command_inner(input: &[&dyn AsRef<OsStr>], cwd: Option<&Path>) -> Command {
+fn get_command_inner(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+    env: Option<&HashMap<String, String>>,
+) -> Command {
     let (cmd, args) = match input {
         [] => panic!("empty command"),
         [cmd, args @ ..] => (cmd, args),
@@ -14,6 +19,9 @@ fn get_command_inner(input: &[&dyn AsRef<OsStr>], cwd: Option<&Path>) -> Command
     if let Some(cwd) = cwd {
         command.current_dir(cwd);
     }
+    if let Some(env) = env {
+        command.envs(env.iter().map(|(k, v)| (k.as_str(), v.as_str())));
+    }
     command
 }
 
@@ -27,7 +35,8 @@ fn check_exit_status(
     } else {
         Err(format!(
             "Command `{}`{} exited with status {:?}",
-            input.iter()
+            input
+                .iter()
                 .map(|s| s.as_ref().to_str().unwrap())
                 .collect::<Vec<_>>()
                 .join(" "),
@@ -41,21 +50,27 @@ 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()
+        input
+            .iter()
             .map(|s| s.as_ref().to_str().unwrap())
             .collect::<Vec<_>>()
             .join(" "),
         cwd.as_ref()
-            .map(|cwd| format!(
-                " (running in folder `{}`)",
-                cwd.display(),
-            ))
+            .map(|cwd| format!(" (running in folder `{}`)", cwd.display(),))
             .unwrap_or_default(),
     )
 }
 
 pub fn run_command(input: &[&dyn AsRef<OsStr>], cwd: Option<&Path>) -> Result<Output, String> {
-    let output = get_command_inner(input, cwd)
+    run_command_with_env(input, cwd, None)
+}
+
+pub fn run_command_with_env(
+    input: &[&dyn AsRef<OsStr>],
+    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))?;
     check_exit_status(input, cwd, output.status)?;
@@ -65,8 +80,10 @@ pub fn run_command(input: &[&dyn AsRef<OsStr>], cwd: Option<&Path>) -> Result<Ou
 pub fn run_command_with_output(
     input: &[&dyn AsRef<OsStr>],
     cwd: Option<&Path>,
+    env: Option<&HashMap<String, String>>,
 ) -> Result<(), String> {
-    let exit_status = get_command_inner(input, cwd).spawn()
+    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))?;
@@ -94,12 +111,69 @@ pub fn cargo_install(to_install: &str) -> Result<(), String> {
         return Ok(());
     }
     // We voluntarily ignore this error.
-    if run_command_with_output(&[&"cargo", &"install", &to_install], None).is_err() {
+    if run_command_with_output(&[&"cargo", &"install", &to_install], None, None).is_err() {
         println!("Skipping installation of `{to_install}`");
     }
     Ok(())
 }
 
+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_owned();
+    if !name.is_empty() {
+        Ok(name)
+    } else {
+        Err(format!("Failed to retrieve the OS name"))
+    }
+}
+
+pub fn get_rustc_host_triple() -> Result<String, String> {
+    let output = run_command(&[&"rustc", &"-vV"], None)?;
+    let content = std::str::from_utf8(&output.stdout).unwrap_or("");
+
+    for line in content.split('\n').map(|line| line.trim()) {
+        if !line.starts_with("host:") {
+            continue;
+        }
+        return Ok(line.split(':').nth(1).unwrap().trim().to_owned());
+    }
+    Err("Cannot find host triple".to_owned())
+}
+
+pub fn get_gcc_path() -> Result<String, String> {
+    let content = match fs::read_to_string("gcc_path") {
+        Ok(c) => c,
+        Err(_) => {
+            return Err(
+                "Please put the path to your custom build of libgccjit in the file \
+                   `gcc_path`, see Readme.md for details"
+                    .into(),
+            )
+        }
+    };
+    match content
+        .split('\n')
+        .map(|l| l.trim())
+        .filter(|l| !l.is_empty())
+        .next()
+    {
+        Some(gcc_path) => {
+            let path = Path::new(gcc_path);
+            if !path.exists() {
+                Err(format!(
+                    "Path `{gcc_path}` contained in the `gcc_path` file doesn't exist"
+                ))
+            } else {
+                Ok(gcc_path.into())
+            }
+        }
+        None => Err("No path found in `gcc_path` file".into()),
+    }
+}
+
 pub struct CloneResult {
     pub ran_clone: bool,
     pub repo_name: String,
@@ -116,11 +190,17 @@ pub fn git_clone(to_clone: &str, dest: Option<&Path>) -> Result<CloneResult, Str
         .map(|dest| dest.join(&repo_name))
         .unwrap_or_else(|| Path::new(&repo_name).into());
     if dest.is_dir() {
-        return Ok(CloneResult { ran_clone: false, repo_name });
+        return Ok(CloneResult {
+            ran_clone: false,
+            repo_name,
+        });
     }
 
-    run_command_with_output(&[&"git", &"clone", &to_clone, &dest], None)?;
-    Ok(CloneResult { ran_clone: true, repo_name })
+    run_command_with_output(&[&"git", &"clone", &to_clone, &dest], None, None)?;
+    Ok(CloneResult {
+        ran_clone: true,
+        repo_name,
+    })
 }
 
 pub fn walk_dir<P, D, F>(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String>
@@ -130,8 +210,11 @@ where
     F: FnMut(&Path) -> Result<(), String>,
 {
     let dir = dir.as_ref();
-    for entry in fs::read_dir(dir).map_err(|e| format!("Failed to read dir `{}`: {e:?}", dir.display()))? {
-        let entry = entry.map_err(|e| format!("Failed to read entry in `{}`: {e:?}", dir.display()))?;
+    for entry in
+        fs::read_dir(dir).map_err(|e| format!("Failed to read dir `{}`: {e:?}", dir.display()))?
+    {
+        let entry =
+            entry.map_err(|e| format!("Failed to read entry in `{}`: {e:?}", dir.display()))?;
         let entry_path = entry.path();
         if entry_path.is_dir() {
             dir_cb(&entry_path)?;
diff --git a/config.sh b/config.sh
index ecc6d56b00e..c686df0c72a 100644
--- a/config.sh
+++ b/config.sh
@@ -48,7 +48,7 @@ fi
 export RUSTFLAGS="$CG_RUSTFLAGS $linker -Csymbol-mangling-version=v0 -Cdebuginfo=2 $disable_lto_flags -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot $TEST_FLAGS"
 
 # FIXME(antoyo): remove once the atomic shim is gone
-if [[ `uname` == 'Darwin' ]]; then
+if [[ unamestr == 'Darwin' ]]; then
    export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
 fi