From e55cd6cffbda98cf71557d8ca6ca255cf051c04a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 31 Mar 2026 23:03:41 +0200 Subject: [PATCH 01/17] wasm: add 22 more utilities to feat_wasm and add WASI platform stubs --- Cargo.toml | 20 +++++++++++++++++++- src/uu/ln/src/ln.rs | 8 ++++++++ src/uu/split/src/platform/mod.rs | 28 ++++++++++++++++++++++++++++ src/uu/touch/src/touch.rs | 4 ++++ src/uu/tsort/src/tsort.rs | 1 - 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb27c7806a8..10b0369ca86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,27 +180,36 @@ feat_Tier1 = [ # It is bit complex to deduplicate with other lists feat_wasm = [ "arch", - "basename", "base32", "base64", "basenc", + "basename", "cat", + "comm", "cp", + "csplit", "cut", "ls", "date", + "dir", "dircolors", "dirname", "echo", "expand", + "expr", "factor", "false", "fmt", "fold", + "head", "join", "link", + "ln", + "ls", + "mkdir", "mv", "nl", + "nproc", "numfmt", "od", "paste", @@ -211,7 +220,13 @@ feat_wasm = [ "printf", "ptx", "pwd", + "readlink", + "realpath", + "rm", + "rmdir", "seq", + "sort", + "split", "shred", "shuf", "sleep", @@ -219,13 +234,16 @@ feat_wasm = [ "sum", "tail", "tee", + "touch", "tr", "true", "truncate", + "tsort", "uname", "unexpand", "uniq", "unlink", + "vdir", "wc", "yes", # cksum family diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 1c7d40b8476..f7c953908a1 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -488,3 +488,11 @@ pub fn symlink, P2: AsRef>(src: P1, dst: P2) -> std::io::R symlink_file(src, dst) } } + +#[cfg(target_os = "wasi")] +fn symlink, P2: AsRef>(_src: P1, _dst: P2) -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "symlinks not supported on this platform", + )) +} diff --git a/src/uu/split/src/platform/mod.rs b/src/uu/split/src/platform/mod.rs index 5afc43eeb7f..0128e7f5552 100644 --- a/src/uu/split/src/platform/mod.rs +++ b/src/uu/split/src/platform/mod.rs @@ -12,6 +12,34 @@ pub use self::windows::instantiate_current_writer; #[cfg(windows)] pub use self::windows::paths_refer_to_same_file; +// WASI: no process spawning (filter) or device/inode comparison. +#[cfg(target_os = "wasi")] +pub fn paths_refer_to_same_file(_p1: &std::ffi::OsStr, _p2: &std::ffi::OsStr) -> bool { + false +} + +#[cfg(target_os = "wasi")] +pub fn instantiate_current_writer( + _filter: Option<&str>, + filename: &str, + is_new: bool, +) -> std::io::Result>> { + let file = if is_new { + std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(std::path::Path::new(filename))? + } else { + std::fs::OpenOptions::new() + .append(true) + .open(std::path::Path::new(filename))? + }; + Ok(std::io::BufWriter::new( + Box::new(file) as Box + )) +} + #[cfg(unix)] mod unix; diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index d1080c1dddf..4cf4dce2299 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -883,6 +883,10 @@ fn pathbuf_from_stdout() -> Result { .map_err(|e| TouchError::WindowsStdoutPathError(e.to_string()))? .into()) } + #[cfg(target_os = "wasi")] + { + Ok(PathBuf::from("/dev/stdout")) + } } #[cfg(test)] diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs index 5b2fb66d6d5..1868af85c3a 100644 --- a/src/uu/tsort/src/tsort.rs +++ b/src/uu/tsort/src/tsort.rs @@ -82,7 +82,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { target_os = "linux", target_os = "android", target_os = "fuchsia", - target_os = "wasi", target_env = "uclibc", target_os = "freebsd", ))] From 28682fc5f08902e310939a7157415318dc43e9b2 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 12:16:30 +0200 Subject: [PATCH 02/17] Remove 'expr' from dependencies in Cargo.toml (needs a c compiler) --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 10b0369ca86..1c63055dd78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,7 +196,6 @@ feat_wasm = [ "dirname", "echo", "expand", - "expr", "factor", "false", "fmt", From 7499e618c1d632d35f7d7ea7df7b77263fe9da7e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 2 Apr 2026 21:36:07 +0200 Subject: [PATCH 03/17] uucore: use wasi OsStrExt/OsStringExt for byte-level access on wasm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WASI provides std::os::wasi::ffi::{OsStrExt, OsStringExt} with the same as_bytes()/from_bytes()/into_vec()/from_vec() API as Unix. Without this, os_str_as_bytes() fell into the not(unix) path requiring UTF-8 validation via to_str(), causing failures like: echo '🍎,🍌,πŸ’,πŸ₯' => "invalid UTF-8 input encountered" --- src/uucore/src/lib/lib.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 4b2cb9b502b..27548688987 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -142,6 +142,8 @@ use std::io::{BufRead, BufReader}; use std::iter; #[cfg(unix)] use std::os::unix::ffi::{OsStrExt, OsStringExt}; +#[cfg(target_os = "wasi")] +use std::os::wasi::ffi::{OsStrExt, OsStringExt}; use std::str; use std::str::Utf8Chunk; use std::sync::{LazyLock, atomic::Ordering}; @@ -434,12 +436,12 @@ impl error::UError for NonUtf8OsStrError {} /// /// This always succeeds on unix platforms, /// and fails on other platforms if the string can't be coerced to UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_str_as_bytes(os_string: &OsStr) -> Result<&[u8], NonUtf8OsStrError> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Ok(os_string.as_bytes()); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] os_string .to_str() .ok_or_else(|| NonUtf8OsStrError { @@ -453,10 +455,10 @@ pub fn os_str_as_bytes(os_string: &OsStr) -> Result<&[u8], NonUtf8OsStrError> { /// This is always lossless on unix platforms, /// and wraps [`OsStr::to_string_lossy`] on non-unix platforms. pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<'_, [u8]> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Cow::from(os_string.as_bytes()); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] match os_string.to_string_lossy() { Cow::Borrowed(slice) => Cow::from(slice.as_bytes()), Cow::Owned(owned) => Cow::from(owned.into_bytes()), @@ -468,12 +470,12 @@ pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<'_, [u8]> { /// /// This always succeeds on unix platforms, /// and fails on other platforms if the bytes can't be parsed as UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_str_from_bytes(bytes: &[u8]) -> error::UResult> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Ok(Cow::Borrowed(OsStr::from_bytes(bytes))); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] Ok(Cow::Owned(OsString::from(str::from_utf8(bytes).map_err( |_| error::UUsageError::new(1, "Unable to transform bytes into OsStr"), )?))) @@ -483,12 +485,12 @@ pub fn os_str_from_bytes(bytes: &[u8]) -> error::UResult> { /// /// This always succeeds on unix platforms, /// and fails on other platforms if the bytes can't be parsed as UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_string_from_vec(vec: Vec) -> error::UResult { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Ok(OsString::from_vec(vec)); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] Ok(OsString::from(String::from_utf8(vec).map_err(|_| { error::UUsageError::new(1, "invalid UTF-8 was detected in one or more arguments") })?)) @@ -498,11 +500,11 @@ pub fn os_string_from_vec(vec: Vec) -> error::UResult { /// /// This always succeeds on unix platforms, /// and fails on other platforms if the bytes can't be parsed as UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_string_to_vec(s: OsString) -> error::UResult> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] let v = s.into_vec(); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] let v = s .into_string() .map_err(|_| { From 1f56171d7e566334d9a61518edbc60ae4da09167 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 2 Apr 2026 21:36:57 +0200 Subject: [PATCH 04/17] echo: add multibyte unicode test cases --- tests/by-util/test_echo.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index 3398708f176..c3c9400995d 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -825,4 +825,14 @@ fn test_emoji_output() { .args(&["πŸ¦€", "loves", "πŸš€", "and", "🌟"]) .succeeds() .stdout_only("πŸ¦€ loves πŸš€ and 🌟\n"); + + new_ucmd!() + .arg("🍎,🍌,πŸ’,πŸ₯") + .succeeds() + .stdout_only("🍎,🍌,πŸ’,πŸ₯\n"); + + new_ucmd!() + .arg("γ“γ‚“γ«γ‘γ―δΈ–η•Œ") + .succeeds() + .stdout_only("γ“γ‚“γ«γ‘γ―δΈ–η•Œ\n"); } From 59cd735bae7fec54f5822c7c3e0460d4799442d8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 2 Apr 2026 21:37:12 +0200 Subject: [PATCH 05/17] CI: add WASI test workflow with wasmtime --- .github/workflows/wasi.yml | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/wasi.yml diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml new file mode 100644 index 00000000000..e66faa22407 --- /dev/null +++ b/.github/workflows/wasi.yml @@ -0,0 +1,39 @@ +# spell-checker:ignore wasip wasmtime +name: WASI + +# spell-checker:ignore TRIGGERPATH +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +# End the current execution if there is a new changeset in the PR. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + test_wasi: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-wasip1 + - uses: Swatinem/rust-cache@v2 + - name: Install wasmtime + run: | + curl https://wasmtime.dev/install.sh -sSf | bash + echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH + - name: Run tests + env: + CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime + run: | + cargo test --target wasm32-wasip1 --no-default-features --features feat_wasm -p uu_echo -p uu_cut From f325cf339ea60bf7472439aaf6ff9d7eb8cb806c Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 2 Apr 2026 14:54:20 +0200 Subject: [PATCH 06/17] wasi: fix warnings --- src/uu/cp/src/cp.rs | 14 +++++++++----- src/uu/mv/src/mv.rs | 2 +- src/uu/wc/src/countable.rs | 4 ++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index af1243779bf..ca82edf41f3 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1896,12 +1896,13 @@ pub(crate) fn copy_attributes( fn symlink_file( source: &Path, dest: &Path, - symlinked_files: &mut HashSet, + #[cfg(not(target_os = "wasi"))] symlinked_files: &mut HashSet, + #[cfg(target_os = "wasi")] _symlinked_files: &mut HashSet, ) -> CopyResult<()> { #[cfg(target_os = "wasi")] { return Err(CpError::IoErrContext( - std::io::Error::new(std::io::ErrorKind::Unsupported, "symlinks not supported"), + io::Error::new(io::ErrorKind::Unsupported, "symlinks not supported"), translate!("cp-error-cannot-create-symlink", "dest" => get_filename(dest).unwrap_or("?").quote(), "source" => get_filename(source).unwrap_or("?").quote()), @@ -1930,10 +1931,13 @@ fn symlink_file( ) })?; } - if let Ok(file_info) = FileInformation::from_path(dest, false) { - symlinked_files.insert(file_info); + #[cfg(not(target_os = "wasi"))] + { + if let Ok(file_info) = FileInformation::from_path(dest, false) { + symlinked_files.insert(file_info); + } + Ok(()) } - Ok(()) } fn context_for(src: &Path, dest: &Path) -> String { diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index fa106ac82c0..985c4ba4a04 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -929,7 +929,7 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> { } #[cfg(target_os = "wasi")] -fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> { +fn rename_symlink_fallback(_from: &Path, _to: &Path) -> io::Result<()> { Err(io::Error::new( io::ErrorKind::Other, translate!("mv-error-no-symlink-support"), diff --git a/src/uu/wc/src/countable.rs b/src/uu/wc/src/countable.rs index 32d56553879..e2ff5b0e2c4 100644 --- a/src/uu/wc/src/countable.rs +++ b/src/uu/wc/src/countable.rs @@ -24,6 +24,7 @@ pub trait WordCountable: AsFd + AsRawFd + Read { pub trait WordCountable: Read { type Buffered: BufRead; fn buffered(self) -> Self::Buffered; + #[cfg(not(target_os = "wasi"))] fn inner_file(&mut self) -> Option<&mut File>; } @@ -40,6 +41,8 @@ impl WordCountable for StdinLock<'_> { fn buffered(self) -> Self::Buffered { self } + + #[cfg(not(target_os = "wasi"))] fn inner_file(&mut self) -> Option<&mut File> { None } @@ -62,6 +65,7 @@ impl WordCountable for File { BufReader::new(self) } + #[cfg(not(target_os = "wasi"))] fn inner_file(&mut self) -> Option<&mut File> { Some(self) } From 8a4faceb855485d4815c438c88272ec534052cec Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 12:43:31 +0200 Subject: [PATCH 07/17] sort: exclude ctrlc dependency and signal handler on wasi --- src/uu/sort/src/tmp_dir.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/uu/sort/src/tmp_dir.rs b/src/uu/sort/src/tmp_dir.rs index cf484e2ff70..0e5bd1f34d4 100644 --- a/src/uu/sort/src/tmp_dir.rs +++ b/src/uu/sort/src/tmp_dir.rs @@ -3,9 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::path::Path; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ fs::File, @@ -15,7 +15,7 @@ use std::{ use tempfile::TempDir; use uucore::error::UResult; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use uucore::{error::USimpleError, show_error, translate}; use crate::{SortError, current_open_fd_count, fd_soft_limit}; @@ -105,6 +105,7 @@ fn ensure_signal_handler_installed(state: Arc>) -> UR } #[cfg(any(target_os = "redox", target_os = "wasi"))] +#[allow(clippy::unnecessary_wraps)] fn ensure_signal_handler_installed(_state: Arc>) -> UResult<()> { Ok(()) } @@ -184,7 +185,7 @@ impl Drop for TmpDirWrapper { /// Remove the directory at `path` by deleting its child files and then itself. /// Errors while deleting child files are ignored. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] fn remove_tmp_dir(path: &Path) -> std::io::Result<()> { if let Ok(read_dir) = std::fs::read_dir(path) { for file in read_dir.flatten() { From 6e47ea28b229053c4a48d291cb4787ef79071509 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 12:44:25 +0200 Subject: [PATCH 08/17] sort: fix clippy warnings on wasi target --- src/uu/sort/src/merge.rs | 2 ++ src/uu/sort/src/sort.rs | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index d2bb3056b92..df03a3da612 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -571,6 +571,8 @@ impl MergeInput for CompressedTmpMergeInput { type InnerRead = ChildStdout; fn finished_reading(self) -> UResult<()> { + // Explicitly close stdout before waiting on the child process. + #[allow(clippy::drop_non_drop)] drop(self.child_stdout); check_child_success(self.child, &self.compress_prog)?; let _ = fs::remove_file(self.path); diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index c3ba3dcb27a..6fe6e4ee7ff 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -37,7 +37,9 @@ use std::ffi::{OsStr, OsString}; use std::fs::{File, OpenOptions}; use std::hash::{Hash, Hasher}; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; -use std::num::{IntErrorKind, NonZero}; +use std::num::IntErrorKind; +#[cfg(not(target_os = "wasi"))] +use std::num::NonZero; use std::ops::Range; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; @@ -2124,12 +2126,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { settings.threads = matches .get_one::(options::PARALLEL) .map_or_else(|| "0".to_string(), String::from); - let num_threads = match settings.threads.parse::() { - Ok(0) | Err(_) => std::thread::available_parallelism().map_or(1, NonZero::get), - Ok(n) => n, - }; #[cfg(not(target_os = "wasi"))] { + let num_threads = match settings.threads.parse::() { + Ok(0) | Err(_) => std::thread::available_parallelism().map_or(1, NonZero::get), + Ok(n) => n, + }; let _ = rayon::ThreadPoolBuilder::new() .num_threads(num_threads) .build_global(); From 6ad9ea2c27e376d95113997f28d302ea79661e46 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 12:45:09 +0200 Subject: [PATCH 09/17] cp: fix clippy warnings on wasi target --- src/uu/cp/src/cp.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index ca82edf41f3..c769feceea9 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1901,13 +1901,12 @@ fn symlink_file( ) -> CopyResult<()> { #[cfg(target_os = "wasi")] { - return Err(CpError::IoErrContext( + Err(CpError::IoErrContext( io::Error::new(io::ErrorKind::Unsupported, "symlinks not supported"), translate!("cp-error-cannot-create-symlink", "dest" => get_filename(dest).unwrap_or("?").quote(), "source" => get_filename(source).unwrap_or("?").quote()), - ) - .into()); + )) } #[cfg(not(any(windows, target_os = "wasi")))] { From 4d916597080ca51f0b5cb1de78bfd988453d5227 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 12:45:46 +0200 Subject: [PATCH 10/17] mv: fix clippy warning on wasi target --- src/uu/mv/src/mv.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 985c4ba4a04..28901767e7b 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -930,10 +930,7 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> { #[cfg(target_os = "wasi")] fn rename_symlink_fallback(_from: &Path, _to: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - translate!("mv-error-no-symlink-support"), - )) + Err(io::Error::other(translate!("mv-error-no-symlink-support"))) } fn rename_dir_fallback( From 335800f6a1d8b46ee0ebbe68552892d564fb6b49 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 13:18:14 +0200 Subject: [PATCH 11/17] CI: fix feat_wasm feature flag in wasi workflow --- .github/workflows/wasi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml index e66faa22407..c4c08974de4 100644 --- a/.github/workflows/wasi.yml +++ b/.github/workflows/wasi.yml @@ -36,4 +36,4 @@ jobs: env: CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime run: | - cargo test --target wasm32-wasip1 --no-default-features --features feat_wasm -p uu_echo -p uu_cut + cargo test --target wasm32-wasip1 --no-default-features -p uu_echo -p uu_cut From 6c5a34813ed4930abad5c839e8d5b61518082618 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 13:19:05 +0200 Subject: [PATCH 12/17] tail: fix clippy warnings on wasi target --- src/uu/tail/src/follow/mod.rs | 12 ++++++------ src/uu/tail/src/paths.rs | 11 +++++++---- src/uu/tail/src/platform/mod.rs | 13 ------------- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/uu/tail/src/follow/mod.rs b/src/uu/tail/src/follow/mod.rs index 71d65d4a96c..2cd9b3af7fc 100644 --- a/src/uu/tail/src/follow/mod.rs +++ b/src/uu/tail/src/follow/mod.rs @@ -3,6 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +#[cfg(not(target_os = "wasi"))] mod files; #[cfg(not(target_os = "wasi"))] mod watch; @@ -21,21 +22,19 @@ mod wasi_stubs { pub struct Observer { pub use_polling: bool, - pub pid: super::super::platform::Pid, } impl Observer { - pub fn from(settings: &Settings) -> Self { - Self { - use_polling: false, - pid: settings.pid, - } + pub fn from(_settings: &Settings) -> Self { + Self { use_polling: false } } + #[allow(clippy::unnecessary_wraps)] pub fn start(&mut self, _settings: &Settings) -> UResult<()> { Ok(()) } + #[allow(clippy::unnecessary_wraps)] pub fn add_path( &mut self, _path: &Path, @@ -46,6 +45,7 @@ mod wasi_stubs { Ok(()) } + #[allow(clippy::unnecessary_wraps)] pub fn add_bad_path( &mut self, _path: &Path, diff --git a/src/uu/tail/src/paths.rs b/src/uu/tail/src/paths.rs index 4fd7dd51c39..8682cb571af 100644 --- a/src/uu/tail/src/paths.rs +++ b/src/uu/tail/src/paths.rs @@ -12,6 +12,7 @@ use std::io::{Seek, SeekFrom}; #[cfg(unix)] use std::os::unix::fs::{FileTypeExt, MetadataExt}; use std::path::{Path, PathBuf}; +#[cfg(not(target_os = "wasi"))] use uucore::error::UResult; use uucore::translate; @@ -157,7 +158,9 @@ impl FileExtTail for File { pub trait MetadataExtTail { fn is_tailable(&self) -> bool; + #[cfg(not(target_os = "wasi"))] fn got_truncated(&self, other: &Metadata) -> UResult; + #[cfg(not(target_os = "wasi"))] fn file_id_eq(&self, other: &Metadata) -> bool; } @@ -175,10 +178,12 @@ impl MetadataExtTail for Metadata { } /// Return true if the file was modified and is now shorter + #[cfg(not(target_os = "wasi"))] fn got_truncated(&self, other: &Metadata) -> UResult { Ok(other.len() < self.len() && other.modified()? != self.modified()?) } + #[cfg(not(target_os = "wasi"))] fn file_id_eq(&self, #[cfg(unix)] other: &Metadata, #[cfg(not(unix))] _: &Metadata) -> bool { #[cfg(unix)] { @@ -197,19 +202,17 @@ impl MetadataExtTail for Metadata { // } false } - #[cfg(not(any(unix, windows)))] - { - false - } } } +#[cfg(not(target_os = "wasi"))] pub trait PathExtTail { fn is_stdin(&self) -> bool; fn is_orphan(&self) -> bool; fn is_tailable(&self) -> bool; } +#[cfg(not(target_os = "wasi"))] impl PathExtTail for Path { fn is_stdin(&self) -> bool { self.eq(Self::new(text::DASH)) diff --git a/src/uu/tail/src/platform/mod.rs b/src/uu/tail/src/platform/mod.rs index 77a9b311e25..bb77501fdcb 100644 --- a/src/uu/tail/src/platform/mod.rs +++ b/src/uu/tail/src/platform/mod.rs @@ -18,19 +18,6 @@ pub use self::windows::{Pid, ProcessChecker, supports_pid_checks}; #[cfg(target_os = "wasi")] pub type Pid = u64; -#[cfg(target_os = "wasi")] -pub struct ProcessChecker; - -#[cfg(target_os = "wasi")] -impl ProcessChecker { - pub fn new(_pid: Pid) -> Self { - Self - } - pub fn is_dead(&mut self) -> bool { - true - } -} - #[cfg(target_os = "wasi")] pub fn supports_pid_checks(_pid: Pid) -> bool { false From 8204244c342f80a811a38b19448f5817fb6ea041 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 13:19:24 +0200 Subject: [PATCH 13/17] split: fix wasi platform stub to accept OsStr filename --- src/uu/split/src/platform/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/split/src/platform/mod.rs b/src/uu/split/src/platform/mod.rs index 0128e7f5552..33dc874586e 100644 --- a/src/uu/split/src/platform/mod.rs +++ b/src/uu/split/src/platform/mod.rs @@ -21,7 +21,7 @@ pub fn paths_refer_to_same_file(_p1: &std::ffi::OsStr, _p2: &std::ffi::OsStr) -> #[cfg(target_os = "wasi")] pub fn instantiate_current_writer( _filter: Option<&str>, - filename: &str, + filename: &std::ffi::OsStr, is_new: bool, ) -> std::io::Result>> { let file = if is_new { From 78373e81add441ce7e110b7cedb9593325a17f08 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 13:19:41 +0200 Subject: [PATCH 14/17] date: fix build without i18n-datetime feature --- src/uu/date/src/date.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index e577d081c49..b82b620a415 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -718,12 +718,19 @@ fn format_date_with_locale_aware_months( let broken_down = BrokenDownTime::from(date); + // When the i18n-datetime feature is enabled (default), use ICU locale-aware + // formatting if the locale requires it. Without the feature (e.g. wasi/wasm + // builds that use --no-default-features), skip localization entirely and + // format with the raw strftime string. + #[cfg(feature = "i18n-datetime")] let result = if !should_use_icu_locale() || skip_localization { broken_down.to_string_with_config(config, format_string) } else { let fmt = localize_format_string(format_string, date.date()); broken_down.to_string_with_config(config, &fmt) }; + #[cfg(not(feature = "i18n-datetime"))] + let result = broken_down.to_string_with_config(config, format_string); result.map_err(|e| e.to_string()) } From 1d09875cf25bd42161a8efa60480662176d5647e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 13:26:44 +0200 Subject: [PATCH 15/17] ignore some CI words --- .vscode/cspell.dictionaries/jargon.wordlist.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 2f26340acdc..e4c1592b475 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -215,6 +215,10 @@ vals inval nofield +# ci +dtolnay +Swatinem + # * clippy uninlined nonminimal From 69d1937d13125dbb83cd1ba2349124103ed61e5e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 13:32:16 +0200 Subject: [PATCH 16/17] adjust the spell-checker ignores --- .github/workflows/CICD.yml | 4 ++-- .github/workflows/FixPR.yml | 2 +- .github/workflows/GnuTests.yml | 4 ++-- .github/workflows/android.yml | 2 +- .github/workflows/benchmarks.yml | 2 +- .github/workflows/code-quality.yml | 2 +- .github/workflows/documentation.yml | 2 +- .github/workflows/freebsd.yml | 2 +- .github/workflows/fuzzing.yml | 2 +- .github/workflows/make.yml | 2 +- .github/workflows/manpage-lint.yml | 2 +- .github/workflows/openbsd.yml | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index a3e1a194c01..4385ae152dc 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -3,9 +3,9 @@ name: CICD # spell-checker:ignore (abbrev/names) CACHEDIR CICD CodeCOV MacOS MinGW MSVC musl taiki # spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic Dwarnings RUSTDOCFLAGS RUSTFLAGS Zpanic CARGOFLAGS CLEVEL nodocs # spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain fuzzers dedupe devel profdata -# spell-checker:ignore (people) Peltoche rivy dtolnay Anson dawidd +# spell-checker:ignore (people) Peltoche rivy Anson dawidd # spell-checker:ignore (shell/tools) binutils choco clippy dmake esac fakeroot fdesc fdescfs gmake grcov halium lcov libclang libfuse libssl limactl nextest nocross pacman popd printf pushd redoxer rsync rustc rustfmt rustup shopt sccache utmpdump xargs zstd -# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest Swatinem tempfile testsuite toybox uutils libsystemd codspeed wasip libexecinfo +# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest tempfile testsuite toybox uutils libsystemd codspeed wasip libexecinfo env: PROJECT_NAME: coreutils diff --git a/.github/workflows/FixPR.yml b/.github/workflows/FixPR.yml index 0024aee5883..224617c6d3e 100644 --- a/.github/workflows/FixPR.yml +++ b/.github/workflows/FixPR.yml @@ -1,6 +1,6 @@ name: FixPR -# spell-checker:ignore Swatinem dtolnay dedupe +# spell-checker:ignore dedupe # Trigger automated fixes for PRs being merged (with associated commits) diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index e063c70552a..f91941feb8d 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -1,10 +1,10 @@ name: GnuTests -# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests Swatinem +# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests # spell-checker:ignore (jargon) submodules devel # spell-checker:ignore (libs/utils) chksum dpkg getenforce gperf lcov libexpect limactl pyinotify setenforce shopt valgrind libattr libcap taiki-e zstd cpio # spell-checker:ignore (options) Ccodegen Coverflow Cpanic Zpanic -# spell-checker:ignore (people) Dawid Dziurla * dawidd dtolnay +# spell-checker:ignore (people) Dawid Dziurla * dawidd # spell-checker:ignore (vars) FILESET SUBDIRS XPASS # spell-checker:ignore userns nodocs diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index eeb68292d00..2837c79f60a 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,6 +1,6 @@ name: Android -# spell-checker:ignore (people) reactivecircus Swatinem dtolnay juliangruber +# spell-checker:ignore (people) reactivecircus juliangruber # spell-checker:ignore (shell/tools) TERMUX nextest udevadm pkill # spell-checker:ignore (misc) swiftshader playstore DATALOSS noaudio diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index d2d13275266..17c37e41a93 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -1,6 +1,6 @@ name: Benchmarks -# spell-checker:ignore (people) dtolnay Swatinem taiki-e +# spell-checker:ignore (people) taiki-e # spell-checker:ignore (misc) codspeed sccache on: diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index e5bad3e1d50..7b7bf87e06a 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -1,6 +1,6 @@ name: Code Quality -# spell-checker:ignore (people) dtolnay juliangruber pell reactivecircus Swatinem taiki-e taplo +# spell-checker:ignore (people) juliangruber pell reactivecircus taiki-e taplo # spell-checker:ignore (misc) TERMUX noaudio pkill swiftshader esac sccache pcoreutils shopt subshell dequote libsystemd on: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 53f830a7911..ae5e8d3c8c1 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,4 +1,4 @@ -# spell-checker:ignore dtolnay libsystemd libattr libcap gsub +# spell-checker:ignore libsystemd libattr libcap gsub name: Check uudoc Documentation Generation diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index f31af77adc2..91879538877 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -1,6 +1,6 @@ name: FreeBSD -# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc nextest copyback logind +# spell-checker:ignore sshfs usesh vmactions taiki esac fdescfs fdesc nextest copyback logind env: # * style job configuration diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index 034f5384f38..47ee6477191 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -1,6 +1,6 @@ name: Fuzzing -# spell-checker:ignore (people) dtolnay Swatinem taiki-e +# spell-checker:ignore (people) taiki-e # spell-checker:ignore (misc) fuzzer env: diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml index 1ebe630c124..936b60669ca 100644 --- a/.github/workflows/make.yml +++ b/.github/workflows/make.yml @@ -5,7 +5,7 @@ name: make # spell-checker:ignore (jargon) deps softprops toolchain # spell-checker:ignore (people) dawidd # spell-checker:ignore (shell/tools) nextest sccache zstd -# spell-checker:ignore (misc) bindir busytest defconfig DESTDIR manpages multisize runtest Swatinem testsuite toybox uutils +# spell-checker:ignore (misc) bindir busytest defconfig DESTDIR manpages multisize runtest testsuite toybox uutils env: PROJECT_NAME: coreutils diff --git a/.github/workflows/manpage-lint.yml b/.github/workflows/manpage-lint.yml index fe30d79afa7..c294e01a26b 100644 --- a/.github/workflows/manpage-lint.yml +++ b/.github/workflows/manpage-lint.yml @@ -1,4 +1,4 @@ -# spell-checker:ignore mandoc uudoc manpages dtolnay libsystemd libattr libcap DESTDIR +# spell-checker:ignore mandoc uudoc manpages libsystemd libattr libcap DESTDIR name: Manpage Validation diff --git a/.github/workflows/openbsd.yml b/.github/workflows/openbsd.yml index 56b10c1d746..797185e04c9 100644 --- a/.github/workflows/openbsd.yml +++ b/.github/workflows/openbsd.yml @@ -1,6 +1,6 @@ name: OpenBSD -# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc sccache nextest copyback logind bindgen libclang +# spell-checker:ignore sshfs usesh vmactions taiki esac fdescfs fdesc sccache nextest copyback logind bindgen libclang env: # * style job configuration From 72d26ac2f38bb29277ff62aa95e31c585fca194e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 17:57:02 +0200 Subject: [PATCH 17/17] wc: remove unnecessary cfg --- src/uu/wc/src/countable.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/uu/wc/src/countable.rs b/src/uu/wc/src/countable.rs index e2ff5b0e2c4..32d56553879 100644 --- a/src/uu/wc/src/countable.rs +++ b/src/uu/wc/src/countable.rs @@ -24,7 +24,6 @@ pub trait WordCountable: AsFd + AsRawFd + Read { pub trait WordCountable: Read { type Buffered: BufRead; fn buffered(self) -> Self::Buffered; - #[cfg(not(target_os = "wasi"))] fn inner_file(&mut self) -> Option<&mut File>; } @@ -41,8 +40,6 @@ impl WordCountable for StdinLock<'_> { fn buffered(self) -> Self::Buffered { self } - - #[cfg(not(target_os = "wasi"))] fn inner_file(&mut self) -> Option<&mut File> { None } @@ -65,7 +62,6 @@ impl WordCountable for File { BufReader::new(self) } - #[cfg(not(target_os = "wasi"))] fn inner_file(&mut self) -> Option<&mut File> { Some(self) }