From ab37e41a965f668178cfe8433b6c2b93ef0fa891 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 3 Apr 2026 12:14:59 +0200 Subject: [PATCH] tail: add WASI support by disabling notify and pid features (#11569) WASI has no file watching (inotify/kqueue) or process management APIs. Disable notify and libc on WASI, provide Observer stubs so tail compiles, and add fallback values for BACKEND and polling_help. The follow mode (-f) returns an error on WASI since it cannot work without a file watching backend. --- Cargo.toml | 1 + src/uu/tail/Cargo.toml | 6 ++-- src/uu/tail/src/args.rs | 2 ++ src/uu/tail/src/follow/mod.rs | 63 +++++++++++++++++++++++++++++++++ src/uu/tail/src/paths.rs | 4 +++ src/uu/tail/src/platform/mod.rs | 22 ++++++++++++ src/uu/tail/src/text.rs | 2 ++ 7 files changed, 98 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0d38e475e..f606eae28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -216,6 +216,7 @@ feat_wasm = [ "shuf", "sleep", "sum", + "tail", "tee", "tr", "true", diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index f67062d6e..eaf269f10 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -21,13 +21,15 @@ path = "src/tail.rs" [dependencies] clap = { workspace = true } -libc = { workspace = true } memchr = { workspace = true } -notify = { workspace = true } uucore = { workspace = true, features = ["fs", "parser-size", "signals"] } same-file = { workspace = true } fluent = { workspace = true } +[target.'cfg(not(target_os = "wasi"))'.dependencies] +libc = { workspace = true } +notify = { workspace = true } + [target.'cfg(unix)'.dependencies] rustix = { workspace = true, features = ["fs"] } diff --git a/src/uu/tail/src/args.rs b/src/uu/tail/src/args.rs index e1c1fe9ae..c1af12edd 100644 --- a/src/uu/tail/src/args.rs +++ b/src/uu/tail/src/args.rs @@ -447,6 +447,8 @@ pub fn uu_app() -> Command { let polling_help = translate!("tail-help-polling-unix"); #[cfg(target_os = "windows")] let polling_help = translate!("tail-help-polling-windows"); + #[cfg(not(any(unix, target_os = "windows")))] + let polling_help = translate!("tail-help-polling-unix"); Command::new(uucore::util_name()) .version(uucore::crate_version!()) diff --git a/src/uu/tail/src/follow/mod.rs b/src/uu/tail/src/follow/mod.rs index 604602a4b..71d65d4a9 100644 --- a/src/uu/tail/src/follow/mod.rs +++ b/src/uu/tail/src/follow/mod.rs @@ -4,6 +4,69 @@ // file that was distributed with this source code. mod files; +#[cfg(not(target_os = "wasi"))] mod watch; +#[cfg(not(target_os = "wasi"))] pub use watch::{Observer, follow}; + +// WASI: notify/inotify are unavailable, so `tail -f` cannot work. +// Provide minimal stubs matching the real Observer API so tail compiles. +#[cfg(target_os = "wasi")] +mod wasi_stubs { + use crate::args::Settings; + use std::io::BufRead; + use std::path::Path; + use uucore::error::{UResult, USimpleError}; + + 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 start(&mut self, _settings: &Settings) -> UResult<()> { + Ok(()) + } + + pub fn add_path( + &mut self, + _path: &Path, + _display_name: &str, + _reader: Option>, + _update_last: bool, + ) -> UResult<()> { + Ok(()) + } + + pub fn add_bad_path( + &mut self, + _path: &Path, + _display_name: &str, + _update_last: bool, + ) -> UResult<()> { + Ok(()) + } + + pub fn follow_name_retry(&self) -> bool { + false + } + } + + pub fn follow(_observer: Observer, _settings: &Settings) -> UResult<()> { + Err(USimpleError::new( + 1, + "follow mode is not supported on this platform", + )) + } +} + +#[cfg(target_os = "wasi")] +pub use wasi_stubs::{Observer, follow}; diff --git a/src/uu/tail/src/paths.rs b/src/uu/tail/src/paths.rs index 6eaeae980..4fd7dd51c 100644 --- a/src/uu/tail/src/paths.rs +++ b/src/uu/tail/src/paths.rs @@ -197,6 +197,10 @@ impl MetadataExtTail for Metadata { // } false } + #[cfg(not(any(unix, windows)))] + { + false + } } } diff --git a/src/uu/tail/src/platform/mod.rs b/src/uu/tail/src/platform/mod.rs index d3220491f..77a9b311e 100644 --- a/src/uu/tail/src/platform/mod.rs +++ b/src/uu/tail/src/platform/mod.rs @@ -14,6 +14,28 @@ pub use self::unix::{ #[cfg(windows)] pub use self::windows::{Pid, ProcessChecker, supports_pid_checks}; +// WASI has no process management; provide stubs so tail compiles. +#[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 +} + #[cfg(unix)] mod unix; diff --git a/src/uu/tail/src/text.rs b/src/uu/tail/src/text.rs index 0c4972345..b48746430 100644 --- a/src/uu/tail/src/text.rs +++ b/src/uu/tail/src/text.rs @@ -18,3 +18,5 @@ pub const BACKEND: &str = "inotify"; pub const BACKEND: &str = "kqueue"; #[cfg(target_os = "windows")] pub const BACKEND: &str = "ReadDirectoryChanges"; +#[cfg(not(any(unix, target_os = "windows")))] +pub const BACKEND: &str = "polling";