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::{
21 Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata,
22};
23use crate::core::config::{Config, LlvmPgoGenerationMode, TargetSelection};
24use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
25use crate::utils::exec::command;
26use crate::utils::helpers::{
27 self, exe, get_clang_cl_resource_dir, libdir, t, unhashed_basename, up_to_date,
28};
29use crate::{CLang, GitRepo, exit, trace};
30
31#[derive(Clone)]
33pub struct LlvmOutput {
34 pub host_llvm_config: PathBuf,
37 llvm_root_dir: PathBuf,
38}
39
40impl LlvmOutput {
41 pub fn root_dir(&self) -> &Path {
44 &self.llvm_root_dir
45 }
46
47 pub fn cmake_dir(&self) -> PathBuf {
49 self.llvm_root_dir.join("lib").join("cmake").join("llvm")
50 }
51}
52
53pub struct LlvmBuildInfo {
54 stamp: BuildStamp,
55 output: LlvmOutput,
56}
57
58pub enum LlvmBuildStatus {
59 AlreadyBuilt(LlvmOutput),
60 ShouldBuild(LlvmBuildInfo),
61}
62
63impl LlvmBuildStatus {
64 pub fn should_build(&self) -> bool {
65 match self {
66 LlvmBuildStatus::AlreadyBuilt(_) => false,
67 LlvmBuildStatus::ShouldBuild(_) => true,
68 }
69 }
70
71 #[cfg(test)]
72 pub fn llvm_result(&self) -> &LlvmOutput {
73 match self {
74 LlvmBuildStatus::AlreadyBuilt(res) => res,
75 LlvmBuildStatus::ShouldBuild(meta) => &meta.output,
76 }
77 }
78}
79
80#[derive(Debug, Clone, Default)]
82struct CcFlags {
83 cflags: OsString,
85 cxxflags: OsString,
87}
88
89impl CcFlags {
90 fn push_all(&mut self, s: impl AsRef<OsStr>) {
91 let s = s.as_ref();
92 self.cflags.push(" ");
93 self.cflags.push(s);
94 self.cxxflags.push(" ");
95 self.cxxflags.push(s);
96 }
97}
98
99#[derive(Debug, Clone, Default)]
101struct LdFlags {
102 exe: OsString,
104 shared: OsString,
106 module: OsString,
108}
109
110impl LdFlags {
111 fn push_all(&mut self, s: impl AsRef<OsStr>) {
112 let s = s.as_ref();
113 self.exe.push(" ");
114 self.exe.push(s);
115 self.shared.push(" ");
116 self.shared.push(s);
117 self.module.push(" ");
118 self.module.push(s);
119 }
120}
121
122pub fn prebuilt_llvm_config(
130 builder: &Builder<'_>,
131 target: TargetSelection,
132 handle_submodule_when_needed: bool,
136) -> LlvmBuildStatus {
137 builder.config.maybe_download_ci_llvm();
138
139 if let Some(config) = builder.config.target_config.get(&target)
142 && let Some(ref s) = config.llvm_config
143 {
144 check_llvm_version(builder, s);
145 let host_llvm_config = s.to_path_buf();
146 let mut llvm_root_dir = host_llvm_config.clone();
147 llvm_root_dir.pop();
148 llvm_root_dir.pop();
149 return LlvmBuildStatus::AlreadyBuilt(LlvmOutput { host_llvm_config, llvm_root_dir });
150 }
151
152 if handle_submodule_when_needed {
153 builder.config.update_submodule("src/llvm-project");
155 }
156
157 let out_dir = builder.llvm_out(target);
158
159 let build_llvm_config = if let Some(build_llvm_config) = builder
160 .config
161 .target_config
162 .get(&builder.config.host_target)
163 .and_then(|config| config.llvm_config.clone())
164 {
165 build_llvm_config
166 } else {
167 let mut llvm_config_ret_dir = builder.llvm_out(builder.config.host_target);
168 llvm_config_ret_dir.push("bin");
169 llvm_config_ret_dir.join(exe("llvm-config", builder.config.host_target))
170 };
171
172 let res = LlvmOutput { host_llvm_config: build_llvm_config, llvm_root_dir: out_dir.clone() };
173
174 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
175 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
176 generate_smart_stamp_hash(
177 builder,
178 &builder.config.src.join("src/llvm-project"),
179 builder.in_tree_llvm_info.sha().unwrap_or_default(),
180 )
181 });
182
183 let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash);
184
185 if stamp.is_up_to_date() {
186 if stamp.stamp().is_empty() {
187 builder.info(
188 "Could not determine the LLVM submodule commit hash. \
189 Assuming that an LLVM rebuild is not necessary.",
190 );
191 builder.info(&format!(
192 "To force LLVM to rebuild, remove the file `{}`",
193 stamp.path().display()
194 ));
195 }
196 return LlvmBuildStatus::AlreadyBuilt(res);
197 }
198
199 LlvmBuildStatus::ShouldBuild(LlvmBuildInfo { stamp, output: res })
200}
201
202pub const LLVM_INVALIDATION_PATHS: &[&str] = &[
204 "src/llvm-project",
205 "src/bootstrap/download-ci-llvm-stamp",
206 "src/version",
208];
209
210pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshness {
212 assert!(cfg!(not(test)), "unit tests shouldn't care about LLVM freshness");
213
214 if is_git {
215 config.check_path_modifications(LLVM_INVALIDATION_PATHS)
216 } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
217 PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() }
218 } else {
219 PathFreshness::MissingUpstream
220 }
221}
222
223pub(crate) fn is_ci_llvm_available_for_target(
228 host_target: &TargetSelection,
229 asserts: bool,
230) -> bool {
231 let supported_platforms = [
235 ("aarch64-unknown-linux-gnu", false),
237 ("aarch64-apple-darwin", false),
238 ("aarch64-pc-windows-msvc", false),
239 ("i686-pc-windows-msvc", false),
240 ("i686-unknown-linux-gnu", false),
241 ("x86_64-unknown-linux-gnu", true),
242 ("x86_64-apple-darwin", true),
243 ("x86_64-pc-windows-gnu", false),
244 ("x86_64-pc-windows-msvc", true),
245 ("aarch64-unknown-linux-musl", false),
247 ("aarch64-pc-windows-gnullvm", false),
248 ("arm-unknown-linux-gnueabi", false),
249 ("arm-unknown-linux-gnueabihf", false),
250 ("armv7-unknown-linux-gnueabihf", false),
251 ("i686-pc-windows-gnu", false),
252 ("loongarch64-unknown-linux-gnu", false),
253 ("loongarch64-unknown-linux-musl", false),
254 ("powerpc-unknown-linux-gnu", false),
255 ("powerpc64-unknown-linux-gnu", false),
256 ("powerpc64-unknown-linux-musl", false),
257 ("powerpc64le-unknown-linux-gnu", false),
258 ("powerpc64le-unknown-linux-musl", false),
259 ("riscv64gc-unknown-linux-gnu", false),
260 ("riscv64gc-unknown-linux-musl", false),
261 ("s390x-unknown-linux-gnu", false),
262 ("x86_64-pc-windows-gnullvm", false),
263 ("x86_64-unknown-freebsd", false),
264 ("x86_64-unknown-illumos", false),
265 ("x86_64-unknown-linux-musl", false),
266 ("x86_64-unknown-netbsd", false),
267 ];
268
269 if !supported_platforms.contains(&(&*host_target.triple, asserts))
270 && (asserts || !supported_platforms.contains(&(&*host_target.triple, true)))
271 {
272 return false;
273 }
274
275 true
276}
277
278#[derive(Debug, Clone, Hash, PartialEq, Eq)]
279pub struct Llvm {
280 pub target: TargetSelection,
281}
282
283impl CommandLineStep for Llvm {
284 type Output = LlvmOutput;
285
286 const IS_HOST: bool = true;
287
288 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
289 run.path("src/llvm-project").path("src/llvm-project/llvm")
290 }
291
292 fn make_run(run: RunConfig<'_>) {
293 run.builder.ensure(Llvm { target: run.target });
294 }
295
296 fn run(self, builder: &Builder<'_>) -> LlvmOutput {
298 let target = self.target;
299 let target_native = if self.target.starts_with("riscv") {
300 let idx = target.triple.find('-').unwrap();
303
304 format!("riscv{}{}", &target.triple[5..7], &target.triple[idx..])
305 } else if self.target.starts_with("powerpc") && self.target.ends_with("freebsd") {
306 format!("{}{}", self.target, "13.0")
309 } else {
310 target.to_string()
311 };
312
313 let LlvmBuildInfo { stamp, output } = match prebuilt_llvm_config(builder, target, true) {
315 LlvmBuildStatus::AlreadyBuilt(p) => return p,
316 LlvmBuildStatus::ShouldBuild(m) => m,
317 };
318
319 if builder.llvm_link_shared() && target.is_windows() && !target.is_windows_gnullvm() {
320 panic!("shared linking to LLVM is not currently supported on {}", target.triple);
321 }
322
323 let _guard = builder.msg_unstaged(Kind::Build, "LLVM", target);
324 t!(stamp.remove());
325 let _time = helpers::timeit(builder);
326 t!(fs::create_dir_all(output.root_dir()));
327
328 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/llvm"));
330 let mut ldflags = LdFlags::default();
331
332 let profile = get_llvm_profile(&builder.config);
333
334 let llvm_targets = match &builder.config.llvm_targets {
337 Some(s) => s,
338 None => {
339 "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
340 Sparc;SystemZ;WebAssembly;X86"
341 }
342 };
343
344 let llvm_exp_targets = match builder.config.llvm_experimental_targets {
345 Some(ref s) => s,
346 None => "AVR;M68k;CSKY;Xtensa",
347 };
348
349 let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" };
350 let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" };
351 let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" };
352 let enable_warnings = if builder.config.llvm_enable_warnings { "ON" } else { "OFF" };
353
354 cfg.out_dir(output.root_dir())
355 .profile(profile)
356 .define("LLVM_ENABLE_ASSERTIONS", assertions)
357 .define("LLVM_UNREACHABLE_OPTIMIZE", "OFF")
358 .define("LLVM_ENABLE_PLUGINS", plugins)
359 .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
360 .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
361 .define("LLVM_INCLUDE_EXAMPLES", "OFF")
362 .define("LLVM_INCLUDE_DOCS", "OFF")
363 .define("LLVM_INCLUDE_BENCHMARKS", "OFF")
364 .define("LLVM_INCLUDE_TESTS", enable_tests)
365 .define("LLVM_ENABLE_LIBEDIT", "OFF")
366 .define("LLVM_ENABLE_BINDINGS", "OFF")
367 .define("LLVM_ENABLE_Z3_SOLVER", "OFF")
368 .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string())
369 .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap())
370 .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native)
371 .define("LLVM_ENABLE_WARNINGS", enable_warnings);
372
373 cfg.define("LLVM_INSTALL_UTILS", "ON");
377
378 if let Some(mode) = builder.config.llvm_pgo.generate_profile.as_ref() {
379 cfg.define("LLVM_BUILD_INSTRUMENTED", "IR");
380 match mode {
381 LlvmPgoGenerationMode::Implicit => {}
382 LlvmPgoGenerationMode::Directory(llvm_profile_dir) => {
383 cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir);
384 }
385 }
386 cfg.define("LLVM_BUILD_RUNTIME", "No");
387 }
388 if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() {
389 cfg.define("LLVM_PROFDATA_FILE", path);
390 }
391
392 if !target.is_msvc() {
394 cfg.define("LLVM_ENABLE_ZLIB", "ON");
395 } else {
396 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
397 }
398
399 if target.contains("apple-ios")
401 || target.contains("apple-tvos")
402 || target.contains("apple-watchos")
403 || target.contains("apple-visionos")
404 {
405 cfg.define("LLVM_ENABLE_PLUGINS", "OFF");
407 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
409 }
410
411 if builder.llvm_link_shared() {
416 cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
417 cfg.define("LLVM_VERSIONED_DYLIB_NAME_ON_DARWIN", "OFF");
419 }
420
421 if (target.starts_with("csky")
422 || target.starts_with("riscv")
423 || target.starts_with("sparc-"))
424 && !target.contains("freebsd")
425 && !target.contains("openbsd")
426 && !target.contains("netbsd")
427 {
428 ldflags.exe.push(" -latomic");
437 ldflags.shared.push(" -latomic");
438 }
439
440 if target.starts_with("mips") && target.contains("netbsd") {
441 ldflags.exe.push(" -latomic");
443 ldflags.shared.push(" -latomic");
444 }
445
446 if target.starts_with("arm64ec") {
447 ldflags.exe.push(" -machine:arm64ec");
450 ldflags.shared.push(" -machine:arm64ec");
451 }
452
453 if target.contains("musl") && builder.crt_static(target).unwrap_or(true) {
456 ldflags.exe.push(" -static");
457 }
458
459 if target.is_msvc() {
460 cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded");
461 cfg.static_crt(true);
462 }
463
464 if target.starts_with("i686") {
465 cfg.define("LLVM_BUILD_32_BITS", "ON");
466 }
467
468 if target.starts_with("x86_64") && target.contains("ohos") {
469 cfg.define("LLVM_TOOL_LLVM_RTDYLD_BUILD", "OFF");
470 }
471
472 let mut enabled_llvm_projects = Vec::new();
473
474 if helpers::forcing_clang_based_tests() {
475 enabled_llvm_projects.push("clang");
476 }
477
478 if builder.config.llvm_polly {
479 enabled_llvm_projects.push("polly");
480 }
481
482 if builder.config.llvm_clang {
483 enabled_llvm_projects.push("clang");
484 }
485
486 cfg.define("LLVM_ENABLE_LIBXML2", "OFF");
489
490 let mut enabled_llvm_runtimes = Vec::new();
491
492 if helpers::forcing_clang_based_tests() {
493 enabled_llvm_runtimes.push("compiler-rt");
494 }
495
496 if !enabled_llvm_projects.is_empty() {
497 enabled_llvm_projects.sort();
498 enabled_llvm_projects.dedup();
499 cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
500 }
501
502 if !enabled_llvm_runtimes.is_empty() {
503 enabled_llvm_runtimes.sort();
504 enabled_llvm_runtimes.dedup();
505 cfg.define("LLVM_ENABLE_RUNTIMES", enabled_llvm_runtimes.join(";"));
506 }
507
508 if let Some(num_linkers) = builder.config.llvm_link_jobs
509 && num_linkers > 0
510 {
511 cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
512 }
513
514 if !builder.config.is_host_target(target) {
516 let LlvmOutput { host_llvm_config, .. } =
517 builder.ensure(Llvm { target: builder.config.host_target });
518 if !builder.config.dry_run() {
519 let llvm_bindir = command(&host_llvm_config)
520 .arg("--bindir")
521 .cached()
522 .run_capture_stdout(builder)
523 .stdout();
524 let host_bin = Path::new(llvm_bindir.trim());
525 cfg.define(
526 "LLVM_TABLEGEN",
527 host_bin.join("llvm-tblgen").with_extension(EXE_EXTENSION),
528 );
529 cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION));
531 }
532 cfg.define("LLVM_CONFIG_PATH", host_llvm_config);
533 if builder.config.llvm_clang {
534 let build_bin =
535 builder.llvm_out(builder.config.host_target).join("build").join("bin");
536 let clang_tblgen = build_bin.join("clang-tblgen").with_extension(EXE_EXTENSION);
537 if !builder.config.dry_run() && !clang_tblgen.exists() {
538 panic!("unable to find {}", clang_tblgen.display());
539 }
540 cfg.define("CLANG_TABLEGEN", clang_tblgen);
541 }
542 }
543
544 let llvm_version_suffix = if let Some(ref suffix) = builder.config.llvm_version_suffix {
545 if !suffix.is_empty() { Some(suffix.to_string()) } else { None }
547 } else if builder.config.channel == "dev" {
548 Some("-rust-dev".to_string())
552 } else {
553 Some(format!("-rust-{}-{}", builder.version, builder.config.channel))
554 };
555 if let Some(ref suffix) = llvm_version_suffix {
556 cfg.define("LLVM_VERSION_SUFFIX", suffix);
557 }
558
559 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
560 configure_llvm(builder, target, &mut cfg);
561
562 for (key, val) in &builder.config.llvm_build_config {
563 cfg.define(key, val);
564 }
565
566 if builder.config.dry_run() {
567 return output;
568 }
569
570 cfg.build();
571
572 let find_llvm_lib_name = |extension| {
574 let major = get_llvm_version_major(builder, &output.host_llvm_config);
575 match &llvm_version_suffix {
576 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
577 None => format!("libLLVM-{major}.{extension}"),
578 }
579 };
580
581 if builder.llvm_link_shared() && target.contains("apple-darwin") {
586 let lib_name = find_llvm_lib_name("dylib");
587 let lib_llvm = output.root_dir().join("build").join("lib").join(lib_name);
588 if !lib_llvm.exists() {
589 t!(builder.symlink_file("libLLVM.dylib", &lib_llvm));
590 }
591 }
592
593 if builder.llvm_link_shared()
597 && builder.config.llvm_optimize
598 && !builder.config.llvm_release_debuginfo
599 {
600 let lib_name = find_llvm_lib_name("so");
602
603 crate::core::build_steps::compile::strip_debug(
606 builder,
607 target,
608 &output.root_dir().join("lib").join(&lib_name),
609 );
610 crate::core::build_steps::compile::strip_debug(
611 builder,
612 target,
613 &output.root_dir().join("build").join("lib").join(&lib_name),
614 );
615 }
616
617 t!(stamp.write());
618
619 output
620 }
621
622 fn metadata(&self) -> Option<StepMetadata> {
623 Some(StepMetadata::build("llvm", self.target))
624 }
625}
626
627pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String {
628 command(llvm_config)
629 .arg("--version")
630 .cached()
631 .run_capture_stdout(builder)
632 .stdout()
633 .trim()
634 .to_owned()
635}
636
637pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 {
638 let version = get_llvm_version(builder, llvm_config);
639 let major_str = version.split_once('.').expect("Failed to parse LLVM version").0;
640 major_str.parse().unwrap()
641}
642
643fn get_llvm_profile(config: &Config) -> &'static str {
644 match (config.llvm_optimize, config.llvm_release_debuginfo) {
645 (false, _) => "Debug",
646 (true, false) => "Release",
647 (true, true) => "RelWithDebInfo",
648 }
649}
650
651fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
652 if builder.config.dry_run() {
653 return;
654 }
655
656 let version = get_llvm_version(builder, llvm_config);
657 let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
658 if let (Some(major), Some(_minor)) = (parts.next(), parts.next())
659 && major >= 21
660 {
661 return;
662 }
663 panic!("\n\nbad LLVM version: {version}, need >=21\n\n")
664}
665
666fn configure_cmake(
667 builder: &Builder<'_>,
668 target: TargetSelection,
669 cfg: &mut cmake::Config,
670 use_compiler_launcher: bool,
671 mut ldflags: LdFlags,
672 ccflags: CcFlags,
673 suppressed_compiler_flag_prefixes: &[&str],
674) {
675 cfg.define("CMAKE_INSTALL_MESSAGE", "LAZY");
678
679 if builder.config.quiet {
680 cfg.define("CMAKE_MESSAGE_LOG_LEVEL", "WARNING");
682
683 if builder.ninja() {
687 cfg.build_arg("--quiet");
688 }
689 }
690
691 cfg.env("DESTDIR", "");
695
696 if builder.ninja() {
697 cfg.generator("Ninja");
698 }
699 cfg.target(&target.triple).host(&builder.config.host_target.triple);
700
701 if !builder.config.is_host_target(target) {
702 cfg.define("CMAKE_CROSSCOMPILING", "True");
703
704 if target.contains("netbsd") {
710 cfg.define("CMAKE_SYSTEM_NAME", "NetBSD");
711 } else if target.contains("dragonfly") {
712 cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
713 } else if target.contains("openbsd") {
714 cfg.define("CMAKE_SYSTEM_NAME", "OpenBSD");
715 } else if target.contains("freebsd") {
716 cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
717 } else if target.is_windows() {
718 cfg.define("CMAKE_SYSTEM_NAME", "Windows");
719 } else if target.contains("haiku") {
720 cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
721 } else if target.contains("solaris") || target.contains("illumos") {
722 cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
723 } else if target.contains("linux") {
724 cfg.define("CMAKE_SYSTEM_NAME", "Linux");
725 } else if target.contains("darwin") {
726 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
728 } else if target.contains("ios") {
729 cfg.define("CMAKE_SYSTEM_NAME", "iOS");
730 } else if target.contains("tvos") {
731 cfg.define("CMAKE_SYSTEM_NAME", "tvOS");
732 } else if target.contains("visionos") {
733 cfg.define("CMAKE_SYSTEM_NAME", "visionOS");
734 } else if target.contains("watchos") {
735 cfg.define("CMAKE_SYSTEM_NAME", "watchOS");
736 } else if target.contains("none") {
737 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
739 } else {
740 builder.info(&format!(
741 "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail",
742 ));
743 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
746 }
747
748 if target.contains("apple") {
756 if !target.contains("darwin") {
757 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
762
763 cfg.define("CMAKE_OSX_SYSROOT", "/");
765 cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", "");
766 }
767
768 if target.starts_with("aarch64") {
771 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
773 } else if target.starts_with("i686") {
774 cfg.define("CMAKE_OSX_ARCHITECTURES", "i386");
776 } else {
777 cfg.define("CMAKE_OSX_ARCHITECTURES", target.triple.split('-').next().unwrap());
778 }
779 }
780 }
781
782 let sanitize_cc = |cc: &Path| {
783 if target.is_msvc() {
784 OsString::from(cc.to_str().unwrap().replace('\\', "/"))
785 } else {
786 cc.as_os_str().to_owned()
787 }
788 };
789
790 if target.is_msvc() && !builder.ninja() {
794 return;
795 }
796
797 let (cc, cxx) = match builder.config.llvm_clang_cl {
798 Some(ref cl) => (cl.into(), cl.into()),
799 None => (builder.cc(target), builder.cxx(target).unwrap()),
800 };
801
802 if use_compiler_launcher && let Some(ref ccache) = builder.config.ccache {
805 cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
806 .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
807 }
808 cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
809 .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx))
810 .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc));
811
812 let has_modern_jobserver = env::var("MAKEFLAGS")
815 .map(|flags| flags.contains("--jobserver-auth=fifo:"))
816 .unwrap_or(false);
817
818 if !has_modern_jobserver {
819 cfg.build_arg("-j").build_arg(builder.jobs().to_string());
820 }
821 let mut cflags = ccflags.cflags.clone();
822 for flag in builder
828 .cc_handled_cflags(target, CLang::C)
829 .into_iter()
830 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::C))
831 .filter(|flag| !suppressed_compiler_flag_prefixes.iter().any(|p| flag.starts_with(p)))
832 {
833 cflags.push(" ");
834 cflags.push(flag);
835 }
836 if let Some(ref s) = builder.config.llvm_cflags {
837 cflags.push(" ");
838 cflags.push(s);
839 }
840 if target.contains("ohos") {
841 cflags.push(" -D_LINUX_SYSINFO_H");
842 }
843 if builder.config.llvm_clang_cl.is_some() {
844 cflags.push(format!(" --target={target}"));
845 }
846 cfg.define("CMAKE_C_FLAGS", cflags);
847 let mut cxxflags = ccflags.cxxflags.clone();
848 for flag in builder
849 .cc_handled_cflags(target, CLang::Cxx)
850 .into_iter()
851 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::Cxx))
852 .filter(|flag| {
853 !suppressed_compiler_flag_prefixes
854 .iter()
855 .any(|suppressed_prefix| flag.starts_with(suppressed_prefix))
856 })
857 {
858 cxxflags.push(" ");
859 cxxflags.push(flag);
860 }
861 if let Some(ref s) = builder.config.llvm_cxxflags {
862 cxxflags.push(" ");
863 cxxflags.push(s);
864 }
865 if target.contains("ohos") {
866 cxxflags.push(" -D_LINUX_SYSINFO_H");
867 }
868 if builder.config.llvm_clang_cl.is_some() {
869 cxxflags.push(format!(" --target={target}"));
870 }
871
872 cfg.define("CMAKE_CXX_FLAGS", cxxflags);
873 if let Some(ar) = builder.ar(target)
874 && ar.is_absolute()
875 {
876 cfg.define("CMAKE_AR", sanitize_cc(&ar));
879 }
880
881 if let Some(ranlib) = builder.ranlib(target)
882 && ranlib.is_absolute()
883 {
884 cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib));
887 }
888
889 if let Some(ref flags) = builder.config.llvm_ldflags {
890 ldflags.push_all(flags);
891 }
892
893 if let Some(flags) = get_var("LDFLAGS", &builder.config.host_target.triple, &target.triple) {
894 ldflags.push_all(&flags);
895 }
896
897 if builder.config.llvm_static_stdcpp
900 && !target.is_msvc()
901 && !target.contains("netbsd")
902 && !target.contains("solaris")
903 {
904 if target.contains("apple") || target.is_windows() {
905 ldflags.push_all("-static-libstdc++");
906 } else {
907 ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
908 }
909 }
910
911 cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared);
912 cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module);
913 cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe);
914
915 if env::var_os("SCCACHE_ERROR_LOG").is_some() {
916 cfg.env("RUSTC_LOG", "sccache=warn");
917 }
918}
919
920fn configure_llvm(builder: &Builder<'_>, target: TargetSelection, cfg: &mut cmake::Config) {
921 if builder.config.llvm_thin_lto {
924 cfg.define("LLVM_ENABLE_LTO", "Thin");
925 if !target.contains("apple") {
926 cfg.define("LLVM_ENABLE_LLD", "ON");
927 }
928 }
929
930 if builder.config.llvm_libzstd {
932 cfg.define("LLVM_ENABLE_ZSTD", "FORCE_ON");
933 cfg.define("LLVM_USE_STATIC_ZSTD", "TRUE");
934 } else {
935 cfg.define("LLVM_ENABLE_ZSTD", "OFF");
936 }
937
938 if let Some(ref linker) = builder.config.llvm_use_linker {
939 cfg.define("LLVM_USE_LINKER", linker);
940 }
941
942 if builder.config.llvm_allow_old_toolchain {
943 cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
944 }
945}
946
947fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
949 let kind = if host == target { "HOST" } else { "TARGET" };
950 let target_u = target.replace('-', "_");
951 env::var_os(format!("{var_base}_{target}"))
952 .or_else(|| env::var_os(format!("{var_base}_{target_u}")))
953 .or_else(|| env::var_os(format!("{kind}_{var_base}")))
954 .or_else(|| env::var_os(var_base))
955}
956
957#[derive(Clone)]
958pub struct BuiltRustOffload {
959 offload: PathBuf,
961}
962
963impl BuiltRustOffload {
964 pub fn rust_offload_path(&self) -> PathBuf {
965 self.offload.clone()
966 }
967
968 pub fn rust_offload_filename(&self) -> String {
969 self.offload.file_name().unwrap().to_str().unwrap().to_owned()
970 }
971}
972
973#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
974pub struct RustOffload {
975 pub target: TargetSelection,
976}
977
978impl CommandLineStep for RustOffload {
979 type Output = BuiltRustOffload;
980 const IS_HOST: bool = true;
981
982 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
983 run.alias("rust-offload")
984 }
985
986 fn make_run(run: RunConfig<'_>) {
987 run.builder.ensure(RustOffload { target: run.target });
988 }
989
990 fn run(self, builder: &Builder<'_>) -> Self::Output {
991 if builder.config.dry_run() {
992 return BuiltRustOffload {
993 offload: builder.config.tempdir().join("rust-offload-dry-run"),
994 };
995 }
996
997 let target = self.target;
998
999 let llvm_output = builder.ensure(Llvm { target });
1000
1001 let out_dir = builder.out.join(self.target.triple).join("rust-offload");
1002
1003 let llvm_version_major =
1004 llvm::get_llvm_version_major(builder, &llvm_output.host_llvm_config);
1005 let lib_ext = std::env::consts::DLL_EXTENSION;
1006 let lib_rust_offload = format!("libRustOffload-{llvm_version_major}");
1007 let build_dir = out_dir.join(libdir(target));
1008 let dylib = build_dir.join(&lib_rust_offload).with_extension(lib_ext);
1009
1010 let mut cfg =
1011 cmake::Config::new(builder.src.join("compiler/rustc_llvm/llvm-wrapper/offload/"));
1012
1013 let mut ldflags = LdFlags::default();
1017 if builder.config.llvm_thin_lto && !target.contains("apple") {
1018 ldflags.push_all("-fuse-ld=lld");
1019 }
1020
1021 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1022
1023 let profile = get_llvm_profile(&builder.config);
1024
1025 cfg.out_dir(&out_dir)
1026 .profile(profile)
1027 .env("LLVM_CONFIG_REAL", &llvm_output.host_llvm_config)
1028 .define("LLVM_DIR", llvm_output.cmake_dir());
1029
1030 cfg.build();
1031
1032 if !dylib.exists() {
1033 eprintln!(
1034 "`{lib_rust_offload}` not found in `{}`. Either the build has failed or RustOffload was built with a wrong version of LLVM",
1035 build_dir.display()
1036 );
1037 exit!(1);
1038 }
1039
1040 BuiltRustOffload { offload: dylib }
1041 }
1042}
1043
1044#[derive(Clone)]
1045pub struct BuiltOmpOffload {
1046 offload: Vec<PathBuf>,
1048}
1049
1050impl BuiltOmpOffload {
1051 pub fn artifact_paths_with_symlink_targets(&self) -> Vec<PathBuf> {
1052 let mut paths = self.offload.clone();
1053
1054 for path in &self.offload {
1055 let mut current = path.clone();
1056
1057 while t!(fs::symlink_metadata(¤t)).file_type().is_symlink() {
1058 let target = t!(fs::read_link(¤t));
1059 current = current.parent().unwrap().join(target);
1060
1061 if paths.contains(¤t) {
1062 break;
1063 }
1064
1065 paths.push(current.clone());
1066 }
1067 }
1068
1069 paths
1070 }
1071}
1072
1073#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1085pub struct OmpOffload {
1086 pub target: TargetSelection,
1087}
1088
1089impl CommandLineStep for OmpOffload {
1090 type Output = BuiltOmpOffload;
1091 const IS_HOST: bool = true;
1092
1093 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1094 run.path("src/llvm-project/offload")
1095 }
1096
1097 fn make_run(run: RunConfig<'_>) {
1098 run.builder.ensure(OmpOffload { target: run.target });
1099 }
1100
1101 #[allow(unused)]
1103 fn run(self, builder: &Builder<'_>) -> Self::Output {
1104 if builder.config.dry_run() {
1105 return BuiltOmpOffload {
1106 offload: vec![builder.config.tempdir().join("llvm-offload-dry-run")],
1107 };
1108 }
1109 let target = self.target;
1110
1111 let llvm_output = builder.ensure(Llvm { target: self.target });
1112
1113 let out_dir = builder.out.join(self.target.triple).join("offload");
1117
1118 let mut files = vec![];
1119 let lib_ext = std::env::consts::DLL_EXTENSION;
1120 files.push(out_dir.join("lib").join("libLLVMOffload").with_extension(lib_ext));
1121 files.push(out_dir.join("lib").join("libomp").with_extension(lib_ext));
1122 files.push(out_dir.join("lib").join("libomptarget").with_extension(lib_ext));
1123 files.push(
1124 out_dir.join("lib").join("amdgcn-amd-amdhsa").join("libompdevice").with_extension("a"),
1125 );
1126 files.push(
1127 out_dir
1128 .join("lib")
1129 .join("amdgcn-amd-amdhsa")
1130 .join("libomptarget-amdgpu")
1131 .with_extension("bc"),
1132 );
1133 files.push(
1134 out_dir
1135 .join("lib")
1136 .join("nvptx64-nvidia-cuda")
1137 .join("libompdevice")
1138 .with_extension("a"),
1139 );
1140 files.push(
1141 out_dir
1142 .join("lib")
1143 .join("nvptx64-nvidia-cuda")
1144 .join("libomptarget-nvptx")
1145 .with_extension("bc"),
1146 );
1147
1148 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1150 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1151 generate_smart_stamp_hash(
1152 builder,
1153 &builder.config.src.join("src/llvm-project/offload"),
1154 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1155 )
1156 });
1157 let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash);
1158
1159 trace!("checking build stamp to see if we need to rebuild offload/openmp artifacts");
1160 if stamp.is_up_to_date() {
1161 trace!(?out_dir, "offload/openmp build artifacts are up to date");
1162 if stamp.stamp().is_empty() {
1163 builder.info(
1164 "Could not determine the Offload submodule commit hash. \
1165 Assuming that an Offload rebuild is not necessary.",
1166 );
1167 builder.info(&format!(
1168 "To force Offload/OpenMP to rebuild, remove the file `{}`",
1169 stamp.path().display()
1170 ));
1171 }
1172 return BuiltOmpOffload { offload: files };
1173 }
1174
1175 trace!(?target, "(re)building offload/openmp artifacts");
1176 builder.info(&format!("Building OpenMP/Offload for {target}"));
1177 t!(stamp.remove());
1178 let _time = helpers::timeit(builder);
1179 t!(fs::create_dir_all(&out_dir));
1180
1181 builder.config.update_submodule("src/llvm-project");
1182
1183 let clang_dir = if !builder.config.llvm_clang {
1186 assert!(&builder.build.config.llvm_clang_dir.is_some());
1188 builder.build.config.llvm_clang_dir.clone()
1189 } else {
1190 None
1192 };
1193
1194 let omp_targets = vec![target.triple.as_ref(), "amdgcn-amd-amdhsa", "nvptx64-nvidia-cuda"];
1198 for omp_target in omp_targets {
1199 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/"));
1200
1201 let mut cflags = CcFlags::default();
1207 if !builder.config.llvm_clang {
1208 let base = builder.llvm_out(target).join("include");
1209 let inc_dir = base.display();
1210 cflags.push_all(format!(" -I {inc_dir}"));
1211 }
1212
1213 let mut ldflags = LdFlags::default();
1217 if builder.config.llvm_thin_lto && !target.contains("apple") {
1218 ldflags.push_all("-fuse-ld=lld");
1219 }
1220
1221 configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]);
1222
1223 let profile = get_llvm_profile(&builder.config);
1226 trace!(?profile);
1227
1228 cfg.out_dir(&out_dir)
1231 .profile(profile)
1232 .env("LLVM_CONFIG_REAL", &llvm_output.host_llvm_config)
1233 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1234 .define("LLVM_INCLUDE_TESTS", "OFF")
1235 .define("OFFLOAD_INCLUDE_TESTS", "OFF")
1236 .define("LLVM_ROOT", builder.llvm_out(target).join("build"))
1237 .define("LLVM_DIR", llvm_output.cmake_dir())
1238 .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target);
1239 if let Some(p) = clang_dir.clone() {
1240 cfg.define("Clang_DIR", p);
1241 }
1242
1243 if *omp_target == *target.triple {
1246 cfg.define("LLVM_ENABLE_RUNTIMES", "openmp;offload");
1248 } else {
1249 cfg.define("OPENMP_INSTALL_LIBDIR", Path::new("lib").join(omp_target));
1251 cfg.define("LLVM_USE_LINKER", "lld");
1252 cfg.define("LLVM_ENABLE_RUNTIMES", "openmp");
1253 cfg.define("CMAKE_C_COMPILER_TARGET", omp_target);
1254 cfg.define("CMAKE_CXX_COMPILER_TARGET", omp_target);
1255 }
1256 cfg.build();
1257 }
1258
1259 t!(stamp.write());
1260
1261 for p in &files {
1262 if !p.exists() {
1265 eprintln!(
1266 "`{p:?}` not found in `{}`. Either the build has failed or Offload was built with a wrong version of LLVM",
1267 out_dir.display()
1268 );
1269 exit!(1);
1270 }
1271 }
1272 BuiltOmpOffload { offload: files }
1273 }
1274}
1275
1276#[derive(Clone)]
1277pub struct BuiltEnzyme {
1278 enzyme: PathBuf,
1280}
1281
1282impl BuiltEnzyme {
1283 pub fn enzyme_path(&self) -> PathBuf {
1284 self.enzyme.clone()
1285 }
1286 pub fn enzyme_filename(&self) -> String {
1287 self.enzyme.file_name().unwrap().to_str().unwrap().to_owned()
1288 }
1289}
1290
1291#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1292pub struct Enzyme {
1293 pub target: TargetSelection,
1294}
1295
1296impl CommandLineStep for Enzyme {
1297 type Output = BuiltEnzyme;
1298 const IS_HOST: bool = true;
1299
1300 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1301 run.path("src/tools/enzyme/enzyme")
1302 }
1303
1304 fn make_run(run: RunConfig<'_>) {
1305 run.builder.ensure(Enzyme { target: run.target });
1306 }
1307
1308 fn run(self, builder: &Builder<'_>) -> Self::Output {
1310 builder.require_submodule(
1311 "src/tools/enzyme",
1312 Some("The Enzyme sources are required for autodiff."),
1313 );
1314 let target = self.target;
1315
1316 if builder.config.dry_run() {
1317 return BuiltEnzyme { enzyme: builder.config.tempdir().join("enzyme-dryrun") };
1318 }
1319
1320 let llvm_output = builder.ensure(Llvm { target });
1321
1322 let enzyme_hash_input = builder.in_tree_llvm_info.sha().unwrap_or_default().to_owned()
1326 + builder.enzyme_info.sha().unwrap_or_default();
1327
1328 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1329 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1330 generate_smart_stamp_hash(
1331 builder,
1332 &builder.config.src.join("src/tools/enzyme"),
1333 &enzyme_hash_input,
1334 )
1335 });
1336
1337 let out_dir = builder.out.join(self.target.triple).join("enzyme");
1338 let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash);
1339
1340 let llvm_version_major =
1341 llvm::get_llvm_version_major(builder, &llvm_output.host_llvm_config);
1342 let lib_ext = std::env::consts::DLL_EXTENSION;
1343 let libenzyme = format!("libEnzyme-{llvm_version_major}");
1344 let build_dir = out_dir.join(libdir(target));
1345 let dylib = build_dir.join(&libenzyme).with_extension(lib_ext);
1346
1347 trace!("checking build stamp to see if we need to rebuild enzyme artifacts");
1348 if stamp.is_up_to_date() {
1349 trace!(?out_dir, "enzyme build artifacts are up to date");
1350 if stamp.stamp().is_empty() {
1351 builder.info(
1352 "Could not determine the Enzyme submodule commit hash. \
1353 Assuming that an Enzyme rebuild is not necessary.",
1354 );
1355 builder.info(&format!(
1356 "To force Enzyme to rebuild, remove the file `{}`",
1357 stamp.path().display()
1358 ));
1359 }
1360 return BuiltEnzyme { enzyme: dylib };
1361 }
1362
1363 let llvm_cmake_dir = llvm_output.cmake_dir();
1364 if !builder.config.dry_run() && !llvm_cmake_dir.is_dir() {
1365 builder.info(&format!(
1366 "WARNING: {:?} does not exist, Enzyme build will likely fail",
1367 llvm_cmake_dir
1368 ));
1369 }
1370
1371 trace!(?target, "(re)building enzyme artifacts");
1372 builder.info(&format!("Building Enzyme for {target}"));
1373 t!(stamp.remove());
1374 let _time = helpers::timeit(builder);
1375 t!(fs::create_dir_all(&out_dir));
1376
1377 let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/"));
1378 let mut cflags = CcFlags::default();
1382 cflags.push_all("-Wno-deprecated");
1383
1384 let mut ldflags = LdFlags::default();
1388 if builder.config.llvm_thin_lto && !target.contains("apple") {
1389 ldflags.push_all("-fuse-ld=lld");
1390 }
1391
1392 configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]);
1393
1394 let profile = get_llvm_profile(&builder.config);
1398 trace!(?profile);
1399
1400 cfg.out_dir(&out_dir)
1401 .profile(profile)
1402 .env("LLVM_CONFIG_REAL", &llvm_output.host_llvm_config)
1403 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1404 .define("ENZYME_EXTERNAL_SHARED_LIB", "ON")
1405 .define("ENZYME_BC_LOADER", "OFF")
1406 .define("LLVM_DIR", llvm_cmake_dir);
1407
1408 cfg.build();
1409
1410 if !dylib.exists() {
1413 eprintln!(
1414 "`{libenzyme}` not found in `{}`. Either the build has failed or Enzyme was built with a wrong version of LLVM",
1415 build_dir.display()
1416 );
1417 exit!(1);
1418 }
1419
1420 t!(stamp.write());
1421 BuiltEnzyme { enzyme: dylib }
1422 }
1423}
1424
1425#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1426pub struct Lld {
1427 pub target: TargetSelection,
1428}
1429
1430impl CommandLineStep for Lld {
1431 type Output = PathBuf;
1432 const IS_HOST: bool = true;
1433
1434 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1435 run.path("src/llvm-project/lld")
1436 }
1437
1438 fn make_run(run: RunConfig<'_>) {
1439 run.builder.ensure(Lld { target: run.target });
1440 }
1441
1442 fn run(self, builder: &Builder<'_>) -> PathBuf {
1444 if builder.config.dry_run() {
1445 return PathBuf::from("lld-out-dir-test-gen");
1446 }
1447 let target = self.target;
1448
1449 let llvm_output = builder.ensure(Llvm { target });
1450
1451 let ci_llvm_bin = llvm_output.host_llvm_config.parent().unwrap();
1456 if ci_llvm_bin.is_dir() && ci_llvm_bin.file_name().unwrap() == "bin" {
1457 let lld_path = ci_llvm_bin.join(exe("lld", target));
1458 if lld_path.exists() {
1459 return ci_llvm_bin.parent().unwrap().to_path_buf();
1462 }
1463 }
1464
1465 let out_dir = builder.out.join(target).join("lld");
1466
1467 let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld");
1468 if lld_stamp.path().exists() {
1469 return out_dir;
1470 }
1471
1472 let _guard = builder.msg_unstaged(Kind::Build, "LLD", target);
1473 let _time = helpers::timeit(builder);
1474 t!(fs::create_dir_all(&out_dir));
1475
1476 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
1477 let mut ldflags = LdFlags::default();
1478
1479 if builder.config.llvm_pgo.generate_profile.is_some()
1484 && target.is_msvc()
1485 && let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref()
1486 {
1487 let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
1490 ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
1491 }
1492
1493 if builder.config.rpath_enabled(target)
1505 && helpers::use_host_linker(target)
1506 && builder.config.llvm_link_shared()
1507 && target.contains("linux")
1508 {
1509 ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'");
1516 }
1517
1518 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1519 configure_llvm(builder, target, &mut cfg);
1520
1521 let profile = get_llvm_profile(&builder.config);
1524
1525 cfg.out_dir(&out_dir)
1526 .profile(profile)
1527 .define("LLVM_CMAKE_DIR", llvm_output.cmake_dir())
1528 .define("LLVM_INCLUDE_TESTS", "OFF");
1529
1530 if !builder.config.is_host_target(target) {
1531 cfg.define(
1533 "LLVM_TABLEGEN_EXE",
1534 llvm_output
1535 .host_llvm_config
1536 .with_file_name("llvm-tblgen")
1537 .with_extension(EXE_EXTENSION),
1538 );
1539 }
1540
1541 cfg.build();
1542
1543 t!(lld_stamp.write());
1544 out_dir
1545 }
1546}
1547
1548#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1549pub struct Sanitizers {
1550 pub target: TargetSelection,
1551}
1552
1553impl CommandLineStep for Sanitizers {
1554 type Output = Vec<SanitizerRuntime>;
1555
1556 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1557 run.alias("sanitizers")
1558 }
1559
1560 fn make_run(run: RunConfig<'_>) {
1561 run.builder.ensure(Sanitizers { target: run.target });
1562 }
1563
1564 fn run(self, builder: &Builder<'_>) -> Self::Output {
1566 let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
1567 if !compiler_rt_dir.exists() {
1568 return Vec::new();
1569 }
1570
1571 let out_dir = builder.native_dir(self.target).join("sanitizers");
1572 let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel);
1573
1574 if builder.config.dry_run() || runtimes.is_empty() {
1575 return runtimes;
1576 }
1577
1578 let LlvmOutput { host_llvm_config, .. } =
1579 builder.ensure(Llvm { target: builder.config.host_target });
1580
1581 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1582 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1583 generate_smart_stamp_hash(
1584 builder,
1585 &builder.config.src.join("src/llvm-project/compiler-rt"),
1586 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1587 )
1588 });
1589
1590 let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash);
1591
1592 if stamp.is_up_to_date() {
1593 if stamp.stamp().is_empty() {
1594 builder.info(&format!(
1595 "Rebuild sanitizers by removing the file `{}`",
1596 stamp.path().display()
1597 ));
1598 }
1599
1600 return runtimes;
1601 }
1602
1603 let _guard = builder.msg_unstaged(Kind::Build, "sanitizers", self.target);
1604 t!(stamp.remove());
1605 let _time = helpers::timeit(builder);
1606
1607 let mut cfg = cmake::Config::new(&compiler_rt_dir);
1608 cfg.profile("Release");
1609 cfg.define("CMAKE_C_COMPILER_TARGET", self.target.triple);
1610 cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
1611 cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
1612 cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
1613 cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
1614 cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
1615 cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
1616 cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
1617 cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
1618 cfg.define("LLVM_CONFIG_PATH", &host_llvm_config);
1619
1620 if self.target.contains("ohos") {
1621 cfg.define("COMPILER_RT_USE_BUILTINS_LIBRARY", "ON");
1622 }
1623
1624 let use_compiler_launcher = !self.target.contains("apple-darwin");
1628 let suppressed_compiler_flag_prefixes: &[&str] =
1633 if self.target.contains("apple-darwin") { &["-mmacosx-version-min="] } else { &[] };
1634 configure_cmake(
1635 builder,
1636 self.target,
1637 &mut cfg,
1638 use_compiler_launcher,
1639 LdFlags::default(),
1640 CcFlags::default(),
1641 suppressed_compiler_flag_prefixes,
1642 );
1643
1644 t!(fs::create_dir_all(&out_dir));
1645 cfg.out_dir(out_dir);
1646
1647 for runtime in &runtimes {
1648 cfg.build_target(&runtime.cmake_target);
1649 cfg.build();
1650 }
1651 t!(stamp.write());
1652
1653 runtimes
1654 }
1655}
1656
1657#[derive(Clone, Debug)]
1658pub struct SanitizerRuntime {
1659 pub cmake_target: String,
1661 pub path: PathBuf,
1663 pub name: String,
1665}
1666
1667fn supported_sanitizers(
1669 out_dir: &Path,
1670 target: TargetSelection,
1671 channel: &str,
1672) -> Vec<SanitizerRuntime> {
1673 let darwin_libs = |os: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1674 components
1675 .iter()
1676 .map(move |c| {
1677 let cmake_c = if *c == "ubsan" { "ubsan_standalone" } else { *c };
1678 SanitizerRuntime {
1679 cmake_target: format!("clang_rt.{cmake_c}_{os}_dynamic"),
1680 path: out_dir
1681 .join(format!("build/lib/darwin/libclang_rt.{cmake_c}_{os}_dynamic.dylib")),
1682 name: format!("librustc-{channel}_rt.{c}.dylib"),
1683 }
1684 })
1685 .collect()
1686 };
1687
1688 let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1689 components
1690 .iter()
1691 .map(move |c| {
1692 let cmake_c = if *c == "ubsan" { "ubsan_standalone" } else { *c };
1693 SanitizerRuntime {
1694 cmake_target: format!("clang_rt.{cmake_c}-{arch}"),
1695 path: out_dir.join(format!("build/lib/{os}/libclang_rt.{cmake_c}-{arch}.a")),
1696 name: format!("librustc-{channel}_rt.{c}.a"),
1697 }
1698 })
1699 .collect()
1700 };
1701
1702 match &*target.triple {
1703 "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1704 "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan", "rtsan"]),
1705 "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan", "rtsan"]),
1706 "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1707 "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
1708 "aarch64-unknown-linux-gnu" => common_libs(
1709 "linux",
1710 "aarch64",
1711 &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan"],
1712 ),
1713 "aarch64-unknown-linux-ohos" => {
1714 common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
1715 }
1716 "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => {
1717 common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"])
1718 }
1719 "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1720 "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
1721 "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]),
1722 "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1723 "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]),
1724 "x86_64-unknown-netbsd" => {
1725 common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"])
1726 }
1727 "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
1728 "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
1729 "x86_64-unknown-linux-gnu" => common_libs(
1730 "linux",
1731 "x86_64",
1732 &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan", "ubsan"],
1733 ),
1734 "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]),
1735 "x86_64-unknown-linux-gnumsan" => common_libs("linux", "x86_64", &["msan"]),
1736 "x86_64-unknown-linux-gnutsan" => common_libs("linux", "x86_64", &["tsan"]),
1737 "x86_64-unknown-linux-musl" => {
1738 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1739 }
1740 "s390x-unknown-linux-gnu" => {
1741 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1742 }
1743 "s390x-unknown-linux-musl" => {
1744 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1745 }
1746 "x86_64-unknown-linux-ohos" => {
1747 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1748 }
1749 _ => Vec::new(),
1750 }
1751}
1752
1753#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1754pub struct CrtBeginEnd {
1755 pub target: TargetSelection,
1756}
1757
1758impl CommandLineStep for CrtBeginEnd {
1759 type Output = PathBuf;
1760
1761 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1762 run.path("src/llvm-project/compiler-rt/lib/crt")
1763 }
1764
1765 fn make_run(run: RunConfig<'_>) {
1766 if run.target.needs_crt_begin_end() {
1767 run.builder.ensure(CrtBeginEnd { target: run.target });
1768 }
1769 }
1770
1771 fn run(self, builder: &Builder<'_>) -> Self::Output {
1773 builder.require_submodule(
1774 "src/llvm-project",
1775 Some("The LLVM sources are required for the CRT from `compiler-rt`."),
1776 );
1777
1778 let out_dir = builder.native_dir(self.target).join("crt");
1779
1780 if builder.config.dry_run() {
1781 return out_dir;
1782 }
1783
1784 let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtbegin.c");
1785 let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtend.c");
1786 if up_to_date(&crtbegin_src, &out_dir.join("crtbeginS.o"))
1787 && up_to_date(&crtend_src, &out_dir.join("crtendS.o"))
1788 {
1789 return out_dir;
1790 }
1791
1792 let _guard = builder.msg_unstaged(Kind::Build, "crtbegin.o and crtend.o", self.target);
1793 t!(fs::create_dir_all(&out_dir));
1794
1795 let mut cfg = cc::Build::new();
1796
1797 if let Some(ar) = builder.ar(self.target) {
1798 cfg.archiver(ar);
1799 }
1800 cfg.compiler(builder.cc(self.target));
1801 cfg.cargo_metadata(false)
1802 .out_dir(&out_dir)
1803 .target(&self.target.triple)
1804 .host(&builder.config.host_target.triple)
1805 .warnings(false)
1806 .debug(false)
1807 .opt_level(3)
1808 .file(crtbegin_src)
1809 .file(crtend_src);
1810
1811 cfg.flag("-std=c11")
1815 .define("CRT_HAS_INITFINI_ARRAY", None)
1816 .define("EH_USE_FRAME_REGISTRY", None);
1817
1818 let objs = cfg.compile_intermediates();
1819 assert_eq!(objs.len(), 2);
1820 for obj in objs {
1821 let base_name = unhashed_basename(&obj);
1822 assert!(base_name == "crtbegin" || base_name == "crtend");
1823 t!(fs::copy(&obj, out_dir.join(format!("{base_name}S.o"))));
1824 t!(fs::rename(&obj, out_dir.join(format!("{base_name}.o"))));
1825 }
1826
1827 out_dir
1828 }
1829}
1830
1831#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1832pub struct Libunwind {
1833 pub target: TargetSelection,
1834}
1835
1836impl CommandLineStep for Libunwind {
1837 type Output = PathBuf;
1838
1839 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1840 run.path("src/llvm-project/libunwind")
1841 }
1842
1843 fn make_run(run: RunConfig<'_>) {
1844 run.builder.ensure(Libunwind { target: run.target });
1845 }
1846
1847 fn run(self, builder: &Builder<'_>) -> Self::Output {
1849 builder.require_submodule(
1850 "src/llvm-project",
1851 Some("The LLVM sources are required for libunwind."),
1852 );
1853
1854 if builder.config.dry_run() {
1855 return PathBuf::new();
1856 }
1857
1858 let out_dir = builder.native_dir(self.target).join("libunwind");
1859 let root = builder.src.join("src/llvm-project/libunwind");
1860
1861 if up_to_date(&root, &out_dir.join("libunwind.a")) {
1862 return out_dir;
1863 }
1864
1865 let _guard = builder.msg_unstaged(Kind::Build, "libunwind.a", self.target);
1866 t!(fs::create_dir_all(&out_dir));
1867
1868 let mut cc_cfg = cc::Build::new();
1869 let mut cpp_cfg = cc::Build::new();
1870
1871 cpp_cfg.cpp(true);
1872 cpp_cfg.cpp_set_stdlib(None);
1873 cpp_cfg.flag("-nostdinc++");
1874 cpp_cfg.flag("-fno-exceptions");
1875 cpp_cfg.flag("-fno-rtti");
1876 cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
1877
1878 for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() {
1879 if let Some(ar) = builder.ar(self.target) {
1880 cfg.archiver(ar);
1881 }
1882 cfg.target(&self.target.triple);
1883 cfg.host(&builder.config.host_target.triple);
1884 cfg.warnings(false);
1885 cfg.debug(false);
1886 cfg.opt_level(3);
1888 cfg.flag("-fstrict-aliasing");
1889 cfg.flag("-funwind-tables");
1890 cfg.flag("-fvisibility=hidden");
1891 cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
1892 cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
1893 cfg.include(root.join("include"));
1894 cfg.cargo_metadata(false);
1895 cfg.out_dir(&out_dir);
1896
1897 if self.target.contains("x86_64-fortanix-unknown-sgx") {
1898 cfg.flag("-fno-stack-protector");
1899 cfg.flag("-ffreestanding");
1900 cfg.flag("-fexceptions");
1901
1902 cfg.flag("-U_FORTIFY_SOURCE");
1904 cfg.define("_FORTIFY_SOURCE", "0");
1905 cfg.define("RUST_SGX", "1");
1906 cfg.define("__NO_STRING_INLINES", None);
1907 cfg.define("__NO_MATH_INLINES", None);
1908 cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
1909 cfg.define("NDEBUG", None);
1910 }
1911 if self.target.is_windows() {
1912 cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
1913 }
1914 }
1915
1916 cc_cfg.compiler(builder.cc(self.target));
1917 if let Ok(cxx) = builder.cxx(self.target) {
1918 cpp_cfg.compiler(cxx);
1919 } else {
1920 cc_cfg.compiler(builder.cc(self.target));
1921 }
1922
1923 if cc_cfg.get_compiler().is_like_gnu() {
1928 cc_cfg.flag("-std=c99");
1929 }
1930 if cpp_cfg.get_compiler().is_like_gnu() {
1931 cpp_cfg.flag("-std=c++11");
1932 }
1933
1934 if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") {
1935 if cpp_cfg.get_compiler().is_like_gnu() {
1939 cpp_cfg.cpp(false);
1940 cpp_cfg.compiler(builder.cc(self.target));
1941 }
1942 }
1943
1944 let mut c_sources = vec![
1945 "Unwind-sjlj.c",
1946 "UnwindLevel1-gcc-ext.c",
1947 "UnwindLevel1.c",
1948 "UnwindRegistersRestore.S",
1949 "UnwindRegistersSave.S",
1950 ];
1951
1952 let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"];
1953 let cpp_len = cpp_sources.len();
1954
1955 if self.target.contains("x86_64-fortanix-unknown-sgx") {
1956 c_sources.push("UnwindRustSgx.c");
1957 }
1958
1959 for src in c_sources {
1960 cc_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1961 }
1962
1963 for src in &cpp_sources {
1964 cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap());
1965 }
1966
1967 cpp_cfg.compile("unwind-cpp");
1968
1969 let mut count = 0;
1971 let mut files = fs::read_dir(&out_dir)
1972 .unwrap()
1973 .map(|entry| entry.unwrap().path().canonicalize().unwrap())
1974 .collect::<Vec<_>>();
1975 files.sort();
1976 for file in files {
1977 if file.is_file() && file.extension() == Some(OsStr::new("o")) {
1978 let base_name = unhashed_basename(&file);
1980 if cpp_sources.iter().any(|f| *base_name == f[..f.len() - 4]) {
1981 cc_cfg.object(&file);
1982 count += 1;
1983 }
1984 }
1985 }
1986 assert_eq!(cpp_len, count, "Can't get object files from {out_dir:?}");
1987
1988 cc_cfg.compile("unwind");
1989 out_dir
1990 }
1991}
1992
1993#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1995pub struct FileCheck {
1996 pub target: TargetSelection,
1997}
1998
1999impl Step for FileCheck {
2000 type Output = PathBuf;
2001
2002 fn run(self, builder: &Builder<'_>) -> Self::Output {
2003 let target_config = builder.config.target_config.get(&self.target);
2004
2005 if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
2007 return s.clone();
2008 };
2009
2010 if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
2015 let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(builder).stdout();
2016 let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", self.target));
2017 let filecheck = if filecheck.exists() {
2018 filecheck
2019 } else {
2020 let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(builder).stdout();
2023 let lib_filecheck =
2024 Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", self.target));
2025 if lib_filecheck.exists() {
2026 lib_filecheck
2027 } else {
2028 filecheck
2032 }
2033 };
2034 return filecheck;
2035 }
2036 let llvm_output = builder.ensure(Llvm { target: self.target });
2038 llvm_output.root_dir().join("bin").join(exe("FileCheck", self.target))
2039 }
2040}