From 247f925e532236aa4e0dc6ac1a3e0a0263083ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sun, 10 May 2026 00:57:57 +0800 Subject: [PATCH] fix: get rid of the Cargo curse (#3952) --- CHANGELOG.md | 2 +- Cargo.lock | 5 +- yazi-actor/Cargo.toml | 2 +- yazi-adapter/Cargo.toml | 2 +- yazi-build/Cargo.toml | 11 +-- yazi-build/build.rs | 176 +++++++++++++++++++++++++++------------ yazi-cli/Cargo.toml | 10 --- yazi-cli/build.rs | 7 +- yazi-config/Cargo.toml | 2 +- yazi-emulator/Cargo.toml | 2 +- yazi-fm/Cargo.toml | 12 +-- yazi-fm/build.rs | 7 +- yazi-term/Cargo.toml | 2 +- yazi-tty/Cargo.toml | 2 +- yazi-tty/src/handle.rs | 80 ++++++++++++++++-- yazi-widgets/Cargo.toml | 2 +- 16 files changed, 219 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4acfeea..aa01d2bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1069,7 +1069,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [v25.12.29]: https://github.com/sxyazi/yazi/compare/v25.5.31...v25.12.29 [v26.1.4]: https://github.com/sxyazi/yazi/compare/v25.12.29...v26.1.4 [v26.1.22]: https://github.com/sxyazi/yazi/compare/v26.1.4...v26.1.22 -[v26.5.6]: https://github.com/sxyazi/yazi/compare/v26.5.6...v26.5.6 +[v26.5.6]: https://github.com/sxyazi/yazi/compare/v26.1.22...v26.5.6 [#4]: https://github.com/sxyazi/yazi/pull/4 [#5]: https://github.com/sxyazi/yazi/pull/5 [#6]: https://github.com/sxyazi/yazi/pull/6 diff --git a/Cargo.lock b/Cargo.lock index 004b84ca..5b3ebe3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5892,8 +5892,9 @@ dependencies = [ [[package]] name = "yazi-build" -version = "26.5.6" +version = "26.5.9" dependencies = [ + "anyhow", "yazi-tty", ] @@ -6345,7 +6346,7 @@ dependencies = [ [[package]] name = "yazi-tty" -version = "26.5.6" +version = "26.5.9" dependencies = [ "libc", "parking_lot", diff --git a/yazi-actor/Cargo.toml b/yazi-actor/Cargo.toml index eb57b525..48e577ac 100644 --- a/yazi-actor/Cargo.toml +++ b/yazi-actor/Cargo.toml @@ -33,7 +33,7 @@ yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } yazi-term = { path = "../yazi-term", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } yazi-watcher = { path = "../yazi-watcher", version = "26.5.6" } yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index ed0c2bb6..e3844700 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -19,7 +19,7 @@ yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies ansi-to-tui = { workspace = true } diff --git a/yazi-build/Cargo.toml b/yazi-build/Cargo.toml index e795abe0..d44264c0 100644 --- a/yazi-build/Cargo.toml +++ b/yazi-build/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "yazi-build" description = "Yazi build system" -version.workspace = true +version = "26.5.9" edition.workspace = true license.workspace = true authors.workspace = true @@ -12,14 +12,9 @@ rust-version.workspace = true [lints] workspace = true -[profile.release] -codegen-units = 1 -lto = true -panic = "abort" -strip = true - [build-dependencies] -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +anyhow = { workspace = true } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } [[bin]] name = "yazi-build" diff --git a/yazi-build/build.rs b/yazi-build/build.rs index 34b78d02..9ed6171e 100644 --- a/yazi-build/build.rs +++ b/yazi-build/build.rs @@ -1,70 +1,140 @@ -use std::{env, error::Error, io::{BufRead, BufReader, Read, Write}, process::{Command, Stdio}, thread}; +use std::{env, fs, io::Write, path::{Path, PathBuf}, process::{self, Command, Stdio}, time::{SystemTime, UNIX_EPOCH}}; +use anyhow::{Context, Result, bail, ensure}; use yazi_tty::TTY; -fn main() -> Result<(), Box> { +fn main() -> Result<()> { yazi_tty::init(); - let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap().to_string_lossy().replace(r"\", "/"); - let crates = if manifest.contains("/git/checkouts/yazi-") { - &["--git", "https://github.com/sxyazi/yazi.git", "yazi-fm", "yazi-cli"] - } else if manifest.contains("/registry/src/index.crates.io-") { - &["yazi-fm", "yazi-cli"][..] + let manifest = env::var_os("CARGO_MANIFEST_DIR") + .context("missing CARGO_MANIFEST_DIR")? + .to_string_lossy() + .replace(r"\", "/"); + + let rev = if manifest.contains("/registry/src/index.crates.io-") { + Some("shipped") + } else if manifest.contains("/git/checkouts/yazi-") { + None } else { + println!("cargo::warning=Unexpected manifest dir: {manifest}"); return Ok(()); }; - let target = env::var("TARGET").unwrap(); - let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - unsafe { - env::set_var("CARGO_TARGET_DIR", "target"); - env::set_var("VERGEN_GIT_SHA", "Crates.io"); - env::set_var("YAZI_CRATE_BUILD", "1"); + let os = env::var("CARGO_CFG_TARGET_OS").context("missing CARGO_CFG_TARGET_OS")?; + let tmp = temp_repo_dir()?; - env::set_var("JEMALLOC_SYS_WITH_LG_PAGE", "16"); - env::set_var("JEMALLOC_SYS_WITH_MALLOC_CONF", "narenas:1"); + TTY.writer().write_all(b"\nCloning Yazi repository...\n")?; + clone_repo(&tmp, rev).context("Failed to clone the Yazi repository")?; - env::set_var( - "MACOSX_DEPLOYMENT_TARGET", - if target == "aarch64-apple-darwin" { "11.0" } else { "10.12" }, - ); - if target == "aarch64-apple-darwin" { - env::set_var("RUSTFLAGS", "-Ctarget-cpu=apple-m1"); - } - }; + TTY.writer().write_all(b"\nBuilding Yazi binaries...\n")?; + build_repo(&tmp, &os).context("Failed to build Yazi from the cloned repository")?; - let profile = if target_os == "windows" { &["--profile", "release-windows"][..] } else { &[] }; - let mut child = Command::new(env::var_os("CARGO").unwrap()) - .args(["install", "--force", "--locked"]) - .args(profile) - .args(crates) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; - - let out = flash(child.stdout.take().unwrap()); - let err = flash(child.stderr.take().unwrap()); - - child.wait()?; - out.join().ok(); - err.join().ok(); + TTY.writer().write_all(b"\nInstalling yazi and ya into cargo bin...\n")?; + install_bins(&tmp, &os).context("Failed to install `yazi` and `ya` into cargo bin")?; Ok(()) } -fn flash(src: R) -> thread::JoinHandle<()> { - thread::spawn(move || { - let reader = BufReader::new(src); - for part in reader.split(b'\n') { - match part { - Ok(mut bytes) => { - bytes.push(b'\n'); - let mut out = TTY.lockout(); - out.write_all(&bytes).ok(); - out.flush().ok(); - } - Err(_) => break, - } - } - }) +fn temp_repo_dir() -> Result { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_nanos()) + .context("Time went backwards")?; + + Ok(env::temp_dir().join(format!("yazi-build-{}-{nonce}", process::id()))) +} + +fn clone_repo(tmp: &Path, rev: Option<&str>) -> Result<()> { + let mut cmd = Command::new("git"); + cmd.args(["-c", "advice.detachedHead=false", "clone", "--depth", "1"]); + + if let Some(rev) = rev { + cmd.args(["--branch", rev]); + } + + run_streamed(cmd.arg("https://github.com/sxyazi/yazi.git").arg(tmp)) +} + +fn build_repo(tmp: &Path, target_os: &str) -> Result<()> { + let cargo = env::var_os("CARGO").unwrap_or_else(|| "cargo".into()); + + let mut cmd = Command::new(cargo); + cmd.current_dir(tmp).arg("build").env("CARGO_TARGET_DIR", "target").arg("--locked"); + + if target_os == "windows" { + cmd.args(["--profile", "release-windows"]); + } else { + cmd.arg("--release"); + } + + run_streamed(&mut cmd) +} + +fn install_bins(tmp: &Path, target_os: &str) -> Result<()> { + let profile = if target_os == "windows" { "release-windows" } else { "release" }; + let ext = if target_os == "windows" { ".exe" } else { "" }; + let bin_dir = cargo_bin_dir()?; + + fs::create_dir_all(&bin_dir)?; + install_bin( + &tmp.join("target").join(profile).join(format!("yazi{ext}")), + &bin_dir.join(format!("yazi{ext}")), + )?; + install_bin( + &tmp.join("target").join(profile).join(format!("ya{ext}")), + &bin_dir.join(format!("ya{ext}")), + )?; + + Ok(()) +} + +fn install_bin(from: &Path, to: &Path) -> Result<()> { + ensure!(from.is_file(), "Built binary not found: {}", from.display()); + + if to.exists() { + fs::remove_file(to) + .with_context(|| format!("failed to remove existing binary: {}", to.display()))?; + } + + fs::copy(from, to) + .with_context(|| format!("failed to copy {} to {}", from.display(), to.display()))?; + fs::set_permissions(to, fs::metadata(from)?.permissions()) + .with_context(|| format!("failed to preserve permissions on {}", to.display()))?; + Ok(()) +} + +fn cargo_bin_dir() -> Result { + if let Some(root) = env::var_os("CARGO_INSTALL_ROOT") { + return Ok(PathBuf::from(root).join("bin")); + } + if let Some(home) = env::var_os("CARGO_HOME") { + return Ok(PathBuf::from(home).join("bin")); + } + if let Some(home) = env::var_os("HOME") { + return Ok(PathBuf::from(home).join(".cargo/bin")); + } + if let Some(home) = env::var_os("USERPROFILE") { + return Ok(PathBuf::from(home).join(".cargo/bin")); + } + bail!("Failed to determine cargo bin directory") +} + +fn run_streamed(cmd: &mut Command) -> Result<()> { + let stdin = { + let input = TTY.lockin(); + Stdio::from(input.try_clone()?) + }; + + let (stdout, stderr) = { + let mut output = TTY.lockout(); + output.flush()?; + + (Stdio::from(output.get_ref().try_clone()?), Stdio::from(output.get_ref().try_clone()?)) + }; + + let status = + cmd.stdin(stdin).stdout(stdout).stderr(stderr).status().context("failed to spawn process")?; + + ensure!(status.success(), "process exited with status {status}"); + Ok(()) } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index d64b482b..eaf05fce 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -12,16 +12,6 @@ rust-version.workspace = true [lints] workspace = true -[profile.release] -codegen-units = 1 -lto = true -panic = "abort" -strip = true - -[profile.release-windows] -inherits = "release" -panic = "unwind" - [dependencies] yazi-boot = { path = "../yazi-boot", version = "26.5.6" } yazi-dds = { path = "../yazi-dds", version = "26.5.6" } diff --git a/yazi-cli/build.rs b/yazi-cli/build.rs index 32995dea..8d596f34 100644 --- a/yazi-cli/build.rs +++ b/yazi-cli/build.rs @@ -9,12 +9,11 @@ use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; fn main() -> Result<(), Box> { let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap().to_string_lossy().replace(r"\", "/"); - if env::var_os("YAZI_CRATE_BUILD").is_none() - && (manifest.contains("/git/checkouts/yazi-") - || manifest.contains("/registry/src/index.crates.io-")) + if manifest.contains("/git/checkouts/yazi-") + || manifest.contains("/registry/src/index.crates.io-") { panic!( - "Due to Cargo's limitations, the `yazi-fm` and `yazi-cli` crates on crates.io must be built with `cargo install --force yazi-build`" + "Due to Cargo's limitations, Yazi on crates.io must be built with `cargo install --force yazi-build`" ); } diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index cc7ad001..cb6130d8 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -18,7 +18,7 @@ yazi-fs = { path = "../yazi-fs", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-emulator/Cargo.toml b/yazi-emulator/Cargo.toml index d2094857..f31ea4ce 100644 --- a/yazi-emulator/Cargo.toml +++ b/yazi-emulator/Cargo.toml @@ -16,7 +16,7 @@ workspace = true yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 2d5cbaf5..74e0322f 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -12,16 +12,6 @@ rust-version.workspace = true [lints] workspace = true -[profile.release] -codegen-units = 1 -lto = true -panic = "abort" -strip = true - -[profile.release-windows] -inherits = "release" -panic = "unwind" - [features] default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] @@ -45,7 +35,7 @@ yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } yazi-term = { path = "../yazi-term", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" } yazi-watcher = { path = "../yazi-watcher", version = "26.5.6" } yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" } diff --git a/yazi-fm/build.rs b/yazi-fm/build.rs index 96e55461..5628c2fc 100644 --- a/yazi-fm/build.rs +++ b/yazi-fm/build.rs @@ -16,12 +16,11 @@ fn main() -> Result<(), Box> { } let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap().to_string_lossy().replace(r"\", "/"); - if env::var_os("YAZI_CRATE_BUILD").is_none() - && (manifest.contains("/git/checkouts/yazi-") - || manifest.contains("/registry/src/index.crates.io-")) + if manifest.contains("/git/checkouts/yazi-") + || manifest.contains("/registry/src/index.crates.io-") { panic!( - "Due to Cargo's limitations, the `yazi-fm` and `yazi-cli` crates on crates.io must be built with `cargo install --force yazi-build`" + "Due to Cargo's limitations, Yazi on crates.io must be built with `cargo install --force yazi-build`" ); } diff --git a/yazi-term/Cargo.toml b/yazi-term/Cargo.toml index db5f2203..c4600bbe 100644 --- a/yazi-term/Cargo.toml +++ b/yazi-term/Cargo.toml @@ -17,7 +17,7 @@ yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-emulator = { path = "../yazi-emulator", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-tty/Cargo.toml b/yazi-tty/Cargo.toml index 1cb61f1d..d62e588e 100644 --- a/yazi-tty/Cargo.toml +++ b/yazi-tty/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "yazi-tty" description = "Yazi TTY access layer" -version.workspace = true +version = "26.5.9" edition.workspace = true license.workspace = true authors.workspace = true diff --git a/yazi-tty/src/handle.rs b/yazi-tty/src/handle.rs index b6b49b88..270350cf 100644 --- a/yazi-tty/src/handle.rs +++ b/yazi-tty/src/handle.rs @@ -1,4 +1,4 @@ -use std::{io::{Error, ErrorKind, Read, Write}, time::Duration}; +use std::{io::{Error, ErrorKind, Read, Write}, ptr, time::Duration}; use tracing::error; @@ -27,6 +27,24 @@ impl Drop for Handle { } } +#[cfg(unix)] +impl From for std::process::Stdio { + fn from(value: Handle) -> Self { + use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd}; + + Self::from(unsafe { OwnedFd::from_raw_fd(value.into_raw_fd()) }) + } +} + +#[cfg(windows)] +impl From for std::process::Stdio { + fn from(value: Handle) -> Self { + use std::os::windows::io::{FromRawHandle, IntoRawHandle, OwnedHandle}; + + Self::from(unsafe { OwnedHandle::from_raw_handle(value.into_raw_handle()) }) + } +} + impl Read for Handle { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { #[cfg(unix)] @@ -75,6 +93,22 @@ impl Write for Handle { fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } +#[cfg(unix)] +impl std::os::fd::IntoRawFd for Handle { + fn into_raw_fd(mut self) -> std::os::fd::RawFd { + self.close = false; + self.inner + } +} + +#[cfg(windows)] +impl std::os::windows::io::IntoRawHandle for Handle { + fn into_raw_handle(mut self) -> std::os::windows::io::RawHandle { + self.close = false; + self.inner + } +} + #[cfg(unix)] impl Handle { pub(super) fn new(out: bool) -> Self { @@ -106,7 +140,7 @@ impl Handle { let mut set: libc::fd_set = std::mem::zeroed(); libc::FD_ZERO(&mut set); libc::FD_SET(self.inner, &mut set); - libc::select(self.inner + 1, &mut set, std::ptr::null_mut(), std::ptr::null_mut(), &mut tv) + libc::select(self.inner + 1, &mut set, ptr::null_mut(), ptr::null_mut(), &mut tv) }; match result { @@ -124,6 +158,13 @@ impl Handle { _ => Ok(b), } } + + pub fn try_clone(&self) -> std::io::Result { + match unsafe { libc::dup(self.inner) } { + -1 => Err(Error::last_os_error()), + fd => Ok(Self { inner: fd, close: true }), + } + } } #[cfg(windows)] @@ -139,10 +180,10 @@ impl Handle { name.as_ptr(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - std::ptr::null_mut(), + ptr::null_mut(), OPEN_EXISTING, 0, - std::ptr::null_mut(), + ptr::null_mut(), ) }; @@ -184,7 +225,7 @@ impl Handle { let mut buf = 0; let mut bytes = 0; - let success = unsafe { ReadFile(self.inner, &mut buf, 1, &mut bytes, std::ptr::null_mut()) }; + let success = unsafe { ReadFile(self.inner, &mut buf, 1, &mut bytes, ptr::null_mut()) }; if success == 0 { return Err(Error::last_os_error()); @@ -193,4 +234,33 @@ impl Handle { } Ok(buf) } + + pub fn try_clone(&self) -> std::io::Result { + use windows_sys::Win32::{Foundation::{DUPLICATE_SAME_ACCESS, DuplicateHandle, HANDLE}, System::Threading::GetCurrentProcess}; + + let proc = unsafe { GetCurrentProcess() }; + let mut handle = ptr::null_mut(); + let status = unsafe { + DuplicateHandle( + proc, + self.inner, + proc, + &mut handle as *mut HANDLE, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ) + }; + + if status == 0 { + Err(Error::last_os_error()) + } else { + Ok(Self { + inner: handle, + close: true, + out_utf8: self.out_utf8, + incomplete_utf8: Default::default(), + }) + } + } } diff --git a/yazi-widgets/Cargo.toml b/yazi-widgets/Cargo.toml index bcf1f357..9f6cce3a 100644 --- a/yazi-widgets/Cargo.toml +++ b/yazi-widgets/Cargo.toml @@ -22,7 +22,7 @@ yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-macro = { path = "../yazi-macro", version = "26.5.6" } yazi-shared = { path = "../yazi-shared", version = "26.5.6" } yazi-shim = { path = "../yazi-shim", version = "26.5.6" } -yazi-tty = { path = "../yazi-tty", version = "26.5.6" } +yazi-tty = { path = "../yazi-tty", version = "26.5.9" } # External dependencies anyhow = { workspace = true }