Skip to content

Commit ba1833d

Browse files
authored
Merge pull request #8104 from Ecordonnier/eco/stdbuf-external
stdbuf: add feat_external_libstdbuf
2 parents b20706f + 99aa51a commit ba1833d

File tree

5 files changed

+82
-9
lines changed

5 files changed

+82
-9
lines changed

.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ linker = "x86_64-unknown-redox-gcc"
33

44
[env]
55
PROJECT_NAME_FOR_VERSION_STRING = "uutils coreutils"
6+
# See feat_external_libstdbuf in src/uu/stdbuf/Cargo.toml
7+
LIBSTDBUF_DIR = "/usr/lib"
68

79
# libstdbuf must be a shared library, so musl libc can't be linked statically
810
# https://github.com/rust-lang/rust/issues/82193

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ test_risky_names = []
3737
# * only build `uudoc` when `--feature uudoc` is activated
3838
uudoc = ["zip", "dep:uuhelp_parser"]
3939
## features
40+
## Optional feature for stdbuf
41+
# "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it
42+
feat_external_libstdbuf = ["stdbuf/feat_external_libstdbuf"]
4043
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
4144
# NOTE:
4245
# * On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time.

src/uu/stdbuf/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# spell-checker:ignore dpkg
12
[package]
23
name = "uu_stdbuf"
34
description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering"
@@ -23,6 +24,25 @@ libstdbuf = { package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" }
2324
tempfile = { workspace = true }
2425
uucore = { workspace = true, features = ["parser"] }
2526

27+
# "feat_external_libstdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into
28+
# the stdbuf binary.
29+
# There are 2 use-cases:
30+
# 1. Installation of uutils-coreutils using cargo install (e.g. from crates.io
31+
# which supports only "cargo install" as installation method). In this case,
32+
# installing libstdbuf.so is impossible, because "cargo install" installs
33+
# only binary programs (no cdylib), thus libstdbuf.so must be embedded into
34+
# stdbuf and written to /tmp at runtime. This is a hack, and may not work
35+
# on some platforms, e.g. because the SELinux permissions may not allow
36+
# stdbuf to write to /tmp, /tmp may be read-only, libstdbuf.so may not work
37+
# at all without SELinux labels, etc.
38+
#
39+
# 2. Installation of uutils-coreutils using an external tool, e.g. dpkg/apt on
40+
# debian. In this case, libstdbuf.so should be installed separately to its
41+
# correct location and the environment variable LIBSTDBUF_DIR configures the
42+
# installation directory during the build. E.g. LIBSTDBUF_DIR="/usr/lib"
43+
[features]
44+
feat_external_libstdbuf = []
45+
2646
[[bin]]
2747
name = "stdbuf"
2848
path = "src/main.rs"

src/uu/stdbuf/build.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ fn main() {
2929
println!("cargo:rerun-if-changed=build.rs");
3030
println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs");
3131

32+
// Check for external stdbuf feature requirements
33+
#[cfg(feature = "feat_external_libstdbuf")]
34+
{
35+
if env::var("LIBSTDBUF_DIR").is_err() {
36+
eprintln!(
37+
"\n\x1b[31mError:\x1b[0m The 'feat_external_libstdbuf' feature requires the LIBSTDBUF_DIR environment variable to be set."
38+
);
39+
eprintln!(
40+
"\x1b[33mUsage:\x1b[0m LIBSTDBUF_DIR=/path/to/lib/directory cargo build --features feat_external_libstdbuf"
41+
);
42+
eprintln!(
43+
"\x1b[33mExample:\x1b[0m LIBSTDBUF_DIR=/usr/lib cargo build --features feat_external_libstdbuf"
44+
);
45+
eprintln!(
46+
"\nThis directory should point to where libstdbuf.so / libstdbuf.dylib will be installed on the target system."
47+
);
48+
std::process::exit(1);
49+
}
50+
}
51+
3252
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
3353
let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());
3454

src/uu/stdbuf/src/stdbuf.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
// spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf
77

88
use clap::{Arg, ArgAction, ArgMatches, Command};
9-
use std::fs::File;
10-
use std::io::Write;
119
use std::os::unix::process::ExitStatusExt;
1210
use std::path::PathBuf;
1311
use std::process;
@@ -29,16 +27,19 @@ mod options {
2927
pub const COMMAND: &str = "command";
3028
}
3129

32-
#[cfg(any(
33-
target_os = "linux",
34-
target_os = "android",
35-
target_os = "freebsd",
36-
target_os = "netbsd",
37-
target_os = "dragonfly"
30+
#[cfg(all(
31+
not(feature = "feat_external_libstdbuf"),
32+
any(
33+
target_os = "linux",
34+
target_os = "android",
35+
target_os = "freebsd",
36+
target_os = "netbsd",
37+
target_os = "dragonfly"
38+
)
3839
))]
3940
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));
4041

41-
#[cfg(target_vendor = "apple")]
42+
#[cfg(all(not(feature = "feat_external_libstdbuf"), target_vendor = "apple"))]
4243
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib"));
4344

4445
enum BufferType {
@@ -137,7 +138,11 @@ fn set_command_env(command: &mut process::Command, buffer_name: &str, buffer_typ
137138
}
138139
}
139140

141+
#[cfg(not(feature = "feat_external_libstdbuf"))]
140142
fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
143+
use std::fs::File;
144+
use std::io::Write;
145+
141146
let (preload, extension) = preload_strings()?;
142147
let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);
143148

@@ -147,6 +152,29 @@ fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
147152
Ok((preload.to_owned(), inject_path))
148153
}
149154

155+
#[cfg(feature = "feat_external_libstdbuf")]
156+
fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
157+
let (preload, extension) = preload_strings()?;
158+
159+
// Use the directory provided at compile time via LIBSTDBUF_DIR environment variable
160+
// This will fail to compile if LIBSTDBUF_DIR is not set, which is the desired behavior
161+
const LIBSTDBUF_DIR: &str = env!("LIBSTDBUF_DIR");
162+
let path_buf = PathBuf::from(LIBSTDBUF_DIR)
163+
.join("libstdbuf")
164+
.with_extension(extension);
165+
if path_buf.exists() {
166+
return Ok((preload.to_owned(), path_buf));
167+
}
168+
169+
Err(USimpleError::new(
170+
1,
171+
format!(
172+
"External libstdbuf not found at configured path: {}",
173+
path_buf.display()
174+
),
175+
))
176+
}
177+
150178
#[uucore::main]
151179
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
152180
let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?;

0 commit comments

Comments
 (0)