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