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
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add that it is explained in src/uu/stdbuf/Cargo.toml

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sylvestre are there other things which I should change?


# libstdbuf must be a shared library, so musl libc can't be linked statically
# https://github.com/rust-lang/rust/issues/82193
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions src/uu/stdbuf/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# spell-checker:ignore dpkg
[package]
name = "uu_stdbuf"
description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering"
Expand All @@ -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"
20 changes: 20 additions & 0 deletions src/uu/stdbuf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
46 changes: 37 additions & 9 deletions src/uu/stdbuf/src/stdbuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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);

Expand All @@ -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)?;
Expand Down
Loading