]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/native.rs
Rollup merge of #97457 - JohnTitor:issue-81899, r=compiler-errors
[rust.git] / src / bootstrap / native.rs
1 //! Compilation of native dependencies like LLVM.
2 //!
3 //! Native projects like LLVM 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 LLVM 3 times as part of a normal bootstrap (we want it cached).
7 //!
8 //! LLVM 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::env;
12 use std::env::consts::EXE_EXTENSION;
13 use std::ffi::{OsStr, OsString};
14 use std::fs::{self, File};
15 use std::io::{self, BufRead, BufReader, ErrorKind};
16 use std::path::{Path, PathBuf};
17 use std::process::{Command, Stdio};
18
19 use once_cell::sync::OnceCell;
20 use xz2::bufread::XzDecoder;
21
22 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
23 use crate::config::TargetSelection;
24 use crate::util::{self, exe, output, t, up_to_date};
25 use crate::{CLang, GitRepo};
26
27 pub struct Meta {
28     stamp: HashStamp,
29     build_llvm_config: PathBuf,
30     out_dir: PathBuf,
31     root: String,
32 }
33
34 // Linker flags to pass to LLVM's CMake invocation.
35 #[derive(Debug, Clone, Default)]
36 struct LdFlags {
37     // CMAKE_EXE_LINKER_FLAGS
38     exe: OsString,
39     // CMAKE_SHARED_LINKER_FLAGS
40     shared: OsString,
41     // CMAKE_MODULE_LINKER_FLAGS
42     module: OsString,
43 }
44
45 impl LdFlags {
46     fn push_all(&mut self, s: impl AsRef<OsStr>) {
47         let s = s.as_ref();
48         self.exe.push(" ");
49         self.exe.push(s);
50         self.shared.push(" ");
51         self.shared.push(s);
52         self.module.push(" ");
53         self.module.push(s);
54     }
55 }
56
57 // This returns whether we've already previously built LLVM.
58 //
59 // It's used to avoid busting caches during x.py check -- if we've already built
60 // LLVM, it's fine for us to not try to avoid doing so.
61 //
62 // This will return the llvm-config if it can get it (but it will not build it
63 // if not).
64 pub fn prebuilt_llvm_config(
65     builder: &Builder<'_>,
66     target: TargetSelection,
67 ) -> Result<PathBuf, Meta> {
68     maybe_download_ci_llvm(builder);
69
70     // If we're using a custom LLVM bail out here, but we can only use a
71     // custom LLVM for the build triple.
72     if let Some(config) = builder.config.target_config.get(&target) {
73         if let Some(ref s) = config.llvm_config {
74             check_llvm_version(builder, s);
75             return Ok(s.to_path_buf());
76         }
77     }
78
79     let root = "src/llvm-project/llvm";
80     let out_dir = builder.llvm_out(target);
81
82     let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
83     if !builder.config.build.contains("msvc") || builder.ninja() {
84         llvm_config_ret_dir.push("build");
85     }
86     llvm_config_ret_dir.push("bin");
87
88     let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build));
89
90     let stamp = out_dir.join("llvm-finished-building");
91     let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
92
93     if builder.config.llvm_skip_rebuild && stamp.path.exists() {
94         builder.info(
95             "Warning: \
96                 Using a potentially stale build of LLVM; \
97                 This may not behave well.",
98         );
99         return Ok(build_llvm_config);
100     }
101
102     if stamp.is_done() {
103         if stamp.hash.is_none() {
104             builder.info(
105                 "Could not determine the LLVM submodule commit hash. \
106                      Assuming that an LLVM rebuild is not necessary.",
107             );
108             builder.info(&format!(
109                 "To force LLVM to rebuild, remove the file `{}`",
110                 stamp.path.display()
111             ));
112         }
113         return Ok(build_llvm_config);
114     }
115
116     Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
117 }
118
119 pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
120     let config = &builder.config;
121     if !config.llvm_from_ci {
122         return;
123     }
124     let mut rev_list = Command::new("git");
125     rev_list.args(&[
126         PathBuf::from("rev-list"),
127         "--author=bors@rust-lang.org".into(),
128         "-n1".into(),
129         "--first-parent".into(),
130         "HEAD".into(),
131         "--".into(),
132         builder.src.join("src/llvm-project"),
133         builder.src.join("src/bootstrap/download-ci-llvm-stamp"),
134         // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
135         builder.src.join("src/version"),
136     ]);
137     let llvm_sha = output(&mut rev_list);
138     let llvm_sha = llvm_sha.trim();
139
140     if llvm_sha == "" {
141         eprintln!("error: could not find commit hash for downloading LLVM");
142         eprintln!("help: maybe your repository history is too shallow?");
143         eprintln!("help: consider disabling `download-ci-llvm`");
144         eprintln!("help: or fetch enough history to include one upstream commit");
145         panic!();
146     }
147
148     let llvm_root = config.ci_llvm_root();
149     let llvm_stamp = llvm_root.join(".llvm-stamp");
150     let key = format!("{}{}", llvm_sha, config.llvm_assertions);
151     if program_out_of_date(&llvm_stamp, &key) && !config.dry_run {
152         download_ci_llvm(builder, &llvm_sha);
153         for binary in ["llvm-config", "FileCheck"] {
154             fix_bin_or_dylib(builder, &llvm_root.join("bin").join(binary));
155         }
156         let llvm_lib = llvm_root.join("lib");
157         for entry in t!(fs::read_dir(&llvm_lib)) {
158             let lib = t!(entry).path();
159             if lib.extension().map_or(false, |ext| ext == "so") {
160                 fix_bin_or_dylib(builder, &lib);
161             }
162         }
163         t!(fs::write(llvm_stamp, key));
164     }
165 }
166
167 fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
168     let llvm_assertions = builder.config.llvm_assertions;
169
170     let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions);
171     let cache_dst = builder.out.join("cache");
172     let rustc_cache = cache_dst.join(cache_prefix);
173     if !rustc_cache.exists() {
174         t!(fs::create_dir_all(&rustc_cache));
175     }
176     let base = "https://ci-artifacts.rust-lang.org";
177     let url = if llvm_assertions {
178         format!("rustc-builds-alt/{}", llvm_sha)
179     } else {
180         format!("rustc-builds/{}", llvm_sha)
181     };
182     let filename = format!("rust-dev-nightly-{}.tar.xz", builder.build.build.triple);
183     let tarball = rustc_cache.join(&filename);
184     if !tarball.exists() {
185         download_component(builder, base, &format!("{}/{}", url, filename), &tarball);
186     }
187     let llvm_root = builder.config.ci_llvm_root();
188     unpack(builder, &tarball, &llvm_root);
189 }
190
191 /// Modifies the interpreter section of 'fname' to fix the dynamic linker,
192 /// or the RPATH section, to fix the dynamic library search path
193 ///
194 /// This is only required on NixOS and uses the PatchELF utility to
195 /// change the interpreter/RPATH of ELF executables.
196 ///
197 /// Please see https://nixos.org/patchelf.html for more information
198 fn fix_bin_or_dylib(builder: &Builder<'_>, fname: &Path) {
199     // FIXME: cache NixOS detection?
200     match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
201         Err(_) => return,
202         Ok(output) if !output.status.success() => return,
203         Ok(output) => {
204             let mut s = output.stdout;
205             if s.last() == Some(&b'\n') {
206                 s.pop();
207             }
208             if s != b"Linux" {
209                 return;
210             }
211         }
212     }
213
214     // If the user has asked binaries to be patched for Nix, then
215     // don't check for NixOS or `/lib`, just continue to the patching.
216     // FIXME: shouldn't this take precedence over the `uname` check above?
217     if !builder.config.patch_binaries_for_nix {
218         // Use `/etc/os-release` instead of `/etc/NIXOS`.
219         // The latter one does not exist on NixOS when using tmpfs as root.
220         const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
221         let os_release = match File::open("/etc/os-release") {
222             Err(e) if e.kind() == ErrorKind::NotFound => return,
223             Err(e) => panic!("failed to access /etc/os-release: {}", e),
224             Ok(f) => f,
225         };
226         if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
227             return;
228         }
229         if Path::new("/lib").exists() {
230             return;
231         }
232     }
233
234     // At this point we're pretty sure the user is running NixOS or using Nix
235     println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());
236
237     // Only build `.nix-deps` once.
238     static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
239     let mut nix_build_succeeded = true;
240     let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
241         // Run `nix-build` to "build" each dependency (which will likely reuse
242         // the existing `/nix/store` copy, or at most download a pre-built copy).
243         //
244         // Importantly, we create a gc-root called `.nix-deps` in the `build/`
245         // directory, but still reference the actual `/nix/store` path in the rpath
246         // as it makes it significantly more robust against changes to the location of
247         // the `.nix-deps` location.
248         //
249         // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
250         // zlib: Needed as a system dependency of `libLLVM-*.so`.
251         // patchelf: Needed for patching ELF binaries (see doc comment above).
252         let nix_deps_dir = builder.out.join(".nix-deps");
253         const NIX_EXPR: &str = "
254         with (import <nixpkgs> {});
255         symlinkJoin {
256             name = \"rust-stage0-dependencies\";
257             paths = [
258                 zlib
259                 patchelf
260                 stdenv.cc.bintools
261             ];
262         }
263         ";
264         nix_build_succeeded = builder.try_run(Command::new("nix-build").args(&[
265             Path::new("-E"),
266             Path::new(NIX_EXPR),
267             Path::new("-o"),
268             &nix_deps_dir,
269         ]));
270         nix_deps_dir
271     });
272     if !nix_build_succeeded {
273         return;
274     }
275
276     let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
277     let rpath_entries = {
278         // ORIGIN is a relative default, all binary and dynamic libraries we ship
279         // appear to have this (even when `../lib` is redundant).
280         // NOTE: there are only two paths here, delimited by a `:`
281         let mut entries = OsString::from("$ORIGIN/../lib:");
282         entries.push(t!(fs::canonicalize(nix_deps_dir)));
283         entries.push("/lib");
284         entries
285     };
286     patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
287     if !fname.extension().map_or(false, |ext| ext == "so") {
288         // Finally, set the corret .interp for binaries
289         let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
290         // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
291         let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
292         patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
293     }
294
295     builder.try_run(patchelf.arg(fname));
296 }
297
298 fn download_component(builder: &Builder<'_>, base: &str, url: &str, dest_path: &Path) {
299     // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
300     let tempfile = builder.tempdir().join(dest_path.file_name().unwrap());
301     // FIXME: support `do_verify` (only really needed for nightly rustfmt)
302     // FIXME: support non-utf8 paths?
303     download_with_retries(builder, tempfile.to_str().unwrap(), &format!("{}/{}", base, url));
304     t!(std::fs::rename(&tempfile, dest_path));
305 }
306
307 fn download_with_retries(builder: &Builder<'_>, tempfile: &str, url: &str) {
308     println!("downloading {}", url);
309     // Try curl. If that fails and we are on windows, fallback to PowerShell.
310     if !builder.check_run(Command::new("curl").args(&[
311         "-#",
312         "-y",
313         "30",
314         "-Y",
315         "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
316         "--connect-timeout",
317         "30", // timeout if cannot connect within 30 seconds
318         "--retry",
319         "3",
320         "-Sf",
321         "-o",
322         tempfile,
323         url,
324     ])) {
325         if builder.build.build.contains("windows-msvc") {
326             println!("Fallback to PowerShell");
327             for _ in 0..3 {
328                 if builder.try_run(Command::new("PowerShell.exe").args(&[
329                     "/nologo",
330                     "-Command",
331                     "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
332                     &format!(
333                         "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
334                         url, tempfile
335                     ),
336                 ])) {
337                     return;
338                 }
339                 println!("\nspurious failure, trying again");
340             }
341         }
342         std::process::exit(1);
343     }
344 }
345
346 fn unpack(builder: &Builder<'_>, tarball: &Path, dst: &Path) {
347     println!("extracting {} to {}", tarball.display(), dst.display());
348     if !dst.exists() {
349         t!(fs::create_dir_all(dst));
350     }
351
352     // FIXME: will need to be a parameter once `download-rustc` is moved to rustbuild
353     const MATCH: &str = "rust-dev";
354
355     // `tarball` ends with `.tar.xz`; strip that suffix
356     // example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
357     let uncompressed_filename =
358         Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
359     let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
360
361     // decompress the file
362     let data = t!(File::open(tarball));
363     let decompressor = XzDecoder::new(BufReader::new(data));
364
365     let mut tar = tar::Archive::new(decompressor);
366     for member in t!(tar.entries()) {
367         let mut member = t!(member);
368         let original_path = t!(member.path()).into_owned();
369         // skip the top-level directory
370         if original_path == directory_prefix {
371             continue;
372         }
373         let mut short_path = t!(original_path.strip_prefix(directory_prefix));
374         if !short_path.starts_with(MATCH) {
375             continue;
376         }
377         short_path = t!(short_path.strip_prefix(MATCH));
378         let dst_path = dst.join(short_path);
379         builder.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
380         if !t!(member.unpack_in(dst)) {
381             panic!("path traversal attack ??");
382         }
383         let src_path = dst.join(original_path);
384         if src_path.is_dir() && dst_path.exists() {
385             continue;
386         }
387         t!(fs::rename(src_path, dst_path));
388     }
389     t!(fs::remove_dir_all(dst.join(directory_prefix)));
390 }
391
392 fn program_out_of_date(stamp: &Path, key: &str) -> bool {
393     if !stamp.exists() {
394         return true;
395     }
396     t!(fs::read_to_string(stamp)) != key
397 }
398
399 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
400 pub struct Llvm {
401     pub target: TargetSelection,
402 }
403
404 impl Step for Llvm {
405     type Output = PathBuf; // path to llvm-config
406
407     const ONLY_HOSTS: bool = true;
408
409     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
410         run.path("src/llvm-project").path("src/llvm-project/llvm")
411     }
412
413     fn make_run(run: RunConfig<'_>) {
414         run.builder.ensure(Llvm { target: run.target });
415     }
416
417     /// Compile LLVM for `target`.
418     fn run(self, builder: &Builder<'_>) -> PathBuf {
419         let target = self.target;
420         let target_native = if self.target.starts_with("riscv") {
421             // RISC-V target triples in Rust is not named the same as C compiler target triples.
422             // This converts Rust RISC-V target triples to C compiler triples.
423             let idx = target.triple.find('-').unwrap();
424
425             format!("riscv{}{}", &target.triple[5..7], &target.triple[idx..])
426         } else if self.target.starts_with("powerpc") && self.target.ends_with("freebsd") {
427             // FreeBSD 13 had incompatible ABI changes on all PowerPC platforms.
428             // Set the version suffix to 13.0 so the correct target details are used.
429             format!("{}{}", self.target, "13.0")
430         } else {
431             target.to_string()
432         };
433
434         let Meta { stamp, build_llvm_config, out_dir, root } =
435             match prebuilt_llvm_config(builder, target) {
436                 Ok(p) => return p,
437                 Err(m) => m,
438             };
439
440         builder.update_submodule(&Path::new("src").join("llvm-project"));
441         if builder.llvm_link_shared()
442             && (target.contains("windows") || target.contains("apple-darwin"))
443         {
444             panic!("shared linking to LLVM is not currently supported on {}", target.triple);
445         }
446
447         builder.info(&format!("Building LLVM for {}", target));
448         t!(stamp.remove());
449         let _time = util::timeit(&builder);
450         t!(fs::create_dir_all(&out_dir));
451
452         // https://llvm.org/docs/CMake.html
453         let mut cfg = cmake::Config::new(builder.src.join(root));
454         let mut ldflags = LdFlags::default();
455
456         let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
457             (false, _) => "Debug",
458             (true, false) => "Release",
459             (true, true) => "RelWithDebInfo",
460         };
461
462         // NOTE: remember to also update `config.toml.example` when changing the
463         // defaults!
464         let llvm_targets = match &builder.config.llvm_targets {
465             Some(s) => s,
466             None => {
467                 "AArch64;ARM;BPF;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\
468                      Sparc;SystemZ;WebAssembly;X86"
469             }
470         };
471
472         let llvm_exp_targets = match builder.config.llvm_experimental_targets {
473             Some(ref s) => s,
474             None => "AVR;M68k",
475         };
476
477         let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" };
478         let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" };
479         let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" };
480
481         cfg.out_dir(&out_dir)
482             .profile(profile)
483             .define("LLVM_ENABLE_ASSERTIONS", assertions)
484             .define("LLVM_ENABLE_PLUGINS", plugins)
485             .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
486             .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
487             .define("LLVM_INCLUDE_EXAMPLES", "OFF")
488             .define("LLVM_INCLUDE_DOCS", "OFF")
489             .define("LLVM_INCLUDE_BENCHMARKS", "OFF")
490             .define("LLVM_INCLUDE_TESTS", enable_tests)
491             .define("LLVM_ENABLE_TERMINFO", "OFF")
492             .define("LLVM_ENABLE_LIBEDIT", "OFF")
493             .define("LLVM_ENABLE_BINDINGS", "OFF")
494             .define("LLVM_ENABLE_Z3_SOLVER", "OFF")
495             .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string())
496             .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap())
497             .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native);
498
499         // Parts of our test suite rely on the `FileCheck` tool, which is built by default in
500         // `build/$TARGET/llvm/build/bin` is but *not* then installed to `build/$TARGET/llvm/bin`.
501         // This flag makes sure `FileCheck` is copied in the final binaries directory.
502         cfg.define("LLVM_INSTALL_UTILS", "ON");
503
504         if builder.config.llvm_profile_generate {
505             cfg.define("LLVM_BUILD_INSTRUMENTED", "IR");
506             cfg.define("LLVM_BUILD_RUNTIME", "No");
507         }
508         if let Some(path) = builder.config.llvm_profile_use.as_ref() {
509             cfg.define("LLVM_PROFDATA_FILE", &path);
510         }
511
512         if target != "aarch64-apple-darwin" && !target.contains("windows") {
513             cfg.define("LLVM_ENABLE_ZLIB", "ON");
514         } else {
515             cfg.define("LLVM_ENABLE_ZLIB", "OFF");
516         }
517
518         // Are we compiling for iOS/tvOS?
519         if target.contains("apple-ios") || target.contains("apple-tvos") {
520             // These two defines prevent CMake from automatically trying to add a MacOSX sysroot, which leads to a compiler error.
521             cfg.define("CMAKE_OSX_SYSROOT", "/");
522             cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", "");
523             // Prevent cmake from adding -bundle to CFLAGS automatically, which leads to a compiler error because "-bitcode_bundle" also gets added.
524             cfg.define("LLVM_ENABLE_PLUGINS", "OFF");
525             // Zlib fails to link properly, leading to a compiler error.
526             cfg.define("LLVM_ENABLE_ZLIB", "OFF");
527         }
528
529         if builder.config.llvm_thin_lto {
530             cfg.define("LLVM_ENABLE_LTO", "Thin");
531             if !target.contains("apple") {
532                 cfg.define("LLVM_ENABLE_LLD", "ON");
533             }
534         }
535
536         // This setting makes the LLVM tools link to the dynamic LLVM library,
537         // which saves both memory during parallel links and overall disk space
538         // for the tools. We don't do this on every platform as it doesn't work
539         // equally well everywhere.
540         //
541         // If we're not linking rustc to a dynamic LLVM, though, then don't link
542         // tools to it.
543         if builder.llvm_link_tools_dynamically(target) && builder.llvm_link_shared() {
544             cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
545         }
546
547         if target.starts_with("riscv") && !target.contains("freebsd") {
548             // RISC-V GCC erroneously requires linking against
549             // `libatomic` when using 1-byte and 2-byte C++
550             // atomics but the LLVM build system check cannot
551             // detect this. Therefore it is set manually here.
552             // FreeBSD uses Clang as its system compiler and
553             // provides no libatomic in its base system so does
554             // not want this.
555             ldflags.exe.push(" -latomic");
556             ldflags.shared.push(" -latomic");
557         }
558
559         if target.contains("msvc") {
560             cfg.define("LLVM_USE_CRT_DEBUG", "MT");
561             cfg.define("LLVM_USE_CRT_RELEASE", "MT");
562             cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
563             cfg.static_crt(true);
564         }
565
566         if target.starts_with("i686") {
567             cfg.define("LLVM_BUILD_32_BITS", "ON");
568         }
569
570         let mut enabled_llvm_projects = Vec::new();
571
572         if util::forcing_clang_based_tests() {
573             enabled_llvm_projects.push("clang");
574             enabled_llvm_projects.push("compiler-rt");
575         }
576
577         if builder.config.llvm_polly {
578             enabled_llvm_projects.push("polly");
579         }
580
581         if builder.config.llvm_clang {
582             enabled_llvm_projects.push("clang");
583         }
584
585         // We want libxml to be disabled.
586         // See https://github.com/rust-lang/rust/pull/50104
587         cfg.define("LLVM_ENABLE_LIBXML2", "OFF");
588
589         if !enabled_llvm_projects.is_empty() {
590             enabled_llvm_projects.sort();
591             enabled_llvm_projects.dedup();
592             cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
593         }
594
595         if let Some(num_linkers) = builder.config.llvm_link_jobs {
596             if num_linkers > 0 {
597                 cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
598             }
599         }
600
601         // Workaround for ppc32 lld limitation
602         if target == "powerpc-unknown-freebsd" {
603             ldflags.exe.push(" -fuse-ld=bfd");
604         }
605
606         // https://llvm.org/docs/HowToCrossCompileLLVM.html
607         if target != builder.config.build {
608             builder.ensure(Llvm { target: builder.config.build });
609             // FIXME: if the llvm root for the build triple is overridden then we
610             //        should use llvm-tblgen from there, also should verify that it
611             //        actually exists most of the time in normal installs of LLVM.
612             let host_bin = builder.llvm_out(builder.config.build).join("bin");
613             cfg.define("CMAKE_CROSSCOMPILING", "True");
614             cfg.define("LLVM_TABLEGEN", host_bin.join("llvm-tblgen").with_extension(EXE_EXTENSION));
615             cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION));
616             cfg.define(
617                 "LLVM_CONFIG_PATH",
618                 host_bin.join("llvm-config").with_extension(EXE_EXTENSION),
619             );
620         }
621
622         if let Some(ref suffix) = builder.config.llvm_version_suffix {
623             // Allow version-suffix="" to not define a version suffix at all.
624             if !suffix.is_empty() {
625                 cfg.define("LLVM_VERSION_SUFFIX", suffix);
626             }
627         } else if builder.config.channel == "dev" {
628             // Changes to a version suffix require a complete rebuild of the LLVM.
629             // To avoid rebuilds during a time of version bump, don't include rustc
630             // release number on the dev channel.
631             cfg.define("LLVM_VERSION_SUFFIX", "-rust-dev");
632         } else {
633             let suffix = format!("-rust-{}-{}", builder.version, builder.config.channel);
634             cfg.define("LLVM_VERSION_SUFFIX", suffix);
635         }
636
637         if let Some(ref linker) = builder.config.llvm_use_linker {
638             cfg.define("LLVM_USE_LINKER", linker);
639         }
640
641         if builder.config.llvm_allow_old_toolchain {
642             cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
643         }
644
645         configure_cmake(builder, target, &mut cfg, true, ldflags);
646
647         for (key, val) in &builder.config.llvm_build_config {
648             cfg.define(key, val);
649         }
650
651         // FIXME: we don't actually need to build all LLVM tools and all LLVM
652         //        libraries here, e.g., we just want a few components and a few
653         //        tools. Figure out how to filter them down and only build the right
654         //        tools and libs on all platforms.
655
656         if builder.config.dry_run {
657             return build_llvm_config;
658         }
659
660         cfg.build();
661
662         t!(stamp.write());
663
664         build_llvm_config
665     }
666 }
667
668 fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
669     if !builder.config.llvm_version_check {
670         return;
671     }
672
673     if builder.config.dry_run {
674         return;
675     }
676
677     let mut cmd = Command::new(llvm_config);
678     let version = output(cmd.arg("--version"));
679     let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
680     if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) {
681         if major >= 12 {
682             return;
683         }
684     }
685     panic!("\n\nbad LLVM version: {}, need >=12.0\n\n", version)
686 }
687
688 fn configure_cmake(
689     builder: &Builder<'_>,
690     target: TargetSelection,
691     cfg: &mut cmake::Config,
692     use_compiler_launcher: bool,
693     mut ldflags: LdFlags,
694 ) {
695     // Do not print installation messages for up-to-date files.
696     // LLVM and LLD builds can produce a lot of those and hit CI limits on log size.
697     cfg.define("CMAKE_INSTALL_MESSAGE", "LAZY");
698
699     // Do not allow the user's value of DESTDIR to influence where
700     // LLVM will install itself. LLVM must always be installed in our
701     // own build directories.
702     cfg.env("DESTDIR", "");
703
704     if builder.ninja() {
705         cfg.generator("Ninja");
706     }
707     cfg.target(&target.triple).host(&builder.config.build.triple);
708
709     if target != builder.config.build {
710         if target.contains("netbsd") {
711             cfg.define("CMAKE_SYSTEM_NAME", "NetBSD");
712         } else if target.contains("freebsd") {
713             cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
714         } else if target.contains("windows") {
715             cfg.define("CMAKE_SYSTEM_NAME", "Windows");
716         } else if target.contains("haiku") {
717             cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
718         } else if target.contains("solaris") || target.contains("illumos") {
719             cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
720         }
721         // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in
722         // that case like CMake we cannot easily determine system version either.
723         //
724         // Since, the LLVM itself makes rather limited use of version checks in
725         // CMakeFiles (and then only in tests), and so far no issues have been
726         // reported, the system version is currently left unset.
727     }
728
729     let sanitize_cc = |cc: &Path| {
730         if target.contains("msvc") {
731             OsString::from(cc.to_str().unwrap().replace("\\", "/"))
732         } else {
733             cc.as_os_str().to_owned()
734         }
735     };
736
737     // MSVC with CMake uses msbuild by default which doesn't respect these
738     // vars that we'd otherwise configure. In that case we just skip this
739     // entirely.
740     if target.contains("msvc") && !builder.ninja() {
741         return;
742     }
743
744     let (cc, cxx) = match builder.config.llvm_clang_cl {
745         Some(ref cl) => (cl.as_ref(), cl.as_ref()),
746         None => (builder.cc(target), builder.cxx(target).unwrap()),
747     };
748
749     // Handle msvc + ninja + ccache specially (this is what the bots use)
750     if target.contains("msvc") && builder.ninja() && builder.config.ccache.is_some() {
751         let mut wrap_cc = env::current_exe().expect("failed to get cwd");
752         wrap_cc.set_file_name("sccache-plus-cl.exe");
753
754         cfg.define("CMAKE_C_COMPILER", sanitize_cc(&wrap_cc))
755             .define("CMAKE_CXX_COMPILER", sanitize_cc(&wrap_cc));
756         cfg.env("SCCACHE_PATH", builder.config.ccache.as_ref().unwrap())
757             .env("SCCACHE_TARGET", target.triple)
758             .env("SCCACHE_CC", &cc)
759             .env("SCCACHE_CXX", &cxx);
760
761         // Building LLVM on MSVC can be a little ludicrous at times. We're so far
762         // off the beaten path here that I'm not really sure this is even half
763         // supported any more. Here we're trying to:
764         //
765         // * Build LLVM on MSVC
766         // * Build LLVM with `clang-cl` instead of `cl.exe`
767         // * Build a project with `sccache`
768         // * Build for 32-bit as well
769         // * Build with Ninja
770         //
771         // For `cl.exe` there are different binaries to compile 32/64 bit which
772         // we use but for `clang-cl` there's only one which internally
773         // multiplexes via flags. As a result it appears that CMake's detection
774         // of a compiler's architecture and such on MSVC **doesn't** pass any
775         // custom flags we pass in CMAKE_CXX_FLAGS below. This means that if we
776         // use `clang-cl.exe` it's always diagnosed as a 64-bit compiler which
777         // definitely causes problems since all the env vars are pointing to
778         // 32-bit libraries.
779         //
780         // To hack around this... again... we pass an argument that's
781         // unconditionally passed in the sccache shim. This'll get CMake to
782         // correctly diagnose it's doing a 32-bit compilation and LLVM will
783         // internally configure itself appropriately.
784         if builder.config.llvm_clang_cl.is_some() && target.contains("i686") {
785             cfg.env("SCCACHE_EXTRA_ARGS", "-m32");
786         }
787     } else {
788         // If ccache is configured we inform the build a little differently how
789         // to invoke ccache while also invoking our compilers.
790         if use_compiler_launcher {
791             if let Some(ref ccache) = builder.config.ccache {
792                 cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
793                     .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
794             }
795         }
796         cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc))
797             .define("CMAKE_CXX_COMPILER", sanitize_cc(cxx))
798             .define("CMAKE_ASM_COMPILER", sanitize_cc(cc));
799     }
800
801     cfg.build_arg("-j").build_arg(builder.jobs().to_string());
802     let mut cflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::C).join(" ").into();
803     if let Some(ref s) = builder.config.llvm_cflags {
804         cflags.push(" ");
805         cflags.push(s);
806     }
807     // Some compiler features used by LLVM (such as thread locals) will not work on a min version below iOS 10.
808     if target.contains("apple-ios") {
809         if target.contains("86-") {
810             cflags.push(" -miphonesimulator-version-min=10.0");
811         } else {
812             cflags.push(" -miphoneos-version-min=10.0");
813         }
814     }
815     if builder.config.llvm_clang_cl.is_some() {
816         cflags.push(&format!(" --target={}", target));
817     }
818     cfg.define("CMAKE_C_FLAGS", cflags);
819     let mut cxxflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::Cxx).join(" ").into();
820     if let Some(ref s) = builder.config.llvm_cxxflags {
821         cxxflags.push(" ");
822         cxxflags.push(s);
823     }
824     if builder.config.llvm_clang_cl.is_some() {
825         cxxflags.push(&format!(" --target={}", target));
826     }
827     cfg.define("CMAKE_CXX_FLAGS", cxxflags);
828     if let Some(ar) = builder.ar(target) {
829         if ar.is_absolute() {
830             // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
831             // tries to resolve this path in the LLVM build directory.
832             cfg.define("CMAKE_AR", sanitize_cc(ar));
833         }
834     }
835
836     if let Some(ranlib) = builder.ranlib(target) {
837         if ranlib.is_absolute() {
838             // LLVM build breaks if `CMAKE_RANLIB` is a relative path, for some reason it
839             // tries to resolve this path in the LLVM build directory.
840             cfg.define("CMAKE_RANLIB", sanitize_cc(ranlib));
841         }
842     }
843
844     if let Some(ref flags) = builder.config.llvm_ldflags {
845         ldflags.push_all(flags);
846     }
847
848     if let Some(flags) = get_var("LDFLAGS", &builder.config.build.triple, &target.triple) {
849         ldflags.push_all(&flags);
850     }
851
852     // For distribution we want the LLVM tools to be *statically* linked to libstdc++.
853     // We also do this if the user explicitly requested static libstdc++.
854     if builder.config.llvm_static_stdcpp {
855         if !target.contains("msvc") && !target.contains("netbsd") {
856             if target.contains("apple") || target.contains("windows") {
857                 ldflags.push_all("-static-libstdc++");
858             } else {
859                 ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
860             }
861         }
862     }
863
864     cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared);
865     cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module);
866     cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe);
867
868     if env::var_os("SCCACHE_ERROR_LOG").is_some() {
869         cfg.env("RUSTC_LOG", "sccache=warn");
870     }
871 }
872
873 // Adapted from https://github.com/alexcrichton/cc-rs/blob/fba7feded71ee4f63cfe885673ead6d7b4f2f454/src/lib.rs#L2347-L2365
874 fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
875     let kind = if host == target { "HOST" } else { "TARGET" };
876     let target_u = target.replace("-", "_");
877     env::var_os(&format!("{}_{}", var_base, target))
878         .or_else(|| env::var_os(&format!("{}_{}", var_base, target_u)))
879         .or_else(|| env::var_os(&format!("{}_{}", kind, var_base)))
880         .or_else(|| env::var_os(var_base))
881 }
882
883 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
884 pub struct Lld {
885     pub target: TargetSelection,
886 }
887
888 impl Step for Lld {
889     type Output = PathBuf;
890     const ONLY_HOSTS: bool = true;
891
892     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
893         run.path("src/llvm-project/lld")
894     }
895
896     fn make_run(run: RunConfig<'_>) {
897         run.builder.ensure(Lld { target: run.target });
898     }
899
900     /// Compile LLD for `target`.
901     fn run(self, builder: &Builder<'_>) -> PathBuf {
902         if builder.config.dry_run {
903             return PathBuf::from("lld-out-dir-test-gen");
904         }
905         let target = self.target;
906
907         let llvm_config = builder.ensure(Llvm { target: self.target });
908
909         let out_dir = builder.lld_out(target);
910         let done_stamp = out_dir.join("lld-finished-building");
911         if done_stamp.exists() {
912             return out_dir;
913         }
914
915         builder.info(&format!("Building LLD for {}", target));
916         let _time = util::timeit(&builder);
917         t!(fs::create_dir_all(&out_dir));
918
919         let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
920         configure_cmake(builder, target, &mut cfg, true, LdFlags::default());
921
922         // This is an awful, awful hack. Discovered when we migrated to using
923         // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
924         // tree, will execute `llvm-config --cmakedir` and then tell CMake about
925         // that directory for later processing. Unfortunately if this path has
926         // forward slashes in it (which it basically always does on Windows)
927         // then CMake will hit a syntax error later on as... something isn't
928         // escaped it seems?
929         //
930         // Instead of attempting to fix this problem in upstream CMake and/or
931         // LLVM/LLD we just hack around it here. This thin wrapper will take the
932         // output from llvm-config and replace all instances of `\` with `/` to
933         // ensure we don't hit the same bugs with escaping. It means that you
934         // can't build on a system where your paths require `\` on Windows, but
935         // there's probably a lot of reasons you can't do that other than this.
936         let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper");
937
938         // Re-use the same flags as llvm to control the level of debug information
939         // generated for lld.
940         let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
941             (false, _) => "Debug",
942             (true, false) => "Release",
943             (true, true) => "RelWithDebInfo",
944         };
945
946         cfg.out_dir(&out_dir)
947             .profile(profile)
948             .env("LLVM_CONFIG_REAL", &llvm_config)
949             .define("LLVM_CONFIG_PATH", llvm_config_shim)
950             .define("LLVM_INCLUDE_TESTS", "OFF");
951
952         // While we're using this horrible workaround to shim the execution of
953         // llvm-config, let's just pile on more. I can't seem to figure out how
954         // to build LLD as a standalone project and also cross-compile it at the
955         // same time. It wants a natively executable `llvm-config` to learn
956         // about LLVM, but then it learns about all the host configuration of
957         // LLVM and tries to link to host LLVM libraries.
958         //
959         // To work around that we tell our shim to replace anything with the
960         // build target with the actual target instead. This'll break parts of
961         // LLD though which try to execute host tools, such as llvm-tblgen, so
962         // we specifically tell it where to find those. This is likely super
963         // brittle and will break over time. If anyone knows better how to
964         // cross-compile LLD it would be much appreciated to fix this!
965         if target != builder.config.build {
966             cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build.triple)
967                 .env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target.triple)
968                 .define(
969                     "LLVM_TABLEGEN_EXE",
970                     llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
971                 );
972         }
973
974         // Explicitly set C++ standard, because upstream doesn't do so
975         // for standalone builds.
976         cfg.define("CMAKE_CXX_STANDARD", "14");
977
978         cfg.build();
979
980         t!(File::create(&done_stamp));
981         out_dir
982     }
983 }
984
985 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
986 pub struct TestHelpers {
987     pub target: TargetSelection,
988 }
989
990 impl Step for TestHelpers {
991     type Output = ();
992
993     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
994         run.path("src/test/auxiliary/rust_test_helpers.c")
995     }
996
997     fn make_run(run: RunConfig<'_>) {
998         run.builder.ensure(TestHelpers { target: run.target })
999     }
1000
1001     /// Compiles the `rust_test_helpers.c` library which we used in various
1002     /// `run-pass` tests for ABI testing.
1003     fn run(self, builder: &Builder<'_>) {
1004         if builder.config.dry_run {
1005             return;
1006         }
1007         // The x86_64-fortanix-unknown-sgx target doesn't have a working C
1008         // toolchain. However, some x86_64 ELF objects can be linked
1009         // without issues. Use this hack to compile the test helpers.
1010         let target = if self.target == "x86_64-fortanix-unknown-sgx" {
1011             TargetSelection::from_user("x86_64-unknown-linux-gnu")
1012         } else {
1013             self.target
1014         };
1015         let dst = builder.test_helpers_out(target);
1016         let src = builder.src.join("src/test/auxiliary/rust_test_helpers.c");
1017         if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
1018             return;
1019         }
1020
1021         builder.info("Building test helpers");
1022         t!(fs::create_dir_all(&dst));
1023         let mut cfg = cc::Build::new();
1024         // FIXME: Workaround for https://github.com/emscripten-core/emscripten/issues/9013
1025         if target.contains("emscripten") {
1026             cfg.pic(false);
1027         }
1028
1029         // We may have found various cross-compilers a little differently due to our
1030         // extra configuration, so inform cc of these compilers. Note, though, that
1031         // on MSVC we still need cc's detection of env vars (ugh).
1032         if !target.contains("msvc") {
1033             if let Some(ar) = builder.ar(target) {
1034                 cfg.archiver(ar);
1035             }
1036             cfg.compiler(builder.cc(target));
1037         }
1038         cfg.cargo_metadata(false)
1039             .out_dir(&dst)
1040             .target(&target.triple)
1041             .host(&builder.config.build.triple)
1042             .opt_level(0)
1043             .warnings(false)
1044             .debug(false)
1045             .file(builder.src.join("src/test/auxiliary/rust_test_helpers.c"))
1046             .compile("rust_test_helpers");
1047     }
1048 }
1049
1050 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1051 pub struct Sanitizers {
1052     pub target: TargetSelection,
1053 }
1054
1055 impl Step for Sanitizers {
1056     type Output = Vec<SanitizerRuntime>;
1057
1058     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1059         run.alias("sanitizers")
1060     }
1061
1062     fn make_run(run: RunConfig<'_>) {
1063         run.builder.ensure(Sanitizers { target: run.target });
1064     }
1065
1066     /// Builds sanitizer runtime libraries.
1067     fn run(self, builder: &Builder<'_>) -> Self::Output {
1068         let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
1069         if !compiler_rt_dir.exists() {
1070             return Vec::new();
1071         }
1072
1073         let out_dir = builder.native_dir(self.target).join("sanitizers");
1074         let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel);
1075         if runtimes.is_empty() {
1076             return runtimes;
1077         }
1078
1079         let llvm_config = builder.ensure(Llvm { target: builder.config.build });
1080         if builder.config.dry_run {
1081             return runtimes;
1082         }
1083
1084         let stamp = out_dir.join("sanitizers-finished-building");
1085         let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
1086
1087         if stamp.is_done() {
1088             if stamp.hash.is_none() {
1089                 builder.info(&format!(
1090                     "Rebuild sanitizers by removing the file `{}`",
1091                     stamp.path.display()
1092                 ));
1093             }
1094             return runtimes;
1095         }
1096
1097         builder.info(&format!("Building sanitizers for {}", self.target));
1098         t!(stamp.remove());
1099         let _time = util::timeit(&builder);
1100
1101         let mut cfg = cmake::Config::new(&compiler_rt_dir);
1102         cfg.profile("Release");
1103         cfg.define("CMAKE_C_COMPILER_TARGET", self.target.triple);
1104         cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
1105         cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
1106         cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
1107         cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
1108         cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
1109         cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
1110         cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
1111         cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
1112         cfg.define("LLVM_CONFIG_PATH", &llvm_config);
1113
1114         // On Darwin targets the sanitizer runtimes are build as universal binaries.
1115         // Unfortunately sccache currently lacks support to build them successfully.
1116         // Disable compiler launcher on Darwin targets to avoid potential issues.
1117         let use_compiler_launcher = !self.target.contains("apple-darwin");
1118         configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher, LdFlags::default());
1119
1120         t!(fs::create_dir_all(&out_dir));
1121         cfg.out_dir(out_dir);
1122
1123         for runtime in &runtimes {
1124             cfg.build_target(&runtime.cmake_target);
1125             cfg.build();
1126         }
1127         t!(stamp.write());
1128
1129         runtimes
1130     }
1131 }
1132
1133 #[derive(Clone, Debug)]
1134 pub struct SanitizerRuntime {
1135     /// CMake target used to build the runtime.
1136     pub cmake_target: String,
1137     /// Path to the built runtime library.
1138     pub path: PathBuf,
1139     /// Library filename that will be used rustc.
1140     pub name: String,
1141 }
1142
1143 /// Returns sanitizers available on a given target.
1144 fn supported_sanitizers(
1145     out_dir: &Path,
1146     target: TargetSelection,
1147     channel: &str,
1148 ) -> Vec<SanitizerRuntime> {
1149     let darwin_libs = |os: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1150         components
1151             .iter()
1152             .map(move |c| SanitizerRuntime {
1153                 cmake_target: format!("clang_rt.{}_{}_dynamic", c, os),
1154                 path: out_dir
1155                     .join(&format!("build/lib/darwin/libclang_rt.{}_{}_dynamic.dylib", c, os)),
1156                 name: format!("librustc-{}_rt.{}.dylib", channel, c),
1157             })
1158             .collect()
1159     };
1160
1161     let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1162         components
1163             .iter()
1164             .map(move |c| SanitizerRuntime {
1165                 cmake_target: format!("clang_rt.{}-{}", c, arch),
1166                 path: out_dir.join(&format!("build/lib/{}/libclang_rt.{}-{}.a", os, c, arch)),
1167                 name: format!("librustc-{}_rt.{}.a", channel, c),
1168             })
1169             .collect()
1170     };
1171
1172     match &*target.triple {
1173         "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1174         "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
1175         "aarch64-unknown-linux-gnu" => {
1176             common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
1177         }
1178         "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1179         "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
1180         "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]),
1181         "x86_64-unknown-netbsd" => {
1182             common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"])
1183         }
1184         "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
1185         "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
1186         "x86_64-unknown-linux-gnu" => {
1187             common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1188         }
1189         "x86_64-unknown-linux-musl" => {
1190             common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1191         }
1192         _ => Vec::new(),
1193     }
1194 }
1195
1196 struct HashStamp {
1197     path: PathBuf,
1198     hash: Option<Vec<u8>>,
1199 }
1200
1201 impl HashStamp {
1202     fn new(path: PathBuf, hash: Option<&str>) -> Self {
1203         HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) }
1204     }
1205
1206     fn is_done(&self) -> bool {
1207         match fs::read(&self.path) {
1208             Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(),
1209             Err(e) if e.kind() == io::ErrorKind::NotFound => false,
1210             Err(e) => {
1211                 panic!("failed to read stamp file `{}`: {}", self.path.display(), e);
1212             }
1213         }
1214     }
1215
1216     fn remove(&self) -> io::Result<()> {
1217         match fs::remove_file(&self.path) {
1218             Ok(()) => Ok(()),
1219             Err(e) => {
1220                 if e.kind() == io::ErrorKind::NotFound {
1221                     Ok(())
1222                 } else {
1223                     Err(e)
1224                 }
1225             }
1226         }
1227     }
1228
1229     fn write(&self) -> io::Result<()> {
1230         fs::write(&self.path, self.hash.as_deref().unwrap_or(b""))
1231     }
1232 }
1233
1234 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1235 pub struct CrtBeginEnd {
1236     pub target: TargetSelection,
1237 }
1238
1239 impl Step for CrtBeginEnd {
1240     type Output = PathBuf;
1241
1242     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1243         run.path("src/llvm-project/compiler-rt/lib/crt")
1244     }
1245
1246     fn make_run(run: RunConfig<'_>) {
1247         run.builder.ensure(CrtBeginEnd { target: run.target });
1248     }
1249
1250     /// Build crtbegin.o/crtend.o for musl target.
1251     fn run(self, builder: &Builder<'_>) -> Self::Output {
1252         let out_dir = builder.native_dir(self.target).join("crt");
1253
1254         if builder.config.dry_run {
1255             return out_dir;
1256         }
1257
1258         let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/crt/crtbegin.c");
1259         let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/crt/crtend.c");
1260         if up_to_date(&crtbegin_src, &out_dir.join("crtbegin.o"))
1261             && up_to_date(&crtend_src, &out_dir.join("crtendS.o"))
1262         {
1263             return out_dir;
1264         }
1265
1266         builder.info("Building crtbegin.o and crtend.o");
1267         t!(fs::create_dir_all(&out_dir));
1268
1269         let mut cfg = cc::Build::new();
1270
1271         if let Some(ar) = builder.ar(self.target) {
1272             cfg.archiver(ar);
1273         }
1274         cfg.compiler(builder.cc(self.target));
1275         cfg.cargo_metadata(false)
1276             .out_dir(&out_dir)
1277             .target(&self.target.triple)
1278             .host(&builder.config.build.triple)
1279             .warnings(false)
1280             .debug(false)
1281             .opt_level(3)
1282             .file(crtbegin_src)
1283             .file(crtend_src);
1284
1285         // Those flags are defined in src/llvm-project/compiler-rt/lib/crt/CMakeLists.txt
1286         // Currently only consumer of those objects is musl, which use .init_array/.fini_array
1287         // instead of .ctors/.dtors
1288         cfg.flag("-std=c11")
1289             .define("CRT_HAS_INITFINI_ARRAY", None)
1290             .define("EH_USE_FRAME_REGISTRY", None);
1291
1292         cfg.compile("crt");
1293
1294         t!(fs::copy(out_dir.join("crtbegin.o"), out_dir.join("crtbeginS.o")));
1295         t!(fs::copy(out_dir.join("crtend.o"), out_dir.join("crtendS.o")));
1296         out_dir
1297     }
1298 }
1299
1300 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1301 pub struct Libunwind {
1302     pub target: TargetSelection,
1303 }
1304
1305 impl Step for Libunwind {
1306     type Output = PathBuf;
1307
1308     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1309         run.path("src/llvm-project/libunwind")
1310     }
1311
1312     fn make_run(run: RunConfig<'_>) {
1313         run.builder.ensure(Libunwind { target: run.target });
1314     }
1315
1316     /// Build linunwind.a
1317     fn run(self, builder: &Builder<'_>) -> Self::Output {
1318         if builder.config.dry_run {
1319             return PathBuf::new();
1320         }
1321
1322         let out_dir = builder.native_dir(self.target).join("libunwind");
1323         let root = builder.src.join("src/llvm-project/libunwind");
1324
1325         if up_to_date(&root, &out_dir.join("libunwind.a")) {
1326             return out_dir;
1327         }
1328
1329         builder.info(&format!("Building libunwind.a for {}", self.target.triple));
1330         t!(fs::create_dir_all(&out_dir));
1331
1332         let mut cc_cfg = cc::Build::new();
1333         let mut cpp_cfg = cc::Build::new();
1334
1335         cpp_cfg.cpp(true);
1336         cpp_cfg.cpp_set_stdlib(None);
1337         cpp_cfg.flag("-nostdinc++");
1338         cpp_cfg.flag("-fno-exceptions");
1339         cpp_cfg.flag("-fno-rtti");
1340         cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
1341
1342         for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() {
1343             if let Some(ar) = builder.ar(self.target) {
1344                 cfg.archiver(ar);
1345             }
1346             cfg.target(&self.target.triple);
1347             cfg.host(&builder.config.build.triple);
1348             cfg.warnings(false);
1349             cfg.debug(false);
1350             // get_compiler() need set opt_level first.
1351             cfg.opt_level(3);
1352             cfg.flag("-fstrict-aliasing");
1353             cfg.flag("-funwind-tables");
1354             cfg.flag("-fvisibility=hidden");
1355             cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
1356             cfg.include(root.join("include"));
1357             cfg.cargo_metadata(false);
1358             cfg.out_dir(&out_dir);
1359
1360             if self.target.contains("x86_64-fortanix-unknown-sgx") {
1361                 cfg.static_flag(true);
1362                 cfg.flag("-fno-stack-protector");
1363                 cfg.flag("-ffreestanding");
1364                 cfg.flag("-fexceptions");
1365
1366                 // easiest way to undefine since no API available in cc::Build to undefine
1367                 cfg.flag("-U_FORTIFY_SOURCE");
1368                 cfg.define("_FORTIFY_SOURCE", "0");
1369                 cfg.define("RUST_SGX", "1");
1370                 cfg.define("__NO_STRING_INLINES", None);
1371                 cfg.define("__NO_MATH_INLINES", None);
1372                 cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
1373                 cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None);
1374                 cfg.define("NDEBUG", None);
1375             }
1376             if self.target.contains("windows") {
1377                 cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
1378                 cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
1379             }
1380         }
1381
1382         cc_cfg.compiler(builder.cc(self.target));
1383         if let Ok(cxx) = builder.cxx(self.target) {
1384             cpp_cfg.compiler(cxx);
1385         } else {
1386             cc_cfg.compiler(builder.cc(self.target));
1387         }
1388
1389         // Don't set this for clang
1390         // By default, Clang builds C code in GNU C17 mode.
1391         // By default, Clang builds C++ code according to the C++98 standard,
1392         // with many C++11 features accepted as extensions.
1393         if cc_cfg.get_compiler().is_like_gnu() {
1394             cc_cfg.flag("-std=c99");
1395         }
1396         if cpp_cfg.get_compiler().is_like_gnu() {
1397             cpp_cfg.flag("-std=c++11");
1398         }
1399
1400         if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") {
1401             // use the same GCC C compiler command to compile C++ code so we do not need to setup the
1402             // C++ compiler env variables on the builders.
1403             // Don't set this for clang++, as clang++ is able to compile this without libc++.
1404             if cpp_cfg.get_compiler().is_like_gnu() {
1405                 cpp_cfg.cpp(false);
1406                 cpp_cfg.compiler(builder.cc(self.target));
1407             }
1408         }
1409
1410         let mut c_sources = vec![
1411             "Unwind-sjlj.c",
1412             "UnwindLevel1-gcc-ext.c",
1413             "UnwindLevel1.c",
1414             "UnwindRegistersRestore.S",
1415             "UnwindRegistersSave.S",
1416         ];
1417
1418         let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"];
1419         let cpp_len = cpp_sources.len();
1420
1421         if self.target.contains("x86_64-fortanix-unknown-sgx") {
1422             c_sources.push("UnwindRustSgx.c");
1423         }
1424
1425         for src in c_sources {
1426             cc_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1427         }
1428
1429         for src in &cpp_sources {
1430             cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1431         }
1432
1433         cpp_cfg.compile("unwind-cpp");
1434
1435         // FIXME: https://github.com/alexcrichton/cc-rs/issues/545#issuecomment-679242845
1436         let mut count = 0;
1437         for entry in fs::read_dir(&out_dir).unwrap() {
1438             let file = entry.unwrap().path().canonicalize().unwrap();
1439             if file.is_file() && file.extension() == Some(OsStr::new("o")) {
1440                 // file name starts with "Unwind-EHABI", "Unwind-seh" or "libunwind"
1441                 let file_name = file.file_name().unwrap().to_str().expect("UTF-8 file name");
1442                 if cpp_sources.iter().any(|f| file_name.starts_with(&f[..f.len() - 4])) {
1443                     cc_cfg.object(&file);
1444                     count += 1;
1445                 }
1446             }
1447         }
1448         assert_eq!(cpp_len, count, "Can't get object files from {:?}", &out_dir);
1449
1450         cc_cfg.compile("unwind");
1451         out_dir
1452     }
1453 }