Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ feat_os_unix_musl = [
# "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
feat_os_windows = [
"feat_Tier1", ## == "feat_os_windows_legacy" + "hostname"
"stdbuf",
]
## (secondary platforms) feature sets
# "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat])
Expand Down
2 changes: 1 addition & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ $(info Detected OS = $(OS))
ifeq (,$(findstring windows,$(OS)))
FEATURE_EXTRACT_UTILS := feat_os_unix
else
FEATURE_EXTRACT_UTILS := feat_Tier1
FEATURE_EXTRACT_UTILS := windows
endif
PROGS := $(shell cargo tree --depth 1 --features $(FEATURE_EXTRACT_UTILS) --format "{p}" --prefix none | sed -E -n 's/^uu_([^ ]+).*/\1/p')

Expand Down
4 changes: 3 additions & 1 deletion src/uu/stdbuf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ doctest = false

[dependencies]
clap = { workspace = true }
libstdbuf = { package = "uu_stdbuf_libstdbuf", version = "0.8.0", path = "src/libstdbuf" }
tempfile = { workspace = true }
uucore = { workspace = true, features = ["parser-size"] }
thiserror = { workspace = true }
fluent = { workspace = true }

[target.'cfg(unix)'.dependencies]
libstdbuf = { package = "uu_stdbuf_libstdbuf", version = "0.8.0", path = "src/libstdbuf" }

# "feat_external_libstdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into
# the stdbuf binary.
# There are 2 use-cases:
Expand Down
9 changes: 5 additions & 4 deletions src/uu/stdbuf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ use std::path::Path;
use std::process::Command;

fn main() {
// do not compile libstdbuf for windows target. The windows stdbuf.exe loads libstdbuf.dll compiled for the cygwin target.
if env::var("CARGO_CFG_UNIX").is_err() {
println!("cargo:rustc-cfg=feature=\"feat_external_libstdbuf\"");
return;
}
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs");

Expand Down Expand Up @@ -38,11 +43,7 @@ fn main() {

let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();

if target_family != "unix" {
return;
}
let dylib_ext = if target_vendor == "apple" {
".dylib"
} else if target_os == "cygwin" {
Expand Down
1 change: 1 addition & 0 deletions src/uu/stdbuf/locales/en-US.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
stdbuf-about = Run COMMAND, with modified buffering operations for its standard streams.
stdbuf-about-windows = Run COMMAND linked against cygwin runtime, with modified buffering operations for its standard streams.

Mandatory arguments to long options are mandatory for short options too.
stdbuf-usage = stdbuf [OPTION]... COMMAND
Expand Down
4 changes: 4 additions & 0 deletions src/uu/stdbuf/src/libstdbuf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
use std::env;

fn main() {
// do not compile libstdbuf for windows target. The windows stdbuf.exe loads libstdbuf.dll compiled for the cygwin target.
if env::var("CARGO_CFG_UNIX").is_err() {
return;
}
// Make sure we're building position-independent code for use with LD_PRELOAD
println!("cargo:rustc-link-arg=-fPIC");

Expand Down
24 changes: 19 additions & 5 deletions src/uu/stdbuf/src/stdbuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf
#[cfg(not(unix))]
compile_error!("stdbuf is not supported on the target");

use clap::{Arg, ArgAction, ArgMatches, Command};
use std::ffi::OsString;
Expand Down Expand Up @@ -42,7 +40,10 @@ const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf
#[cfg(all(not(feature = "feat_external_libstdbuf"), target_vendor = "apple"))]
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib"));

#[cfg(all(not(feature = "feat_external_libstdbuf"), target_os = "cygwin"))]
#[cfg(all(
not(feature = "feat_external_libstdbuf"),
any(target_os = "cygwin", target_os = "windows")
))]
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dll"));

enum BufferType {
Expand Down Expand Up @@ -89,7 +90,7 @@ fn preload_strings() -> (&'static str, &'static str) {
("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(target_os = "cygwin")]
#[cfg(any(target_os = "cygwin", windows))]
fn preload_strings() -> (&'static str, &'static str) {
("LD_PRELOAD", "dll")
}
Expand Down Expand Up @@ -211,7 +212,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
command.args(command_params);

// Replace the current process with the target program (no fork) using exec.
#[cfg(unix)]
let e = command.exec();
#[cfg(windows)]
let e = match command.spawn() {
Ok(mut child) => {
let status = child.wait().unwrap();
process::exit(status.code().unwrap_or(0));
}
Err(err) => err,
};
// exec() only returns if there was an error
match e.kind() {
std::io::ErrorKind::PermissionDenied => Err(USimpleError::new(
Expand All @@ -230,10 +240,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}

pub fn uu_app() -> Command {
#[cfg(unix)]
let about = translate!("stdbuf-about");
#[cfg(windows)]
let about = translate!("stdbuf-about-windows");
Command::new("stdbuf")
.version(uucore::crate_version!())
.help_template(uucore::localized_help_template("stdbuf"))
.about(translate!("stdbuf-about"))
.about(about)
.after_help(translate!("stdbuf-after-help"))
.override_usage(format_usage(&translate!("stdbuf-usage")))
.trailing_var_arg(true)
Expand Down
30 changes: 15 additions & 15 deletions tests/by-util/test_stdbuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore cmdline dyld dylib PDEATHSIG setvbuf

#[cfg(target_os = "linux")]
use uutests::at_and_ucmd;
use uutests::new_ucmd;
#[cfg(not(target_os = "windows"))]
use uutests::util::TestScenario;
use uutests::util_name;
#[cfg(unix)]
use uutests::{new_ucmd, util::TestScenario, util_name};

#[test]
#[cfg(unix)]
fn invalid_input() {
new_ucmd!().arg("-/").fails_with_code(125);
}

#[cfg(not(feature = "feat_external_libstdbuf"))]
#[cfg(all(unix, not(feature = "feat_external_libstdbuf")))]
#[test]
fn test_permission() {
new_ucmd!()
Expand All @@ -28,7 +28,7 @@ fn test_permission() {
// LD_DEBUG is not available on macOS, OpenBSD, Android, or musl
#[cfg(all(
feature = "feat_external_libstdbuf",
not(target_os = "windows"),
unix,
not(target_os = "openbsd"),
not(target_os = "macos"),
not(target_os = "android"),
Expand Down Expand Up @@ -121,7 +121,7 @@ fn test_stdbuf_search_order_exe_dir_first() {
);
}

#[cfg(not(feature = "feat_external_libstdbuf"))]
#[cfg(all(unix, not(feature = "feat_external_libstdbuf")))]
#[test]
fn test_no_such() {
new_ucmd!()
Expand All @@ -135,7 +135,7 @@ fn test_no_such() {
// does not provide musl-compiled system utilities (like head), leading to dynamic linker errors
// when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD.
#[cfg(all(
not(target_os = "windows"),
unix,
not(target_os = "freebsd"),
not(target_os = "openbsd"),
not(all(target_arch = "x86_64", target_env = "musl"))
Expand All @@ -157,7 +157,7 @@ fn test_stdbuf_unbuffered_stdout() {
// does not provide musl-compiled system utilities (like head), leading to dynamic linker errors
// when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD.
#[cfg(all(
not(target_os = "windows"),
unix,
not(target_os = "freebsd"),
not(target_os = "openbsd"),
not(all(target_arch = "x86_64", target_env = "musl"))
Expand All @@ -174,8 +174,8 @@ fn test_stdbuf_line_buffered_stdout() {
.stdout_is("The quick brown fox jumps over the lazy dog.");
}

#[cfg(not(target_os = "windows"))]
#[test]
#[cfg(unix)]
fn test_stdbuf_no_buffer_option_fails() {
let ts = TestScenario::new(util_name!());

Expand All @@ -185,8 +185,8 @@ fn test_stdbuf_no_buffer_option_fails() {
.stderr_contains("the following required arguments were not provided:");
}

#[cfg(not(target_os = "windows"))]
#[test]
#[cfg(unix)]
fn test_stdbuf_no_command_fails_with_125() {
// Test that missing command fails with exit code 125 (stdbuf error)
// This verifies proper error handling without unwrap panic
Expand All @@ -200,7 +200,7 @@ fn test_stdbuf_no_command_fails_with_125() {
// does not provide musl-compiled system utilities (like tail), leading to dynamic linker errors
// when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD.
#[cfg(all(
not(target_os = "windows"),
unix,
not(target_os = "freebsd"),
not(target_os = "openbsd"),
not(all(target_arch = "x86_64", target_env = "musl"))
Expand All @@ -214,17 +214,17 @@ fn test_stdbuf_trailing_var_arg() {
.stdout_is("jumps over the lazy dog.");
}

#[cfg(not(target_os = "windows"))]
#[test]
#[cfg(unix)]
fn test_stdbuf_line_buffering_stdin_fails() {
new_ucmd!()
.args(&["-i", "L", "head"])
.fails()
.usage_error("line buffering stdin is meaningless");
}

#[cfg(not(target_os = "windows"))]
#[test]
#[cfg(unix)]
fn test_stdbuf_invalid_mode_fails() {
let options = ["--input", "--output", "--error"];
for option in &options {
Expand Down Expand Up @@ -254,7 +254,7 @@ fn test_stdbuf_invalid_mode_fails() {
// and is sometimes disabled. Disable test on Android for now.
// musl libc dynamic loader does not support LD_DEBUG, so disable on musl targets as well.
#[cfg(all(
not(target_os = "windows"),
unix,
not(target_os = "openbsd"),
not(target_os = "macos"),
not(target_os = "android"),
Expand Down
Loading