|
| 1 | +//! Compilation of native dependencies like GCC. |
| 2 | +//! |
| 3 | +//! Native projects like GCC unfortunately aren't suited just yet for |
| 4 | +//! compilation in build scripts that Cargo has. This is because the |
| 5 | +//! compilation takes a *very* long time but also because we don't want to |
| 6 | +//! compile GCC 3 times as part of a normal bootstrap (we want it cached). |
| 7 | +//! |
| 8 | +//! GCC and compiler-rt are essentially just wired up to everything else to |
| 9 | +//! ensure that they're always in place if needed. |
| 10 | +
|
| 11 | +use std::fs; |
| 12 | +use std::path::PathBuf; |
| 13 | +use std::sync::OnceLock; |
| 14 | + |
| 15 | +use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; |
| 16 | +use crate::core::config::TargetSelection; |
| 17 | +use crate::utils::exec::command; |
| 18 | +use crate::utils::helpers::{self, t, HashStamp}; |
| 19 | +use crate::{generate_smart_stamp_hash, Kind}; |
| 20 | + |
| 21 | +pub struct Meta { |
| 22 | + stamp: HashStamp, |
| 23 | + out_dir: PathBuf, |
| 24 | + install_dir: PathBuf, |
| 25 | + root: PathBuf, |
| 26 | +} |
| 27 | + |
| 28 | +pub enum GccBuildStatus { |
| 29 | + AlreadyBuilt, |
| 30 | + ShouldBuild(Meta), |
| 31 | +} |
| 32 | + |
| 33 | +/// This returns whether we've already previously built GCC. |
| 34 | +/// |
| 35 | +/// It's used to avoid busting caches during x.py check -- if we've already built |
| 36 | +/// GCC, it's fine for us to not try to avoid doing so. |
| 37 | +pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus { |
| 38 | + // Initialize the gcc submodule if not initialized already. |
| 39 | + builder.config.update_submodule("src/gcc"); |
| 40 | + |
| 41 | + // FIXME (GuillaumeGomez): To be done once gccjit has been built in the CI. |
| 42 | + // builder.config.maybe_download_ci_gcc(); |
| 43 | + |
| 44 | + let root = builder.src.join("src/gcc"); |
| 45 | + let out_dir = builder.gcc_out(target).join("build"); |
| 46 | + let install_dir = builder.gcc_out(target).join("install"); |
| 47 | + |
| 48 | + static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new(); |
| 49 | + let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { |
| 50 | + generate_smart_stamp_hash( |
| 51 | + builder, |
| 52 | + &builder.config.src.join("src/gcc"), |
| 53 | + builder.in_tree_gcc_info.sha().unwrap_or_default(), |
| 54 | + ) |
| 55 | + }); |
| 56 | + |
| 57 | + let stamp = out_dir.join("gcc-finished-building"); |
| 58 | + let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); |
| 59 | + |
| 60 | + if stamp.is_done() { |
| 61 | + if stamp.hash.is_none() { |
| 62 | + builder.info( |
| 63 | + "Could not determine the GCC submodule commit hash. \ |
| 64 | + Assuming that an GCC rebuild is not necessary.", |
| 65 | + ); |
| 66 | + builder.info(&format!( |
| 67 | + "To force GCC to rebuild, remove the file `{}`", |
| 68 | + stamp.path.display() |
| 69 | + )); |
| 70 | + } |
| 71 | + return GccBuildStatus::AlreadyBuilt; |
| 72 | + } |
| 73 | + |
| 74 | + GccBuildStatus::ShouldBuild(Meta { stamp, out_dir, install_dir, root }) |
| 75 | +} |
| 76 | + |
| 77 | +#[derive(Debug, Clone, Hash, PartialEq, Eq)] |
| 78 | +pub struct Gcc { |
| 79 | + pub target: TargetSelection, |
| 80 | +} |
| 81 | + |
| 82 | +impl Step for Gcc { |
| 83 | + type Output = bool; |
| 84 | + |
| 85 | + const ONLY_HOSTS: bool = true; |
| 86 | + |
| 87 | + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
| 88 | + run.path("src/gcc").alias("gcc") |
| 89 | + } |
| 90 | + |
| 91 | + fn make_run(run: RunConfig<'_>) { |
| 92 | + run.builder.ensure(Gcc { target: run.target }); |
| 93 | + } |
| 94 | + |
| 95 | + /// Compile GCC for `target`. |
| 96 | + fn run(self, builder: &Builder<'_>) -> bool { |
| 97 | + let target = self.target; |
| 98 | + |
| 99 | + // If GCC has already been built, we avoid building it again. |
| 100 | + let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config(builder, target) |
| 101 | + { |
| 102 | + GccBuildStatus::AlreadyBuilt => return true, |
| 103 | + GccBuildStatus::ShouldBuild(m) => m, |
| 104 | + }; |
| 105 | + |
| 106 | + let _guard = builder.msg_unstaged(Kind::Build, "GCC", target); |
| 107 | + t!(stamp.remove()); |
| 108 | + let _time = helpers::timeit(builder); |
| 109 | + t!(fs::create_dir_all(&out_dir)); |
| 110 | + |
| 111 | + if builder.config.dry_run() { |
| 112 | + return true; |
| 113 | + } |
| 114 | + |
| 115 | + command(root.join("contrib/download_prerequisites")).current_dir(&root).run(builder); |
| 116 | + command(root.join("configure")) |
| 117 | + .current_dir(&out_dir) |
| 118 | + .arg("--enable-host-shared") |
| 119 | + .arg("--enable-languages=jit") |
| 120 | + .arg("--enable-checking=release") |
| 121 | + .arg("--disable-bootstrap") |
| 122 | + .arg("--disable-multilib") |
| 123 | + .arg(format!("--prefix={}", install_dir.display())) |
| 124 | + .run(builder); |
| 125 | + command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder); |
| 126 | + command("make").current_dir(&out_dir).arg("install").run(builder); |
| 127 | + |
| 128 | + let lib_alias = install_dir.join("lib/libgccjit.so.0"); |
| 129 | + if !lib_alias.exists() { |
| 130 | + t!(builder.symlink_file(install_dir.join("lib/libgccjit.so"), lib_alias,)); |
| 131 | + } |
| 132 | + |
| 133 | + t!(stamp.write()); |
| 134 | + |
| 135 | + true |
| 136 | + } |
| 137 | +} |
0 commit comments