Add support for "download"

This commit is contained in:
Guillaume Gomez 2024-02-12 18:07:05 +01:00
parent 588db24344
commit eee04a48d9
2 changed files with 179 additions and 25 deletions

View File

@ -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
}
pub fn setup_gcc_path(&mut self) -> Result<(), String> {
let ConfigFile { gcc_path, .. } = ConfigFile::new(self.config_file.as_deref())?;
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 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)"
);
}
}

1
libgccjit.version Normal file
View File

@ -0,0 +1 @@
2fc8940e1