fix(natives): repaired win32 build and bazel feature drift

- Synced pi-builtins bazel crate_features with cargo's resolved default
  set: bazel features are literal, so the meta-features never expanded
  and the procs/rg cluster (nohup, pgrep, pidwait, pkill, proc-match,
  ps, rg, sleep, timeout, top) was silently compiled out, leaving the
  process builtins unregistered under bazel and failing pi-shell tests.
- Repaired windows compilation of pi-builtins: cfg-gated the
  uucore::mode import in mkdir, imported std::env in sort's non-unix
  locale probe, mapped ProcInfo::pid through a closure in kill, brought
  MetadataExt into scope in wc, and replaced stat's unstable
  windows_by_handle metadata with a stable GetFileInformationByHandle
  query (volume serial, link count, file index, no-dereference aware).
- Imported HashSet for pi-shell's windows-only PATH merge.
- Added a clippy-ported bazel config + CI bucket so pi-builtins keeps
  its manifest-declared clippy allows under the bazel aspect while rustc
  warnings stay denied, and zeroed the remaining windows-target rustc
  warnings (unused params/imports in find, mv, rm, proc_match, ps).
This commit is contained in:
can1357
2026-08-09 03:17:34 +02:00
parent 896bf5f33e
commit 311c32eaf5
15 changed files with 197 additions and 112 deletions
+10
View File
@@ -95,12 +95,19 @@ rust_library(
"util.mkdir",
"util.mktemp",
"util.mv",
"util.nohup",
"util.nproc",
"util.paste",
"util.pgrep",
"util.pidwait",
"util.pkill",
"util.printenv",
"util.proc-match",
"util.procs",
"util.ps",
"util.readlink",
"util.realpath",
"util.rg",
"util.rm",
"util.sed",
"util.seq",
@@ -109,12 +116,15 @@ rust_library(
"util.sha256sum",
"util.sha384sum",
"util.sha512sum",
"util.sleep",
"util.sort",
"util.sponge",
"util.stat",
"util.tac",
"util.tail",
"util.tee",
"util.timeout",
"util.top",
"util.touch",
"util.tr",
"util.truncate",
+1 -1
View File
@@ -776,7 +776,7 @@ pub mod matchers {
}
#[cfg(not(unix))]
fn matches(&self, _file_info: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
fn matches(&self, _file_info: &WalkEntry, _matcher_io: &mut MatcherIO) -> bool {
false
}
}
+1 -1
View File
@@ -140,7 +140,7 @@ impl builtins::Command for KillCommand {
ProcInfo::all()
.into_iter()
.filter(|process| process.status() == ProcessStatus::Running)
.map(ProcInfo::pid)
.map(|process| process.pid())
.collect()
};
#[cfg(unix)]
+3 -1
View File
@@ -11,7 +11,9 @@ use std::{
use brush_core::{ShellExtensions, builtins::Registration};
use clap::{Arg, ArgAction, ArgMatches, Command, builder::ValueParser, parser::ValuesRef};
use uucore::{display::Quotable, fs, mode};
use uucore::{display::Quotable, fs};
#[cfg(not(windows))]
use uucore::mode;
#[cfg(all(unix, target_os = "linux"))]
use uucore::fsxattr;
+1 -1
View File
@@ -1097,7 +1097,7 @@ fn rename_fifo_fallback(host: &mut Host, from: &Path, to: &Path) -> io::Result<(
#[cfg(not(unix))]
#[expect(clippy::unnecessary_wraps, reason = "fn sig must match on all platforms")]
fn rename_fifo_fallback(host: &mut Host, _from: &Path, _to: &Path) -> io::Result<()> {
fn rename_fifo_fallback(_host: &mut Host, _from: &Path, _to: &Path) -> io::Result<()> {
Ok(())
}
+3 -3
View File
@@ -20,9 +20,9 @@ use std::{
time::Duration,
};
use brush_core::{
ExecutionContext, ExecutionExitCode, ExecutionResult, openfiles::OpenFiles,
};
use brush_core::{ExecutionContext, ExecutionExitCode, ExecutionResult};
#[cfg(unix)]
use brush_core::openfiles::OpenFiles;
use tokio_util::sync::CancellationToken;
use crate::{kill::signal_number, proc_snapshot};
+7 -3
View File
@@ -1,11 +1,15 @@
//! The `ps` process-status builtin, moved from `pi-shell`.
#[cfg(unix)]
use std::collections::HashSet;
#[cfg(unix)]
use std::fs;
#[cfg(unix)]
use std::path::{Path, PathBuf};
use std::{
collections::{HashMap, HashSet},
collections::HashMap,
fmt::Write as _,
fs,
io::{self, Write},
path::{Path, PathBuf},
time::{Duration, SystemTime, UNIX_EPOCH},
};
+1 -1
View File
@@ -1126,7 +1126,7 @@ fn is_readable_metadata(metadata: &Metadata) -> bool {
/// Whether the given file or directory is readable.
#[cfg(any(not(unix), target_os = "redox"))]
fn is_readable(host: &mut Host, _path: &Path) -> bool {
fn is_readable(_host: &mut Host, _path: &Path) -> bool {
true
}
+1
View File
@@ -4164,6 +4164,7 @@ fn default_merge_batch_size() -> usize {
#[cfg(not(unix))]
fn locale_failed_to_set() -> bool {
use std::env;
env::var_os("LC_ALL").as_deref() == Some(OsStr::new("missing"))
}
+60 -10
View File
@@ -48,8 +48,6 @@ pub(crate) fn stat_builtin<SE: ShellExtensions>() -> Registration<SE> {
mod imp {
#[cfg(unix)]
use std::os::unix::fs::{FileTypeExt, MetadataExt};
#[cfg(windows)]
use std::os::windows::fs::MetadataExt;
use std::{
borrow::Cow,
cell::OnceCell,
@@ -1951,10 +1949,9 @@ for details about the options it supports.";
}
}
/// file-status path is Unix-only (`std::os::unix`); this reimplements the
/// GNU directives on top of `std::fs::Metadata`, the `windows_by_handle`
/// metadata extensions (inode / link count / device via
/// `GetFileInformationByHandle`), and the Win32 volume APIs for
/// `--file-system` mode.
/// GNU directives on top of `std::fs::Metadata`, direct
/// `GetFileInformationByHandle` queries (inode / link count / device),
/// and the Win32 volume APIs for `--file-system` mode.
#[cfg(windows)]
mod win {
use std::{
@@ -2066,6 +2063,52 @@ for details about the options it supports.";
(u64::from(high) << 32) | u64::from(low)
}
/// Per-file identity numbers for `%d`/`%D`/`%h`/`%i`: volume serial,
/// hard-link count, and NTFS file index.
pub struct HandleInfo {
pub volume_serial: u64,
pub links: u64,
pub file_index: u64,
}
/// Query [`HandleInfo`] via `GetFileInformationByHandle`, the stable
/// replacement for std's unstable `windows_by_handle` metadata
/// extensions. `follow_links` mirrors how the caller's metadata was
/// obtained, so a `--no-dereference` stat reports the link itself.
/// Returns `None` when the file cannot be opened or queried.
pub fn handle_info(path: &Path, follow_links: bool) -> Option<HandleInfo> {
use std::os::windows::{fs::OpenOptionsExt, io::AsRawHandle};
use windows_sys::Win32::Storage::FileSystem::{
BY_HANDLE_FILE_INFORMATION, FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_OPEN_REPARSE_POINT, GetFileInformationByHandle,
};
// `FILE_FLAG_BACKUP_SEMANTICS` is required to open directories;
// `access_mode(0)` asks for metadata access only.
let mut flags = FILE_FLAG_BACKUP_SEMANTICS;
if !follow_links {
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
}
let file = std::fs::OpenOptions::new()
.access_mode(0)
.custom_flags(flags)
.open(path)
.ok()?;
// SAFETY: zeroed BY_HANDLE_FILE_INFORMATION is a valid out
// buffer, and the handle stays open across the call.
let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };
if unsafe { GetFileInformationByHandle(file.as_raw_handle(), &mut info) } == 0 {
return None;
}
Some(HandleInfo {
volume_serial: u64::from(info.dwVolumeSerialNumber),
links: u64::from(info.nNumberOfLinks),
file_index: (u64::from(info.nFileIndexHigh) << 32)
| u64::from(info.nFileIndexLow),
})
}
/// File-system status collected for `stat --file-system` on Windows.
pub struct StatFs {
pub fs_type: String,
@@ -2249,6 +2292,11 @@ for details about the options it supports.";
},
Token::Directive { flag, width, precision, format } => {
let mode = win::synth_mode(meta);
// `%d`/`%D`/`%h`/`%i` need a fresh handle query; skip it for
// every other directive.
let ids = matches!(format, 'd' | 'D' | 'h' | 'i')
.then(|| win::handle_info(resolved, !meta.file_type().is_symlink()))
.flatten();
let output = match format {
// access rights in octal
'a' => OutputType::UnsignedOct(0o7777 & mode),
@@ -2264,9 +2312,11 @@ for details about the options it supports.";
'C' => OutputType::Str("unsupported for this operating system".to_string()),
// device number: Windows volume serial number
'd' if flag.major || flag.minor => OutputType::Unsigned(0),
'd' => OutputType::Unsigned(meta.volume_serial_number().map_or(0, u64::from)),
'd' => OutputType::Unsigned(ids.as_ref().map_or(0, |ids| ids.volume_serial)),
// device number in hex
'D' => OutputType::UnsignedHex(meta.volume_serial_number().map_or(0, u64::from)),
'D' => {
OutputType::UnsignedHex(ids.as_ref().map_or(0, |ids| ids.volume_serial))
},
// raw mode in hex
'f' => OutputType::UnsignedHex(u64::from(mode)),
// file type
@@ -2276,9 +2326,9 @@ for details about the options it supports.";
// group name of owner
'G' => OutputType::Str("UNKNOWN".to_string()),
// number of hard links
'h' => OutputType::Unsigned(meta.number_of_links().map_or(1, u64::from)),
'h' => OutputType::Unsigned(ids.as_ref().map_or(1, |ids| ids.links)),
// inode number (NTFS file index)
'i' => OutputType::Unsigned(meta.file_index().unwrap_or(0)),
'i' => OutputType::Unsigned(ids.as_ref().map_or(0, |ids| ids.file_index)),
// mount point (not resolved on Windows)
'm' => OutputType::Str(String::new()),
// file name
+2
View File
@@ -14,6 +14,8 @@ mod count_fast {
use super::WordCountable;
use super::{wc_simd_allowed, word_count::WordCount};
#[cfg(windows)]
use std::os::windows::fs::MetadataExt;
#[cfg(windows)]
const FILE_ATTRIBUTE_ARCHIVE: u32 = 32;
#[cfg(windows)]
const FILE_ATTRIBUTE_NORMAL: u32 = 128;