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, LlvmCiMode, LlvmPgoGenerationMode, TargetSelection};
24use crate::core::session::{CLang, GitRepo};
25use crate::trace;
26use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
27use crate::utils::exec::command;
28use crate::utils::helpers::{
29 self, exe, get_clang_cl_resource_dir, libdir, t, unhashed_basename, up_to_date,
30};
31
32pub const LLVM_CI_LINK_TYPE_PATH: &str = "link-type.txt";
34
35#[derive(Copy, Clone)]
36pub enum LlvmKind {
37 BuiltLocally,
39 DownloadedFromCi,
41 External,
43}
44
45#[derive(Clone)]
47pub struct LlvmOutput {
48 llvm_config: PathBuf,
49 link_shared: bool,
50 llvm_root_dir: PathBuf,
51 kind: LlvmKind,
52}
53
54impl LlvmOutput {
55 pub fn root_dir(&self) -> &Path {
58 &self.llvm_root_dir
59 }
60
61 pub fn cmake_dir(&self) -> PathBuf {
63 self.llvm_root_dir.join("lib").join("cmake").join("llvm")
64 }
65
66 pub fn link_shared(&self) -> bool {
68 self.link_shared
69 }
70
71 pub fn kind(&self) -> LlvmKind {
73 self.kind
74 }
75
76 pub fn llvm_config(&self) -> &Path {
81 &self.llvm_config
82 }
83}
84
85pub struct LlvmBuildInfo {
86 stamp: BuildStamp,
87 output: LlvmOutput,
88}
89
90pub enum LlvmBuildStatus {
91 AlreadyBuilt(LlvmOutput),
92 ShouldBuild(LlvmBuildInfo),
93}
94
95impl LlvmBuildStatus {
96 pub fn llvm_output(&self) -> &LlvmOutput {
97 match self {
98 LlvmBuildStatus::AlreadyBuilt(res) => res,
99 LlvmBuildStatus::ShouldBuild(meta) => &meta.output,
100 }
101 }
102}
103
104#[derive(Debug, Clone, Default)]
106struct CcFlags {
107 cflags: OsString,
109 cxxflags: OsString,
111}
112
113impl CcFlags {
114 fn push_all(&mut self, s: impl AsRef<OsStr>) {
115 let s = s.as_ref();
116 self.cflags.push(" ");
117 self.cflags.push(s);
118 self.cxxflags.push(" ");
119 self.cxxflags.push(s);
120 }
121}
122
123#[derive(Debug, Clone, Default)]
125struct LdFlags {
126 exe: OsString,
128 shared: OsString,
130 module: OsString,
132}
133
134impl LdFlags {
135 fn push_all(&mut self, s: impl AsRef<OsStr>) {
136 let s = s.as_ref();
137 self.exe.push(" ");
138 self.exe.push(s);
139 self.shared.push(" ");
140 self.shared.push(s);
141 self.module.push(" ");
142 self.module.push(s);
143 }
144}
145
146pub fn prebuilt_llvm_output(builder: &Builder<'_>, target: TargetSelection) -> Option<LlvmOutput> {
154 let llvm_ci = builder.ensure(LlvmFromCi { target });
156 if let Some(llvm) = llvm_ci {
157 return Some(llvm.output);
158 }
159
160 if let Some(config) = builder.config.target_config.get(&target)
162 && let Some(ref s) = config.llvm_config
163 {
164 if target == builder.host_target {
166 check_llvm_version(builder, s);
167 }
168 let llvm_config = s.to_path_buf();
169 let mut llvm_root_dir = llvm_config.clone();
170 llvm_root_dir.pop();
171 llvm_root_dir.pop();
172
173 return Some(LlvmOutput {
174 llvm_config,
175 link_shared: llvm_link_shared(&builder.config),
176 llvm_root_dir,
177 kind: LlvmKind::External,
178 });
179 }
180
181 match get_locally_built_llvm_build_status(builder, target) {
184 LlvmBuildStatus::AlreadyBuilt(output) => Some(output),
185 LlvmBuildStatus::ShouldBuild(_) => None,
186 }
187}
188
189pub fn get_llvm_build_status(builder: &Builder<'_>, target: TargetSelection) -> LlvmBuildStatus {
196 if let Some(prebuilt_output) = prebuilt_llvm_output(builder, target) {
197 return LlvmBuildStatus::AlreadyBuilt(prebuilt_output);
198 }
199
200 builder.config.update_submodule("src/llvm-project");
203
204 get_locally_built_llvm_build_status(builder, target)
205}
206
207fn get_locally_built_llvm_build_status(
211 builder: &Builder<'_>,
212 target: TargetSelection,
213) -> LlvmBuildStatus {
214 let out_dir = llvm_output_dir(builder, target);
215
216 let res = LlvmOutput {
217 llvm_config: out_dir.join("bin").join(exe("llvm-config", target)),
218 link_shared: llvm_link_shared(&builder.config),
219 llvm_root_dir: out_dir.clone(),
220 kind: LlvmKind::BuiltLocally,
221 };
222
223 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
224 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
225 generate_smart_stamp_hash(
226 builder,
227 &builder.config.src.join("src/llvm-project"),
228 builder.in_tree_llvm_info.sha().unwrap_or_default(),
229 )
230 });
231
232 let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash);
233
234 if stamp.is_up_to_date() {
235 if stamp.stamp().is_empty() {
236 builder.info(
237 "Could not determine the LLVM submodule commit hash. \
238 Assuming that an LLVM rebuild is not necessary.",
239 );
240 builder.info(&format!(
241 "To force LLVM to rebuild, remove the file `{}`",
242 stamp.path().display()
243 ));
244 }
245 return LlvmBuildStatus::AlreadyBuilt(res);
246 }
247
248 LlvmBuildStatus::ShouldBuild(LlvmBuildInfo { stamp, output: res })
249}
250
251fn llvm_output_dir(builder: &Builder<'_>, target: TargetSelection) -> PathBuf {
255 builder.config.out.join(target).join("llvm")
256}
257
258fn try_download_ci_llvm(builder: &Builder<'_>, target: TargetSelection) -> Option<DownloadedLlvm> {
259 match builder.config.llvm_ci_mode {
260 LlvmCiMode::BuildLocally => return None,
261 LlvmCiMode::DownloadFromCi => {}
262 }
263
264 if target != builder.host_target {
266 crate::debug!("LLVM not available on CI for non-host target {target}");
267 return None;
268 }
269
270 if !is_ci_llvm_available_for_target(&target, builder.config.llvm_assertions) {
271 crate::debug!(
272 "LLVM not available on CI for target={target} and assertions={}",
273 builder.config.llvm_assertions
274 );
275 return None;
276 }
277
278 let ci_llvm = builder.config.maybe_download_host_ci_llvm()?;
279 let link_shared = if !builder.config.dry_run() {
280 let link_type = t!(
281 std::fs::read_to_string(ci_llvm.join(LLVM_CI_LINK_TYPE_PATH)),
282 format!("LLVM downloaded from CI is missing the following file: {}", ci_llvm.display())
283 );
284 link_type == "dynamic"
285 } else {
286 false
287 };
288
289 Some(DownloadedLlvm {
290 output: LlvmOutput {
291 llvm_config: ci_llvm.join("bin").join(exe("llvm-config", builder.host_target)),
292 link_shared,
293 llvm_root_dir: ci_llvm,
294 kind: LlvmKind::DownloadedFromCi,
295 },
296 })
297}
298
299fn llvm_link_shared(config: &Config) -> bool {
306 config.llvm_link_shared.unwrap_or(false)
309}
310
311pub const LLVM_INVALIDATION_PATHS: &[&str] = &[
313 "src/llvm-project",
314 "src/bootstrap/download-ci-llvm-stamp",
315 "src/version",
317];
318
319pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshness {
321 assert!(cfg!(not(test)), "unit tests shouldn't care about LLVM freshness");
322
323 if is_git {
324 config.check_path_modifications(LLVM_INVALIDATION_PATHS)
325 } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
326 PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() }
327 } else {
328 PathFreshness::MissingUpstream
329 }
330}
331
332pub(crate) fn is_ci_llvm_available_for_target(
337 host_target: &TargetSelection,
338 asserts: bool,
339) -> bool {
340 let supported_platforms = [
344 ("aarch64-unknown-linux-gnu", false),
346 ("aarch64-apple-darwin", false),
347 ("aarch64-pc-windows-msvc", false),
348 ("i686-pc-windows-msvc", false),
349 ("i686-unknown-linux-gnu", false),
350 ("x86_64-unknown-linux-gnu", true),
351 ("x86_64-apple-darwin", true),
352 ("x86_64-pc-windows-gnu", false),
353 ("x86_64-pc-windows-msvc", true),
354 ("aarch64-unknown-linux-musl", false),
356 ("aarch64-pc-windows-gnullvm", false),
357 ("arm-unknown-linux-gnueabi", false),
358 ("arm-unknown-linux-gnueabihf", false),
359 ("armv7-unknown-linux-gnueabihf", false),
360 ("i686-pc-windows-gnu", false),
361 ("loongarch64-unknown-linux-gnu", false),
362 ("loongarch64-unknown-linux-musl", false),
363 ("powerpc-unknown-linux-gnu", false),
364 ("powerpc64-unknown-linux-gnu", false),
365 ("powerpc64-unknown-linux-musl", false),
366 ("powerpc64le-unknown-linux-gnu", false),
367 ("powerpc64le-unknown-linux-musl", false),
368 ("riscv64gc-unknown-linux-gnu", false),
369 ("riscv64gc-unknown-linux-musl", false),
370 ("s390x-unknown-linux-gnu", false),
371 ("x86_64-pc-windows-gnullvm", false),
372 ("x86_64-unknown-freebsd", false),
373 ("x86_64-unknown-illumos", false),
374 ("x86_64-unknown-linux-musl", false),
375 ("x86_64-unknown-netbsd", false),
376 ];
377
378 supported_platforms.contains(&(&*host_target.triple, asserts))
380 || supported_platforms.contains(&(&*host_target.triple, true))
382}
383
384#[derive(Clone)]
385pub struct DownloadedLlvm {
386 pub output: LlvmOutput,
387}
388
389#[derive(Debug, Clone, Hash, PartialEq, Eq)]
399pub struct LlvmFromCi {
400 pub target: TargetSelection,
401}
402
403impl Step for LlvmFromCi {
404 type Output = Option<DownloadedLlvm>;
405
406 fn run(self, builder: &Builder<'_>) -> Self::Output {
407 let llvm_ci = try_download_ci_llvm(builder, self.target)?;
408 check_llvm_version(builder, llvm_ci.output.llvm_config());
410 Some(llvm_ci)
411 }
412}
413
414#[derive(Debug, Clone, Hash, PartialEq, Eq)]
415pub struct Llvm {
416 pub target: TargetSelection,
417}
418
419impl CommandLineStep for Llvm {
420 type Output = LlvmOutput;
421
422 const IS_HOST: bool = true;
423
424 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
425 run.path("src/llvm-project").path("src/llvm-project/llvm")
426 }
427
428 fn make_run(run: RunConfig<'_>) {
429 run.builder.ensure(Llvm { target: run.target });
430 }
431
432 fn run(self, builder: &Builder<'_>) -> LlvmOutput {
434 let target = self.target;
435 let target_native = if self.target.starts_with("riscv") {
436 let idx = target.triple.find('-').unwrap();
439
440 format!("riscv{}{}", &target.triple[5..7], &target.triple[idx..])
441 } else if self.target.starts_with("powerpc") && self.target.ends_with("freebsd") {
442 format!("{}{}", self.target, "13.0")
445 } else {
446 target.to_string()
447 };
448
449 let LlvmBuildInfo { stamp, output } = match get_llvm_build_status(builder, target) {
451 LlvmBuildStatus::AlreadyBuilt(p) => return p,
452 LlvmBuildStatus::ShouldBuild(m) => m,
453 };
454
455 let link_shared = llvm_link_shared(&builder.config);
456
457 if link_shared && target.is_windows() && !target.is_windows_gnullvm() {
458 panic!("shared linking to LLVM is not currently supported on {}", target.triple);
459 }
460
461 let _guard = builder.msg_unstaged(Kind::Build, "LLVM", target);
462 t!(stamp.remove());
463 let _time = helpers::timeit(builder);
464 t!(fs::create_dir_all(output.root_dir()));
465
466 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/llvm"));
468 let mut ldflags = LdFlags::default();
469
470 let profile = get_llvm_profile(&builder.config);
471
472 let llvm_targets = match &builder.config.llvm_targets {
475 Some(s) => s,
476 None => {
477 "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
478 Sparc;SystemZ;WebAssembly;X86"
479 }
480 };
481
482 let llvm_exp_targets = match builder.config.llvm_experimental_targets {
483 Some(ref s) => s,
484 None => "AVR;M68k;CSKY;Xtensa",
485 };
486
487 let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" };
488 let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" };
489 let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" };
490 let enable_warnings = if builder.config.llvm_enable_warnings { "ON" } else { "OFF" };
491
492 cfg.out_dir(output.root_dir())
493 .profile(profile)
494 .define("LLVM_ENABLE_ASSERTIONS", assertions)
495 .define("LLVM_UNREACHABLE_OPTIMIZE", "OFF")
496 .define("LLVM_ENABLE_PLUGINS", plugins)
497 .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
498 .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets)
499 .define("LLVM_INCLUDE_EXAMPLES", "OFF")
500 .define("LLVM_INCLUDE_DOCS", "OFF")
501 .define("LLVM_INCLUDE_BENCHMARKS", "OFF")
502 .define("LLVM_INCLUDE_TESTS", enable_tests)
503 .define("LLVM_ENABLE_LIBEDIT", "OFF")
504 .define("LLVM_ENABLE_BINDINGS", "OFF")
505 .define("LLVM_ENABLE_Z3_SOLVER", "OFF")
506 .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string())
507 .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap())
508 .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native)
509 .define("LLVM_ENABLE_WARNINGS", enable_warnings);
510
511 cfg.define("LLVM_INSTALL_UTILS", "ON");
515
516 if let Some(mode) = builder.config.llvm_pgo.generate_profile.as_ref() {
517 cfg.define("LLVM_BUILD_INSTRUMENTED", "IR");
518 match mode {
519 LlvmPgoGenerationMode::Implicit => {}
520 LlvmPgoGenerationMode::Directory(llvm_profile_dir) => {
521 cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir);
522 }
523 }
524 cfg.define("LLVM_BUILD_RUNTIME", "No");
525 }
526 if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() {
527 cfg.define("LLVM_PROFDATA_FILE", path);
528 }
529
530 if !target.is_msvc() {
532 cfg.define("LLVM_ENABLE_ZLIB", "ON");
533 } else {
534 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
535 }
536
537 if target.contains("apple-ios")
539 || target.contains("apple-tvos")
540 || target.contains("apple-watchos")
541 || target.contains("apple-visionos")
542 {
543 cfg.define("LLVM_ENABLE_PLUGINS", "OFF");
545 cfg.define("LLVM_ENABLE_ZLIB", "OFF");
547 }
548
549 if link_shared {
554 cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
555 cfg.define("LLVM_VERSIONED_DYLIB_NAME_ON_DARWIN", "OFF");
557 }
558
559 if (target.starts_with("csky")
560 || target.starts_with("riscv")
561 || target.starts_with("sparc-"))
562 && !target.contains("freebsd")
563 && !target.contains("openbsd")
564 && !target.contains("netbsd")
565 {
566 ldflags.exe.push(" -latomic");
575 ldflags.shared.push(" -latomic");
576 }
577
578 if target.starts_with("mips") && target.contains("netbsd") {
579 ldflags.exe.push(" -latomic");
581 ldflags.shared.push(" -latomic");
582 }
583
584 if target.starts_with("arm64ec") {
585 ldflags.exe.push(" -machine:arm64ec");
588 ldflags.shared.push(" -machine:arm64ec");
589 }
590
591 if target.contains("musl") && builder.crt_static(target).unwrap_or(true) {
594 ldflags.exe.push(" -static");
595 }
596
597 if target.is_msvc() {
598 cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded");
599 cfg.static_crt(true);
600 }
601
602 if target.starts_with("i686") {
603 cfg.define("LLVM_BUILD_32_BITS", "ON");
604 }
605
606 if target.starts_with("x86_64") && target.contains("ohos") {
607 cfg.define("LLVM_TOOL_LLVM_RTDYLD_BUILD", "OFF");
608 }
609
610 let mut enabled_llvm_projects = Vec::new();
611
612 if helpers::forcing_clang_based_tests() {
613 enabled_llvm_projects.push("clang");
614 }
615
616 if builder.config.llvm_polly {
617 enabled_llvm_projects.push("polly");
618 }
619
620 if builder.config.llvm_clang {
621 enabled_llvm_projects.push("clang");
622 }
623
624 cfg.define("LLVM_ENABLE_LIBXML2", "OFF");
627
628 let mut enabled_llvm_runtimes = Vec::new();
629
630 if helpers::forcing_clang_based_tests() {
631 enabled_llvm_runtimes.push("compiler-rt");
632 }
633
634 if !enabled_llvm_projects.is_empty() {
635 enabled_llvm_projects.sort();
636 enabled_llvm_projects.dedup();
637 cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
638 }
639
640 if !enabled_llvm_runtimes.is_empty() {
641 enabled_llvm_runtimes.sort();
642 enabled_llvm_runtimes.dedup();
643 cfg.define("LLVM_ENABLE_RUNTIMES", enabled_llvm_runtimes.join(";"));
644 }
645
646 if let Some(num_linkers) = builder.config.llvm_link_jobs
647 && num_linkers > 0
648 {
649 cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
650 }
651
652 if !builder.config.is_host_target(target) {
654 let llvm_host = builder.ensure(Llvm { target: builder.config.host_target });
655 if !builder.config.dry_run() {
656 let llvm_bindir = command(llvm_host.llvm_config())
657 .arg("--bindir")
658 .cached()
659 .run_capture_stdout(builder)
660 .stdout();
661 let host_bin = Path::new(llvm_bindir.trim());
662 cfg.define(
663 "LLVM_TABLEGEN",
664 host_bin.join("llvm-tblgen").with_extension(EXE_EXTENSION),
665 );
666 cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION));
668 }
669 cfg.define("LLVM_CONFIG_PATH", llvm_host.llvm_config());
670 if builder.config.llvm_clang {
671 let build_bin = llvm_host.root_dir().join("bin");
672 let clang_tblgen = build_bin.join("clang-tblgen").with_extension(EXE_EXTENSION);
673 if !builder.config.dry_run() && !clang_tblgen.exists() {
674 panic!("unable to find {}", clang_tblgen.display());
675 }
676 cfg.define("CLANG_TABLEGEN", clang_tblgen);
677 }
678 }
679
680 let llvm_version_suffix = if let Some(ref suffix) = builder.config.llvm_version_suffix {
681 if !suffix.is_empty() { Some(suffix.to_string()) } else { None }
683 } else if builder.config.channel == "dev" {
684 Some("-rust-dev".to_string())
688 } else {
689 Some(format!("-rust-{}-{}", builder.version, builder.config.channel))
690 };
691 if let Some(ref suffix) = llvm_version_suffix {
692 cfg.define("LLVM_VERSION_SUFFIX", suffix);
693 }
694
695 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
696 configure_llvm(builder, target, &mut cfg);
697
698 for (key, val) in &builder.config.llvm_build_config {
699 cfg.define(key, val);
700 }
701
702 if builder.config.dry_run() {
703 return output;
704 }
705
706 cfg.build();
707
708 let find_llvm_lib_name = |extension| {
710 let llvm_config = if target == builder.host_target {
711 output.llvm_config().to_path_buf()
712 } else {
713 builder.ensure(Llvm { target: builder.host_target }).llvm_config().to_path_buf()
714 };
715
716 let major = get_llvm_version_major(builder, &llvm_config);
717 match &llvm_version_suffix {
718 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
719 None => format!("libLLVM-{major}.{extension}"),
720 }
721 };
722
723 if link_shared && target.contains("apple-darwin") {
728 let lib_name = find_llvm_lib_name("dylib");
729 let lib_llvm = output.root_dir().join("build").join("lib").join(lib_name);
730 if !lib_llvm.exists() {
731 t!(builder.symlink_file("libLLVM.dylib", &lib_llvm));
732 }
733 }
734
735 if link_shared && builder.config.llvm_optimize && !builder.config.llvm_release_debuginfo {
739 let lib_name = find_llvm_lib_name("so");
741
742 crate::core::build_steps::compile::strip_debug(
745 builder,
746 target,
747 &output.root_dir().join("lib").join(&lib_name),
748 );
749 crate::core::build_steps::compile::strip_debug(
750 builder,
751 target,
752 &output.root_dir().join("build").join("lib").join(&lib_name),
753 );
754 }
755
756 t!(stamp.write());
757
758 output
759 }
760
761 fn metadata(&self) -> Option<StepMetadata> {
762 Some(StepMetadata::build("llvm", self.target))
763 }
764}
765
766pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String {
768 command(llvm_config)
769 .arg("--version")
770 .cached()
771 .run_capture_stdout(builder)
772 .stdout()
773 .trim()
774 .to_owned()
775}
776
777pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 {
779 let version = get_llvm_version(builder, llvm_config);
780 let major_str = version.split_once('.').expect("Failed to parse LLVM version").0;
781 major_str.parse().unwrap()
782}
783
784fn get_llvm_profile(config: &Config) -> &'static str {
785 match (config.llvm_optimize, config.llvm_release_debuginfo) {
786 (false, _) => "Debug",
787 (true, false) => "Release",
788 (true, true) => "RelWithDebInfo",
789 }
790}
791
792fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
793 if builder.config.dry_run() {
794 return;
795 }
796
797 let version = get_llvm_version(builder, llvm_config);
798 let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
799 if let (Some(major), Some(_minor)) = (parts.next(), parts.next())
800 && major >= 21
801 {
802 return;
803 }
804 panic!("\n\nbad LLVM version: {version}, need >=21\n\n")
805}
806
807fn configure_cmake(
808 builder: &Builder<'_>,
809 target: TargetSelection,
810 cfg: &mut cmake::Config,
811 use_compiler_launcher: bool,
812 mut ldflags: LdFlags,
813 ccflags: CcFlags,
814 suppressed_compiler_flag_prefixes: &[&str],
815) {
816 cfg.define("CMAKE_INSTALL_MESSAGE", "LAZY");
819
820 if builder.config.quiet {
821 cfg.define("CMAKE_MESSAGE_LOG_LEVEL", "WARNING");
823
824 if builder.ninja() {
828 cfg.build_arg("--quiet");
829 }
830 }
831
832 cfg.env("DESTDIR", "");
836
837 if builder.ninja() {
838 cfg.generator("Ninja");
839 }
840 cfg.target(&target.triple).host(&builder.config.host_target.triple);
841
842 if !builder.config.is_host_target(target) {
843 cfg.define("CMAKE_CROSSCOMPILING", "True");
844
845 if target.contains("netbsd") {
851 cfg.define("CMAKE_SYSTEM_NAME", "NetBSD");
852 } else if target.contains("dragonfly") {
853 cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
854 } else if target.contains("openbsd") {
855 cfg.define("CMAKE_SYSTEM_NAME", "OpenBSD");
856 } else if target.contains("freebsd") {
857 cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
858 } else if target.is_windows() {
859 cfg.define("CMAKE_SYSTEM_NAME", "Windows");
860 } else if target.contains("haiku") {
861 cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
862 } else if target.contains("solaris") || target.contains("illumos") {
863 cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
864 } else if target.contains("linux") {
865 cfg.define("CMAKE_SYSTEM_NAME", "Linux");
866 } else if target.contains("darwin") {
867 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
869 } else if target.contains("ios") {
870 cfg.define("CMAKE_SYSTEM_NAME", "iOS");
871 } else if target.contains("tvos") {
872 cfg.define("CMAKE_SYSTEM_NAME", "tvOS");
873 } else if target.contains("visionos") {
874 cfg.define("CMAKE_SYSTEM_NAME", "visionOS");
875 } else if target.contains("watchos") {
876 cfg.define("CMAKE_SYSTEM_NAME", "watchOS");
877 } else if target.contains("none") {
878 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
880 } else {
881 builder.info(&format!(
882 "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail",
883 ));
884 cfg.define("CMAKE_SYSTEM_NAME", "Generic");
887 }
888
889 if target.contains("apple") {
897 if !target.contains("darwin") {
898 cfg.define("CMAKE_SYSTEM_NAME", "Darwin");
903
904 cfg.define("CMAKE_OSX_SYSROOT", "/");
906 cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", "");
907 }
908
909 if target.starts_with("aarch64") {
912 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
914 } else if target.starts_with("i686") {
915 cfg.define("CMAKE_OSX_ARCHITECTURES", "i386");
917 } else {
918 cfg.define("CMAKE_OSX_ARCHITECTURES", target.triple.split('-').next().unwrap());
919 }
920 }
921 }
922
923 let sanitize_cc = |cc: &Path| {
924 if target.is_msvc() {
925 OsString::from(cc.to_str().unwrap().replace('\\', "/"))
926 } else {
927 cc.as_os_str().to_owned()
928 }
929 };
930
931 if target.is_msvc() && !builder.ninja() {
935 return;
936 }
937
938 let (cc, cxx) = match builder.config.llvm_clang_cl {
939 Some(ref cl) => (cl.into(), cl.into()),
940 None => (builder.cc(target), builder.cxx(target).unwrap()),
941 };
942
943 if use_compiler_launcher && let Some(ref ccache) = builder.config.ccache {
946 cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
947 .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
948 }
949 cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
950 .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx))
951 .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc));
952
953 let has_modern_jobserver = env::var("MAKEFLAGS")
956 .map(|flags| flags.contains("--jobserver-auth=fifo:"))
957 .unwrap_or(false);
958
959 if !has_modern_jobserver {
960 cfg.build_arg("-j").build_arg(builder.jobs().to_string());
961 }
962 let mut cflags = ccflags.cflags.clone();
963 for flag in builder
969 .cc_handled_cflags(target, CLang::C)
970 .into_iter()
971 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::C))
972 .filter(|flag| !suppressed_compiler_flag_prefixes.iter().any(|p| flag.starts_with(p)))
973 {
974 cflags.push(" ");
975 cflags.push(flag);
976 }
977 if let Some(ref s) = builder.config.llvm_cflags {
978 cflags.push(" ");
979 cflags.push(s);
980 }
981 if target.contains("ohos") {
982 cflags.push(" -D_LINUX_SYSINFO_H");
983 }
984 if builder.config.llvm_clang_cl.is_some() {
985 cflags.push(format!(" --target={target}"));
986 }
987 cfg.define("CMAKE_C_FLAGS", cflags);
988 let mut cxxflags = ccflags.cxxflags.clone();
989 for flag in builder
990 .cc_handled_cflags(target, CLang::Cxx)
991 .into_iter()
992 .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::Cxx))
993 .filter(|flag| {
994 !suppressed_compiler_flag_prefixes
995 .iter()
996 .any(|suppressed_prefix| flag.starts_with(suppressed_prefix))
997 })
998 {
999 cxxflags.push(" ");
1000 cxxflags.push(flag);
1001 }
1002 if let Some(ref s) = builder.config.llvm_cxxflags {
1003 cxxflags.push(" ");
1004 cxxflags.push(s);
1005 }
1006 if target.contains("ohos") {
1007 cxxflags.push(" -D_LINUX_SYSINFO_H");
1008 }
1009 if builder.config.llvm_clang_cl.is_some() {
1010 cxxflags.push(format!(" --target={target}"));
1011 }
1012
1013 cfg.define("CMAKE_CXX_FLAGS", cxxflags);
1014 if let Some(ar) = builder.ar(target)
1015 && ar.is_absolute()
1016 {
1017 cfg.define("CMAKE_AR", sanitize_cc(&ar));
1020 }
1021
1022 if let Some(ranlib) = builder.ranlib(target)
1023 && ranlib.is_absolute()
1024 {
1025 cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib));
1028 }
1029
1030 if let Some(ref flags) = builder.config.llvm_ldflags {
1031 ldflags.push_all(flags);
1032 }
1033
1034 if let Some(flags) = get_var("LDFLAGS", &builder.config.host_target.triple, &target.triple) {
1035 ldflags.push_all(&flags);
1036 }
1037
1038 if builder.config.llvm_static_stdcpp
1041 && !target.is_msvc()
1042 && !target.contains("netbsd")
1043 && !target.contains("solaris")
1044 {
1045 if target.contains("apple") || target.is_windows() {
1046 ldflags.push_all("-static-libstdc++");
1047 } else {
1048 ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
1049 }
1050 }
1051
1052 cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared);
1053 cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module);
1054 cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe);
1055
1056 if env::var_os("SCCACHE_ERROR_LOG").is_some() {
1057 cfg.env("RUSTC_LOG", "sccache=warn");
1058 }
1059}
1060
1061fn configure_llvm(builder: &Builder<'_>, target: TargetSelection, cfg: &mut cmake::Config) {
1062 if builder.config.llvm_thin_lto {
1065 cfg.define("LLVM_ENABLE_LTO", "Thin");
1066 if !target.contains("apple") {
1067 cfg.define("LLVM_ENABLE_LLD", "ON");
1068 }
1069 }
1070
1071 if builder.config.llvm_libzstd {
1073 cfg.define("LLVM_ENABLE_ZSTD", "FORCE_ON");
1074 cfg.define("LLVM_USE_STATIC_ZSTD", "TRUE");
1075 } else {
1076 cfg.define("LLVM_ENABLE_ZSTD", "OFF");
1077 }
1078
1079 if let Some(ref linker) = builder.config.llvm_use_linker {
1080 cfg.define("LLVM_USE_LINKER", linker);
1081 }
1082
1083 if builder.config.llvm_allow_old_toolchain {
1084 cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
1085 }
1086}
1087
1088fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
1090 let kind = if host == target { "HOST" } else { "TARGET" };
1091 let target_u = target.replace('-', "_");
1092 env::var_os(format!("{var_base}_{target}"))
1093 .or_else(|| env::var_os(format!("{var_base}_{target_u}")))
1094 .or_else(|| env::var_os(format!("{kind}_{var_base}")))
1095 .or_else(|| env::var_os(var_base))
1096}
1097
1098#[derive(Clone)]
1099pub struct BuiltRustOffload {
1100 offload: PathBuf,
1102}
1103
1104impl BuiltRustOffload {
1105 pub fn rust_offload_path(&self) -> PathBuf {
1106 self.offload.clone()
1107 }
1108
1109 pub fn rust_offload_filename(&self) -> String {
1110 self.offload.file_name().unwrap().to_str().unwrap().to_owned()
1111 }
1112}
1113
1114#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1115pub struct RustOffload {
1116 pub target: TargetSelection,
1117}
1118
1119impl CommandLineStep for RustOffload {
1120 type Output = BuiltRustOffload;
1121 const IS_HOST: bool = true;
1122
1123 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1124 run.alias("rust-offload")
1125 }
1126
1127 fn make_run(run: RunConfig<'_>) {
1128 run.builder.ensure(RustOffload { target: run.target });
1129 }
1130
1131 fn run(self, builder: &Builder<'_>) -> Self::Output {
1132 if builder.config.dry_run() {
1133 return BuiltRustOffload {
1134 offload: builder.config.tempdir().join("rust-offload-dry-run"),
1135 };
1136 }
1137
1138 let target = self.target;
1139
1140 let llvm_output = builder.ensure(Llvm { target });
1141
1142 let out_dir = builder.out.join(self.target.triple).join("rust-offload");
1143
1144 let llvm_version_major = get_llvm_version_major(builder, &builder.host_llvm_config());
1145 let lib_ext = std::env::consts::DLL_EXTENSION;
1146 let lib_rust_offload = format!("libRustOffload-{llvm_version_major}");
1147 let build_dir = out_dir.join(libdir(target));
1148 let dylib = build_dir.join(&lib_rust_offload).with_extension(lib_ext);
1149
1150 let mut cfg =
1151 cmake::Config::new(builder.src.join("compiler/rustc_llvm/llvm-wrapper/offload/"));
1152
1153 let mut ldflags = LdFlags::default();
1157 if builder.config.llvm_thin_lto && !target.contains("apple") {
1158 ldflags.push_all("-fuse-ld=lld");
1159 }
1160
1161 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1162
1163 let profile = get_llvm_profile(&builder.config);
1164
1165 cfg.out_dir(&out_dir)
1166 .profile(profile)
1167 .env("LLVM_CONFIG_REAL", llvm_output.llvm_config())
1168 .define("LLVM_DIR", llvm_output.cmake_dir());
1169
1170 cfg.build();
1171
1172 if !dylib.exists() {
1173 eprintln!(
1174 "`{lib_rust_offload}` not found in `{}`. Either the build has failed or RustOffload was built with a wrong version of LLVM",
1175 build_dir.display()
1176 );
1177 helpers::exit_process(1);
1178 }
1179
1180 BuiltRustOffload { offload: dylib }
1181 }
1182}
1183
1184#[derive(Clone)]
1185pub struct BuiltOmpOffload {
1186 offload: Vec<PathBuf>,
1188}
1189
1190impl BuiltOmpOffload {
1191 pub fn artifact_paths_with_symlink_targets(&self) -> Vec<PathBuf> {
1192 let mut paths = self.offload.clone();
1193
1194 for path in &self.offload {
1195 let mut current = path.clone();
1196
1197 while t!(fs::symlink_metadata(¤t)).file_type().is_symlink() {
1198 let target = t!(fs::read_link(¤t));
1199 current = current.parent().unwrap().join(target);
1200
1201 if paths.contains(¤t) {
1202 break;
1203 }
1204
1205 paths.push(current.clone());
1206 }
1207 }
1208
1209 paths
1210 }
1211}
1212
1213#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1225pub struct OmpOffload {
1226 pub target: TargetSelection,
1227}
1228
1229impl CommandLineStep for OmpOffload {
1230 type Output = BuiltOmpOffload;
1231 const IS_HOST: bool = true;
1232
1233 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1234 run.path("src/llvm-project/offload")
1235 }
1236
1237 fn make_run(run: RunConfig<'_>) {
1238 run.builder.ensure(OmpOffload { target: run.target });
1239 }
1240
1241 #[allow(unused)]
1243 fn run(self, builder: &Builder<'_>) -> Self::Output {
1244 if builder.config.dry_run() {
1245 return BuiltOmpOffload {
1246 offload: vec![builder.config.tempdir().join("llvm-offload-dry-run")],
1247 };
1248 }
1249 let target = self.target;
1250
1251 let llvm_output = builder.ensure(Llvm { target });
1252
1253 let out_dir = builder.out.join(self.target.triple).join("offload");
1257
1258 let mut files = vec![];
1259 let lib_ext = std::env::consts::DLL_EXTENSION;
1260 files.push(out_dir.join("lib").join("libLLVMOffload").with_extension(lib_ext));
1261 files.push(out_dir.join("lib").join("libomp").with_extension(lib_ext));
1262 files.push(out_dir.join("lib").join("libomptarget").with_extension(lib_ext));
1263 files.push(
1264 out_dir.join("lib").join("amdgcn-amd-amdhsa").join("libompdevice").with_extension("a"),
1265 );
1266 files.push(
1267 out_dir
1268 .join("lib")
1269 .join("amdgcn-amd-amdhsa")
1270 .join("libomptarget-amdgpu")
1271 .with_extension("bc"),
1272 );
1273 files.push(
1274 out_dir
1275 .join("lib")
1276 .join("nvptx64-nvidia-cuda")
1277 .join("libompdevice")
1278 .with_extension("a"),
1279 );
1280 files.push(
1281 out_dir
1282 .join("lib")
1283 .join("nvptx64-nvidia-cuda")
1284 .join("libomptarget-nvptx")
1285 .with_extension("bc"),
1286 );
1287
1288 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1290 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1291 generate_smart_stamp_hash(
1292 builder,
1293 &builder.config.src.join("src/llvm-project/offload"),
1294 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1295 )
1296 });
1297 let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash);
1298
1299 trace!("checking build stamp to see if we need to rebuild offload/openmp artifacts");
1300 if stamp.is_up_to_date() {
1301 trace!(?out_dir, "offload/openmp build artifacts are up to date");
1302 if stamp.stamp().is_empty() {
1303 builder.info(
1304 "Could not determine the Offload submodule commit hash. \
1305 Assuming that an Offload rebuild is not necessary.",
1306 );
1307 builder.info(&format!(
1308 "To force Offload/OpenMP to rebuild, remove the file `{}`",
1309 stamp.path().display()
1310 ));
1311 }
1312 return BuiltOmpOffload { offload: files };
1313 }
1314
1315 trace!(?target, "(re)building offload/openmp artifacts");
1316 builder.info(&format!("Building OpenMP/Offload for {target}"));
1317 t!(stamp.remove());
1318 let _time = helpers::timeit(builder);
1319 t!(fs::create_dir_all(&out_dir));
1320
1321 builder.config.update_submodule("src/llvm-project");
1322
1323 let offload_clang_dir = if !builder.config.llvm_clang {
1324 builder.sess.config.offload_clang_dir.clone()
1326 } else {
1327 None
1329 };
1330
1331 let clang_bin_dir = if builder.config.llvm_clang {
1340 llvm_output.llvm_config().parent().map(Path::to_path_buf)
1341 } else {
1342 offload_clang_dir
1348 .as_deref()
1349 .and_then(|dir| dir.ancestors().nth(3))
1350 .map(|prefix| prefix.join("bin"))
1351 }
1352 .filter(|dir| dir.join(exe("clang", target)).exists());
1353
1354 let Some(clang_bin_dir) = clang_bin_dir else {
1355 eprintln!(
1356 "Building Offload requires a clang binary. Please either set `llvm.offload-clang-dir` or enable `llvm.clang` to build it."
1357 );
1358 helpers::exit_process(1);
1359 };
1360 let clang = clang_bin_dir.join(exe("clang", target));
1361 let clangxx = clang_bin_dir.join(exe("clang++", target));
1362
1363 let cxx_lib_dir = builder.cxx(target).ok().and_then(|cxx| {
1369 let stdout = command(&cxx)
1370 .arg("-print-file-name=libstdc++.so")
1371 .cached()
1372 .run_capture_stdout(builder)
1373 .stdout();
1374 let libstdcxx = PathBuf::from(stdout.trim());
1375 if !libstdcxx.is_absolute() {
1376 return None;
1377 }
1378 libstdcxx.parent().map(Path::to_path_buf)
1379 });
1380
1381 let omp_targets = vec![target.triple.as_ref(), "amdgcn-amd-amdhsa", "nvptx64-nvidia-cuda"];
1385 for omp_target in omp_targets {
1386 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/"));
1387
1388 let mut cflags = CcFlags::default();
1393 if !builder.config.llvm_clang {
1394 let base = llvm_output.root_dir().join("include");
1395 let inc_dir = base.display();
1396 cflags.push_all(format!(" -I {inc_dir}"));
1397 }
1398
1399 let mut ldflags = LdFlags::default();
1403 if builder.config.llvm_thin_lto && !target.contains("apple") {
1404 ldflags.push_all("-fuse-ld=lld");
1405 }
1406 if *omp_target == *target.triple
1407 && let Some(dir) = &cxx_lib_dir
1408 {
1409 ldflags.push_all(format!("-L{}", dir.display()));
1410 }
1411
1412 configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]);
1413
1414 cfg.define("CMAKE_C_COMPILER", &clang)
1415 .define("CMAKE_CXX_COMPILER", &clangxx)
1416 .define("CMAKE_ASM_COMPILER", &clang);
1417
1418 let profile = get_llvm_profile(&builder.config);
1421 trace!(?profile);
1422
1423 cfg.out_dir(&out_dir)
1426 .profile(profile)
1427 .env("LLVM_CONFIG_REAL", llvm_output.llvm_config())
1428 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1429 .define("LLVM_INCLUDE_TESTS", "OFF")
1430 .define("OFFLOAD_INCLUDE_TESTS", "OFF")
1431 .define("LLVM_ROOT", llvm_output.root_dir().join("build"))
1432 .define("LLVM_DIR", llvm_output.cmake_dir())
1433 .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target);
1434 if let Some(p) = offload_clang_dir.clone() {
1435 cfg.define("Clang_DIR", p);
1436 }
1437
1438 if *omp_target == *target.triple {
1441 cfg.define("LLVM_ENABLE_RUNTIMES", "openmp;offload");
1443 } else {
1444 cfg.define("OPENMP_INSTALL_LIBDIR", Path::new("lib").join(omp_target));
1446 cfg.define("LLVM_USE_LINKER", "lld");
1447 cfg.define("LLVM_ENABLE_RUNTIMES", "openmp");
1448 cfg.define("CMAKE_C_COMPILER_TARGET", omp_target);
1449 cfg.define("CMAKE_CXX_COMPILER_TARGET", omp_target);
1450 }
1451 cfg.build();
1452 }
1453
1454 t!(stamp.write());
1455
1456 for p in &files {
1457 if !p.exists() {
1460 eprintln!(
1461 "`{p:?}` not found in `{}`. Either the build has failed or Offload was built with a wrong version of LLVM",
1462 out_dir.display()
1463 );
1464 helpers::exit_process(1);
1465 }
1466 }
1467 BuiltOmpOffload { offload: files }
1468 }
1469}
1470
1471#[derive(Clone)]
1472pub struct BuiltEnzyme {
1473 enzyme: PathBuf,
1475}
1476
1477impl BuiltEnzyme {
1478 pub fn enzyme_path(&self) -> PathBuf {
1479 self.enzyme.clone()
1480 }
1481 pub fn enzyme_filename(&self) -> String {
1482 self.enzyme.file_name().unwrap().to_str().unwrap().to_owned()
1483 }
1484}
1485
1486#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1487pub struct Enzyme {
1488 pub target: TargetSelection,
1489}
1490
1491impl CommandLineStep for Enzyme {
1492 type Output = BuiltEnzyme;
1493 const IS_HOST: bool = true;
1494
1495 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1496 run.path("src/tools/enzyme/enzyme")
1497 }
1498
1499 fn make_run(run: RunConfig<'_>) {
1500 run.builder.ensure(Enzyme { target: run.target });
1501 }
1502
1503 fn run(self, builder: &Builder<'_>) -> Self::Output {
1505 builder.require_submodule(
1506 "src/tools/enzyme",
1507 Some("The Enzyme sources are required for autodiff."),
1508 );
1509 let target = self.target;
1510
1511 if builder.config.dry_run() {
1512 return BuiltEnzyme { enzyme: builder.config.tempdir().join("enzyme-dryrun") };
1513 }
1514
1515 let llvm_output = builder.ensure(Llvm { target });
1516
1517 let enzyme_hash_input = builder.in_tree_llvm_info.sha().unwrap_or_default().to_owned()
1521 + builder.enzyme_info.sha().unwrap_or_default();
1522
1523 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1524 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1525 generate_smart_stamp_hash(
1526 builder,
1527 &builder.config.src.join("src/tools/enzyme"),
1528 &enzyme_hash_input,
1529 )
1530 });
1531
1532 let out_dir = builder.out.join(self.target.triple).join("enzyme");
1533 let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash);
1534
1535 let llvm_version_major = llvm::get_llvm_version_major(builder, &builder.host_llvm_config());
1536 let lib_ext = std::env::consts::DLL_EXTENSION;
1537 let libenzyme = format!("libEnzyme-{llvm_version_major}");
1538 let build_dir = out_dir.join(libdir(target));
1539 let dylib = build_dir.join(&libenzyme).with_extension(lib_ext);
1540
1541 trace!("checking build stamp to see if we need to rebuild enzyme artifacts");
1542 if stamp.is_up_to_date() {
1543 trace!(?out_dir, "enzyme build artifacts are up to date");
1544 if stamp.stamp().is_empty() {
1545 builder.info(
1546 "Could not determine the Enzyme submodule commit hash. \
1547 Assuming that an Enzyme rebuild is not necessary.",
1548 );
1549 builder.info(&format!(
1550 "To force Enzyme to rebuild, remove the file `{}`",
1551 stamp.path().display()
1552 ));
1553 }
1554 return BuiltEnzyme { enzyme: dylib };
1555 }
1556
1557 let llvm_cmake_dir = llvm_output.cmake_dir();
1558 if !builder.config.dry_run() && !llvm_cmake_dir.is_dir() {
1559 builder.info(&format!(
1560 "WARNING: {:?} does not exist, Enzyme build will likely fail",
1561 llvm_cmake_dir
1562 ));
1563 }
1564
1565 trace!(?target, "(re)building enzyme artifacts");
1566 builder.info(&format!("Building Enzyme for {target}"));
1567 t!(stamp.remove());
1568 let _time = helpers::timeit(builder);
1569 t!(fs::create_dir_all(&out_dir));
1570
1571 let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/"));
1572 let mut cflags = CcFlags::default();
1576 cflags.push_all("-Wno-deprecated");
1577
1578 let mut ldflags = LdFlags::default();
1582 if builder.config.llvm_thin_lto && !target.contains("apple") {
1583 ldflags.push_all("-fuse-ld=lld");
1584 }
1585
1586 configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]);
1587
1588 let profile = get_llvm_profile(&builder.config);
1592 trace!(?profile);
1593
1594 cfg.out_dir(&out_dir)
1595 .profile(profile)
1596 .env("LLVM_CONFIG_REAL", llvm_output.llvm_config())
1597 .define("LLVM_ENABLE_ASSERTIONS", "ON")
1598 .define("ENZYME_EXTERNAL_SHARED_LIB", "ON")
1599 .define("ENZYME_BC_LOADER", "OFF")
1600 .define("LLVM_DIR", llvm_cmake_dir);
1601
1602 cfg.build();
1603
1604 if !dylib.exists() {
1607 eprintln!(
1608 "`{libenzyme}` not found in `{}`. Either the build has failed or Enzyme was built with a wrong version of LLVM",
1609 build_dir.display()
1610 );
1611 helpers::exit_process(1);
1612 }
1613
1614 t!(stamp.write());
1615 BuiltEnzyme { enzyme: dylib }
1616 }
1617}
1618
1619#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1620pub struct Lld {
1621 pub target: TargetSelection,
1622}
1623
1624impl CommandLineStep for Lld {
1625 type Output = PathBuf;
1626 const IS_HOST: bool = true;
1627
1628 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1629 run.path("src/llvm-project/lld")
1630 }
1631
1632 fn make_run(run: RunConfig<'_>) {
1633 run.builder.ensure(Lld { target: run.target });
1634 }
1635
1636 fn run(self, builder: &Builder<'_>) -> PathBuf {
1638 if builder.config.dry_run() {
1639 return PathBuf::from("lld-out-dir-test-gen");
1640 }
1641 let target = self.target;
1642
1643 let llvm_output = builder.ensure(Llvm { target });
1644
1645 if matches!(llvm_output.kind, LlvmKind::DownloadedFromCi) {
1650 let bin_dir = llvm_output.root_dir().join("bin");
1651 let lld_path = bin_dir.join(exe("lld", target));
1652 if lld_path.exists() {
1653 return bin_dir.parent().unwrap().to_path_buf();
1656 }
1657 }
1658
1659 let out_dir = builder.out.join(target).join("lld");
1660
1661 let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld");
1662 if lld_stamp.path().exists() {
1663 return out_dir;
1664 }
1665
1666 let _guard = builder.msg_unstaged(Kind::Build, "LLD", target);
1667 let _time = helpers::timeit(builder);
1668 t!(fs::create_dir_all(&out_dir));
1669
1670 let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
1671 let mut ldflags = LdFlags::default();
1672
1673 if builder.config.llvm_pgo.generate_profile.is_some()
1678 && target.is_msvc()
1679 && let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref()
1680 {
1681 let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
1684 ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
1685 }
1686
1687 if builder.config.rpath_enabled(target)
1699 && helpers::use_host_linker(target)
1700 && llvm_output.link_shared()
1701 && target.contains("linux")
1702 {
1703 ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'");
1710 }
1711
1712 configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]);
1713 configure_llvm(builder, target, &mut cfg);
1714
1715 let profile = get_llvm_profile(&builder.config);
1718
1719 cfg.out_dir(&out_dir)
1720 .profile(profile)
1721 .define("LLVM_CMAKE_DIR", llvm_output.cmake_dir())
1722 .define("LLVM_INCLUDE_TESTS", "OFF");
1723
1724 if !builder.config.is_host_target(target) {
1725 cfg.define(
1727 "LLVM_TABLEGEN_EXE",
1728 builder
1729 .host_llvm_config()
1730 .with_file_name("llvm-tblgen")
1731 .with_extension(EXE_EXTENSION),
1732 );
1733 }
1734
1735 cfg.build();
1736
1737 t!(lld_stamp.write());
1738 out_dir
1739 }
1740}
1741
1742#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1743pub struct Sanitizers {
1744 pub target: TargetSelection,
1745}
1746
1747impl CommandLineStep for Sanitizers {
1748 type Output = Vec<SanitizerRuntime>;
1749
1750 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1751 run.alias("sanitizers")
1752 }
1753
1754 fn make_run(run: RunConfig<'_>) {
1755 run.builder.ensure(Sanitizers { target: run.target });
1756 }
1757
1758 fn run(self, builder: &Builder<'_>) -> Self::Output {
1760 let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
1761 if !compiler_rt_dir.exists() {
1762 return Vec::new();
1763 }
1764
1765 let out_dir = builder.native_dir(self.target).join("sanitizers");
1766 let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel);
1767
1768 if builder.config.dry_run() || runtimes.is_empty() {
1769 return runtimes;
1770 }
1771
1772 let llvm_host = builder.ensure(Llvm { target: builder.config.host_target });
1773
1774 static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
1775 let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
1776 generate_smart_stamp_hash(
1777 builder,
1778 &builder.config.src.join("src/llvm-project/compiler-rt"),
1779 builder.in_tree_llvm_info.sha().unwrap_or_default(),
1780 )
1781 });
1782
1783 let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash);
1784
1785 if stamp.is_up_to_date() {
1786 if stamp.stamp().is_empty() {
1787 builder.info(&format!(
1788 "Rebuild sanitizers by removing the file `{}`",
1789 stamp.path().display()
1790 ));
1791 }
1792
1793 return runtimes;
1794 }
1795
1796 let _guard = builder.msg_unstaged(Kind::Build, "sanitizers", self.target);
1797 t!(stamp.remove());
1798 let _time = helpers::timeit(builder);
1799
1800 let mut cfg = cmake::Config::new(&compiler_rt_dir);
1801 cfg.profile("Release");
1802 cfg.define("CMAKE_C_COMPILER_TARGET", self.target.triple);
1803 cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
1804 cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
1805 cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
1806 cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
1807 cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
1808 cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
1809 cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
1810 cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
1811 cfg.define("LLVM_CONFIG_PATH", llvm_host.llvm_config());
1812
1813 if self.target.contains("ohos") {
1814 cfg.define("COMPILER_RT_USE_BUILTINS_LIBRARY", "ON");
1815 }
1816
1817 let use_compiler_launcher = !self.target.contains("apple-darwin");
1821 let suppressed_compiler_flag_prefixes: &[&str] =
1826 if self.target.contains("apple-darwin") { &["-mmacosx-version-min="] } else { &[] };
1827 configure_cmake(
1828 builder,
1829 self.target,
1830 &mut cfg,
1831 use_compiler_launcher,
1832 LdFlags::default(),
1833 CcFlags::default(),
1834 suppressed_compiler_flag_prefixes,
1835 );
1836
1837 t!(fs::create_dir_all(&out_dir));
1838 cfg.out_dir(out_dir);
1839
1840 for runtime in &runtimes {
1841 cfg.build_target(&runtime.cmake_target);
1842 cfg.build();
1843 }
1844 t!(stamp.write());
1845
1846 runtimes
1847 }
1848}
1849
1850#[derive(Clone, Debug)]
1851pub struct SanitizerRuntime {
1852 pub cmake_target: String,
1854 pub path: PathBuf,
1856 pub name: String,
1858}
1859
1860fn supported_sanitizers(
1862 out_dir: &Path,
1863 target: TargetSelection,
1864 channel: &str,
1865) -> Vec<SanitizerRuntime> {
1866 let darwin_libs = |os: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1867 components
1868 .iter()
1869 .map(move |c| {
1870 let cmake_c = if *c == "ubsan" { "ubsan_standalone" } else { *c };
1871 SanitizerRuntime {
1872 cmake_target: format!("clang_rt.{cmake_c}_{os}_dynamic"),
1873 path: out_dir
1874 .join(format!("build/lib/darwin/libclang_rt.{cmake_c}_{os}_dynamic.dylib")),
1875 name: format!("librustc-{channel}_rt.{c}.dylib"),
1876 }
1877 })
1878 .collect()
1879 };
1880
1881 let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec<SanitizerRuntime> {
1882 components
1883 .iter()
1884 .map(move |c| {
1885 let cmake_c = if *c == "ubsan" { "ubsan_standalone" } else { *c };
1886 SanitizerRuntime {
1887 cmake_target: format!("clang_rt.{cmake_c}-{arch}"),
1888 path: out_dir.join(format!("build/lib/{os}/libclang_rt.{cmake_c}-{arch}.a")),
1889 name: format!("librustc-{channel}_rt.{c}.a"),
1890 }
1891 })
1892 .collect()
1893 };
1894
1895 match &*target.triple {
1896 "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1897 "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan", "rtsan"]),
1898 "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan", "rtsan"]),
1899 "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1900 "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
1901 "aarch64-unknown-linux-gnu" => common_libs(
1902 "linux",
1903 "aarch64",
1904 &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan"],
1905 ),
1906 "aarch64-unknown-linux-ohos" => {
1907 common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
1908 }
1909 "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => {
1910 common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"])
1911 }
1912 "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]),
1913 "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
1914 "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]),
1915 "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
1916 "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]),
1917 "x86_64-unknown-netbsd" => {
1918 common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"])
1919 }
1920 "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
1921 "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
1922 "x86_64-unknown-linux-gnu" => common_libs(
1923 "linux",
1924 "x86_64",
1925 &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan", "ubsan"],
1926 ),
1927 "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]),
1928 "x86_64-unknown-linux-gnumsan" => common_libs("linux", "x86_64", &["msan"]),
1929 "x86_64-unknown-linux-gnutsan" => common_libs("linux", "x86_64", &["tsan"]),
1930 "x86_64-unknown-linux-musl" => {
1931 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1932 }
1933 "s390x-unknown-linux-gnu" => {
1934 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1935 }
1936 "s390x-unknown-linux-musl" => {
1937 common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
1938 }
1939 "x86_64-unknown-linux-ohos" => {
1940 common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
1941 }
1942 _ => Vec::new(),
1943 }
1944}
1945
1946#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1947pub struct CrtBeginEnd {
1948 pub target: TargetSelection,
1949}
1950
1951impl CommandLineStep for CrtBeginEnd {
1952 type Output = PathBuf;
1953
1954 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1955 run.path("src/llvm-project/compiler-rt/lib/crt")
1956 }
1957
1958 fn make_run(run: RunConfig<'_>) {
1959 if run.target.needs_crt_begin_end() {
1960 run.builder.ensure(CrtBeginEnd { target: run.target });
1961 }
1962 }
1963
1964 fn run(self, builder: &Builder<'_>) -> Self::Output {
1966 builder.require_submodule(
1967 "src/llvm-project",
1968 Some("The LLVM sources are required for the CRT from `compiler-rt`."),
1969 );
1970
1971 let out_dir = builder.native_dir(self.target).join("crt");
1972
1973 if builder.config.dry_run() {
1974 return out_dir;
1975 }
1976
1977 let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtbegin.c");
1978 let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtend.c");
1979 if up_to_date(&crtbegin_src, &out_dir.join("crtbeginS.o"))
1980 && up_to_date(&crtend_src, &out_dir.join("crtendS.o"))
1981 {
1982 return out_dir;
1983 }
1984
1985 let _guard = builder.msg_unstaged(Kind::Build, "crtbegin.o and crtend.o", self.target);
1986 t!(fs::create_dir_all(&out_dir));
1987
1988 let mut cfg = cc::Build::new();
1989
1990 if let Some(ar) = builder.ar(self.target) {
1991 cfg.archiver(ar);
1992 }
1993 cfg.compiler(builder.cc(self.target));
1994 cfg.cargo_metadata(false)
1995 .out_dir(&out_dir)
1996 .target(&self.target.triple)
1997 .host(&builder.config.host_target.triple)
1998 .warnings(false)
1999 .debug(false)
2000 .opt_level(3)
2001 .file(crtbegin_src)
2002 .file(crtend_src);
2003
2004 cfg.flag("-std=c11")
2008 .define("CRT_HAS_INITFINI_ARRAY", None)
2009 .define("EH_USE_FRAME_REGISTRY", None);
2010
2011 let objs = cfg.compile_intermediates();
2012 assert_eq!(objs.len(), 2);
2013 for obj in objs {
2014 let base_name = unhashed_basename(&obj);
2015 assert!(base_name == "crtbegin" || base_name == "crtend");
2016 t!(fs::copy(&obj, out_dir.join(format!("{base_name}S.o"))));
2017 t!(fs::rename(&obj, out_dir.join(format!("{base_name}.o"))));
2018 }
2019
2020 out_dir
2021 }
2022}
2023
2024#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2025pub struct Libunwind {
2026 pub target: TargetSelection,
2027}
2028
2029impl CommandLineStep for Libunwind {
2030 type Output = PathBuf;
2031
2032 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2033 run.path("src/llvm-project/libunwind")
2034 }
2035
2036 fn make_run(run: RunConfig<'_>) {
2037 run.builder.ensure(Libunwind { target: run.target });
2038 }
2039
2040 fn run(self, builder: &Builder<'_>) -> Self::Output {
2042 builder.require_submodule(
2043 "src/llvm-project",
2044 Some("The LLVM sources are required for libunwind."),
2045 );
2046
2047 if builder.config.dry_run() {
2048 return PathBuf::new();
2049 }
2050
2051 let out_dir = builder.native_dir(self.target).join("libunwind");
2052 let root = builder.src.join("src/llvm-project/libunwind");
2053
2054 if up_to_date(&root, &out_dir.join("libunwind.a")) {
2055 return out_dir;
2056 }
2057
2058 let _guard = builder.msg_unstaged(Kind::Build, "libunwind.a", self.target);
2059 t!(fs::create_dir_all(&out_dir));
2060
2061 let mut cc_cfg = cc::Build::new();
2062 let mut cpp_cfg = cc::Build::new();
2063
2064 cpp_cfg.cpp(true);
2065 cpp_cfg.cpp_set_stdlib(None);
2066 cpp_cfg.flag("-nostdinc++");
2067 cpp_cfg.flag("-fno-exceptions");
2068 cpp_cfg.flag("-fno-rtti");
2069 cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
2070
2071 for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() {
2072 if let Some(ar) = builder.ar(self.target) {
2073 cfg.archiver(ar);
2074 }
2075 cfg.target(&self.target.triple);
2076 cfg.host(&builder.config.host_target.triple);
2077 cfg.warnings(false);
2078 cfg.debug(false);
2079 cfg.opt_level(3);
2081 cfg.flag("-fstrict-aliasing");
2082 cfg.flag("-funwind-tables");
2083 cfg.flag("-fvisibility=hidden");
2084 cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
2085 cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
2086 cfg.include(root.join("include"));
2087 cfg.cargo_metadata(false);
2088 cfg.out_dir(&out_dir);
2089
2090 if self.target.contains("x86_64-fortanix-unknown-sgx") {
2091 cfg.flag("-fno-stack-protector");
2092 cfg.flag("-ffreestanding");
2093 cfg.flag("-fexceptions");
2094
2095 cfg.flag("-U_FORTIFY_SOURCE");
2097 cfg.define("_FORTIFY_SOURCE", "0");
2098 cfg.define("RUST_SGX", "1");
2099 cfg.define("__NO_STRING_INLINES", None);
2100 cfg.define("__NO_MATH_INLINES", None);
2101 cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
2102 cfg.define("NDEBUG", None);
2103 }
2104 if self.target.is_windows() {
2105 cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
2106 }
2107 }
2108
2109 cc_cfg.compiler(builder.cc(self.target));
2110 if let Ok(cxx) = builder.cxx(self.target) {
2111 cpp_cfg.compiler(cxx);
2112 } else {
2113 cc_cfg.compiler(builder.cc(self.target));
2114 }
2115
2116 if cc_cfg.get_compiler().is_like_gnu() {
2121 cc_cfg.flag("-std=c99");
2122 }
2123 if cpp_cfg.get_compiler().is_like_gnu() {
2124 cpp_cfg.flag("-std=c++11");
2125 }
2126
2127 if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") {
2128 if cpp_cfg.get_compiler().is_like_gnu() {
2132 cpp_cfg.cpp(false);
2133 cpp_cfg.compiler(builder.cc(self.target));
2134 }
2135 }
2136
2137 let mut c_sources = vec![
2138 "Unwind-sjlj.c",
2139 "UnwindLevel1-gcc-ext.c",
2140 "UnwindLevel1.c",
2141 "UnwindRegistersRestore.S",
2142 "UnwindRegistersSave.S",
2143 ];
2144
2145 let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"];
2146 let cpp_len = cpp_sources.len();
2147
2148 if self.target.contains("x86_64-fortanix-unknown-sgx") {
2149 c_sources.push("UnwindRustSgx.c");
2150 }
2151
2152 for src in c_sources {
2153 cc_cfg.file(root.join("src").join(src).canonicalize().unwrap());
2154 }
2155
2156 for src in &cpp_sources {
2157 cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap());
2158 }
2159
2160 cpp_cfg.compile("unwind-cpp");
2161
2162 let mut count = 0;
2164 let mut files = fs::read_dir(&out_dir)
2165 .unwrap()
2166 .map(|entry| entry.unwrap().path().canonicalize().unwrap())
2167 .collect::<Vec<_>>();
2168 files.sort();
2169 for file in files {
2170 if file.is_file() && file.extension() == Some(OsStr::new("o")) {
2171 let base_name = unhashed_basename(&file);
2173 if cpp_sources.iter().any(|f| *base_name == f[..f.len() - 4]) {
2174 cc_cfg.object(&file);
2175 count += 1;
2176 }
2177 }
2178 }
2179 assert_eq!(cpp_len, count, "Can't get object files from {out_dir:?}");
2180
2181 cc_cfg.compile("unwind");
2182 out_dir
2183 }
2184}
2185
2186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2188pub struct FileCheck {
2189 pub target: TargetSelection,
2190}
2191
2192impl Step for FileCheck {
2193 type Output = PathBuf;
2194
2195 fn run(self, builder: &Builder<'_>) -> Self::Output {
2196 let target_config = builder.config.target_config.get(&self.target);
2197
2198 if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
2200 return s.clone();
2201 };
2202
2203 if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
2208 let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(builder).stdout();
2209 let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", self.target));
2210 let filecheck = if filecheck.exists() {
2211 filecheck
2212 } else {
2213 let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(builder).stdout();
2216 let lib_filecheck =
2217 Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", self.target));
2218 if lib_filecheck.exists() {
2219 lib_filecheck
2220 } else {
2221 filecheck
2225 }
2226 };
2227 return filecheck;
2228 }
2229 let llvm_output = builder.ensure(Llvm { target: self.target });
2231 llvm_output.root_dir().join("bin").join(exe("FileCheck", self.target))
2232 }
2233}