|
1 | | -use std::process::Command; |
| 1 | +use std::{env, fs, iter, path::{Path, PathBuf}, process::Command}; |
2 | 2 |
|
3 | | -use anyhow::{Context, Result}; |
| 3 | +use anyhow::{Context, Result, ensure}; |
| 4 | +use yazi_macro::ok_or_not_found; |
4 | 5 |
|
5 | | -use super::{cargo, is_windows_target, run}; |
| 6 | +use super::{cargo, is_linux_target, is_sparc64_target, is_windows_target, run, workspace_root}; |
6 | 7 |
|
7 | 8 | pub(super) struct Build { |
8 | 9 | pub(super) target: String, |
@@ -33,11 +34,64 @@ impl Build { |
33 | 34 | if self.completions { |
34 | 35 | cmd.env("YAZI_GEN_COMPLETIONS", "1"); |
35 | 36 | } |
| 37 | + if is_linux_target(&self.target) && !is_sparc64_target(&self.target) { |
| 38 | + self.expose_lld(&mut cmd)?; |
| 39 | + } |
36 | 40 |
|
37 | 41 | run(&mut cmd).context("failed to build Yazi") |
38 | 42 | } |
39 | 43 |
|
40 | 44 | pub(super) fn profile(&self) -> &'static str { |
41 | 45 | if is_windows_target(&self.target) { "release-windows" } else { "release" } |
42 | 46 | } |
| 47 | + |
| 48 | + fn expose_lld(&self, cmd: &mut Command) -> Result<()> { |
| 49 | + let rust_lld = Self::rustc_libdir()? |
| 50 | + .parent() |
| 51 | + .context("Rust target libdir has no parent")? |
| 52 | + .join("bin/rust-lld"); |
| 53 | + ensure!(rust_lld.is_file(), "{} does not exist", rust_lld.display()); |
| 54 | + |
| 55 | + let temp_dir = workspace_root()?.join("target/.rust-lld"); |
| 56 | + fs::create_dir_all(&temp_dir).context("failed to create `target/.rust-lld` directory")?; |
| 57 | + for name in ["ld.lld".to_owned()].into_iter().chain(self.lld_alias()) { |
| 58 | + let link = temp_dir.join(name); |
| 59 | + ok_or_not_found!(fs::remove_file(&link)); |
| 60 | + #[cfg(unix)] |
| 61 | + std::os::unix::fs::symlink(&rust_lld, link)?; |
| 62 | + #[cfg(not(unix))] |
| 63 | + anyhow::bail!("bundled rust-lld setup requires a Unix host"); |
| 64 | + } |
| 65 | + |
| 66 | + let path = env::var_os("PATH").unwrap_or_default(); |
| 67 | + cmd.env("PATH", env::join_paths(iter::once(temp_dir).chain(env::split_paths(&path)))?); |
| 68 | + Ok(()) |
| 69 | + } |
| 70 | + |
| 71 | + fn rustc_libdir() -> Result<PathBuf> { |
| 72 | + let output = Command::new(env::var_os("RUSTC").unwrap_or_else(|| "rustc".into())) |
| 73 | + .args(["--print", "target-libdir"]) |
| 74 | + .output() |
| 75 | + .context("failed to locate the Rust target libdir")?; |
| 76 | + |
| 77 | + ensure!(output.status.success(), "`rustc --print target-libdir` failed"); |
| 78 | + Ok(String::from_utf8(output.stdout)?.trim().into()) |
| 79 | + } |
| 80 | + |
| 81 | + // Cross GCC resolves `-fuse-ld=lld` as `<toolchain-prefix>ld.lld`. |
| 82 | + // For example: |
| 83 | + // `aarch64-linux-musl-gcc.sh` => `aarch64-linux-musl-ld.lld` |
| 84 | + // if Cargo has no configured linker: |
| 85 | + // `CROSS_TOOLCHAIN_PREFIX=x86_64-linux-musl-` => `x86_64-linux-musl-ld.lld` |
| 86 | + fn lld_alias(&self) -> Option<String> { |
| 87 | + let var = env::var_os(format!( |
| 88 | + "CARGO_TARGET_{}_LINKER", |
| 89 | + self.target.replace('-', "_").to_ascii_uppercase() |
| 90 | + )) |
| 91 | + .or_else(|| env::var_os("CROSS_TOOLCHAIN_PREFIX"))?; |
| 92 | + |
| 93 | + let prefix = Path::new(&var).file_stem()?.to_str()?.trim_end_matches("gcc"); |
| 94 | + |
| 95 | + Some(format!("{prefix}ld.lld")) |
| 96 | + } |
43 | 97 | } |
0 commit comments