Skip to content

Commit db7b8a6

Browse files
oech3Ecordonnier
authored andcommitted
stdbuf: build on Windows (depending on cygwin dll)
1 parent a8d51a2 commit db7b8a6

8 files changed

Lines changed: 49 additions & 26 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ feat_os_unix_musl = [
275275
# "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
276276
feat_os_windows = [
277277
"feat_Tier1", ## == "feat_os_windows_legacy" + "hostname"
278+
"stdbuf",
278279
]
279280
## (secondary platforms) feature sets
280281
# "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat])

GNUmakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ $(info Detected OS = $(OS))
8181
ifeq (,$(findstring windows,$(OS)))
8282
FEATURE_EXTRACT_UTILS := feat_os_unix
8383
else
84-
FEATURE_EXTRACT_UTILS := feat_Tier1
84+
FEATURE_EXTRACT_UTILS := windows
8585
endif
8686
PROGS := $(shell cargo tree --depth 1 --features $(FEATURE_EXTRACT_UTILS) --format "{p}" --prefix none | sed -E -n 's/^uu_([^ ]+).*/\1/p')
8787

src/uu/stdbuf/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ doctest = false
2222

2323
[dependencies]
2424
clap = { workspace = true }
25-
libstdbuf = { package = "uu_stdbuf_libstdbuf", version = "0.8.0", path = "src/libstdbuf" }
2625
tempfile = { workspace = true }
2726
uucore = { workspace = true, features = ["parser-size"] }
2827
thiserror = { workspace = true }
2928
fluent = { workspace = true }
3029

30+
[target.'cfg(unix)'.dependencies]
31+
libstdbuf = { package = "uu_stdbuf_libstdbuf", version = "0.8.0", path = "src/libstdbuf" }
32+
3133
# "feat_external_libstdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into
3234
# the stdbuf binary.
3335
# There are 2 use-cases:

src/uu/stdbuf/build.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ use std::path::Path;
1010
use std::process::Command;
1111

1212
fn main() {
13+
// do not compile libstdbuf for windows target. The windows stdbuf.exe loads libstdbuf.dll compiled for the cygwin target.
14+
if env::var("CARGO_CFG_UNIX").is_err() {
15+
println!("cargo:rustc-cfg=feature=\"feat_external_libstdbuf\"");
16+
return;
17+
}
1318
println!("cargo:rerun-if-changed=build.rs");
1419
println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs");
1520

@@ -38,11 +43,7 @@ fn main() {
3843

3944
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
4045
let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
41-
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
4246

43-
if target_family != "unix" {
44-
return;
45-
}
4647
let dylib_ext = if target_vendor == "apple" {
4748
".dylib"
4849
} else if target_os == "cygwin" {

src/uu/stdbuf/locales/en-US.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
stdbuf-about = Run COMMAND, with modified buffering operations for its standard streams.
2+
stdbuf-about-windows = Run COMMAND linked against cygwin runtime, with modified buffering operations for its standard streams.
23
34
Mandatory arguments to long options are mandatory for short options too.
45
stdbuf-usage = stdbuf [OPTION]... COMMAND

src/uu/stdbuf/src/libstdbuf/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
use std::env;
88

99
fn main() {
10+
// do not compile libstdbuf for windows target. The windows stdbuf.exe loads libstdbuf.dll compiled for the cygwin target.
11+
if env::var("CARGO_CFG_UNIX").is_err() {
12+
return;
13+
}
1014
// Make sure we're building position-independent code for use with LD_PRELOAD
1115
println!("cargo:rustc-link-arg=-fPIC");
1216

src/uu/stdbuf/src/stdbuf.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
// file that was distributed with this source code.
55

66
// spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf
7-
#[cfg(not(unix))]
8-
compile_error!("stdbuf is not supported on the target");
97

108
use clap::{Arg, ArgAction, ArgMatches, Command};
119
use std::ffi::OsString;
@@ -42,7 +40,10 @@ const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf
4240
#[cfg(all(not(feature = "feat_external_libstdbuf"), target_vendor = "apple"))]
4341
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib"));
4442

45-
#[cfg(all(not(feature = "feat_external_libstdbuf"), target_os = "cygwin"))]
43+
#[cfg(all(
44+
not(feature = "feat_external_libstdbuf"),
45+
any(target_os = "cygwin", target_os = "windows")
46+
))]
4647
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dll"));
4748

4849
enum BufferType {
@@ -89,7 +90,7 @@ fn preload_strings() -> (&'static str, &'static str) {
8990
("DYLD_LIBRARY_PATH", "dylib")
9091
}
9192

92-
#[cfg(target_os = "cygwin")]
93+
#[cfg(any(target_os = "cygwin", windows))]
9394
fn preload_strings() -> (&'static str, &'static str) {
9495
("LD_PRELOAD", "dll")
9596
}
@@ -211,7 +212,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
211212
command.args(command_params);
212213

213214
// Replace the current process with the target program (no fork) using exec.
215+
#[cfg(unix)]
214216
let e = command.exec();
217+
#[cfg(windows)]
218+
let e = match command.spawn() {
219+
Ok(mut child) => {
220+
let status = child.wait().unwrap();
221+
process::exit(status.code().unwrap_or(0));
222+
}
223+
Err(err) => err,
224+
};
215225
// exec() only returns if there was an error
216226
match e.kind() {
217227
std::io::ErrorKind::PermissionDenied => Err(USimpleError::new(
@@ -230,10 +240,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
230240
}
231241

232242
pub fn uu_app() -> Command {
243+
#[cfg(unix)]
244+
let about = translate!("stdbuf-about");
245+
#[cfg(windows)]
246+
let about = translate!("stdbuf-about-windows");
233247
Command::new("stdbuf")
234248
.version(uucore::crate_version!())
235249
.help_template(uucore::localized_help_template("stdbuf"))
236-
.about(translate!("stdbuf-about"))
250+
.about(about)
237251
.after_help(translate!("stdbuf-after-help"))
238252
.override_usage(format_usage(&translate!("stdbuf-usage")))
239253
.trailing_var_arg(true)

tests/by-util/test_stdbuf.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55
// spell-checker:ignore cmdline dyld dylib PDEATHSIG setvbuf
6+
67
#[cfg(target_os = "linux")]
78
use uutests::at_and_ucmd;
8-
use uutests::new_ucmd;
9-
#[cfg(not(target_os = "windows"))]
10-
use uutests::util::TestScenario;
11-
use uutests::util_name;
9+
#[cfg(unix)]
10+
use uutests::{new_ucmd, util::TestScenario, util_name};
1211

1312
#[test]
13+
#[cfg(unix)]
1414
fn invalid_input() {
1515
new_ucmd!().arg("-/").fails_with_code(125);
1616
}
1717

18-
#[cfg(not(feature = "feat_external_libstdbuf"))]
18+
#[cfg(all(unix, not(feature = "feat_external_libstdbuf")))]
1919
#[test]
2020
fn test_permission() {
2121
new_ucmd!()
@@ -28,7 +28,7 @@ fn test_permission() {
2828
// LD_DEBUG is not available on macOS, OpenBSD, Android, or musl
2929
#[cfg(all(
3030
feature = "feat_external_libstdbuf",
31-
not(target_os = "windows"),
31+
unix,
3232
not(target_os = "openbsd"),
3333
not(target_os = "macos"),
3434
not(target_os = "android"),
@@ -121,7 +121,7 @@ fn test_stdbuf_search_order_exe_dir_first() {
121121
);
122122
}
123123

124-
#[cfg(not(feature = "feat_external_libstdbuf"))]
124+
#[cfg(all(unix, not(feature = "feat_external_libstdbuf")))]
125125
#[test]
126126
fn test_no_such() {
127127
new_ucmd!()
@@ -135,7 +135,7 @@ fn test_no_such() {
135135
// does not provide musl-compiled system utilities (like head), leading to dynamic linker errors
136136
// when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD.
137137
#[cfg(all(
138-
not(target_os = "windows"),
138+
unix,
139139
not(target_os = "freebsd"),
140140
not(target_os = "openbsd"),
141141
not(all(target_arch = "x86_64", target_env = "musl"))
@@ -157,7 +157,7 @@ fn test_stdbuf_unbuffered_stdout() {
157157
// does not provide musl-compiled system utilities (like head), leading to dynamic linker errors
158158
// when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD.
159159
#[cfg(all(
160-
not(target_os = "windows"),
160+
unix,
161161
not(target_os = "freebsd"),
162162
not(target_os = "openbsd"),
163163
not(all(target_arch = "x86_64", target_env = "musl"))
@@ -174,8 +174,8 @@ fn test_stdbuf_line_buffered_stdout() {
174174
.stdout_is("The quick brown fox jumps over the lazy dog.");
175175
}
176176

177-
#[cfg(not(target_os = "windows"))]
178177
#[test]
178+
#[cfg(unix)]
179179
fn test_stdbuf_no_buffer_option_fails() {
180180
let ts = TestScenario::new(util_name!());
181181

@@ -185,8 +185,8 @@ fn test_stdbuf_no_buffer_option_fails() {
185185
.stderr_contains("the following required arguments were not provided:");
186186
}
187187

188-
#[cfg(not(target_os = "windows"))]
189188
#[test]
189+
#[cfg(unix)]
190190
fn test_stdbuf_no_command_fails_with_125() {
191191
// Test that missing command fails with exit code 125 (stdbuf error)
192192
// This verifies proper error handling without unwrap panic
@@ -200,7 +200,7 @@ fn test_stdbuf_no_command_fails_with_125() {
200200
// does not provide musl-compiled system utilities (like tail), leading to dynamic linker errors
201201
// when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD.
202202
#[cfg(all(
203-
not(target_os = "windows"),
203+
unix,
204204
not(target_os = "freebsd"),
205205
not(target_os = "openbsd"),
206206
not(all(target_arch = "x86_64", target_env = "musl"))
@@ -214,17 +214,17 @@ fn test_stdbuf_trailing_var_arg() {
214214
.stdout_is("jumps over the lazy dog.");
215215
}
216216

217-
#[cfg(not(target_os = "windows"))]
218217
#[test]
218+
#[cfg(unix)]
219219
fn test_stdbuf_line_buffering_stdin_fails() {
220220
new_ucmd!()
221221
.args(&["-i", "L", "head"])
222222
.fails()
223223
.usage_error("line buffering stdin is meaningless");
224224
}
225225

226-
#[cfg(not(target_os = "windows"))]
227226
#[test]
227+
#[cfg(unix)]
228228
fn test_stdbuf_invalid_mode_fails() {
229229
let options = ["--input", "--output", "--error"];
230230
for option in &options {
@@ -254,7 +254,7 @@ fn test_stdbuf_invalid_mode_fails() {
254254
// and is sometimes disabled. Disable test on Android for now.
255255
// musl libc dynamic loader does not support LD_DEBUG, so disable on musl targets as well.
256256
#[cfg(all(
257-
not(target_os = "windows"),
257+
unix,
258258
not(target_os = "openbsd"),
259259
not(target_os = "macos"),
260260
not(target_os = "android"),

0 commit comments

Comments
 (0)