Skip to content

Commit ac708d9

Browse files
authored
Merge pull request #430 from GuillaumeGomez/download-config
Add `download` config
2 parents 560e65c + b80a999 commit ac708d9

File tree

10 files changed

+306
-87
lines changed

10 files changed

+306
-87
lines changed

.github/workflows/ci.yml

+10-10
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,10 @@ jobs:
4949
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests.
5050
run: sudo apt-get install ninja-build ripgrep llvm-14-tools
5151

52-
- name: Download artifact
53-
run: curl -LO https://github.com/antoyo/gcc/releases/latest/download/gcc-13.deb
54-
55-
- name: Setup path to libgccjit
56-
run: |
57-
sudo dpkg --force-overwrite -i gcc-13.deb
58-
echo 'gcc-path = "/usr/lib/"' > config.toml
59-
6052
- name: Set env
6153
run: |
6254
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
63-
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
64-
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
55+
echo 'download-gccjit = true' > config.toml
6556
6657
#- name: Cache rust repository
6758
## We only clone the rust repository for rustc tests
@@ -78,6 +69,15 @@ jobs:
7869
# TODO: remove --features master when it is back to the default.
7970
./y.sh build --features master
8071
# TODO: remove --features master when it is back to the default.
72+
73+
- name: Set env (part 2)
74+
run: |
75+
# Set the `LD_LIBRARY_PATH` and `LIBRARY_PATH` env variables...
76+
echo "LD_LIBRARY_PATH="$(./y.sh info | grep -v Using) >> $GITHUB_ENV
77+
echo "LIBRARY_PATH="$(./y.sh info | grep -v Using) >> $GITHUB_ENV
78+
79+
- name: Build (part 2)
80+
run: |
8181
cargo test --features master
8282
./y.sh clean all
8383

build_system/src/build.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ fn build_codegen(args: &mut BuildArg) -> Result<(), String> {
207207
}
208208
run_command_with_output_and_env(&command, None, Some(&env))?;
209209

210-
args.config_info.setup(&mut env, None)?;
210+
args.config_info.setup(&mut env, false)?;
211211

212212
// We voluntarily ignore the error.
213213
let _ = fs::remove_dir_all("target/out");
@@ -229,7 +229,7 @@ pub fn run() -> Result<(), String> {
229229
Some(args) => args,
230230
None => return Ok(()),
231231
};
232-
args.config_info.setup_gcc_path(None)?;
232+
args.config_info.setup_gcc_path()?;
233233
build_codegen(&mut args)?;
234234
Ok(())
235235
}

build_system/src/cargo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub fn run() -> Result<(), String> {
7777
})?;
7878

7979
let mut env: HashMap<String, String> = std::env::vars().collect();
80-
ConfigInfo::default().setup(&mut env, None)?;
80+
ConfigInfo::default().setup(&mut env, false)?;
8181
let toolchain = get_toolchain()?;
8282

8383
let toolchain_version = rustc_toolchain_version_info(&toolchain)?;

build_system/src/config.rs

+214-46
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use crate::utils::{get_os_name, rustc_version_info, split_args};
1+
use crate::utils::{
2+
create_symlink, get_os_name, run_command_with_output, rustc_version_info, split_args,
3+
};
24
use std::collections::HashMap;
35
use std::env as std_env;
46
use std::ffi::OsStr;
57
use std::fs;
6-
use std::path::Path;
8+
use std::path::{Path, PathBuf};
79

810
use boml::{types::TomlValue, Toml};
911

@@ -23,8 +25,12 @@ impl Channel {
2325
}
2426
}
2527

26-
fn failed_config_parsing(config_file: &str, err: &str) -> Result<ConfigFile, String> {
27-
Err(format!("Failed to parse `{}`: {}", config_file, err))
28+
fn failed_config_parsing(config_file: &Path, err: &str) -> Result<ConfigFile, String> {
29+
Err(format!(
30+
"Failed to parse `{}`: {}",
31+
config_file.display(),
32+
err
33+
))
2834
}
2935

3036
#[derive(Default)]
@@ -34,12 +40,11 @@ pub struct ConfigFile {
3440
}
3541

3642
impl ConfigFile {
37-
pub fn new(config_file: Option<&str>) -> Result<Self, String> {
38-
let config_file = config_file.unwrap_or("config.toml");
43+
pub fn new(config_file: &Path) -> Result<Self, String> {
3944
let content = fs::read_to_string(config_file).map_err(|_| {
4045
format!(
4146
"Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project",
42-
config_file,
47+
config_file.display(),
4348
)
4449
})?;
4550
let toml = Toml::parse(&content).map_err(|err| {
@@ -70,19 +75,30 @@ impl ConfigFile {
7075
_ => return failed_config_parsing(config_file, &format!("Unknown key `{}`", key)),
7176
}
7277
}
73-
if config.gcc_path.is_none() && config.download_gccjit.is_none() {
74-
return failed_config_parsing(
75-
config_file,
76-
"At least one of `gcc-path` or `download-gccjit` value must be set",
77-
);
78-
}
79-
if let Some(gcc_path) = config.gcc_path.as_mut() {
80-
let path = Path::new(gcc_path);
81-
*gcc_path = path
82-
.canonicalize()
83-
.map_err(|err| format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err))?
84-
.display()
85-
.to_string();
78+
match (config.gcc_path.as_mut(), config.download_gccjit) {
79+
(None, None | Some(false)) => {
80+
return failed_config_parsing(
81+
config_file,
82+
"At least one of `gcc-path` or `download-gccjit` value must be set",
83+
)
84+
}
85+
(Some(_), Some(true)) => {
86+
println!(
87+
"WARNING: both `gcc-path` and `download-gccjit` arguments are used, \
88+
ignoring `gcc-path`"
89+
);
90+
}
91+
(Some(gcc_path), _) => {
92+
let path = Path::new(gcc_path);
93+
*gcc_path = path
94+
.canonicalize()
95+
.map_err(|err| {
96+
format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err)
97+
})?
98+
.display()
99+
.to_string();
100+
}
101+
_ => {}
86102
}
87103
Ok(config)
88104
}
@@ -104,6 +120,13 @@ pub struct ConfigInfo {
104120
pub sysroot_path: String,
105121
pub gcc_path: String,
106122
config_file: Option<String>,
123+
// This is used in particular in rust compiler bootstrap because it doesn't run at the root
124+
// of the `cg_gcc` folder, making it complicated for us to get access to local files we need
125+
// like `libgccjit.version` or `config.toml`.
126+
cg_gcc_path: Option<PathBuf>,
127+
// Needed for the `info` command which doesn't want to actually download the lib if needed,
128+
// just to set the `gcc_path` field to display it.
129+
pub no_download: bool,
107130
}
108131

109132
impl ConfigInfo {
@@ -146,6 +169,14 @@ impl ConfigInfo {
146169
"--release-sysroot" => self.sysroot_release_channel = true,
147170
"--release" => self.channel = Channel::Release,
148171
"--sysroot-panic-abort" => self.sysroot_panic_abort = true,
172+
"--cg_gcc-path" => match args.next() {
173+
Some(arg) if !arg.is_empty() => {
174+
self.cg_gcc_path = Some(arg.into());
175+
}
176+
_ => {
177+
return Err("Expected a value after `--cg_gcc-path`, found nothing".to_string())
178+
}
179+
},
149180
_ => return Ok(false),
150181
}
151182
Ok(true)
@@ -159,30 +190,165 @@ impl ConfigInfo {
159190
command
160191
}
161192

162-
pub fn setup_gcc_path(&mut self, override_gcc_path: Option<&str>) -> Result<(), String> {
163-
let ConfigFile { gcc_path, .. } = ConfigFile::new(self.config_file.as_deref())?;
193+
fn download_gccjit(
194+
&self,
195+
output_dir: &Path,
196+
libgccjit_so_name: &str,
197+
commit: &str,
198+
) -> Result<(), String> {
199+
// Download time!
200+
let tempfile_name = format!("{}.download", libgccjit_so_name);
201+
let tempfile = output_dir.join(&tempfile_name);
202+
let is_in_ci = std::env::var("GITHUB_ACTIONS").is_ok();
164203

165-
self.gcc_path = match override_gcc_path {
166-
Some(path) => {
167-
if gcc_path.is_some() {
168-
println!(
169-
"overriding setting from `{}`",
170-
self.config_file.as_deref().unwrap_or("config.toml")
171-
);
172-
}
173-
path.to_string()
174-
}
204+
let url = format!(
205+
"https://github.com/antoyo/gcc/releases/download/master-{}/libgccjit.so",
206+
commit,
207+
);
208+
209+
println!("Downloading `{}`...", url);
210+
// Try curl. If that fails and we are on windows, fallback to PowerShell.
211+
let mut ret = run_command_with_output(
212+
&[
213+
&"curl",
214+
&"--speed-time",
215+
&"30",
216+
&"--speed-limit",
217+
&"10", // timeout if speed is < 10 bytes/sec for > 30 seconds
218+
&"--connect-timeout",
219+
&"30", // timeout if cannot connect within 30 seconds
220+
&"-o",
221+
&tempfile_name,
222+
&"--retry",
223+
&"3",
224+
&"-SRfL",
225+
if is_in_ci { &"-s" } else { &"--progress-bar" },
226+
&url.as_str(),
227+
],
228+
Some(&output_dir),
229+
);
230+
if ret.is_err() && cfg!(windows) {
231+
eprintln!("Fallback to PowerShell");
232+
ret = run_command_with_output(
233+
&[
234+
&"PowerShell.exe",
235+
&"/nologo",
236+
&"-Command",
237+
&"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
238+
&format!(
239+
"(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
240+
url,
241+
tempfile_name,
242+
).as_str(),
243+
],
244+
Some(&output_dir),
245+
);
246+
}
247+
ret?;
248+
249+
let libgccjit_so = output_dir.join(libgccjit_so_name);
250+
// If we reach this point, it means the file was correctly downloaded, so let's
251+
// rename it!
252+
std::fs::rename(&tempfile, &libgccjit_so).map_err(|err| {
253+
format!(
254+
"Failed to rename `{}` into `{}`: {:?}",
255+
tempfile.display(),
256+
libgccjit_so.display(),
257+
err,
258+
)
259+
})?;
260+
261+
println!("Downloaded libgccjit.so version {} successfully!", commit);
262+
// We need to create a link named `libgccjit.so.0` because that's what the linker is
263+
// looking for.
264+
create_symlink(
265+
&libgccjit_so,
266+
output_dir.join(&format!("{}.0", libgccjit_so_name)),
267+
)
268+
}
269+
270+
fn download_gccjit_if_needed(&mut self) -> Result<(), String> {
271+
let output_dir = Path::new(
272+
std::env::var("CARGO_TARGET_DIR")
273+
.as_deref()
274+
.unwrap_or("target"),
275+
)
276+
.join("libgccjit");
277+
278+
let commit_hash_file = self.compute_path("libgccjit.version");
279+
let content = fs::read_to_string(&commit_hash_file).map_err(|_| {
280+
format!(
281+
"Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project",
282+
commit_hash_file.display(),
283+
)
284+
})?;
285+
let commit = content.trim();
286+
// This is a very simple check to ensure this is not a path. For the rest, it'll just fail
287+
// when trying to download the file so we should be fine.
288+
if commit.contains('/') || commit.contains('\\') {
289+
return Err(format!(
290+
"{}: invalid commit hash `{}`",
291+
commit_hash_file.display(),
292+
commit,
293+
));
294+
}
295+
let output_dir = output_dir.join(commit);
296+
if !output_dir.is_dir() {
297+
std::fs::create_dir_all(&output_dir).map_err(|err| {
298+
format!(
299+
"failed to create folder `{}`: {:?}",
300+
output_dir.display(),
301+
err,
302+
)
303+
})?;
304+
}
305+
let output_dir = output_dir.canonicalize().map_err(|err| {
306+
format!(
307+
"Failed to get absolute path of `{}`: {:?}",
308+
output_dir.display(),
309+
err
310+
)
311+
})?;
312+
313+
let libgccjit_so_name = "libgccjit.so";
314+
let libgccjit_so = output_dir.join(libgccjit_so_name);
315+
if !libgccjit_so.is_file() && !self.no_download {
316+
self.download_gccjit(&output_dir, libgccjit_so_name, commit)?;
317+
}
318+
319+
self.gcc_path = output_dir.display().to_string();
320+
println!("Using `{}` as path for libgccjit", self.gcc_path);
321+
Ok(())
322+
}
323+
324+
pub fn compute_path<P: AsRef<Path>>(&self, other: P) -> PathBuf {
325+
match self.cg_gcc_path {
326+
Some(ref path) => path.join(other),
327+
None => PathBuf::new().join(other),
328+
}
329+
}
330+
331+
pub fn setup_gcc_path(&mut self) -> Result<(), String> {
332+
let config_file = match self.config_file.as_deref() {
333+
Some(config_file) => config_file.into(),
334+
None => self.compute_path("config.toml"),
335+
};
336+
let ConfigFile {
337+
gcc_path,
338+
download_gccjit,
339+
} = ConfigFile::new(&config_file)?;
340+
341+
if let Some(true) = download_gccjit {
342+
self.download_gccjit_if_needed()?;
343+
return Ok(());
344+
}
345+
self.gcc_path = match gcc_path {
346+
Some(path) => path,
175347
None => {
176-
match gcc_path {
177-
Some(path) => path,
178-
// FIXME: Once we support "download", rewrite this.
179-
None => {
180-
return Err(format!(
181-
"missing `gcc-path` value from `{}`",
182-
self.config_file.as_deref().unwrap_or("config.toml"),
183-
))
184-
}
185-
}
348+
return Err(format!(
349+
"missing `gcc-path` value from `{}`",
350+
config_file.display(),
351+
))
186352
}
187353
};
188354
Ok(())
@@ -191,12 +357,12 @@ impl ConfigInfo {
191357
pub fn setup(
192358
&mut self,
193359
env: &mut HashMap<String, String>,
194-
override_gcc_path: Option<&str>,
360+
use_system_gcc: bool,
195361
) -> Result<(), String> {
196362
env.insert("CARGO_INCREMENTAL".to_string(), "0".to_string());
197363

198-
if self.gcc_path.is_empty() || override_gcc_path.is_some() {
199-
self.setup_gcc_path(override_gcc_path)?;
364+
if self.gcc_path.is_empty() && !use_system_gcc {
365+
self.setup_gcc_path()?;
200366
}
201367
env.insert("GCC_PATH".to_string(), self.gcc_path.clone());
202368

@@ -375,7 +541,9 @@ impl ConfigInfo {
375541
--release : Build in release mode
376542
--release-sysroot : Build sysroot in release mode
377543
--sysroot-panic-abort : Build the sysroot without unwinding support
378-
--config-file : Location of the config file to be used"
544+
--config-file : Location of the config file to be used
545+
--cg_gcc-path : Location of the rustc_codegen_gcc root folder (used
546+
when ran from another directory)"
379547
);
380548
}
381549
}

0 commit comments

Comments
 (0)