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::git::PathFreshness;
18
19use crate::core::build_steps::llvm;
20use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun, StepMetadata};
21use crate::core::config::{Config, LlvmPgoGenerationMode, 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, libdir, t, unhashed_basename, up_to_date,
26};
27use crate::{CLang, GitRepo, Kind, exit, 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 assert!(cfg!(not(test)), "unit tests shouldn't care about LLVM freshness");
205
206 if is_git {
207 config.check_path_modifications(LLVM_INVALIDATION_PATHS)
208 } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
209 PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() }
210 } else {
211 PathFreshness::MissingUpstream
212 }
213}
214
215pub(crate) fn is_ci_llvm_available_for_target(
220 host_target: &TargetSelection,
221 asserts: bool,
222) -> bool {
223 let supported_platforms = [
227 ("aarch64-unknown-linux-gnu", false),
229 ("aarch64-apple-darwin", false),
230 ("aarch64-pc-windows-msvc", false),
231 ("i686-pc-windows-msvc", false),
232 ("i686-unknown-linux-gnu", false),
233 ("x86_64-unknown-linux-gnu", true),
234 ("x86_64-apple-darwin", true),
235 ("x86_64-pc-windows-gnu", false),
236 ("x86_64-pc-windows-msvc", true),
237 ("aarch64-unknown-linux-musl", false),
239 ("aarch64-pc-windows-gnullvm", false),
240 ("arm-unknown-linux-gnueabi", false),
241 ("arm-unknown-linux-gnueabihf", false),
242 ("armv7-unknown-linux-gnueabihf", false),
243 ("i686-pc-windows-gnu", false),
244 ("loongarch64-unknown-linux-gnu", false),
245 ("loongarch64-unknown-linux-musl", false),
246 ("powerpc-unknown-linux-gnu", false),
247 ("powerpc64-unknown-linux-gnu", false),
248 ("powerpc64-unknown-linux-musl", false),
249 ("powerpc64le-unknown-linux-gnu", false),
250 ("powerpc64le-unknown-linux-musl", false),
251 ("riscv64gc-unknown-linux-gnu", false),
252 ("riscv64gc-unknown-linux-musl", false),
253 ("s390x-unknown-linux-gnu", false),
254 ("x86_64-pc-windows-gnullvm", false),
255 ("x86_64-unknown-freebsd", false),
256 ("x86_64-unknown-illumos", false),
257 ("x86_64-unknown-linux-musl", false),
258 ("x86_64-unknown-netbsd", false),
259 ];
260
261 if !supported_platforms.contains(&(&*host_target.triple, asserts))
262 && (asserts || !supported_platforms.contains(&(&*host_target.triple, true)))
263 {
264 return false;
265 }
266
267 true
268}
269
270#[derive(Debug, Clone, Hash, PartialEq, Eq)]
271pub struct Llvm {
272 pub target: TargetSelection,
273}
274
275impl CommandLineStep for Llvm {
276 type Output = LlvmResult;
277
278 const IS_HOST: bool = true;
279
280 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
281 run.path("src/llvm-project").path("src/llvm-project/llvm")
282 }
283
284 fn make_run(run: RunConfig<'_>) {
285 run.builder.ensure(Llvm { target: run.target });
286 }
287
288 fn run(self, builder: &Builder<'_>) -> LlvmResult {
290 let target = self.target;
291 let target_native = if self.target.starts_with("riscv") {
292 let idx = target.triple.find('-').unwrap();
295
296 format!("riscv{}{}", &target.triple[5..7], &target.triple[idx..])
297 } else if self.target.starts_with("powerpc") && self.target.ends_with("freebsd") {
298 format!("{}{}", self.target, "13.0")
301 } else {
302 target.to_string()
303 };
304
305 let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target, true) {
307 LlvmBuildStatus::AlreadyBuilt(p) => return p,
308 LlvmBuildStatus::ShouldBuild(m) => m,
309 };
310
311 if builder.llvm_link_shared() && target.is_windows() && !target.is_windows_gnullvm() {
312 panic!("shared linking to LLVM is not currently supported on {}", target.triple);
313 }
314
315 let _guard = builder.msg_unstaged(Kind::Build, "LLVM", target);
316 t!(stamp.remove());
317 let _time = helpers::timeit(builder);
318 t!(fs::create_dir_all(&out_dir));
319
320 let mut cfg = cmake::Config::new(builder.src.join(root));
322 let mut ldflags = LdFlags::default();
323
324 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
325 (false, _) => "Debug",
326 (true, false) => "Release",
327 (true, true) => "RelWithDebInfo",
328 };
329
330 let llvm_targets = match &builder.config.llvm_targets {
333 Some(s) => s,
334 None => {
335 "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
336 Sparc;SystemZ;WebAssembly;X86"
337 }
338 };
339
340 let llvm_exp_targets = match builder.config.llvm_experimental_targets {
341 Some(ref s) => s,
342 None => "AVR;M68k;CSKY;Xtensa",
343 };
344
345 let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" };
346 let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" };
347 let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" };
348 let enable_warnings = if builder.config.llvm_enable_warnings { "ON" } else { "OFF" };
349
350 cfg.out_dir(&out_dir)
351 .profile(profile)
352 .define("LLVM_ENABLE_ASSERTIONS", assertions)
353 .define("LLVM_UNREACHABLE_OPTIMIZE", "OFF")
354 .define("LLVM_ENABLE_PLUGINS", plugins)
355 .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
356 .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
357 .define("LLVM_INCLUDE_EXAMPLES", "OFF")
358 .define("LLVM_INCLUDE_DOCS", "OFF")
359 .define("LLVM_INCLUDE_BENCHMARKS", "OFF")
360 .define("LLVM_INCLUDE_TESTS", enable_tests)
361 .define("LLVM_ENABLE_LIBEDIT", "OFF")
362 .define("LLVM_ENABLE_BINDINGS", "OFF")
363 .define("LLVM_ENABLE_Z3_SOLVER", "OFF")
364 .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string())
365 .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap())
366 .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native)
367 .define("LLVM_ENABLE_WARNINGS", enable_warnings);
368
369 cfg.define("LLVM_INSTALL_UTILS", "ON");
373
374 if let Some(mode) = builder.config.llvm_pgo.generate_profile.as_ref() {
375 cfg.define("LLVM_BUILD_INSTRUMENTED", "IR");
376 match mode {
377 LlvmPgoGenerationMode::Implicit => {}
378 LlvmPgoGenerationMode::Directory(llvm_profile_dir) => {
379 cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir);
380 }
381 }
382 cfg.define("LLVM_BUILD_RUNTIME", "No");
383 }
384 if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() {
385 cfg.define("LLVM_PROFDATA_FILE", path);
386 }
387
388 if !target.is_msvc() {
390 cfg.define("LLVM_ENABLE_ZLIB", "ON");
391 } else {
392 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
393 }
394
395 if target.contains("apple-ios")
397 || target.contains("apple-tvos")
398 || target.contains("apple-watchos")
399 || target.contains("apple-visionos")
400 {
401 cfg.define("LLVM_ENABLE_PLUGINS", "OFF");
403 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
405 }
406
407 if builder.llvm_link_shared() {
412 cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
413 cfg.define("LLVM_VERSIONED_DYLIB_NAME_ON_DARWIN", "OFF");
415 }
416
417 if (target.starts_with("csky")
418 || target.starts_with("riscv")
419 || target.starts_with("sparc-"))
420 && !target.contains("freebsd")
421 && !target.contains("openbsd")
422 && !target.contains("netbsd")
423 {
424 ldflags.exe.push(" -latomic");
433 ldflags.shared.push(" -latomic");
434 }
435
436 if target.starts_with("mips") && target.contains("netbsd") {
437 ldflags.exe.push(" -latomic");
439 ldflags.shared.push(" -latomic");
440 }
441
442 if target.starts_with("arm64ec") {
443 ldflags.exe.push(" -machine:arm64ec");
446 ldflags.shared.push(" -machine:arm64ec");
447 }
448
449 if target.contains("musl") && builder.crt_static(target).unwrap_or(true) {
452 ldflags.exe.push(" -static");
453 }
454
455 if target.is_msvc() {
456 cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded");
457 cfg.static_crt(true);
458 }
459
460 if target.starts_with("i686") {
461 cfg.define("LLVM_BUILD_32_BITS", "ON");
462 }
463
464 if target.starts_with("x86_64") && target.contains("ohos") {
465 cfg.define("LLVM_TOOL_LLVM_RTDYLD_BUILD", "OFF");
466 }
467
468 let mut enabled_llvm_projects = Vec::new();
469
470 if helpers::forcing_clang_based_tests() {
471 enabled_llvm_projects.push("clang");
472 }
473
474 if builder.config.llvm_polly {
475 enabled_llvm_projects.push("polly");
476 }
477
478 if builder.config.llvm_clang {
479 enabled_llvm_projects.push("clang");
480 }
481
482 cfg.define("LLVM_ENABLE_LIBXML2", "OFF");
485
486 let mut enabled_llvm_runtimes = Vec::new();
487
488 if helpers::forcing_clang_based_tests() {
489 enabled_llvm_runtimes.push("compiler-rt");
490 }
491
492 if !enabled_llvm_projects.is_empty() {
493 enabled_llvm_projects.sort();
494 enabled_llvm_projects.dedup();
495 cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
496 }
497
498 if !enabled_llvm_runtimes.is_empty() {
499 enabled_llvm_runtimes.sort();
500 enabled_llvm_runtimes.dedup();
501 cfg.define("LLVM_ENABLE_RUNTIMES", enabled_llvm_runtimes.join(";"));
502 }
503
504 if let Some(num_linkers) = builder.config.llvm_link_jobs
505 && num_linkers > 0
506 {
507 cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
508 }
509
510 if !builder.config.is_host_target(target) {
512 let LlvmResult { host_llvm_config, .. } =
513 builder.ensure(Llvm { target: builder.config.host_target });
514 if !builder.config.dry_run() {
515 let llvm_bindir = command(&host_llvm_config)
516 .arg("--bindir")
517 .cached()
518 .run_capture_stdout(builder)
519 .stdout();
520 let host_bin = Path::new(llvm_bindir.trim());
521 cfg.define(
522 "LLVM_TABLEGEN",
523 host_bin.join("llvm-tblgen").with_extension(EXE_EXTENSION),
524 );
525 cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION));
527 }
528 cfg.define("LLVM_CONFIG_PATH", host_llvm_config);
529 if builder.config.llvm_clang {
530 let build_bin =
531 builder.llvm_out(builder.config.host_target).join("build").join("bin");
532 let clang_tblgen = build_bin.join("clang-tblgen").with_extension(EXE_EXTENSION);
533 if !builder.config.dry_run() && !clang_tblgen.exists() {
534 panic!("unable to find {}", clang_tblgen.display());
535 }
536 cfg.define("CLANG_TABLEGEN", clang_tblgen);
537 }
538 }
539
540 let llvm_version_suffix = if let Some(ref suffix) = builder.config.llvm_version_suffix {
541 if !suffix.is_empty() { Some(suffix.to_string()) } else { None }
543 } else if builder.config.channel == "dev" {
544 Some("-rust-dev".to_string())
548 } else {
549 Some(format!("-rust-{}-{}", builder.version, builder.config.channel))
550 };
551 if let Some(ref suffix) = llvm_version_suffix {
552 cfg.define("LLVM_VERSION_SUFFIX", suffix);
553 }
554
555 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
556 configure_llvm(builder, target, &mut cfg);
557
558 for (key, val) in &builder.config.llvm_build_config {
559 cfg.define(key, val);
560 }
561
562 if builder.config.dry_run() {
563 return res;
564 }
565
566 cfg.build();
567
568 let find_llvm_lib_name = |extension| {
570 let major = get_llvm_version_major(builder, &res.host_llvm_config);
571 match &llvm_version_suffix {
572 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
573 None => format!("libLLVM-{major}.{extension}"),
574 }
575 };
576
577 if builder.llvm_link_shared() && target.contains("apple-darwin") {
582 let lib_name = find_llvm_lib_name("dylib");
583 let lib_llvm = out_dir.join("build").join("lib").join(lib_name);
584 if !lib_llvm.exists() {
585 t!(builder.symlink_file("libLLVM.dylib", &lib_llvm));
586 }
587 }
588
589 if builder.llvm_link_shared()
593 && builder.config.llvm_optimize
594 && !builder.config.llvm_release_debuginfo
595 {
596 let lib_name = find_llvm_lib_name("so");
598
599 crate::core::build_steps::compile::strip_debug(
602 builder,
603 target,
604 &out_dir.join("lib").join(&lib_name),
605 );
606 crate::core::build_steps::compile::strip_debug(
607 builder,
608 target,
609 &out_dir.join("build").join("lib").join(&lib_name),
610 );
611 }
612
613 t!(stamp.write());
614
615 res
616 }
617
618 fn metadata(&self) -> Option<StepMetadata> {
619 Some(StepMetadata::build("llvm", self.target))
620 }
621}
622
623pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String {
624 command(llvm_config)
625 .arg("--version")
626 .cached()
627 .run_capture_stdout(builder)
628 .stdout()
629 .trim()
630 .to_owned()
631}
632
633pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 {
634 let version = get_llvm_version(builder, llvm_config);
635 let major_str = version.split_once('.').expect("Failed to parse LLVM version").0;
636 major_str.parse().unwrap()
637}
638
639fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
640 if builder.config.dry_run() {
641 return;
642 }
643
644 let version = get_llvm_version(builder, llvm_config);
645 let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
646 if let (Some(major), Some(_minor)) = (parts.next(), parts.next())
647 && major >= 21
648 {
649 return;
650 }
651 panic!("\n\nbad LLVM version: {version}, need >=21\n\n")
652}
653
654fn configure_cmake(
655 builder: &Builder<'_>,
656 target: TargetSelection,
657 cfg: &mut cmake::Config,
658 use_compiler_launcher: bool,
659 mut ldflags: LdFlags,
660 ccflags: CcFlags,
661 suppressed_compiler_flag_prefixes: &[&str],
662) {
663 cfg.define("CMAKE_INSTALL_MESSAGE", "LAZY");
666
667 if builder.config.quiet {
668 cfg.define("CMAKE_MESSAGE_LOG_LEVEL", "WARNING");
670
671 if builder.ninja() {
675 cfg.build_arg("--quiet");
676 }
677 }
678
679 cfg.env("DESTDIR", "");
683
684 if builder.ninja() {
685 cfg.generator("Ninja");
686 }
687 cfg.target(&target.triple).host(&builder.config.host_target.triple);
688
689 if !builder.config.is_host_target(target) {
690 cfg.define("CMAKE_CROSSCOMPILING", "True");
691
692 if target.contains("netbsd") {
698 cfg.define("CMAKE_SYSTEM_NAME", "NetBSD");
699 } else if target.contains("dragonfly") {
700 cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
701 } else if target.contains("openbsd") {
702 cfg.define("CMAKE_SYSTEM_NAME", "OpenBSD");
703 } else if target.contains("freebsd") {
704 cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
705 } else if target.is_windows() {
706 cfg.define("CMAKE_SYSTEM_NAME", "Windows");
707 } else if target.contains("haiku") {
708 cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
709 } else if target.contains("solaris") || target.contains("illumos") {
710 cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
711 } else if target.contains("linux") {
712 cfg.define("CMAKE_SYSTEM_NAME", "Linux");
713 } else if target.contains("darwin") {
714 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
716 } else if target.contains("ios") {
717 cfg.define("CMAKE_SYSTEM_NAME", "iOS");
718 } else if target.contains("tvos") {
719 cfg.define("CMAKE_SYSTEM_NAME", "tvOS");
720 } else if target.contains("visionos") {
721 cfg.define("CMAKE_SYSTEM_NAME", "visionOS");
722 } else if target.contains("watchos") {
723 cfg.define("CMAKE_SYSTEM_NAME", "watchOS");
724 } else if target.contains("none") {
725 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
727 } else {
728 builder.info(&format!(
729 "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail",
730 ));
731 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
734 }
735
736 if target.contains("apple") {
744 if !target.contains("darwin") {
745 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
750
751 cfg.define("CMAKE_OSX_SYSROOT", "/");
753 cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", "");
754 }
755
756 if target.starts_with("aarch64") {
759 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
761 } else if target.starts_with("i686") {
762 cfg.define("CMAKE_OSX_ARCHITECTURES", "i386");
764 } else {
765 cfg.define("CMAKE_OSX_ARCHITECTURES", target.triple.split('-').next().unwrap());
766 }
767 }
768 }
769
770 let sanitize_cc = |cc: &Path| {
771 if target.is_msvc() {
772 OsString::from(cc.to_str().unwrap().replace('\\', "/"))
773 } else {
774 cc.as_os_str().to_owned()
775 }
776 };
777
778 if target.is_msvc() && !builder.ninja() {
782 return;
783 }
784
785 let (cc, cxx) = match builder.config.llvm_clang_cl {
786 Some(ref cl) => (cl.into(), cl.into()),
787 None => (builder.cc(target), builder.cxx(target).unwrap()),
788 };
789
790 if use_compiler_launcher && let Some(ref ccache) = builder.config.ccache {
793 cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
794 .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
795 }
796 cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
797 .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx))
798 .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc));
799
800 let has_modern_jobserver = env::var("MAKEFLAGS")
803 .map(|flags| flags.contains("--jobserver-auth=fifo:"))
804 .unwrap_or(false);
805
806 if !has_modern_jobserver {
807 cfg.build_arg("-j").build_arg(builder.jobs().to_string());
808 }
809 let mut cflags = ccflags.cflags.clone();
810 for flag in builder
816 .cc_handled_cflags(target, CLang::C)
817 .into_iter()
818 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::C))
819 .filter(|flag| !suppressed_compiler_flag_prefixes.iter().any(|p| flag.starts_with(p)))
820 {
821 cflags.push(" ");
822 cflags.push(flag);
823 }
824 if let Some(ref s) = builder.config.llvm_cflags {
825 cflags.push(" ");
826 cflags.push(s);
827 }
828 if target.contains("ohos") {
829 cflags.push(" -D_LINUX_SYSINFO_H");
830 }
831 if builder.config.llvm_clang_cl.is_some() {
832 cflags.push(format!(" --target={target}"));
833 }
834 cfg.define("CMAKE_C_FLAGS", cflags);
835 let mut cxxflags = ccflags.cxxflags.clone();
836 for flag in builder
837 .cc_handled_cflags(target, CLang::Cxx)
838 .into_iter()
839 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::Cxx))
840 .filter(|flag| {
841 !suppressed_compiler_flag_prefixes
842 .iter()
843 .any(|suppressed_prefix| flag.starts_with(suppressed_prefix))
844 })
845 {
846 cxxflags.push(" ");
847 cxxflags.push(flag);
848 }
849 if let Some(ref s) = builder.config.llvm_cxxflags {
850 cxxflags.push(" ");
851 cxxflags.push(s);
852 }
853 if target.contains("ohos") {
854 cxxflags.push(" -D_LINUX_SYSINFO_H");
855 }
856 if builder.config.llvm_clang_cl.is_some() {
857 cxxflags.push(format!(" --target={target}"));
858 }
859
860 cfg.define("CMAKE_CXX_FLAGS", cxxflags);
861 if let Some(ar) = builder.ar(target)
862 && ar.is_absolute()
863 {
864 cfg.define("CMAKE_AR", sanitize_cc(&ar));
867 }
868
869 if let Some(ranlib) = builder.ranlib(target)
870 && ranlib.is_absolute()
871 {
872 cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib));
875 }
876
877 if let Some(ref flags) = builder.config.llvm_ldflags {
878 ldflags.push_all(flags);
879 }
880
881 if let Some(flags) = get_var("LDFLAGS", &builder.config.host_target.triple, &target.triple) {
882 ldflags.push_all(&flags);
883 }
884
885 if builder.config.llvm_static_stdcpp
888 && !target.is_msvc()
889 && !target.contains("netbsd")
890 && !target.contains("solaris")
891 {
892 if target.contains("apple") || target.is_windows() {
893 ldflags.push_all("-static-libstdc++");
894 } else {
895 ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
896 }
897 }
898
899 cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared);
900 cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module);
901 cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe);
902
903 if env::var_os("SCCACHE_ERROR_LOG").is_some() {
904 cfg.env("RUSTC_LOG", "sccache=warn");
905 }
906}
907
908fn configure_llvm(builder: &Builder<'_>, target: TargetSelection, cfg: &mut cmake::Config) {
909 if builder.config.llvm_thin_lto {
912 cfg.define("LLVM_ENABLE_LTO", "Thin");
913 if !target.contains("apple") {
914 cfg.define("LLVM_ENABLE_LLD", "ON");
915 }
916 }
917
918 if builder.config.llvm_libzstd {
920 cfg.define("LLVM_ENABLE_ZSTD", "FORCE_ON");
921 cfg.define("LLVM_USE_STATIC_ZSTD", "TRUE");
922 } else {
923 cfg.define("LLVM_ENABLE_ZSTD", "OFF");
924 }
925
926 if let Some(ref linker) = builder.config.llvm_use_linker {
927 cfg.define("LLVM_USE_LINKER", linker);
928 }
929
930 if builder.config.llvm_allow_old_toolchain {
931 cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
932 }
933}
934
935fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
937 let kind = if host == target { "HOST" } else { "TARGET" };
938 let target_u = target.replace('-', "_");
939 env::var_os(format!("{var_base}_{target}"))
940 .or_else(|| env::var_os(format!("{var_base}_{target_u}")))
941 .or_else(|| env::var_os(format!("{kind}_{var_base}")))
942 .or_else(|| env::var_os(var_base))
943}
944
945#[derive(Clone)]
946pub struct BuiltRustOffload {
947 offload: PathBuf,
949}
950
951impl BuiltRustOffload {
952 pub fn rust_offload_path(&self) -> PathBuf {
953 self.offload.clone()
954 }
955
956 pub fn rust_offload_filename(&self) -> String {
957 self.offload.file_name().unwrap().to_str().unwrap().to_owned()
958 }
959}
960
961#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
962pub struct RustOffload {
963 pub target: TargetSelection,
964}
965
966impl CommandLineStep for RustOffload {
967 type Output = BuiltRustOffload;
968 const IS_HOST: bool = true;
969
970 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
971 run.alias("rust-offload")
972 }
973
974 fn make_run(run: RunConfig<'_>) {
975 run.builder.ensure(RustOffload { target: run.target });
976 }
977
978 fn run(self, builder: &Builder<'_>) -> Self::Output {
979 if builder.config.dry_run() {
980 return BuiltRustOffload {
981 offload: builder.config.tempdir().join("rust-offload-dry-run"),
982 };
983 }
984
985 let target = self.target;
986
987 let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
988
989 let out_dir = builder.rust_offload_out(target);
990
991 let llvm_version_major = llvm::get_llvm_version_major(builder, &host_llvm_config);
992 let lib_ext = std::env::consts::DLL_EXTENSION;
993 let lib_rust_offload = format!("libRustOffload-{llvm_version_major}");
994 let build_dir = out_dir.join(libdir(target));
995 let dylib = build_dir.join(&lib_rust_offload).with_extension(lib_ext);
996
997 let mut cfg =
998 cmake::Config::new(builder.src.join("compiler/rustc_llvm/llvm-wrapper/offload/"));
999
1000 let mut ldflags = LdFlags::default();
1004 if builder.config.llvm_thin_lto && !target.contains("apple") {
1005 ldflags.push_all("-fuse-ld=lld");
1006 }
1007
1008 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1009
1010 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1011 (false, _) => "Debug",
1012 (true, false) => "Release",
1013 (true, true) => "RelWithDebInfo",
1014 };
1015
1016 cfg.out_dir(&out_dir)
1017 .profile(profile)
1018 .env("LLVM_CONFIG_REAL", &host_llvm_config)
1019 .define("LLVM_DIR", llvm_cmake_dir);
1020
1021 cfg.build();
1022
1023 if !dylib.exists() {
1024 eprintln!(
1025 "`{lib_rust_offload}` not found in `{}`. Either the build has failed or RustOffload was built with a wrong version of LLVM",
1026 build_dir.display()
1027 );
1028 exit!(1);
1029 }
1030
1031 BuiltRustOffload { offload: dylib }
1032 }
1033}
1034
1035#[derive(Clone)]
1036pub struct BuiltOmpOffload {
1037 offload: Vec<PathBuf>,
1039}
1040
1041impl BuiltOmpOffload {
1042 pub fn offload_paths(&self) -> Vec<PathBuf> {
1043 self.offload.clone()
1044 }
1045}
1046
1047#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1059pub struct OmpOffload {
1060 pub target: TargetSelection,
1061}
1062
1063impl CommandLineStep for OmpOffload {
1064 type Output = BuiltOmpOffload;
1065 const IS_HOST: bool = true;
1066
1067 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1068 run.path("src/llvm-project/offload")
1069 }
1070
1071 fn make_run(run: RunConfig<'_>) {
1072 run.builder.ensure(OmpOffload { target: run.target });
1073 }
1074
1075 #[allow(unused)]
1077 fn run(self, builder: &Builder<'_>) -> Self::Output {
1078 if builder.config.dry_run() {
1079 return BuiltOmpOffload {
1080 offload: vec![builder.config.tempdir().join("llvm-offload-dry-run")],
1081 };
1082 }
1083 let target = self.target;
1084
1085 let LlvmResult { host_llvm_config, llvm_cmake_dir } =
1086 builder.ensure(Llvm { target: self.target });
1087
1088 let out_dir = builder.omp_offload_out(target);
1092
1093 let mut files = vec![];
1094 let lib_ext = std::env::consts::DLL_EXTENSION;
1095 files.push(out_dir.join("lib").join("libLLVMOffload").with_extension(lib_ext));
1096 files.push(out_dir.join("lib").join("libomp").with_extension(lib_ext));
1097 files.push(out_dir.join("lib").join("libomptarget").with_extension(lib_ext));
1098
1099 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1101 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1102 generate_smart_stamp_hash(
1103 builder,
1104 &builder.config.src.join("src/llvm-project/offload"),
1105 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1106 )
1107 });
1108 let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash);
1109
1110 trace!("checking build stamp to see if we need to rebuild offload/openmp artifacts");
1111 if stamp.is_up_to_date() {
1112 trace!(?out_dir, "offload/openmp build artifacts are up to date");
1113 if stamp.stamp().is_empty() {
1114 builder.info(
1115 "Could not determine the Offload submodule commit hash. \
1116 Assuming that an Offload rebuild is not necessary.",
1117 );
1118 builder.info(&format!(
1119 "To force Offload/OpenMP to rebuild, remove the file `{}`",
1120 stamp.path().display()
1121 ));
1122 }
1123 return BuiltOmpOffload { offload: files };
1124 }
1125
1126 trace!(?target, "(re)building offload/openmp artifacts");
1127 builder.info(&format!("Building OpenMP/Offload for {target}"));
1128 t!(stamp.remove());
1129 let _time = helpers::timeit(builder);
1130 t!(fs::create_dir_all(&out_dir));
1131
1132 builder.config.update_submodule("src/llvm-project");
1133
1134 let clang_dir = if !builder.config.llvm_clang {
1137 assert!(&builder.build.config.llvm_clang_dir.is_some());
1139 builder.build.config.llvm_clang_dir.clone()
1140 } else {
1141 None
1143 };
1144
1145 let omp_targets = vec![target.triple.as_ref(), "amdgcn-amd-amdhsa", "nvptx64-nvidia-cuda"];
1149 for omp_target in omp_targets {
1150 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/"));
1151
1152 let mut cflags = CcFlags::default();
1158 if !builder.config.llvm_clang {
1159 let base = builder.llvm_out(target).join("include");
1160 let inc_dir = base.display();
1161 cflags.push_all(format!(" -I {inc_dir}"));
1162 }
1163
1164 configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]);
1165
1166 let profile =
1169 match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1170 (false, _) => "Debug",
1171 (true, false) => "Release",
1172 (true, true) => "RelWithDebInfo",
1173 };
1174 trace!(?profile);
1175
1176 cfg.out_dir(&out_dir)
1179 .profile(profile)
1180 .env("LLVM_CONFIG_REAL", &host_llvm_config)
1181 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1182 .define("LLVM_INCLUDE_TESTS", "OFF")
1183 .define("OFFLOAD_INCLUDE_TESTS", "OFF")
1184 .define("LLVM_ROOT", builder.llvm_out(target).join("build"))
1185 .define("LLVM_DIR", llvm_cmake_dir.clone())
1186 .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target);
1187 if let Some(p) = clang_dir.clone() {
1188 cfg.define("Clang_DIR", p);
1189 }
1190
1191 if *omp_target == *target.triple {
1194 cfg.define("LLVM_ENABLE_RUNTIMES", "openmp;offload");
1196 } else {
1197 cfg.define("LLVM_USE_LINKER", "lld");
1199 cfg.define("LLVM_ENABLE_RUNTIMES", "openmp");
1200 cfg.define("CMAKE_C_COMPILER_TARGET", omp_target);
1201 cfg.define("CMAKE_CXX_COMPILER_TARGET", omp_target);
1202 }
1203 cfg.build();
1204 }
1205
1206 t!(stamp.write());
1207
1208 for p in &files {
1209 if !p.exists() {
1212 eprintln!(
1213 "`{p:?}` not found in `{}`. Either the build has failed or Offload was built with a wrong version of LLVM",
1214 out_dir.display()
1215 );
1216 exit!(1);
1217 }
1218 }
1219 BuiltOmpOffload { offload: files }
1220 }
1221}
1222
1223#[derive(Clone)]
1224pub struct BuiltEnzyme {
1225 enzyme: PathBuf,
1227}
1228
1229impl BuiltEnzyme {
1230 pub fn enzyme_path(&self) -> PathBuf {
1231 self.enzyme.clone()
1232 }
1233 pub fn enzyme_filename(&self) -> String {
1234 self.enzyme.file_name().unwrap().to_str().unwrap().to_owned()
1235 }
1236}
1237
1238#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1239pub struct Enzyme {
1240 pub target: TargetSelection,
1241}
1242
1243impl CommandLineStep for Enzyme {
1244 type Output = BuiltEnzyme;
1245 const IS_HOST: bool = true;
1246
1247 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1248 run.path("src/tools/enzyme/enzyme")
1249 }
1250
1251 fn make_run(run: RunConfig<'_>) {
1252 run.builder.ensure(Enzyme { target: run.target });
1253 }
1254
1255 fn run(self, builder: &Builder<'_>) -> Self::Output {
1257 builder.require_submodule(
1258 "src/tools/enzyme",
1259 Some("The Enzyme sources are required for autodiff."),
1260 );
1261 let target = self.target;
1262
1263 if builder.config.dry_run() {
1264 return BuiltEnzyme { enzyme: builder.config.tempdir().join("enzyme-dryrun") };
1265 }
1266
1267 let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
1268
1269 let enzyme_hash_input = builder.in_tree_llvm_info.sha().unwrap_or_default().to_owned()
1273 + builder.enzyme_info.sha().unwrap_or_default();
1274
1275 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1276 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1277 generate_smart_stamp_hash(
1278 builder,
1279 &builder.config.src.join("src/tools/enzyme"),
1280 &enzyme_hash_input,
1281 )
1282 });
1283
1284 let out_dir = builder.enzyme_out(target);
1285 let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash);
1286
1287 let llvm_version_major = llvm::get_llvm_version_major(builder, &host_llvm_config);
1288 let lib_ext = std::env::consts::DLL_EXTENSION;
1289 let libenzyme = format!("libEnzyme-{llvm_version_major}");
1290 let build_dir = out_dir.join(libdir(target));
1291 let dylib = build_dir.join(&libenzyme).with_extension(lib_ext);
1292
1293 trace!("checking build stamp to see if we need to rebuild enzyme artifacts");
1294 if stamp.is_up_to_date() {
1295 trace!(?out_dir, "enzyme build artifacts are up to date");
1296 if stamp.stamp().is_empty() {
1297 builder.info(
1298 "Could not determine the Enzyme submodule commit hash. \
1299 Assuming that an Enzyme rebuild is not necessary.",
1300 );
1301 builder.info(&format!(
1302 "To force Enzyme to rebuild, remove the file `{}`",
1303 stamp.path().display()
1304 ));
1305 }
1306 return BuiltEnzyme { enzyme: dylib };
1307 }
1308
1309 if !builder.config.dry_run() && !llvm_cmake_dir.is_dir() {
1310 builder.info(&format!(
1311 "WARNING: {} does not exist, Enzyme build will likely fail",
1312 llvm_cmake_dir.display()
1313 ));
1314 }
1315
1316 trace!(?target, "(re)building enzyme artifacts");
1317 builder.info(&format!("Building Enzyme for {target}"));
1318 t!(stamp.remove());
1319 let _time = helpers::timeit(builder);
1320 t!(fs::create_dir_all(&out_dir));
1321
1322 let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/"));
1323 let mut cflags = CcFlags::default();
1327 cflags.push_all("-Wno-deprecated");
1328
1329 let mut ldflags = LdFlags::default();
1333 if builder.config.llvm_thin_lto && !target.contains("apple") {
1334 ldflags.push_all("-fuse-ld=lld");
1335 }
1336
1337 configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]);
1338
1339 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1343 (false, _) => "Debug",
1344 (true, false) => "Release",
1345 (true, true) => "RelWithDebInfo",
1346 };
1347 trace!(?profile);
1348
1349 cfg.out_dir(&out_dir)
1350 .profile(profile)
1351 .env("LLVM_CONFIG_REAL", &host_llvm_config)
1352 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1353 .define("ENZYME_EXTERNAL_SHARED_LIB", "ON")
1354 .define("ENZYME_BC_LOADER", "OFF")
1355 .define("LLVM_DIR", llvm_cmake_dir);
1356
1357 cfg.build();
1358
1359 if !dylib.exists() {
1362 eprintln!(
1363 "`{libenzyme}` not found in `{}`. Either the build has failed or Enzyme was built with a wrong version of LLVM",
1364 build_dir.display()
1365 );
1366 exit!(1);
1367 }
1368
1369 t!(stamp.write());
1370 BuiltEnzyme { enzyme: dylib }
1371 }
1372}
1373
1374#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1375pub struct Lld {
1376 pub target: TargetSelection,
1377}
1378
1379impl CommandLineStep for Lld {
1380 type Output = PathBuf;
1381 const IS_HOST: bool = true;
1382
1383 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1384 run.path("src/llvm-project/lld")
1385 }
1386
1387 fn make_run(run: RunConfig<'_>) {
1388 run.builder.ensure(Lld { target: run.target });
1389 }
1390
1391 fn run(self, builder: &Builder<'_>) -> PathBuf {
1393 if builder.config.dry_run() {
1394 return PathBuf::from("lld-out-dir-test-gen");
1395 }
1396 let target = self.target;
1397
1398 let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
1399
1400 let ci_llvm_bin = host_llvm_config.parent().unwrap();
1405 if ci_llvm_bin.is_dir() && ci_llvm_bin.file_name().unwrap() == "bin" {
1406 let lld_path = ci_llvm_bin.join(exe("lld", target));
1407 if lld_path.exists() {
1408 return ci_llvm_bin.parent().unwrap().to_path_buf();
1411 }
1412 }
1413
1414 let out_dir = builder.lld_out(target);
1415
1416 let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld");
1417 if lld_stamp.path().exists() {
1418 return out_dir;
1419 }
1420
1421 let _guard = builder.msg_unstaged(Kind::Build, "LLD", target);
1422 let _time = helpers::timeit(builder);
1423 t!(fs::create_dir_all(&out_dir));
1424
1425 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
1426 let mut ldflags = LdFlags::default();
1427
1428 if builder.config.llvm_pgo.generate_profile.is_some()
1433 && target.is_msvc()
1434 && let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref()
1435 {
1436 let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
1439 ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
1440 }
1441
1442 if builder.config.rpath_enabled(target)
1454 && helpers::use_host_linker(target)
1455 && builder.config.llvm_link_shared()
1456 && target.contains("linux")
1457 {
1458 ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'");
1465 }
1466
1467 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1468 configure_llvm(builder, target, &mut cfg);
1469
1470 let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
1473 (false, _) => "Debug",
1474 (true, false) => "Release",
1475 (true, true) => "RelWithDebInfo",
1476 };
1477
1478 cfg.out_dir(&out_dir)
1479 .profile(profile)
1480 .define("LLVM_CMAKE_DIR", llvm_cmake_dir)
1481 .define("LLVM_INCLUDE_TESTS", "OFF");
1482
1483 if !builder.config.is_host_target(target) {
1484 cfg.define(
1486 "LLVM_TABLEGEN_EXE",
1487 host_llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
1488 );
1489 }
1490
1491 cfg.build();
1492
1493 t!(lld_stamp.write());
1494 out_dir
1495 }
1496}
1497
1498#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1499pub struct Sanitizers {
1500 pub target: TargetSelection,
1501}
1502
1503impl CommandLineStep for Sanitizers {
1504 type Output = Vec<SanitizerRuntime>;
1505
1506 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1507 run.alias("sanitizers")
1508 }
1509
1510 fn make_run(run: RunConfig<'_>) {
1511 run.builder.ensure(Sanitizers { target: run.target });
1512 }
1513
1514 fn run(self, builder: &Builder<'_>) -> Self::Output {
1516 let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
1517 if !compiler_rt_dir.exists() {
1518 return Vec::new();
1519 }
1520
1521 let out_dir = builder.native_dir(self.target).join("sanitizers");
1522 let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel);
1523
1524 if builder.config.dry_run() || runtimes.is_empty() {
1525 return runtimes;
1526 }
1527
1528 let LlvmResult { host_llvm_config, .. } =
1529 builder.ensure(Llvm { target: builder.config.host_target });
1530
1531 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1532 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1533 generate_smart_stamp_hash(
1534 builder,
1535 &builder.config.src.join("src/llvm-project/compiler-rt"),
1536 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1537 )
1538 });
1539
1540 let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash);
1541
1542 if stamp.is_up_to_date() {
1543 if stamp.stamp().is_empty() {
1544 builder.info(&format!(
1545 "Rebuild sanitizers by removing the file `{}`",
1546 stamp.path().display()
1547 ));
1548 }
1549
1550 return runtimes;
1551 }
1552
1553 let _guard = builder.msg_unstaged(Kind::Build, "sanitizers", self.target);
1554 t!(stamp.remove());
1555 let _time = helpers::timeit(builder);
1556
1557 let mut cfg = cmake::Config::new(&compiler_rt_dir);
1558 cfg.profile("Release");
1559 cfg.define("CMAKE_C_COMPILER_TARGET", self.target.triple);
1560 cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
1561 cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
1562 cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
1563 cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
1564 cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
1565 cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
1566 cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
1567 cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
1568 cfg.define("LLVM_CONFIG_PATH", &host_llvm_config);
1569
1570 if self.target.contains("ohos") {
1571 cfg.define("COMPILER_RT_USE_BUILTINS_LIBRARY", "ON");
1572 }
1573
1574 let use_compiler_launcher = !self.target.contains("apple-darwin");
1578 let suppressed_compiler_flag_prefixes: &[&str] =
1583 if self.target.contains("apple-darwin") { &["-mmacosx-version-min="] } else { &[] };
1584 configure_cmake(
1585 builder,
1586 self.target,
1587 &mut cfg,
1588 use_compiler_launcher,
1589 LdFlags::default(),
1590 CcFlags::default(),
1591 suppressed_compiler_flag_prefixes,
1592 );
1593
1594 t!(fs::create_dir_all(&out_dir));
1595 cfg.out_dir(out_dir);
1596
1597 for runtime in &runtimes {
1598 cfg.build_target(&runtime.cmake_target);
1599 cfg.build();
1600 }
1601 t!(stamp.write());
1602
1603 runtimes
1604 }
1605}
1606
1607#[derive(Clone, Debug)]
1608pub struct SanitizerRuntime {
1609 pub cmake_target: String,
1611 pub path: PathBuf,
1613 pub name: String,
1615}
1616
1617fn supported_sanitizers(
1619 out_dir: &Path,
1620 target: TargetSelection,
1621 channel: &str,
1622) -> Vec<SanitizerRuntime> {
1623 let darwin_libs = |os: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1624 components
1625 .iter()
1626 .map(move |c| {
1627 let cmake_c = if *c == "ubsan" { "ubsan_standalone" } else { *c };
1628 SanitizerRuntime {
1629 cmake_target: format!("clang_rt.{cmake_c}_{os}_dynamic"),
1630 path: out_dir
1631 .join(format!("build/lib/darwin/libclang_rt.{cmake_c}_{os}_dynamic.dylib")),
1632 name: format!("librustc-{channel}_rt.{c}.dylib"),
1633 }
1634 })
1635 .collect()
1636 };
1637
1638 let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1639 components
1640 .iter()
1641 .map(move |c| {
1642 let cmake_c = if *c == "ubsan" { "ubsan_standalone" } else { *c };
1643 SanitizerRuntime {
1644 cmake_target: format!("clang_rt.{cmake_c}-{arch}"),
1645 path: out_dir.join(format!("build/lib/{os}/libclang_rt.{cmake_c}-{arch}.a")),
1646 name: format!("librustc-{channel}_rt.{c}.a"),
1647 }
1648 })
1649 .collect()
1650 };
1651
1652 match &*target.triple {
1653 "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1654 "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan", "rtsan"]),
1655 "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan", "rtsan"]),
1656 "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1657 "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
1658 "aarch64-unknown-linux-gnu" => common_libs(
1659 "linux",
1660 "aarch64",
1661 &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan"],
1662 ),
1663 "aarch64-unknown-linux-ohos" => {
1664 common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
1665 }
1666 "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => {
1667 common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"])
1668 }
1669 "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1670 "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
1671 "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]),
1672 "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1673 "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]),
1674 "x86_64-unknown-netbsd" => {
1675 common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"])
1676 }
1677 "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
1678 "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
1679 "x86_64-unknown-linux-gnu" => common_libs(
1680 "linux",
1681 "x86_64",
1682 &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan", "ubsan"],
1683 ),
1684 "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]),
1685 "x86_64-unknown-linux-gnumsan" => common_libs("linux", "x86_64", &["msan"]),
1686 "x86_64-unknown-linux-gnutsan" => common_libs("linux", "x86_64", &["tsan"]),
1687 "x86_64-unknown-linux-musl" => {
1688 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1689 }
1690 "s390x-unknown-linux-gnu" => {
1691 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1692 }
1693 "s390x-unknown-linux-musl" => {
1694 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1695 }
1696 "x86_64-unknown-linux-ohos" => {
1697 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1698 }
1699 _ => Vec::new(),
1700 }
1701}
1702
1703#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1704pub struct CrtBeginEnd {
1705 pub target: TargetSelection,
1706}
1707
1708impl CommandLineStep for CrtBeginEnd {
1709 type Output = PathBuf;
1710
1711 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1712 run.path("src/llvm-project/compiler-rt/lib/crt")
1713 }
1714
1715 fn make_run(run: RunConfig<'_>) {
1716 if run.target.needs_crt_begin_end() {
1717 run.builder.ensure(CrtBeginEnd { target: run.target });
1718 }
1719 }
1720
1721 fn run(self, builder: &Builder<'_>) -> Self::Output {
1723 builder.require_submodule(
1724 "src/llvm-project",
1725 Some("The LLVM sources are required for the CRT from `compiler-rt`."),
1726 );
1727
1728 let out_dir = builder.native_dir(self.target).join("crt");
1729
1730 if builder.config.dry_run() {
1731 return out_dir;
1732 }
1733
1734 let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtbegin.c");
1735 let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtend.c");
1736 if up_to_date(&crtbegin_src, &out_dir.join("crtbeginS.o"))
1737 && up_to_date(&crtend_src, &out_dir.join("crtendS.o"))
1738 {
1739 return out_dir;
1740 }
1741
1742 let _guard = builder.msg_unstaged(Kind::Build, "crtbegin.o and crtend.o", self.target);
1743 t!(fs::create_dir_all(&out_dir));
1744
1745 let mut cfg = cc::Build::new();
1746
1747 if let Some(ar) = builder.ar(self.target) {
1748 cfg.archiver(ar);
1749 }
1750 cfg.compiler(builder.cc(self.target));
1751 cfg.cargo_metadata(false)
1752 .out_dir(&out_dir)
1753 .target(&self.target.triple)
1754 .host(&builder.config.host_target.triple)
1755 .warnings(false)
1756 .debug(false)
1757 .opt_level(3)
1758 .file(crtbegin_src)
1759 .file(crtend_src);
1760
1761 cfg.flag("-std=c11")
1765 .define("CRT_HAS_INITFINI_ARRAY", None)
1766 .define("EH_USE_FRAME_REGISTRY", None);
1767
1768 let objs = cfg.compile_intermediates();
1769 assert_eq!(objs.len(), 2);
1770 for obj in objs {
1771 let base_name = unhashed_basename(&obj);
1772 assert!(base_name == "crtbegin" || base_name == "crtend");
1773 t!(fs::copy(&obj, out_dir.join(format!("{base_name}S.o"))));
1774 t!(fs::rename(&obj, out_dir.join(format!("{base_name}.o"))));
1775 }
1776
1777 out_dir
1778 }
1779}
1780
1781#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1782pub struct Libunwind {
1783 pub target: TargetSelection,
1784}
1785
1786impl CommandLineStep for Libunwind {
1787 type Output = PathBuf;
1788
1789 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1790 run.path("src/llvm-project/libunwind")
1791 }
1792
1793 fn make_run(run: RunConfig<'_>) {
1794 run.builder.ensure(Libunwind { target: run.target });
1795 }
1796
1797 fn run(self, builder: &Builder<'_>) -> Self::Output {
1799 builder.require_submodule(
1800 "src/llvm-project",
1801 Some("The LLVM sources are required for libunwind."),
1802 );
1803
1804 if builder.config.dry_run() {
1805 return PathBuf::new();
1806 }
1807
1808 let out_dir = builder.native_dir(self.target).join("libunwind");
1809 let root = builder.src.join("src/llvm-project/libunwind");
1810
1811 if up_to_date(&root, &out_dir.join("libunwind.a")) {
1812 return out_dir;
1813 }
1814
1815 let _guard = builder.msg_unstaged(Kind::Build, "libunwind.a", self.target);
1816 t!(fs::create_dir_all(&out_dir));
1817
1818 let mut cc_cfg = cc::Build::new();
1819 let mut cpp_cfg = cc::Build::new();
1820
1821 cpp_cfg.cpp(true);
1822 cpp_cfg.cpp_set_stdlib(None);
1823 cpp_cfg.flag("-nostdinc++");
1824 cpp_cfg.flag("-fno-exceptions");
1825 cpp_cfg.flag("-fno-rtti");
1826 cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
1827
1828 for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() {
1829 if let Some(ar) = builder.ar(self.target) {
1830 cfg.archiver(ar);
1831 }
1832 cfg.target(&self.target.triple);
1833 cfg.host(&builder.config.host_target.triple);
1834 cfg.warnings(false);
1835 cfg.debug(false);
1836 cfg.opt_level(3);
1838 cfg.flag("-fstrict-aliasing");
1839 cfg.flag("-funwind-tables");
1840 cfg.flag("-fvisibility=hidden");
1841 cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
1842 cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
1843 cfg.include(root.join("include"));
1844 cfg.cargo_metadata(false);
1845 cfg.out_dir(&out_dir);
1846
1847 if self.target.contains("x86_64-fortanix-unknown-sgx") {
1848 cfg.flag("-fno-stack-protector");
1849 cfg.flag("-ffreestanding");
1850 cfg.flag("-fexceptions");
1851
1852 cfg.flag("-U_FORTIFY_SOURCE");
1854 cfg.define("_FORTIFY_SOURCE", "0");
1855 cfg.define("RUST_SGX", "1");
1856 cfg.define("__NO_STRING_INLINES", None);
1857 cfg.define("__NO_MATH_INLINES", None);
1858 cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
1859 cfg.define("NDEBUG", None);
1860 }
1861 if self.target.is_windows() {
1862 cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
1863 }
1864 }
1865
1866 cc_cfg.compiler(builder.cc(self.target));
1867 if let Ok(cxx) = builder.cxx(self.target) {
1868 cpp_cfg.compiler(cxx);
1869 } else {
1870 cc_cfg.compiler(builder.cc(self.target));
1871 }
1872
1873 if cc_cfg.get_compiler().is_like_gnu() {
1878 cc_cfg.flag("-std=c99");
1879 }
1880 if cpp_cfg.get_compiler().is_like_gnu() {
1881 cpp_cfg.flag("-std=c++11");
1882 }
1883
1884 if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") {
1885 if cpp_cfg.get_compiler().is_like_gnu() {
1889 cpp_cfg.cpp(false);
1890 cpp_cfg.compiler(builder.cc(self.target));
1891 }
1892 }
1893
1894 let mut c_sources = vec![
1895 "Unwind-sjlj.c",
1896 "UnwindLevel1-gcc-ext.c",
1897 "UnwindLevel1.c",
1898 "UnwindRegistersRestore.S",
1899 "UnwindRegistersSave.S",
1900 ];
1901
1902 let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"];
1903 let cpp_len = cpp_sources.len();
1904
1905 if self.target.contains("x86_64-fortanix-unknown-sgx") {
1906 c_sources.push("UnwindRustSgx.c");
1907 }
1908
1909 for src in c_sources {
1910 cc_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1911 }
1912
1913 for src in &cpp_sources {
1914 cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1915 }
1916
1917 cpp_cfg.compile("unwind-cpp");
1918
1919 let mut count = 0;
1921 let mut files = fs::read_dir(&out_dir)
1922 .unwrap()
1923 .map(|entry| entry.unwrap().path().canonicalize().unwrap())
1924 .collect::<Vec<_>>();
1925 files.sort();
1926 for file in files {
1927 if file.is_file() && file.extension() == Some(OsStr::new("o")) {
1928 let base_name = unhashed_basename(&file);
1930 if cpp_sources.iter().any(|f| *base_name == f[..f.len() - 4]) {
1931 cc_cfg.object(&file);
1932 count += 1;
1933 }
1934 }
1935 }
1936 assert_eq!(cpp_len, count, "Can't get object files from {out_dir:?}");
1937
1938 cc_cfg.compile("unwind");
1939 out_dir
1940 }
1941}