Skip to content

Commit 676d383

Browse files
authored
Rollup merge of #145763 - Kobzol:llvm-bindir-cross, r=Mark-Simulacrum
Ship LLVM tools for the correct target when cross-compiling The LLVM config returned from the `Llvm` step in bootstrap was always the *host* LLVM config (as we cannot execute the cross-compiled LLVM config). But this wasn't obvious in bootstrap before (there was just a comment about it, but that's it), which caused a bug where bootstrap was copying LLVM tools from the host target to the cross-compiled rustc sysroot. This was probably happening for quite a long time, we just haven't noticed before. Note that I consider this to be mostly a hotfix, I plan to refactor the LLVM handling in bootstrap soon-ish to make it harder to misuse and be better in general. Fixes: #145699
2 parents 87f49e4 + 6315b49 commit 676d383

File tree

6 files changed

+84
-39
lines changed

6 files changed

+84
-39
lines changed

src/bootstrap/src/core/build_steps/compile.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,8 +1394,8 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
13941394
if builder.config.llvm_enzyme {
13951395
cargo.env("LLVM_ENZYME", "1");
13961396
}
1397-
let llvm::LlvmResult { llvm_config, .. } = builder.ensure(llvm::Llvm { target });
1398-
cargo.env("LLVM_CONFIG", &llvm_config);
1397+
let llvm::LlvmResult { host_llvm_config, .. } = builder.ensure(llvm::Llvm { target });
1398+
cargo.env("LLVM_CONFIG", &host_llvm_config);
13991399

14001400
// Some LLVM linker flags (-L and -l) may be needed to link `rustc_llvm`. Its build script
14011401
// expects these to be passed via the `LLVM_LINKER_FLAGS` env variable, separated by
@@ -2001,14 +2001,52 @@ impl Step for Assemble {
20012001
if builder.config.llvm_enabled(target_compiler.host) {
20022002
trace!("target_compiler.host" = ?target_compiler.host, "LLVM enabled");
20032003

2004-
let llvm::LlvmResult { llvm_config, .. } =
2005-
builder.ensure(llvm::Llvm { target: target_compiler.host });
2004+
let target = target_compiler.host;
2005+
let llvm::LlvmResult { host_llvm_config, .. } = builder.ensure(llvm::Llvm { target });
20062006
if !builder.config.dry_run() && builder.config.llvm_tools_enabled {
20072007
trace!("LLVM tools enabled");
20082008

2009-
let llvm_bin_dir =
2010-
command(llvm_config).arg("--bindir").run_capture_stdout(builder).stdout();
2011-
let llvm_bin_dir = Path::new(llvm_bin_dir.trim());
2009+
let host_llvm_bin_dir = command(&host_llvm_config)
2010+
.arg("--bindir")
2011+
.run_capture_stdout(builder)
2012+
.stdout()
2013+
.trim()
2014+
.to_string();
2015+
2016+
let llvm_bin_dir = if target == builder.host_target {
2017+
PathBuf::from(host_llvm_bin_dir)
2018+
} else {
2019+
// If we're cross-compiling, we cannot run the target llvm-config in order to
2020+
// figure out where binaries are located. We thus have to guess.
2021+
let external_llvm_config = builder
2022+
.config
2023+
.target_config
2024+
.get(&target)
2025+
.and_then(|t| t.llvm_config.clone());
2026+
if let Some(external_llvm_config) = external_llvm_config {
2027+
// If we have an external LLVM, just hope that the bindir is the directory
2028+
// where the LLVM config is located
2029+
external_llvm_config.parent().unwrap().to_path_buf()
2030+
} else {
2031+
// If we have built LLVM locally, then take the path of the host bindir
2032+
// relative to its output build directory, and then apply it to the target
2033+
// LLVM output build directory.
2034+
let host_llvm_out = builder.llvm_out(builder.host_target);
2035+
let target_llvm_out = builder.llvm_out(target);
2036+
if let Ok(relative_path) =
2037+
Path::new(&host_llvm_bin_dir).strip_prefix(host_llvm_out)
2038+
{
2039+
target_llvm_out.join(relative_path)
2040+
} else {
2041+
// This is the most desperate option, just replace the host target with
2042+
// the actual target in the directory path...
2043+
PathBuf::from(
2044+
host_llvm_bin_dir
2045+
.replace(&*builder.host_target.triple, &target.triple),
2046+
)
2047+
}
2048+
}
2049+
};
20122050

20132051
// Since we've already built the LLVM tools, install them to the sysroot.
20142052
// This is the equivalent of installing the `llvm-tools-preview` component via

src/bootstrap/src/core/build_steps/dist.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,11 +2230,12 @@ fn maybe_install_llvm(
22302230
builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary);
22312231
}
22322232
!builder.config.dry_run()
2233-
} else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
2234-
llvm::prebuilt_llvm_config(builder, target, true)
2233+
} else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult {
2234+
host_llvm_config, ..
2235+
}) = llvm::prebuilt_llvm_config(builder, target, true)
22352236
{
22362237
trace!("LLVM already built, installing LLVM files");
2237-
let mut cmd = command(llvm_config);
2238+
let mut cmd = command(host_llvm_config);
22382239
cmd.arg("--libfiles");
22392240
builder.verbose(|| println!("running {cmd:?}"));
22402241
let files = cmd.run_capture_stdout(builder).stdout();

src/bootstrap/src/core/build_steps/llvm.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::{CLang, GitRepo, Kind, trace};
2929
pub struct LlvmResult {
3030
/// Path to llvm-config binary.
3131
/// NB: This is always the host llvm-config!
32-
pub llvm_config: PathBuf,
32+
pub host_llvm_config: PathBuf,
3333
/// Path to LLVM cmake directory for the target.
3434
pub llvm_cmake_dir: PathBuf,
3535
}
@@ -109,14 +109,14 @@ pub fn prebuilt_llvm_config(
109109
&& let Some(ref s) = config.llvm_config
110110
{
111111
check_llvm_version(builder, s);
112-
let llvm_config = s.to_path_buf();
113-
let mut llvm_cmake_dir = llvm_config.clone();
112+
let host_llvm_config = s.to_path_buf();
113+
let mut llvm_cmake_dir = host_llvm_config.clone();
114114
llvm_cmake_dir.pop();
115115
llvm_cmake_dir.pop();
116116
llvm_cmake_dir.push("lib");
117117
llvm_cmake_dir.push("cmake");
118118
llvm_cmake_dir.push("llvm");
119-
return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir });
119+
return LlvmBuildStatus::AlreadyBuilt(LlvmResult { host_llvm_config, llvm_cmake_dir });
120120
}
121121

122122
if handle_submodule_when_needed {
@@ -141,7 +141,7 @@ pub fn prebuilt_llvm_config(
141141
};
142142

143143
let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
144-
let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir };
144+
let res = LlvmResult { host_llvm_config: build_llvm_config, llvm_cmake_dir };
145145

146146
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
147147
let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
@@ -483,11 +483,11 @@ impl Step for Llvm {
483483

484484
// https://llvm.org/docs/HowToCrossCompileLLVM.html
485485
if !builder.config.is_host_target(target) {
486-
let LlvmResult { llvm_config, .. } =
486+
let LlvmResult { host_llvm_config, .. } =
487487
builder.ensure(Llvm { target: builder.config.host_target });
488488
if !builder.config.dry_run() {
489489
let llvm_bindir =
490-
command(&llvm_config).arg("--bindir").run_capture_stdout(builder).stdout();
490+
command(&host_llvm_config).arg("--bindir").run_capture_stdout(builder).stdout();
491491
let host_bin = Path::new(llvm_bindir.trim());
492492
cfg.define(
493493
"LLVM_TABLEGEN",
@@ -496,7 +496,7 @@ impl Step for Llvm {
496496
// LLVM_NM is required for cross compiling using MSVC
497497
cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION));
498498
}
499-
cfg.define("LLVM_CONFIG_PATH", llvm_config);
499+
cfg.define("LLVM_CONFIG_PATH", host_llvm_config);
500500
if builder.config.llvm_clang {
501501
let build_bin =
502502
builder.llvm_out(builder.config.host_target).join("build").join("bin");
@@ -538,7 +538,7 @@ impl Step for Llvm {
538538

539539
// Helper to find the name of LLVM's shared library on darwin and linux.
540540
let find_llvm_lib_name = |extension| {
541-
let major = get_llvm_version_major(builder, &res.llvm_config);
541+
let major = get_llvm_version_major(builder, &res.host_llvm_config);
542542
match &llvm_version_suffix {
543543
Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
544544
None => format!("libLLVM-{major}.{extension}"),
@@ -915,7 +915,7 @@ impl Step for Enzyme {
915915
}
916916
let target = self.target;
917917

918-
let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: self.target });
918+
let LlvmResult { host_llvm_config, .. } = builder.ensure(Llvm { target: self.target });
919919

920920
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
921921
let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
@@ -969,7 +969,7 @@ impl Step for Enzyme {
969969

970970
cfg.out_dir(&out_dir)
971971
.profile(profile)
972-
.env("LLVM_CONFIG_REAL", &llvm_config)
972+
.env("LLVM_CONFIG_REAL", &host_llvm_config)
973973
.define("LLVM_ENABLE_ASSERTIONS", "ON")
974974
.define("ENZYME_EXTERNAL_SHARED_LIB", "ON")
975975
.define("ENZYME_BC_LOADER", "OFF")
@@ -1006,13 +1006,13 @@ impl Step for Lld {
10061006
}
10071007
let target = self.target;
10081008

1009-
let LlvmResult { llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
1009+
let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
10101010

10111011
// The `dist` step packages LLD next to LLVM's binaries for download-ci-llvm. The root path
10121012
// we usually expect here is `./build/$triple/ci-llvm/`, with the binaries in its `bin`
10131013
// subfolder. We check if that's the case, and if LLD's binary already exists there next to
10141014
// `llvm-config`: if so, we can use it instead of building LLVM/LLD from source.
1015-
let ci_llvm_bin = llvm_config.parent().unwrap();
1015+
let ci_llvm_bin = host_llvm_config.parent().unwrap();
10161016
if ci_llvm_bin.is_dir() && ci_llvm_bin.file_name().unwrap() == "bin" {
10171017
let lld_path = ci_llvm_bin.join(exe("lld", target));
10181018
if lld_path.exists() {
@@ -1095,7 +1095,7 @@ impl Step for Lld {
10951095
// Use the host llvm-tblgen binary.
10961096
cfg.define(
10971097
"LLVM_TABLEGEN_EXE",
1098-
llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
1098+
host_llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
10991099
);
11001100
}
11011101

@@ -1136,7 +1136,7 @@ impl Step for Sanitizers {
11361136
return runtimes;
11371137
}
11381138

1139-
let LlvmResult { llvm_config, .. } =
1139+
let LlvmResult { host_llvm_config, .. } =
11401140
builder.ensure(Llvm { target: builder.config.host_target });
11411141

11421142
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
@@ -1176,7 +1176,7 @@ impl Step for Sanitizers {
11761176
cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
11771177
cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
11781178
cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
1179-
cfg.define("LLVM_CONFIG_PATH", &llvm_config);
1179+
cfg.define("LLVM_CONFIG_PATH", &host_llvm_config);
11801180

11811181
if self.target.contains("ohos") {
11821182
cfg.define("COMPILER_RT_USE_BUILTINS_LIBRARY", "ON");

src/bootstrap/src/core/build_steps/test.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,12 +2038,14 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?}
20382038
let mut llvm_components_passed = false;
20392039
let mut copts_passed = false;
20402040
if builder.config.llvm_enabled(compiler.host) {
2041-
let llvm::LlvmResult { llvm_config, .. } =
2041+
let llvm::LlvmResult { host_llvm_config, .. } =
20422042
builder.ensure(llvm::Llvm { target: builder.config.host_target });
20432043
if !builder.config.dry_run() {
2044-
let llvm_version = get_llvm_version(builder, &llvm_config);
2045-
let llvm_components =
2046-
command(&llvm_config).arg("--components").run_capture_stdout(builder).stdout();
2044+
let llvm_version = get_llvm_version(builder, &host_llvm_config);
2045+
let llvm_components = command(&host_llvm_config)
2046+
.arg("--components")
2047+
.run_capture_stdout(builder)
2048+
.stdout();
20472049
// Remove trailing newline from llvm-config output.
20482050
cmd.arg("--llvm-version")
20492051
.arg(llvm_version.trim())
@@ -2061,7 +2063,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?}
20612063
// rustc args as a workaround.
20622064
if !builder.config.dry_run() && suite.ends_with("fulldeps") {
20632065
let llvm_libdir =
2064-
command(&llvm_config).arg("--libdir").run_capture_stdout(builder).stdout();
2066+
command(&host_llvm_config).arg("--libdir").run_capture_stdout(builder).stdout();
20652067
let link_llvm = if target.is_msvc() {
20662068
format!("-Clink-arg=-LIBPATH:{llvm_libdir}")
20672069
} else {
@@ -2075,7 +2077,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?}
20752077
// tools. Pass the path to run-make tests so they can use them.
20762078
// (The coverage-run tests also need these tools to process
20772079
// coverage reports.)
2078-
let llvm_bin_path = llvm_config
2080+
let llvm_bin_path = host_llvm_config
20792081
.parent()
20802082
.expect("Expected llvm-config to be contained in directory");
20812083
assert!(llvm_bin_path.is_dir());

src/bootstrap/src/core/builder/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,11 +1647,15 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
16471647
///
16481648
/// Note that this returns `None` if LLVM is disabled, or if we're in a
16491649
/// check build or dry-run, where there's no need to build all of LLVM.
1650+
///
1651+
/// FIXME(@kobzol)
1652+
/// **WARNING**: This actually returns the **HOST** LLVM config, not LLVM config for the given
1653+
/// *target*.
16501654
pub fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
16511655
if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() {
1652-
let llvm::LlvmResult { llvm_config, .. } = self.ensure(llvm::Llvm { target });
1653-
if llvm_config.is_file() {
1654-
return Some(llvm_config);
1656+
let llvm::LlvmResult { host_llvm_config, .. } = self.ensure(llvm::Llvm { target });
1657+
if host_llvm_config.is_file() {
1658+
return Some(host_llvm_config);
16551659
}
16561660
}
16571661
None

src/bootstrap/src/core/builder/tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,14 @@ fn test_prebuilt_llvm_config_path_resolution() {
434434
false,
435435
)
436436
.llvm_result()
437-
.llvm_config
437+
.host_llvm_config
438438
.clone();
439439
let actual = drop_win_disk_prefix_if_present(actual);
440440
assert_eq!(expected, actual);
441441

442442
let actual = prebuilt_llvm_config(&builder, builder.config.host_target, false)
443443
.llvm_result()
444-
.llvm_config
444+
.host_llvm_config
445445
.clone();
446446
let actual = drop_win_disk_prefix_if_present(actual);
447447
assert_eq!(expected, actual);
@@ -459,7 +459,7 @@ fn test_prebuilt_llvm_config_path_resolution() {
459459

460460
let actual = prebuilt_llvm_config(&builder, builder.config.host_target, false)
461461
.llvm_result()
462-
.llvm_config
462+
.host_llvm_config
463463
.clone();
464464
let expected = builder
465465
.out
@@ -482,7 +482,7 @@ fn test_prebuilt_llvm_config_path_resolution() {
482482

483483
let actual = prebuilt_llvm_config(&builder, builder.config.host_target, false)
484484
.llvm_result()
485-
.llvm_config
485+
.host_llvm_config
486486
.clone();
487487
let expected = builder
488488
.out

0 commit comments

Comments
 (0)