1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

Merge pull request #7972 from Ecordonnier/eco/stdbuf-regression-test

stdbuf: fix cross-compilation
This commit is contained in:
Sylvestre Ledru 2025-06-02 13:26:38 +02:00 committed by GitHub
commit b5d4b0ee1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 532 additions and 250 deletions

View file

@ -2,6 +2,7 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore dyld dylib setvbuf
use uutests::new_ucmd;
#[cfg(not(target_os = "windows"))]
use uutests::util::TestScenario;
@ -30,10 +31,21 @@ fn test_no_such() {
.stderr_contains("No such file or directory");
}
#[cfg(all(not(target_os = "windows"), not(target_os = "openbsd")))]
// Disabled on x86_64-unknown-linux-musl because the cross-rs Docker image for this target
// 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"),
not(target_os = "freebsd"),
not(target_os = "openbsd"),
not(all(target_arch = "x86_64", target_env = "musl"))
))]
#[test]
fn test_stdbuf_unbuffered_stdout() {
// This is a basic smoke test
// Note: This test only verifies that stdbuf does not crash and that output is passed through as expected
// for simple, short-lived commands. It does not guarantee that buffering is actually modified or that
// libstdbuf is loaded and functioning correctly.
new_ucmd!()
.args(&["-o0", "head"])
.pipe_in("The quick brown fox jumps over the lazy dog.")
@ -41,9 +53,20 @@ fn test_stdbuf_unbuffered_stdout() {
.stdout_is("The quick brown fox jumps over the lazy dog.");
}
#[cfg(all(not(target_os = "windows"), not(target_os = "openbsd")))]
// Disabled on x86_64-unknown-linux-musl because the cross-rs Docker image for this target
// 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"),
not(target_os = "freebsd"),
not(target_os = "openbsd"),
not(all(target_arch = "x86_64", target_env = "musl"))
))]
#[test]
fn test_stdbuf_line_buffered_stdout() {
// Note: This test only verifies that stdbuf does not crash and that output is passed through as expected
// for simple, short-lived commands. It does not guarantee that buffering is actually modified or that
// libstdbuf is loaded and functioning correctly.
new_ucmd!()
.args(&["-oL", "head"])
.pipe_in("The quick brown fox jumps over the lazy dog.")
@ -62,7 +85,15 @@ fn test_stdbuf_no_buffer_option_fails() {
.stderr_contains("the following required arguments were not provided:");
}
#[cfg(all(not(target_os = "windows"), not(target_os = "openbsd")))]
// Disabled on x86_64-unknown-linux-musl because the cross-rs Docker image for this target
// 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"),
not(target_os = "freebsd"),
not(target_os = "openbsd"),
not(all(target_arch = "x86_64", target_env = "musl"))
))]
#[test]
fn test_stdbuf_trailing_var_arg() {
new_ucmd!()
@ -105,3 +136,83 @@ fn test_stdbuf_invalid_mode_fails() {
}
}
}
// macos uses DYLD_PRINT_LIBRARIES, not LD_DEBUG, so disable on macos at the moment.
// On modern Android (Bionic, API 37+), LD_DEBUG is supported and behaves similarly to glibc.
// On older Android versions (Bionic, API < 37), LD_DEBUG uses integer values instead of strings
// 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"),
not(target_os = "openbsd"),
not(target_os = "macos"),
not(target_os = "android"),
not(target_env = "musl")
))]
#[test]
fn test_libstdbuf_preload() {
use std::process::Command;
// Run a simple program with LD_DEBUG=symbols to verify that libstdbuf is loaded correctly
// and that there are no architecture mismatches when preloading the library.
// Note: This does not check which setvbuf implementation is used, as our libstdbuf does not override setvbuf.
// for https://github.com/uutils/coreutils/issues/6591
let scene = TestScenario::new(util_name!());
let coreutils_bin = &scene.bin_path;
// Test with our own echo (should have the correct architecture even when cross-compiled using cross-rs,
// in which case the "system" echo will be the host architecture)
let uutils_echo_cmd = format!(
"LD_DEBUG=symbols {} stdbuf -oL {} echo test 2>&1",
coreutils_bin.display(),
coreutils_bin.display()
);
let uutils_output = Command::new("sh")
.arg("-c")
.arg(&uutils_echo_cmd)
.output()
.expect("Failed to run uutils echo test");
let uutils_debug = String::from_utf8_lossy(&uutils_output.stdout);
// Check if libstdbuf.so / libstdbuf.dylib is in the lookup path.
// With GLIBC, the log should contain something like:
// "symbol=setvbuf; lookup in file=/tmp/.tmp0mfmCg/libstdbuf.so [0]"
// With FreeBSD dynamic loader, the log should contain something like:
// cspell:disable-next-line
// "calling init function for /tmp/.tmpu11rhP/libstdbuf.so at ..."
let libstdbuf_in_path = if cfg!(target_os = "freebsd") {
uutils_debug
.lines()
.any(|line| line.contains("calling init function") && line.contains("libstdbuf"))
} else {
uutils_debug.contains("symbol=setvbuf")
&& uutils_debug.contains("lookup in file=")
&& uutils_debug.contains("libstdbuf")
};
// Check for lack of architecture mismatch error. The potential error message with GLIBC is:
// cspell:disable-next-line
// "ERROR: ld.so: object '/tmp/.tmpCLq8jl/libstdbuf.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored."
let arch_mismatch_line = uutils_debug
.lines()
.find(|line| line.contains("cannot be preloaded"));
println!("LD_DEBUG output: {}", uutils_debug);
let no_arch_mismatch = arch_mismatch_line.is_none();
println!("libstdbuf in lookup path: {}", libstdbuf_in_path);
println!("No architecture mismatch: {}", no_arch_mismatch);
if let Some(error_line) = arch_mismatch_line {
println!("Architecture mismatch error: {}", error_line);
}
assert!(
libstdbuf_in_path,
"libstdbuf should be in lookup path with uutils echo"
);
assert!(
no_arch_mismatch,
"uutils echo should not show architecture mismatch"
);
}