about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2024-02-12 18:07:05 +0100
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2024-02-13 15:43:42 +0100
commiteee04a48d9b0ba2ca7e18c6465c51a63feed8e08 (patch)
treee09e3ff9bcb4dad5faa3a1483690ae244ac8103a
parent588db24344dc2b626bb050067e9e6cda2de3bc59 (diff)
downloadrust-eee04a48d9b0ba2ca7e18c6465c51a63feed8e08.tar.gz
rust-eee04a48d9b0ba2ca7e18c6465c51a63feed8e08.zip
Add support for "download"
-rw-r--r--build_system/src/config.rs201
-rw-r--r--libgccjit.version1
2 files changed, 178 insertions, 24 deletions
diff --git a/build_system/src/config.rs b/build_system/src/config.rs
index 49782fc64ef..0201e3509dc 100644
--- a/build_system/src/config.rs
+++ b/build_system/src/config.rs
@@ -1,9 +1,9 @@
-use crate::utils::{get_os_name, rustc_version_info, split_args};
+use crate::utils::{get_os_name, run_command_with_output, rustc_version_info, split_args};
 use std::collections::HashMap;
 use std::env as std_env;
 use std::ffi::OsStr;
 use std::fs;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 
 use boml::{types::TomlValue, Toml};
 
@@ -23,8 +23,12 @@ impl Channel {
     }
 }
 
-fn failed_config_parsing(config_file: &str, err: &str) -> Result<ConfigFile, String> {
-    Err(format!("Failed to parse `{}`: {}", config_file, err))
+fn failed_config_parsing(config_file: &Path, err: &str) -> Result<ConfigFile, String> {
+    Err(format!(
+        "Failed to parse `{}`: {}",
+        config_file.display(),
+        err
+    ))
 }
 
 #[derive(Default)]
@@ -34,12 +38,11 @@ pub struct ConfigFile {
 }
 
 impl ConfigFile {
-    pub fn new(config_file: Option<&str>) -> Result<Self, String> {
-        let config_file = config_file.unwrap_or("config.toml");
+    pub fn new(config_file: &Path) -> Result<Self, String> {
         let content = fs::read_to_string(config_file).map_err(|_| {
             format!(
                 "Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project",
-                config_file,
+                config_file.display(),
             )
         })?;
         let toml = Toml::parse(&content).map_err(|err| {
@@ -70,19 +73,30 @@ impl ConfigFile {
                 _ => return failed_config_parsing(config_file, &format!("Unknown key `{}`", key)),
             }
         }
-        if config.gcc_path.is_none() && config.download_gccjit.is_none() {
-            return failed_config_parsing(
-                config_file,
-                "At least one of `gcc-path` or `download-gccjit` value must be set",
-            );
-        }
-        if let Some(gcc_path) = config.gcc_path.as_mut() {
-            let path = Path::new(gcc_path);
-            *gcc_path = path
-                .canonicalize()
-                .map_err(|err| format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err))?
-                .display()
-                .to_string();
+        match (config.gcc_path.as_mut(), config.download_gccjit) {
+            (None, None | Some(false)) => {
+                return failed_config_parsing(
+                    config_file,
+                    "At least one of `gcc-path` or `download-gccjit` value must be set",
+                )
+            }
+            (Some(_), Some(true)) => {
+                println!(
+                    "WARNING: both `gcc-path` and `download-gccjit` arguments are used, \
+                    ignoring `gcc-path`"
+                );
+            }
+            (Some(gcc_path), _) => {
+                let path = Path::new(gcc_path);
+                *gcc_path = path
+                    .canonicalize()
+                    .map_err(|err| {
+                        format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err)
+                    })?
+                    .display()
+                    .to_string();
+            }
+            _ => {}
         }
         Ok(config)
     }
@@ -104,6 +118,7 @@ pub struct ConfigInfo {
     pub sysroot_path: String,
     pub gcc_path: String,
     config_file: Option<String>,
+    cg_gcc_path: Option<PathBuf>,
 }
 
 impl ConfigInfo {
@@ -146,6 +161,14 @@ impl ConfigInfo {
             "--release-sysroot" => self.sysroot_release_channel = true,
             "--release" => self.channel = Channel::Release,
             "--sysroot-panic-abort" => self.sysroot_panic_abort = true,
+            "--cg_gcc-path" => match args.next() {
+                Some(arg) if !arg.is_empty() => {
+                    self.cg_gcc_path = Some(arg.into());
+                }
+                _ => {
+                    return Err("Expected a value after `--cg_gcc-path`, found nothing".to_string())
+                }
+            },
             _ => return Ok(false),
         }
         Ok(true)
@@ -159,16 +182,144 @@ impl ConfigInfo {
         command
     }
 
+    fn download_gccjit_if_needed(&mut self) -> Result<(), String> {
+        let output_dir = Path::new(
+            std::env::var("CARGO_TARGET_DIR")
+                .as_deref()
+                .unwrap_or("target"),
+        )
+        .join("libgccjit");
+
+        let commit_hash_file = self.compute_path("libgccjit.version");
+        let content = fs::read_to_string(&commit_hash_file).map_err(|_| {
+            format!(
+                "Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project",
+                commit_hash_file.display(),
+            )
+        })?;
+        let commit = content.trim();
+        if commit.contains('/') || commit.contains('\\') {
+            return Err(format!(
+                "{}: invalid commit hash `{}`",
+                commit_hash_file.display(),
+                commit
+            ));
+        }
+        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,
+                )
+            })?;
+        }
+        let libgccjit_so = output_dir.join("libgccjit.so");
+        if !libgccjit_so.is_file() {
+            // Download time!
+            let tempfile_name = "libgccjit.so.download";
+            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);
+            // Try curl. If that fails and we are on windows, fallback to PowerShell.
+            let mut ret = run_command_with_output(
+                &[
+                    &"curl",
+                    &"--speed-time",
+                    &"30",
+                    &"--speed-limit",
+                    &"10", // timeout if speed is < 10 bytes/sec for > 30 seconds
+                    &"--connect-timeout",
+                    &"30", // timeout if cannot connect within 30 seconds
+                    &"-o",
+                    &tempfile_name,
+                    &"--retry",
+                    &"3",
+                    &"-SRfL",
+                    if is_in_ci { &"-s" } else { &"--progress-bar" },
+                    &url.as_str(),
+                ],
+                Some(&output_dir),
+            );
+            if ret.is_err() && cfg!(windows) {
+                eprintln!("Fallback to PowerShell");
+                ret = run_command_with_output(
+                    &[
+                        &"PowerShell.exe",
+                        &"/nologo",
+                        &"-Command",
+                        &"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
+                        &format!(
+                            "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
+                            url,
+                            tempfile_name,
+                        ).as_str(),
+                    ],
+                    Some(&output_dir),
+                );
+            }
+            ret?;
+
+            // If we reach this point, it means the file was correctly downloaded, so let's
+            // rename it!
+            std::fs::rename(&tempfile, &libgccjit_so).map_err(|err| {
+                format!(
+                    "Failed to rename `{}` into `{}`: {:?}",
+                    tempfile.display(),
+                    libgccjit_so.display(),
+                    err,
+                )
+            })?;
+
+            println!("Downloaded libgccjit.so version {} successfully!", commit);
+        }
+
+        self.gcc_path = output_dir
+            .canonicalize()
+            .map_err(|err| {
+                format!(
+                    "Failed to get absolute path of `{}`: {:?}",
+                    output_dir.display(),
+                    err
+                )
+            })?
+            .display()
+            .to_string();
+        println!("Using `{}` as path for libgccjit", self.gcc_path);
+        Ok(())
+    }
+
+    pub fn compute_path<P: AsRef<Path>>(&self, other: P) -> PathBuf {
+        match self.cg_gcc_path {
+            Some(ref path) => path.join(other),
+            None => PathBuf::new().join(other),
+        }
+    }
+
     pub fn setup_gcc_path(&mut self) -> Result<(), String> {
-        let ConfigFile { gcc_path, .. } = ConfigFile::new(self.config_file.as_deref())?;
+        let config_file = self.compute_path(self.config_file.as_deref().unwrap_or("config.toml"));
+        let ConfigFile {
+            gcc_path,
+            download_gccjit,
+        } = ConfigFile::new(&config_file)?;
 
+        if let Some(true) = download_gccjit {
+            self.download_gccjit_if_needed()?;
+            return Ok(());
+        }
         self.gcc_path = match gcc_path {
             Some(path) => path,
-            // FIXME: Once we support "download", rewrite this.
             None => {
                 return Err(format!(
                     "missing `gcc-path` value from `{}`",
-                    self.config_file.as_deref().unwrap_or("config.toml"),
+                    config_file.display(),
                 ))
             }
         };
@@ -362,7 +513,9 @@ impl ConfigInfo {
     --release              : Build in release mode
     --release-sysroot      : Build sysroot in release mode
     --sysroot-panic-abort  : Build the sysroot without unwinding support
-    --config-file          : Location of the config file to be used"
+    --config-file          : Location of the config file to be used
+    --cg_gcc-path          : Location of the rustc_codegen_gcc root folder (used
+                             for accessing any file from the project)"
         );
     }
 }
diff --git a/libgccjit.version b/libgccjit.version
new file mode 100644
index 00000000000..3fc84f4ddd4
--- /dev/null
+++ b/libgccjit.version
@@ -0,0 +1 @@
+2fc8940e1