mirror of
https://github.com/uutils/coreutils.git
synced 2025-07-07 21:45:01 +00:00

Summary: Partial fix for https://github.com/uutils/coreutils/issues/6591 The current code declare libstdbuf as a build-dependency of stdbuf as a workaround to enforce that libstdbuf is compiled before stdbuf. This breaks cross-compilation, because build-dependencies were compiled for the host architecture, and not for the target architecture. The reason this workaround is necessary is that bindeps is available only in nightly at the moment: https://rust-lang.github.io/rfcs/3028-cargo-binary-dependencies.html This commit replaces the "build-dependency" workaround with another workaround: calling cargo manually to build libstdbuf in the build.rs of stdbuf, in order to ensure that libstdbuf is built before stdbuf. Changes: - Removed cpp/cpp_build dependencies: The cpp, cpp_build, and related dependencies were removed because they made cross-compilation in a build.rs file very complex, since you need to pass proper CXX env variables for cross-compilation, whereas cross-compiling rust code using cargo is quite simple. Provided Rust implementations for getting stdin, stdout, and stderr pointers. Switched from C++/cpp macro-based initialization to using the Rust ctor crate for library initialization. - Remove "feat_require_crate_cpp" which is not needed any more, since stdbuf was the only utility using the cpp crate. Tests: This commit fixes e.g. this test: cross test --target aarch64-unknown-linux-gnu --features stdbuf test_stdbuf::test_libstdbuf_preload -- --nocapture - The "i686" build of stdbuf was also broken (stdbuf 32 bits, but libstdbuf 64 bits) and test_stdbuf::test_libstdbuf_preload of the i686 builds in github CI serves as regression test for this issue, no need to add a cross-rs test for aarch64. - The x86_64 musl build of stdbuf was also broken and was passing tests in CI only because it was compiled with the wrong libc (glibc instead of musl) Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
218 lines
8 KiB
Rust
218 lines
8 KiB
Rust
// This file is part of the uutils coreutils package.
|
|
//
|
|
// 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;
|
|
use uutests::util_name;
|
|
|
|
#[test]
|
|
fn invalid_input() {
|
|
new_ucmd!().arg("-/").fails_with_code(125);
|
|
}
|
|
|
|
#[test]
|
|
fn test_permission() {
|
|
new_ucmd!()
|
|
.arg("-o1")
|
|
.arg(".")
|
|
.fails_with_code(126)
|
|
.stderr_contains("Permission denied");
|
|
}
|
|
|
|
#[test]
|
|
fn test_no_such() {
|
|
new_ucmd!()
|
|
.arg("-o1")
|
|
.arg("no_such")
|
|
.fails_with_code(127)
|
|
.stderr_contains("No such file or directory");
|
|
}
|
|
|
|
// 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.")
|
|
.succeeds()
|
|
.stdout_is("The quick brown fox jumps over the lazy dog.");
|
|
}
|
|
|
|
// 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.")
|
|
.succeeds()
|
|
.stdout_is("The quick brown fox jumps over the lazy dog.");
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
#[test]
|
|
fn test_stdbuf_no_buffer_option_fails() {
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
ts.ucmd()
|
|
.args(&["head"])
|
|
.fails()
|
|
.stderr_contains("the following required arguments were not provided:");
|
|
}
|
|
|
|
// 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!()
|
|
.args(&["-i", "1024", "tail", "-1"])
|
|
.pipe_in("The quick brown fox\njumps over the lazy dog.")
|
|
.succeeds()
|
|
.stdout_is("jumps over the lazy dog.");
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
#[test]
|
|
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]
|
|
fn test_stdbuf_invalid_mode_fails() {
|
|
let options = ["--input", "--output", "--error"];
|
|
for option in &options {
|
|
new_ucmd!()
|
|
.args(&[*option, "1024R", "head"])
|
|
.fails_with_code(125)
|
|
.usage_error("invalid mode '1024R': Value too large for defined data type");
|
|
new_ucmd!()
|
|
.args(&[*option, "1Y", "head"])
|
|
.fails_with_code(125)
|
|
.stderr_contains("stdbuf: invalid mode '1Y': Value too large for defined data type");
|
|
#[cfg(target_pointer_width = "32")]
|
|
{
|
|
new_ucmd!()
|
|
.args(&[*option, "5GB", "head"])
|
|
.fails_with_code(125)
|
|
.stderr_contains(
|
|
"stdbuf: invalid mode '5GB': Value too large for defined data type",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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"
|
|
);
|
|
}
|