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