1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #8104 from Ecordonnier/eco/stdbuf-external

stdbuf: add feat_external_libstdbuf
This commit is contained in:
Sylvestre Ledru 2025-06-18 10:40:05 +02:00 committed by GitHub
commit ba1833d39b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 82 additions and 9 deletions

View file

@ -3,6 +3,8 @@ linker = "x86_64-unknown-redox-gcc"
[env]
PROJECT_NAME_FOR_VERSION_STRING = "uutils coreutils"
# See feat_external_libstdbuf in src/uu/stdbuf/Cargo.toml
LIBSTDBUF_DIR = "/usr/lib"
# libstdbuf must be a shared library, so musl libc can't be linked statically
# https://github.com/rust-lang/rust/issues/82193

View file

@ -37,6 +37,9 @@ test_risky_names = []
# * only build `uudoc` when `--feature uudoc` is activated
uudoc = ["zip", "dep:uuhelp_parser"]
## features
## Optional feature for stdbuf
# "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it
feat_external_libstdbuf = ["stdbuf/feat_external_libstdbuf"]
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
# NOTE:
# * On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time.

View file

@ -1,3 +1,4 @@
# spell-checker:ignore dpkg
[package]
name = "uu_stdbuf"
description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering"
@ -23,6 +24,25 @@ libstdbuf = { package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" }
tempfile = { workspace = true }
uucore = { workspace = true, features = ["parser"] }
# "feat_external_libstdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into
# the stdbuf binary.
# There are 2 use-cases:
# 1. Installation of uutils-coreutils using cargo install (e.g. from crates.io
# which supports only "cargo install" as installation method). In this case,
# installing libstdbuf.so is impossible, because "cargo install" installs
# only binary programs (no cdylib), thus libstdbuf.so must be embedded into
# stdbuf and written to /tmp at runtime. This is a hack, and may not work
# on some platforms, e.g. because the SELinux permissions may not allow
# stdbuf to write to /tmp, /tmp may be read-only, libstdbuf.so may not work
# at all without SELinux labels, etc.
#
# 2. Installation of uutils-coreutils using an external tool, e.g. dpkg/apt on
# debian. In this case, libstdbuf.so should be installed separately to its
# correct location and the environment variable LIBSTDBUF_DIR configures the
# installation directory during the build. E.g. LIBSTDBUF_DIR="/usr/lib"
[features]
feat_external_libstdbuf = []
[[bin]]
name = "stdbuf"
path = "src/main.rs"

View file

@ -29,6 +29,26 @@ fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs");
// Check for external stdbuf feature requirements
#[cfg(feature = "feat_external_libstdbuf")]
{
if env::var("LIBSTDBUF_DIR").is_err() {
eprintln!(
"\n\x1b[31mError:\x1b[0m The 'feat_external_libstdbuf' feature requires the LIBSTDBUF_DIR environment variable to be set."
);
eprintln!(
"\x1b[33mUsage:\x1b[0m LIBSTDBUF_DIR=/path/to/lib/directory cargo build --features feat_external_libstdbuf"
);
eprintln!(
"\x1b[33mExample:\x1b[0m LIBSTDBUF_DIR=/usr/lib cargo build --features feat_external_libstdbuf"
);
eprintln!(
"\nThis directory should point to where libstdbuf.so / libstdbuf.dylib will be installed on the target system."
);
std::process::exit(1);
}
}
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());

View file

@ -6,8 +6,6 @@
// spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf
use clap::{Arg, ArgAction, ArgMatches, Command};
use std::fs::File;
use std::io::Write;
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process;
@ -29,16 +27,19 @@ mod options {
pub const COMMAND: &str = "command";
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "dragonfly"
#[cfg(all(
not(feature = "feat_external_libstdbuf"),
any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "dragonfly"
)
))]
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));
#[cfg(target_vendor = "apple")]
#[cfg(all(not(feature = "feat_external_libstdbuf"), target_vendor = "apple"))]
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib"));
enum BufferType {
@ -137,7 +138,11 @@ fn set_command_env(command: &mut process::Command, buffer_name: &str, buffer_typ
}
}
#[cfg(not(feature = "feat_external_libstdbuf"))]
fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
use std::fs::File;
use std::io::Write;
let (preload, extension) = preload_strings()?;
let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);
@ -147,6 +152,29 @@ fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
Ok((preload.to_owned(), inject_path))
}
#[cfg(feature = "feat_external_libstdbuf")]
fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
let (preload, extension) = preload_strings()?;
// Use the directory provided at compile time via LIBSTDBUF_DIR environment variable
// This will fail to compile if LIBSTDBUF_DIR is not set, which is the desired behavior
const LIBSTDBUF_DIR: &str = env!("LIBSTDBUF_DIR");
let path_buf = PathBuf::from(LIBSTDBUF_DIR)
.join("libstdbuf")
.with_extension(extension);
if path_buf.exists() {
return Ok((preload.to_owned(), path_buf));
}
Err(USimpleError::new(
1,
format!(
"External libstdbuf not found at configured path: {}",
path_buf.display()
),
))
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?;