1use std::env::consts::EXE_EXTENSION;
12use std::ffi::{OsStr, OsString};
13use std::path::{Path, PathBuf};
14use std::sync::OnceLock;
15use std::{env, fs};
16
17use build_helper::exit;
18use build_helper::git::PathFreshness;
19
20use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata};
21use crate::core::config::{Config, TargetSelection};
22use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
23use crate::utils::exec::command;
24use crate::utils::helpers::{
25 self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date,
26};
27use crate::{CLang, GitRepo, Kind, trace};
28
29#[derive(Clone)]
30pub struct LlvmResult {
31 pub host_llvm_config: PathBuf,
34 pub llvm_cmake_dir: PathBuf,
36}
37
38pub struct Meta {
39 stamp: BuildStamp,
40 res: LlvmResult,
41 out_dir: PathBuf,
42 root: String,
43}
44
45pub enum LlvmBuildStatus {
46 AlreadyBuilt(LlvmResult),
47 ShouldBuild(Meta),
48}
49
50impl LlvmBuildStatus {
51 pub fn should_build(&self) -> bool {
52 match self {
53 LlvmBuildStatus::AlreadyBuilt(_) => false,
54 LlvmBuildStatus::ShouldBuild(_) => true,
55 }
56 }
57
58 #[cfg(test)]
59 pub fn llvm_result(&self) -> &LlvmResult {
60 match self {
61 LlvmBuildStatus::AlreadyBuilt(res) => res,
62 LlvmBuildStatus::ShouldBuild(meta) => &meta.res,
63 }
64 }
65}
66
67#[derive(Debug, Clone, Default)]
69struct CcFlags {
70 cflags: OsString,
72 cxxflags: OsString,
74}
75
76impl CcFlags {
77 fn push_all(&mut self, s: impl AsRef<OsStr>) {
78 let s = s.as_ref();
79 self.cflags.push(" ");
80 self.cflags.push(s);
81 self.cxxflags.push(" ");
82 self.cxxflags.push(s);
83 }
84}
85
86#[derive(Debug, Clone, Default)]
88struct LdFlags {
89 exe: OsString,
91 shared: OsString,
93 module: OsString,
95}
96
97impl LdFlags {
98 fn push_all(&mut self, s: impl AsRef<OsStr>) {
99 let s = s.as_ref();
100 self.exe.push(" ");
101 self.exe.push(s);
102 self.shared.push(" ");
103 self.shared.push(s);
104 self.module.push(" ");
105 self.module.push(s);
106 }
107}
108
109pub fn prebuilt_llvm_config(
117 builder: &Builder<'_>,
118 target: TargetSelection,
119 handle_submodule_when_needed: bool,
123) -> LlvmBuildStatus {
124 builder.config.maybe_download_ci_llvm();
125
126 if let Some(config) = builder.config.target_config.get(&target)
129 && let Some(ref s) = config.llvm_config
130 {
131 check_llvm_version(builder, s);
132 let host_llvm_config = s.to_path_buf();
133 let mut llvm_cmake_dir = host_llvm_config.clone();
134 llvm_cmake_dir.pop();
135 llvm_cmake_dir.pop();
136 llvm_cmake_dir.push("lib");
137 llvm_cmake_dir.push("cmake");
138 llvm_cmake_dir.push("llvm");
139 return LlvmBuildStatus::AlreadyBuilt(LlvmResult { host_llvm_config, llvm_cmake_dir });
140 }
141
142 if handle_submodule_when_needed {
143 builder.config.update_submodule("src/llvm-project");
145 }
146
147 let root = "src/llvm-project/llvm";
148 let out_dir = builder.llvm_out(target);
149
150 let build_llvm_config = if let Some(build_llvm_config) = builder
151 .config
152 .target_config
153 .get(&builder.config.host_target)
154 .and_then(|config| config.llvm_config.clone())
155 {
156 build_llvm_config
157 } else {
158 let mut llvm_config_ret_dir = builder.llvm_out(builder.config.host_target);
159 llvm_config_ret_dir.push("bin");
160 llvm_config_ret_dir.join(exe("llvm-config", builder.config.host_target))
161 };
162
163 let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
164 let res = LlvmResult { host_llvm_config: build_llvm_config, llvm_cmake_dir };
165
166 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
167 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
168 generate_smart_stamp_hash(
169 builder,
170 &builder.config.src.join("src/llvm-project"),
171 builder.in_tree_llvm_info.sha().unwrap_or_default(),
172 )
173 });
174
175 let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash);
176
177 if stamp.is_up_to_date() {
178 if stamp.stamp().is_empty() {
179 builder.info(
180 "Could not determine the LLVM submodule commit hash. \
181 Assuming that an LLVM rebuild is not necessary.",
182 );
183 builder.info(&format!(
184 "To force LLVM to rebuild, remove the file `{}`",
185 stamp.path().display()
186 ));
187 }
188 return LlvmBuildStatus::AlreadyBuilt(res);
189 }
190
191 LlvmBuildStatus::ShouldBuild(Meta { stamp, res, out_dir, root: root.into() })
192}
193
194pub const LLVM_INVALIDATION_PATHS: &[&str] = &[
196 "src/llvm-project",
197 "src/bootstrap/download-ci-llvm-stamp",
198 "src/version",
200];
201
202pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshness {
204 if is_git {
205 config.check_path_modifications(LLVM_INVALIDATION_PATHS)
206 } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
207 PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() }
208 } else {
209 PathFreshness::MissingUpstream
210 }
211}
212
213pub(crate) fn is_ci_llvm_available_for_target(
218 host_target: &TargetSelection,
219 asserts: bool,
220) -> bool {
221 let supported_platforms = [
225 ("aarch64-unknown-linux-gnu", false),
227 ("aarch64-apple-darwin", false),
228 ("aarch64-pc-windows-msvc", false),
229 ("i686-pc-windows-msvc", false),
230 ("i686-unknown-linux-gnu", false),
231 ("x86_64-unknown-linux-gnu", true),
232 ("x86_64-apple-darwin", true),
233 ("x86_64-pc-windows-gnu", false),
234 ("x86_64-pc-windows-msvc", true),
235 ("aarch64-unknown-linux-musl", false),
237 ("aarch64-pc-windows-gnullvm", false),
238 ("arm-unknown-linux-gnueabi", false),
239 ("arm-unknown-linux-gnueabihf", false),
240 ("armv7-unknown-linux-gnueabihf", false),
241 ("i686-pc-windows-gnu", false),
242 ("loongarch64-unknown-linux-gnu", false),
243 ("loongarch64-unknown-linux-musl", false),
244 ("powerpc-unknown-linux-gnu", false),
245 ("powerpc64-unknown-linux-gnu", false),
246 ("powerpc64le-unknown-linux-gnu", false),
247 ("powerpc64le-unknown-linux-musl", false),
248 ("riscv64gc-unknown-linux-gnu", false),
249 ("s390x-unknown-linux-gnu", false),
250 ("x86_64-pc-windows-gnullvm", false),
251 ("x86_64-unknown-freebsd", false),
252 ("x86_64-unknown-illumos", false),
253 ("x86_64-unknown-linux-musl", false),
254 ("x86_64-unknown-netbsd", false),
255 ];
256
257 if !supported_platforms.contains(&(&*host_target.triple, asserts))
258 && (asserts || !supported_platforms.contains(&(&*host_target.triple, true)))
259 {
260 return false;
261 }
262
263 true
264}
265
266#[derive(Debug, Clone, Hash, PartialEq, Eq)]
267pub struct Llvm {
268 pub target: TargetSelection,
269}
270
271impl Step for Llvm {
272 type Output = LlvmResult;
273
274 const IS_HOST: bool = true;
275
276 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
277 run.path("src/llvm-project").path("src/llvm-project/llvm")
278 }
279
280 fn make_run(run: RunConfig<'_>) {
281 run.builder.ensure(Llvm { target: run.target });
282 }
283
284 fn run(self, builder: &Builder<'_>) -> LlvmResult {
286 let target = self.target;
287 let target_native = if self.target.starts_with("riscv") {
288 let idx = target.triple.find('-').unwrap();
291
292 format!("riscv{}{}", &target.triple[5..7], &target.triple[idx..])
293 } else if self.target.starts_with("powerpc") && self.target.ends_with("freebsd") {
294 format!("{}{}", self.target, "13.0")
297 } else {
298 target.to_string()
299 };
300
301 let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target, true) {
303 LlvmBuildStatus::AlreadyBuilt(p) => return p,
304 LlvmBuildStatus::ShouldBuild(m) => m,
305 };
306
307 if builder.llvm_link_shared() && target.is_windows() && !target.is_windows_gnullvm() {
308 panic!("shared linking to LLVM is not currently supported on {}", target.triple);
309 }
310
311 let _guard = builder.msg_unstaged(Kind::Build, "LLVM", target);
312 t!(stamp.remove());
313 let _time = helpers::timeit(builder);
314 t!(fs::create_dir_all(&out_dir));
315
316 let mut cfg = cmake::Config::new(builder.src.join(root));
318 let mut ldflags = LdFlags::default();
319
320 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
321 (false, _) => "Debug",
322 (true, false) => "Release",
323 (true, true) => "RelWithDebInfo",
324 };
325
326 let llvm_targets = match &builder.config.llvm_targets {
329 Some(s) => s,
330 None => {
331 "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
332 Sparc;SystemZ;WebAssembly;X86"
333 }
334 };
335
336 let llvm_exp_targets = match builder.config.llvm_experimental_targets {
337 Some(ref s) => s,
338 None => "AVR;M68k;CSKY;Xtensa",
339 };
340
341 let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" };
342 let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" };
343 let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" };
344 let enable_warnings = if builder.config.llvm_enable_warnings { "ON" } else { "OFF" };
345
346 cfg.out_dir(&out_dir)
347 .profile(profile)
348 .define("LLVM_ENABLE_ASSERTIONS", assertions)
349 .define("LLVM_UNREACHABLE_OPTIMIZE", "OFF")
350 .define("LLVM_ENABLE_PLUGINS", plugins)
351 .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
352 .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
353 .define("LLVM_INCLUDE_EXAMPLES", "OFF")
354 .define("LLVM_INCLUDE_DOCS", "OFF")
355 .define("LLVM_INCLUDE_BENCHMARKS", "OFF")
356 .define("LLVM_INCLUDE_TESTS", enable_tests)
357 .define("LLVM_ENABLE_LIBEDIT", "OFF")
358 .define("LLVM_ENABLE_BINDINGS", "OFF")
359 .define("LLVM_ENABLE_Z3_SOLVER", "OFF")
360 .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string())
361 .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap())
362 .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native)
363 .define("LLVM_ENABLE_WARNINGS", enable_warnings);
364
365 cfg.define("LLVM_INSTALL_UTILS", "ON");
369
370 if builder.config.llvm_profile_generate {
371 cfg.define("LLVM_BUILD_INSTRUMENTED", "IR");
372 if let Ok(llvm_profile_dir) = std::env::var("LLVM_PROFILE_DIR") {
373 cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir);
374 }
375 cfg.define("LLVM_BUILD_RUNTIME", "No");
376 }
377 if let Some(path) = builder.config.llvm_profile_use.as_ref() {
378 cfg.define("LLVM_PROFDATA_FILE", path);
379 }
380
381 if !target.is_msvc() {
383 cfg.define("LLVM_ENABLE_ZLIB", "ON");
384 } else {
385 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
386 }
387
388 if target.contains("apple-ios")
390 || target.contains("apple-tvos")
391 || target.contains("apple-watchos")
392 || target.contains("apple-visionos")
393 {
394 cfg.define("LLVM_ENABLE_PLUGINS", "OFF");
396 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
398 }
399
400 if builder.llvm_link_shared() {
405 cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
406 }
407
408 if (target.starts_with("csky")
409 || target.starts_with("riscv")
410 || target.starts_with("sparc-"))
411 && !target.contains("freebsd")
412 && !target.contains("openbsd")
413 && !target.contains("netbsd")
414 {
415 ldflags.exe.push(" -latomic");
424 ldflags.shared.push(" -latomic");
425 }
426
427 if target.starts_with("mips") && target.contains("netbsd") {
428 ldflags.exe.push(" -latomic");
430 ldflags.shared.push(" -latomic");
431 }
432
433 if target.starts_with("arm64ec") {
434 ldflags.exe.push(" -machine:arm64ec");
437 ldflags.shared.push(" -machine:arm64ec");
438 }
439
440 if target.is_msvc() {
441 cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded");
442 cfg.static_crt(true);
443 }
444
445 if target.starts_with("i686") {
446 cfg.define("LLVM_BUILD_32_BITS", "ON");
447 }
448
449 if target.starts_with("x86_64") && target.contains("ohos") {
450 cfg.define("LLVM_TOOL_LLVM_RTDYLD_BUILD", "OFF");
451 }
452
453 let mut enabled_llvm_projects = Vec::new();
454
455 if helpers::forcing_clang_based_tests() {
456 enabled_llvm_projects.push("clang");
457 }
458
459 if builder.config.llvm_polly {
460 enabled_llvm_projects.push("polly");
461 }
462
463 if builder.config.llvm_clang {
464 enabled_llvm_projects.push("clang");
465 }
466
467 cfg.define("LLVM_ENABLE_LIBXML2", "OFF");
470
471 let mut enabled_llvm_runtimes = Vec::new();
472
473 if helpers::forcing_clang_based_tests() {
474 enabled_llvm_runtimes.push("compiler-rt");
475 }
476
477 if !enabled_llvm_projects.is_empty() {
478 enabled_llvm_projects.sort();
479 enabled_llvm_projects.dedup();
480 cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
481 }
482
483 if !enabled_llvm_runtimes.is_empty() {
484 enabled_llvm_runtimes.sort();
485 enabled_llvm_runtimes.dedup();
486 cfg.define("LLVM_ENABLE_RUNTIMES", enabled_llvm_runtimes.join(";"));
487 }
488
489 if let Some(num_linkers) = builder.config.llvm_link_jobs
490 && num_linkers > 0
491 {
492 cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
493 }
494
495 if !builder.config.is_host_target(target) {
497 let LlvmResult { host_llvm_config, .. } =
498 builder.ensure(Llvm { target: builder.config.host_target });
499 if !builder.config.dry_run() {
500 let llvm_bindir = command(&host_llvm_config)
501 .arg("--bindir")
502 .cached()
503 .run_capture_stdout(builder)
504 .stdout();
505 let host_bin = Path::new(llvm_bindir.trim());
506 cfg.define(
507 "LLVM_TABLEGEN",
508 host_bin.join("llvm-tblgen").with_extension(EXE_EXTENSION),
509 );
510 cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION));
512 }
513 cfg.define("LLVM_CONFIG_PATH", host_llvm_config);
514 if builder.config.llvm_clang {
515 let build_bin =
516 builder.llvm_out(builder.config.host_target).join("build").join("bin");
517 let clang_tblgen = build_bin.join("clang-tblgen").with_extension(EXE_EXTENSION);
518 if !builder.config.dry_run() && !clang_tblgen.exists() {
519 panic!("unable to find {}", clang_tblgen.display());
520 }
521 cfg.define("CLANG_TABLEGEN", clang_tblgen);
522 }
523 }
524
525 let llvm_version_suffix = if let Some(ref suffix) = builder.config.llvm_version_suffix {
526 if !suffix.is_empty() { Some(suffix.to_string()) } else { None }
528 } else if builder.config.channel == "dev" {
529 Some("-rust-dev".to_string())
533 } else {
534 Some(format!("-rust-{}-{}", builder.version, builder.config.channel))
535 };
536 if let Some(ref suffix) = llvm_version_suffix {
537 cfg.define("LLVM_VERSION_SUFFIX", suffix);
538 }
539
540 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
541 configure_llvm(builder, target, &mut cfg);
542
543 for (key, val) in &builder.config.llvm_build_config {
544 cfg.define(key, val);
545 }
546
547 if builder.config.dry_run() {
548 return res;
549 }
550
551 cfg.build();
552
553 let find_llvm_lib_name = |extension| {
555 let major = get_llvm_version_major(builder, &res.host_llvm_config);
556 match &llvm_version_suffix {
557 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
558 None => format!("libLLVM-{major}.{extension}"),
559 }
560 };
561
562 if builder.llvm_link_shared() && target.contains("apple-darwin") {
568 let lib_name = find_llvm_lib_name("dylib");
569 let lib_llvm = out_dir.join("build").join("lib").join(lib_name);
570 if !lib_llvm.exists() {
571 t!(builder.symlink_file("libLLVM.dylib", &lib_llvm));
572 }
573 }
574
575 if builder.llvm_link_shared()
579 && builder.config.llvm_optimize
580 && !builder.config.llvm_release_debuginfo
581 {
582 let lib_name = find_llvm_lib_name("so");
584
585 crate::core::build_steps::compile::strip_debug(
588 builder,
589 target,
590 &out_dir.join("lib").join(&lib_name),
591 );
592 crate::core::build_steps::compile::strip_debug(
593 builder,
594 target,
595 &out_dir.join("build").join("lib").join(&lib_name),
596 );
597 }
598
599 t!(stamp.write());
600
601 res
602 }
603
604 fn metadata(&self) -> Option<StepMetadata> {
605 Some(StepMetadata::build("llvm", self.target))
606 }
607}
608
609pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String {
610 command(llvm_config)
611 .arg("--version")
612 .cached()
613 .run_capture_stdout(builder)
614 .stdout()
615 .trim()
616 .to_owned()
617}
618
619pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 {
620 let version = get_llvm_version(builder, llvm_config);
621 let major_str = version.split_once('.').expect("Failed to parse LLVM version").0;
622 major_str.parse().unwrap()
623}
624
625fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
626 if builder.config.dry_run() {
627 return;
628 }
629
630 let version = get_llvm_version(builder, llvm_config);
631 let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
632 if let (Some(major), Some(_minor)) = (parts.next(), parts.next())
633 && major >= 20
634 {
635 return;
636 }
637 panic!("\n\nbad LLVM version: {version}, need >=20\n\n")
638}
639
640fn configure_cmake(
641 builder: &Builder<'_>,
642 target: TargetSelection,
643 cfg: &mut cmake::Config,
644 use_compiler_launcher: bool,
645 mut ldflags: LdFlags,
646 ccflags: CcFlags,
647 suppressed_compiler_flag_prefixes: &[&str],
648) {
649 cfg.define("CMAKE_INSTALL_MESSAGE", "LAZY");
652
653 cfg.env("DESTDIR", "");
657
658 if builder.ninja() {
659 cfg.generator("Ninja");
660 }
661 cfg.target(&target.triple).host(&builder.config.host_target.triple);
662
663 if !builder.config.is_host_target(target) {
664 cfg.define("CMAKE_CROSSCOMPILING", "True");
665
666 if target.contains("netbsd") {
672 cfg.define("CMAKE_SYSTEM_NAME", "NetBSD");
673 } else if target.contains("dragonfly") {
674 cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
675 } else if target.contains("openbsd") {
676 cfg.define("CMAKE_SYSTEM_NAME", "OpenBSD");
677 } else if target.contains("freebsd") {
678 cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
679 } else if target.is_windows() {
680 cfg.define("CMAKE_SYSTEM_NAME", "Windows");
681 } else if target.contains("haiku") {
682 cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
683 } else if target.contains("solaris") || target.contains("illumos") {
684 cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
685 } else if target.contains("linux") {
686 cfg.define("CMAKE_SYSTEM_NAME", "Linux");
687 } else if target.contains("darwin") {
688 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
690 } else if target.contains("ios") {
691 cfg.define("CMAKE_SYSTEM_NAME", "iOS");
692 } else if target.contains("tvos") {
693 cfg.define("CMAKE_SYSTEM_NAME", "tvOS");
694 } else if target.contains("visionos") {
695 cfg.define("CMAKE_SYSTEM_NAME", "visionOS");
696 } else if target.contains("watchos") {
697 cfg.define("CMAKE_SYSTEM_NAME", "watchOS");
698 } else if target.contains("none") {
699 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
701 } else {
702 builder.info(&format!(
703 "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail",
704 ));
705 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
708 }
709
710 if target.contains("apple") {
718 if !target.contains("darwin") {
719 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
724
725 cfg.define("CMAKE_OSX_SYSROOT", "/");
727 cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", "");
728 }
729
730 if target.starts_with("aarch64") {
733 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
735 } else if target.starts_with("i686") {
736 cfg.define("CMAKE_OSX_ARCHITECTURES", "i386");
738 } else {
739 cfg.define("CMAKE_OSX_ARCHITECTURES", target.triple.split('-').next().unwrap());
740 }
741 }
742 }
743
744 let sanitize_cc = |cc: &Path| {
745 if target.is_msvc() {
746 OsString::from(cc.to_str().unwrap().replace('\\', "/"))
747 } else {
748 cc.as_os_str().to_owned()
749 }
750 };
751
752 if target.is_msvc() && !builder.ninja() {
756 return;
757 }
758
759 let (cc, cxx) = match builder.config.llvm_clang_cl {
760 Some(ref cl) => (cl.into(), cl.into()),
761 None => (builder.cc(target), builder.cxx(target).unwrap()),
762 };
763
764 if use_compiler_launcher && let Some(ref ccache) = builder.config.ccache {
767 cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
768 .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
769 }
770 cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
771 .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx))
772 .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc));
773
774 cfg.build_arg("-j").build_arg(builder.jobs().to_string());
775 let mut cflags = ccflags.cflags.clone();
776 for flag in builder
782 .cc_handled_clags(target, CLang::C)
783 .into_iter()
784 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::C))
785 .filter(|flag| !suppressed_compiler_flag_prefixes.iter().any(|p| flag.starts_with(p)))
786 {
787 cflags.push(" ");
788 cflags.push(flag);
789 }
790 if let Some(ref s) = builder.config.llvm_cflags {
791 cflags.push(" ");
792 cflags.push(s);
793 }
794 if target.contains("ohos") {
795 cflags.push(" -D_LINUX_SYSINFO_H");
796 }
797 if builder.config.llvm_clang_cl.is_some() {
798 cflags.push(format!(" --target={target}"));
799 }
800 cfg.define("CMAKE_C_FLAGS", cflags);
801 let mut cxxflags = ccflags.cxxflags.clone();
802 for flag in builder
803 .cc_handled_clags(target, CLang::Cxx)
804 .into_iter()
805 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::Cxx))
806 .filter(|flag| {
807 !suppressed_compiler_flag_prefixes
808 .iter()
809 .any(|suppressed_prefix| flag.starts_with(suppressed_prefix))
810 })
811 {
812 cxxflags.push(" ");
813 cxxflags.push(flag);
814 }
815 if let Some(ref s) = builder.config.llvm_cxxflags {
816 cxxflags.push(" ");
817 cxxflags.push(s);
818 }
819 if target.contains("ohos") {
820 cxxflags.push(" -D_LINUX_SYSINFO_H");
821 }
822 if builder.config.llvm_clang_cl.is_some() {
823 cxxflags.push(format!(" --target={target}"));
824 }
825
826 cfg.define("CMAKE_CXX_FLAGS", cxxflags);
827 if let Some(ar) = builder.ar(target)
828 && ar.is_absolute()
829 {
830 cfg.define("CMAKE_AR", sanitize_cc(&ar));
833 }
834
835 if let Some(ranlib) = builder.ranlib(target)
836 && ranlib.is_absolute()
837 {
838 cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib));
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.host_target.triple, &target.triple) {
848 ldflags.push_all(&flags);
849 }
850
851 if builder.config.llvm_static_stdcpp
854 && !target.is_msvc()
855 && !target.contains("netbsd")
856 && !target.contains("solaris")
857 {
858 if target.contains("apple") || target.is_windows() {
859 ldflags.push_all("-static-libstdc++");
860 } else {
861 ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
862 }
863 }
864
865 cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared);
866 cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module);
867 cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe);
868
869 if env::var_os("SCCACHE_ERROR_LOG").is_some() {
870 cfg.env("RUSTC_LOG", "sccache=warn");
871 }
872}
873
874fn configure_llvm(builder: &Builder<'_>, target: TargetSelection, cfg: &mut cmake::Config) {
875 if builder.config.llvm_thin_lto {
878 cfg.define("LLVM_ENABLE_LTO", "Thin");
879 if !target.contains("apple") {
880 cfg.define("LLVM_ENABLE_LLD", "ON");
881 }
882 }
883
884 if builder.config.llvm_libzstd {
886 cfg.define("LLVM_ENABLE_ZSTD", "FORCE_ON");
887 cfg.define("LLVM_USE_STATIC_ZSTD", "TRUE");
888 } else {
889 cfg.define("LLVM_ENABLE_ZSTD", "OFF");
890 }
891
892 if let Some(ref linker) = builder.config.llvm_use_linker {
893 cfg.define("LLVM_USE_LINKER", linker);
894 }
895
896 if builder.config.llvm_allow_old_toolchain {
897 cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
898 }
899}
900
901fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
903 let kind = if host == target { "HOST" } else { "TARGET" };
904 let target_u = target.replace('-', "_");
905 env::var_os(format!("{var_base}_{target}"))
906 .or_else(|| env::var_os(format!("{var_base}_{target_u}")))
907 .or_else(|| env::var_os(format!("{kind}_{var_base}")))
908 .or_else(|| env::var_os(var_base))
909}
910
911#[derive(Clone)]
912pub struct BuiltOmpOffload {
913 offload: Vec<PathBuf>,
915}
916
917impl BuiltOmpOffload {
918 pub fn offload_paths(&self) -> Vec<PathBuf> {
919 self.offload.clone()
920 }
921}
922
923#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
935pub struct OmpOffload {
936 pub target: TargetSelection,
937}
938
939impl Step for OmpOffload {
940 type Output = BuiltOmpOffload;
941 const IS_HOST: bool = true;
942
943 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
944 run.path("src/llvm-project/offload")
945 }
946
947 fn make_run(run: RunConfig<'_>) {
948 run.builder.ensure(OmpOffload { target: run.target });
949 }
950
951 #[allow(unused)]
953 fn run(self, builder: &Builder<'_>) -> Self::Output {
954 if builder.config.dry_run() {
955 return BuiltOmpOffload {
956 offload: vec![builder.config.tempdir().join("llvm-offload-dry-run")],
957 };
958 }
959 let target = self.target;
960
961 let LlvmResult { host_llvm_config, llvm_cmake_dir } =
962 builder.ensure(Llvm { target: self.target });
963
964 let out_dir = builder.offload_out(target);
968
969 let mut files = vec![];
970 let lib_ext = std::env::consts::DLL_EXTENSION;
971 files.push(out_dir.join("lib").join("libLLVMOffload").with_extension(lib_ext));
972 files.push(out_dir.join("lib").join("libomp").with_extension(lib_ext));
973 files.push(out_dir.join("lib").join("libomptarget").with_extension(lib_ext));
974
975 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
977 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
978 generate_smart_stamp_hash(
979 builder,
980 &builder.config.src.join("src/llvm-project/offload"),
981 builder.in_tree_llvm_info.sha().unwrap_or_default(),
982 )
983 });
984 let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash);
985
986 trace!("checking build stamp to see if we need to rebuild offload/openmp artifacts");
987 if stamp.is_up_to_date() {
988 trace!(?out_dir, "offload/openmp build artifacts are up to date");
989 if stamp.stamp().is_empty() {
990 builder.info(
991 "Could not determine the Offload submodule commit hash. \
992 Assuming that an Offload rebuild is not necessary.",
993 );
994 builder.info(&format!(
995 "To force Offload/OpenMP to rebuild, remove the file `{}`",
996 stamp.path().display()
997 ));
998 }
999 return BuiltOmpOffload { offload: files };
1000 }
1001
1002 trace!(?target, "(re)building offload/openmp artifacts");
1003 builder.info(&format!("Building OpenMP/Offload for {target}"));
1004 t!(stamp.remove());
1005 let _time = helpers::timeit(builder);
1006 t!(fs::create_dir_all(&out_dir));
1007
1008 builder.config.update_submodule("src/llvm-project");
1009 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/"));
1010
1011 let mut cflags = CcFlags::default();
1017 if !builder.config.llvm_clang {
1018 let base = builder.llvm_out(target).join("include");
1019 let inc_dir = base.display();
1020 cflags.push_all(format!(" -I {inc_dir}"));
1021 }
1022
1023 configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]);
1024
1025 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1028 (false, _) => "Debug",
1029 (true, false) => "Release",
1030 (true, true) => "RelWithDebInfo",
1031 };
1032 trace!(?profile);
1033
1034 let clang_dir = if !builder.config.llvm_clang {
1037 assert!(&builder.build.config.llvm_clang_dir.is_some());
1039 builder.build.config.llvm_clang_dir.clone()
1040 } else {
1041 None
1043 };
1044
1045 cfg.out_dir(&out_dir)
1049 .profile(profile)
1050 .env("LLVM_CONFIG_REAL", &host_llvm_config)
1051 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1052 .define("LLVM_ENABLE_RUNTIMES", "openmp;offload")
1053 .define("LLVM_INCLUDE_TESTS", "OFF")
1054 .define("OFFLOAD_INCLUDE_TESTS", "OFF")
1055 .define("OPENMP_STANDALONE_BUILD", "ON")
1056 .define("LLVM_ROOT", builder.llvm_out(target).join("build"))
1057 .define("LLVM_DIR", llvm_cmake_dir);
1058 if let Some(p) = clang_dir {
1059 cfg.define("Clang_DIR", p);
1060 }
1061 cfg.build();
1062
1063 t!(stamp.write());
1064
1065 for p in &files {
1066 if !p.exists() {
1069 eprintln!(
1070 "`{p:?}` not found in `{}`. Either the build has failed or Offload was built with a wrong version of LLVM",
1071 out_dir.display()
1072 );
1073 exit!(1);
1074 }
1075 }
1076 BuiltOmpOffload { offload: files }
1077 }
1078}
1079
1080#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1081pub struct Enzyme {
1082 pub target: TargetSelection,
1083}
1084
1085impl Step for Enzyme {
1086 type Output = PathBuf;
1087 const IS_HOST: bool = true;
1088
1089 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1090 run.path("src/tools/enzyme/enzyme")
1091 }
1092
1093 fn make_run(run: RunConfig<'_>) {
1094 run.builder.ensure(Enzyme { target: run.target });
1095 }
1096
1097 fn run(self, builder: &Builder<'_>) -> PathBuf {
1099 builder.require_submodule(
1100 "src/tools/enzyme",
1101 Some("The Enzyme sources are required for autodiff."),
1102 );
1103 if builder.config.dry_run() {
1104 let out_dir = builder.enzyme_out(self.target);
1105 return out_dir;
1106 }
1107 let target = self.target;
1108
1109 let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
1110
1111 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1112 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1113 generate_smart_stamp_hash(
1114 builder,
1115 &builder.config.src.join("src/tools/enzyme"),
1116 builder.enzyme_info.sha().unwrap_or_default(),
1117 )
1118 });
1119
1120 let out_dir = builder.enzyme_out(target);
1121 let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash);
1122
1123 trace!("checking build stamp to see if we need to rebuild enzyme artifacts");
1124 if stamp.is_up_to_date() {
1125 trace!(?out_dir, "enzyme build artifacts are up to date");
1126 if stamp.stamp().is_empty() {
1127 builder.info(
1128 "Could not determine the Enzyme submodule commit hash. \
1129 Assuming that an Enzyme rebuild is not necessary.",
1130 );
1131 builder.info(&format!(
1132 "To force Enzyme to rebuild, remove the file `{}`",
1133 stamp.path().display()
1134 ));
1135 }
1136 return out_dir;
1137 }
1138
1139 if !builder.config.dry_run() && !llvm_cmake_dir.is_dir() {
1140 builder.info(&format!(
1141 "WARNING: {} does not exist, Enzyme build will likely fail",
1142 llvm_cmake_dir.display()
1143 ));
1144 }
1145
1146 trace!(?target, "(re)building enzyme artifacts");
1147 builder.info(&format!("Building Enzyme for {target}"));
1148 t!(stamp.remove());
1149 let _time = helpers::timeit(builder);
1150 t!(fs::create_dir_all(&out_dir));
1151
1152 builder.config.update_submodule("src/tools/enzyme");
1153 let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/"));
1154 let mut cflags = CcFlags::default();
1158 cflags.push_all("-Wno-deprecated");
1159 configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]);
1160
1161 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1165 (false, _) => "Debug",
1166 (true, false) => "Release",
1167 (true, true) => "RelWithDebInfo",
1168 };
1169 trace!(?profile);
1170
1171 cfg.out_dir(&out_dir)
1172 .profile(profile)
1173 .env("LLVM_CONFIG_REAL", &host_llvm_config)
1174 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1175 .define("ENZYME_EXTERNAL_SHARED_LIB", "ON")
1176 .define("ENZYME_BC_LOADER", "OFF")
1177 .define("LLVM_DIR", llvm_cmake_dir);
1178
1179 cfg.build();
1180
1181 t!(stamp.write());
1182 out_dir
1183 }
1184}
1185
1186#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1187pub struct Lld {
1188 pub target: TargetSelection,
1189}
1190
1191impl Step for Lld {
1192 type Output = PathBuf;
1193 const IS_HOST: bool = true;
1194
1195 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1196 run.path("src/llvm-project/lld")
1197 }
1198
1199 fn make_run(run: RunConfig<'_>) {
1200 run.builder.ensure(Lld { target: run.target });
1201 }
1202
1203 fn run(self, builder: &Builder<'_>) -> PathBuf {
1205 if builder.config.dry_run() {
1206 return PathBuf::from("lld-out-dir-test-gen");
1207 }
1208 let target = self.target;
1209
1210 let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
1211
1212 let ci_llvm_bin = host_llvm_config.parent().unwrap();
1217 if ci_llvm_bin.is_dir() && ci_llvm_bin.file_name().unwrap() == "bin" {
1218 let lld_path = ci_llvm_bin.join(exe("lld", target));
1219 if lld_path.exists() {
1220 return ci_llvm_bin.parent().unwrap().to_path_buf();
1223 }
1224 }
1225
1226 let out_dir = builder.lld_out(target);
1227
1228 let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld");
1229 if lld_stamp.path().exists() {
1230 return out_dir;
1231 }
1232
1233 let _guard = builder.msg_unstaged(Kind::Build, "LLD", target);
1234 let _time = helpers::timeit(builder);
1235 t!(fs::create_dir_all(&out_dir));
1236
1237 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
1238 let mut ldflags = LdFlags::default();
1239
1240 if builder.config.llvm_profile_generate
1245 && target.is_msvc()
1246 && let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref()
1247 {
1248 let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
1251 ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
1252 }
1253
1254 if builder.config.rpath_enabled(target)
1266 && helpers::use_host_linker(target)
1267 && builder.config.llvm_link_shared()
1268 && target.contains("linux")
1269 {
1270 ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'");
1277 }
1278
1279 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1280 configure_llvm(builder, target, &mut cfg);
1281
1282 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1285 (false, _) => "Debug",
1286 (true, false) => "Release",
1287 (true, true) => "RelWithDebInfo",
1288 };
1289
1290 cfg.out_dir(&out_dir)
1291 .profile(profile)
1292 .define("LLVM_CMAKE_DIR", llvm_cmake_dir)
1293 .define("LLVM_INCLUDE_TESTS", "OFF");
1294
1295 if !builder.config.is_host_target(target) {
1296 cfg.define(
1298 "LLVM_TABLEGEN_EXE",
1299 host_llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
1300 );
1301 }
1302
1303 cfg.build();
1304
1305 t!(lld_stamp.write());
1306 out_dir
1307 }
1308}
1309
1310#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1311pub struct Sanitizers {
1312 pub target: TargetSelection,
1313}
1314
1315impl Step for Sanitizers {
1316 type Output = Vec<SanitizerRuntime>;
1317
1318 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1319 run.alias("sanitizers")
1320 }
1321
1322 fn make_run(run: RunConfig<'_>) {
1323 run.builder.ensure(Sanitizers { target: run.target });
1324 }
1325
1326 fn run(self, builder: &Builder<'_>) -> Self::Output {
1328 let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
1329 if !compiler_rt_dir.exists() {
1330 return Vec::new();
1331 }
1332
1333 let out_dir = builder.native_dir(self.target).join("sanitizers");
1334 let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel);
1335
1336 if builder.config.dry_run() || runtimes.is_empty() {
1337 return runtimes;
1338 }
1339
1340 let LlvmResult { host_llvm_config, .. } =
1341 builder.ensure(Llvm { target: builder.config.host_target });
1342
1343 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1344 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1345 generate_smart_stamp_hash(
1346 builder,
1347 &builder.config.src.join("src/llvm-project/compiler-rt"),
1348 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1349 )
1350 });
1351
1352 let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash);
1353
1354 if stamp.is_up_to_date() {
1355 if stamp.stamp().is_empty() {
1356 builder.info(&format!(
1357 "Rebuild sanitizers by removing the file `{}`",
1358 stamp.path().display()
1359 ));
1360 }
1361
1362 return runtimes;
1363 }
1364
1365 let _guard = builder.msg_unstaged(Kind::Build, "sanitizers", self.target);
1366 t!(stamp.remove());
1367 let _time = helpers::timeit(builder);
1368
1369 let mut cfg = cmake::Config::new(&compiler_rt_dir);
1370 cfg.profile("Release");
1371 cfg.define("CMAKE_C_COMPILER_TARGET", self.target.triple);
1372 cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
1373 cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
1374 cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
1375 cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
1376 cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
1377 cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
1378 cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
1379 cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
1380 cfg.define("LLVM_CONFIG_PATH", &host_llvm_config);
1381
1382 if self.target.contains("ohos") {
1383 cfg.define("COMPILER_RT_USE_BUILTINS_LIBRARY", "ON");
1384 }
1385
1386 let use_compiler_launcher = !self.target.contains("apple-darwin");
1390 let suppressed_compiler_flag_prefixes: &[&str] =
1395 if self.target.contains("apple-darwin") { &["-mmacosx-version-min="] } else { &[] };
1396 configure_cmake(
1397 builder,
1398 self.target,
1399 &mut cfg,
1400 use_compiler_launcher,
1401 LdFlags::default(),
1402 CcFlags::default(),
1403 suppressed_compiler_flag_prefixes,
1404 );
1405
1406 t!(fs::create_dir_all(&out_dir));
1407 cfg.out_dir(out_dir);
1408
1409 for runtime in &runtimes {
1410 cfg.build_target(&runtime.cmake_target);
1411 cfg.build();
1412 }
1413 t!(stamp.write());
1414
1415 runtimes
1416 }
1417}
1418
1419#[derive(Clone, Debug)]
1420pub struct SanitizerRuntime {
1421 pub cmake_target: String,
1423 pub path: PathBuf,
1425 pub name: String,
1427}
1428
1429fn supported_sanitizers(
1431 out_dir: &Path,
1432 target: TargetSelection,
1433 channel: &str,
1434) -> Vec<SanitizerRuntime> {
1435 let darwin_libs = |os: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1436 components
1437 .iter()
1438 .map(move |c| SanitizerRuntime {
1439 cmake_target: format!("clang_rt.{c}_{os}_dynamic"),
1440 path: out_dir.join(format!("build/lib/darwin/libclang_rt.{c}_{os}_dynamic.dylib")),
1441 name: format!("librustc-{channel}_rt.{c}.dylib"),
1442 })
1443 .collect()
1444 };
1445
1446 let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1447 components
1448 .iter()
1449 .map(move |c| SanitizerRuntime {
1450 cmake_target: format!("clang_rt.{c}-{arch}"),
1451 path: out_dir.join(format!("build/lib/{os}/libclang_rt.{c}-{arch}.a")),
1452 name: format!("librustc-{channel}_rt.{c}.a"),
1453 })
1454 .collect()
1455 };
1456
1457 match &*target.triple {
1458 "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1459 "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan", "rtsan"]),
1460 "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan", "rtsan"]),
1461 "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1462 "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
1463 "aarch64-unknown-linux-gnu" => {
1464 common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan"])
1465 }
1466 "aarch64-unknown-linux-ohos" => {
1467 common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
1468 }
1469 "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => {
1470 common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"])
1471 }
1472 "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1473 "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
1474 "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]),
1475 "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1476 "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]),
1477 "x86_64-unknown-netbsd" => {
1478 common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"])
1479 }
1480 "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
1481 "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
1482 "x86_64-unknown-linux-gnu" => common_libs(
1483 "linux",
1484 "x86_64",
1485 &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan"],
1486 ),
1487 "x86_64-unknown-linux-musl" => {
1488 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1489 }
1490 "s390x-unknown-linux-gnu" => {
1491 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1492 }
1493 "s390x-unknown-linux-musl" => {
1494 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1495 }
1496 "x86_64-unknown-linux-ohos" => {
1497 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1498 }
1499 _ => Vec::new(),
1500 }
1501}
1502
1503#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1504pub struct CrtBeginEnd {
1505 pub target: TargetSelection,
1506}
1507
1508impl Step for CrtBeginEnd {
1509 type Output = PathBuf;
1510
1511 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1512 run.path("src/llvm-project/compiler-rt/lib/crt")
1513 }
1514
1515 fn make_run(run: RunConfig<'_>) {
1516 if run.target.needs_crt_begin_end() {
1517 run.builder.ensure(CrtBeginEnd { target: run.target });
1518 }
1519 }
1520
1521 fn run(self, builder: &Builder<'_>) -> Self::Output {
1523 builder.require_submodule(
1524 "src/llvm-project",
1525 Some("The LLVM sources are required for the CRT from `compiler-rt`."),
1526 );
1527
1528 let out_dir = builder.native_dir(self.target).join("crt");
1529
1530 if builder.config.dry_run() {
1531 return out_dir;
1532 }
1533
1534 let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtbegin.c");
1535 let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtend.c");
1536 if up_to_date(&crtbegin_src, &out_dir.join("crtbeginS.o"))
1537 && up_to_date(&crtend_src, &out_dir.join("crtendS.o"))
1538 {
1539 return out_dir;
1540 }
1541
1542 let _guard = builder.msg_unstaged(Kind::Build, "crtbegin.o and crtend.o", self.target);
1543 t!(fs::create_dir_all(&out_dir));
1544
1545 let mut cfg = cc::Build::new();
1546
1547 if let Some(ar) = builder.ar(self.target) {
1548 cfg.archiver(ar);
1549 }
1550 cfg.compiler(builder.cc(self.target));
1551 cfg.cargo_metadata(false)
1552 .out_dir(&out_dir)
1553 .target(&self.target.triple)
1554 .host(&builder.config.host_target.triple)
1555 .warnings(false)
1556 .debug(false)
1557 .opt_level(3)
1558 .file(crtbegin_src)
1559 .file(crtend_src);
1560
1561 cfg.flag("-std=c11")
1565 .define("CRT_HAS_INITFINI_ARRAY", None)
1566 .define("EH_USE_FRAME_REGISTRY", None);
1567
1568 let objs = cfg.compile_intermediates();
1569 assert_eq!(objs.len(), 2);
1570 for obj in objs {
1571 let base_name = unhashed_basename(&obj);
1572 assert!(base_name == "crtbegin" || base_name == "crtend");
1573 t!(fs::copy(&obj, out_dir.join(format!("{base_name}S.o"))));
1574 t!(fs::rename(&obj, out_dir.join(format!("{base_name}.o"))));
1575 }
1576
1577 out_dir
1578 }
1579}
1580
1581#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1582pub struct Libunwind {
1583 pub target: TargetSelection,
1584}
1585
1586impl Step for Libunwind {
1587 type Output = PathBuf;
1588
1589 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1590 run.path("src/llvm-project/libunwind")
1591 }
1592
1593 fn make_run(run: RunConfig<'_>) {
1594 run.builder.ensure(Libunwind { target: run.target });
1595 }
1596
1597 fn run(self, builder: &Builder<'_>) -> Self::Output {
1599 builder.require_submodule(
1600 "src/llvm-project",
1601 Some("The LLVM sources are required for libunwind."),
1602 );
1603
1604 if builder.config.dry_run() {
1605 return PathBuf::new();
1606 }
1607
1608 let out_dir = builder.native_dir(self.target).join("libunwind");
1609 let root = builder.src.join("src/llvm-project/libunwind");
1610
1611 if up_to_date(&root, &out_dir.join("libunwind.a")) {
1612 return out_dir;
1613 }
1614
1615 let _guard = builder.msg_unstaged(Kind::Build, "libunwind.a", self.target);
1616 t!(fs::create_dir_all(&out_dir));
1617
1618 let mut cc_cfg = cc::Build::new();
1619 let mut cpp_cfg = cc::Build::new();
1620
1621 cpp_cfg.cpp(true);
1622 cpp_cfg.cpp_set_stdlib(None);
1623 cpp_cfg.flag("-nostdinc++");
1624 cpp_cfg.flag("-fno-exceptions");
1625 cpp_cfg.flag("-fno-rtti");
1626 cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
1627
1628 for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() {
1629 if let Some(ar) = builder.ar(self.target) {
1630 cfg.archiver(ar);
1631 }
1632 cfg.target(&self.target.triple);
1633 cfg.host(&builder.config.host_target.triple);
1634 cfg.warnings(false);
1635 cfg.debug(false);
1636 cfg.opt_level(3);
1638 cfg.flag("-fstrict-aliasing");
1639 cfg.flag("-funwind-tables");
1640 cfg.flag("-fvisibility=hidden");
1641 cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
1642 cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
1643 cfg.include(root.join("include"));
1644 cfg.cargo_metadata(false);
1645 cfg.out_dir(&out_dir);
1646
1647 if self.target.contains("x86_64-fortanix-unknown-sgx") {
1648 cfg.static_flag(true);
1649 cfg.flag("-fno-stack-protector");
1650 cfg.flag("-ffreestanding");
1651 cfg.flag("-fexceptions");
1652
1653 cfg.flag("-U_FORTIFY_SOURCE");
1655 cfg.define("_FORTIFY_SOURCE", "0");
1656 cfg.define("RUST_SGX", "1");
1657 cfg.define("__NO_STRING_INLINES", None);
1658 cfg.define("__NO_MATH_INLINES", None);
1659 cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
1660 cfg.define("NDEBUG", None);
1661 }
1662 if self.target.is_windows() {
1663 cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
1664 }
1665 }
1666
1667 cc_cfg.compiler(builder.cc(self.target));
1668 if let Ok(cxx) = builder.cxx(self.target) {
1669 cpp_cfg.compiler(cxx);
1670 } else {
1671 cc_cfg.compiler(builder.cc(self.target));
1672 }
1673
1674 if cc_cfg.get_compiler().is_like_gnu() {
1679 cc_cfg.flag("-std=c99");
1680 }
1681 if cpp_cfg.get_compiler().is_like_gnu() {
1682 cpp_cfg.flag("-std=c++11");
1683 }
1684
1685 if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") {
1686 if cpp_cfg.get_compiler().is_like_gnu() {
1690 cpp_cfg.cpp(false);
1691 cpp_cfg.compiler(builder.cc(self.target));
1692 }
1693 }
1694
1695 let mut c_sources = vec![
1696 "Unwind-sjlj.c",
1697 "UnwindLevel1-gcc-ext.c",
1698 "UnwindLevel1.c",
1699 "UnwindRegistersRestore.S",
1700 "UnwindRegistersSave.S",
1701 ];
1702
1703 let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"];
1704 let cpp_len = cpp_sources.len();
1705
1706 if self.target.contains("x86_64-fortanix-unknown-sgx") {
1707 c_sources.push("UnwindRustSgx.c");
1708 }
1709
1710 for src in c_sources {
1711 cc_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1712 }
1713
1714 for src in &cpp_sources {
1715 cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1716 }
1717
1718 cpp_cfg.compile("unwind-cpp");
1719
1720 let mut count = 0;
1722 let mut files = fs::read_dir(&out_dir)
1723 .unwrap()
1724 .map(|entry| entry.unwrap().path().canonicalize().unwrap())
1725 .collect::<Vec<_>>();
1726 files.sort();
1727 for file in files {
1728 if file.is_file() && file.extension() == Some(OsStr::new("o")) {
1729 let base_name = unhashed_basename(&file);
1731 if cpp_sources.iter().any(|f| *base_name == f[..f.len() - 4]) {
1732 cc_cfg.object(&file);
1733 count += 1;
1734 }
1735 }
1736 }
1737 assert_eq!(cpp_len, count, "Can't get object files from {out_dir:?}");
1738
1739 cc_cfg.compile("unwind");
1740 out_dir
1741 }
1742}