deb: print raw build output again with --verbose

The record-log wrapper provides a sink for every command, which removed
the runner's subprocess-inherit fallback -- and with it the raw output
of --verbose runs (and of piped stdout, where the live view disables
itself): Quiet's None sink got wrapped inside, every line went to
build.log, and the terminal showed nothing but pkh's own logs, breaking
what --help still promises. When the caller's view has no sink of its
own, a raw terminal sink (stdout/stderr writers, one per stream) now
takes its place inside the record-log wrapper: the terminal shows the
raw lines again while build.log keeps the full tee. A view that
consumes lines itself (the live view on a tty) is wrapped exactly as
before.
This commit is contained in:
2026-09-29 15:19:17 +02:00
parent 562626e878
commit 2de191cf6d
2 changed files with 168 additions and 5 deletions
+98
View File
@@ -1524,6 +1524,49 @@ impl LineSink for RecordLogSink {
}
}
/// A sink writing captured lines to two raw writers, one per stream:
/// stdout lines to the first, stderr lines to the second.
///
/// This is the record-log view's stand-in for the subprocess-inherit
/// fallback: with `--verbose` (or a piped stdout, where the live view
/// disables itself) the caller's view is [`crate::report::Quiet`], whose
/// sink is `None` — the runner would let the subprocess inherit the
/// terminal — but the record-log wrapper always provides a sink, so
/// the fallback is never reached anymore. Wrapping this sink in its
/// place keeps the raw lines on the terminal while the record's
/// build.log keeps the full tee.
pub(crate) struct RawWriterSink {
stdout: Mutex<Box<dyn Write + Send>>,
stderr: Mutex<Box<dyn Write + Send>>,
}
impl RawWriterSink {
/// A sink over explicit writer pairs (tests capture into memory).
pub(crate) fn new(stdout: Box<dyn Write + Send>, stderr: Box<dyn Write + Send>) -> Self {
Self {
stdout: Mutex::new(stdout),
stderr: Mutex::new(stderr),
}
}
/// A sink over the real terminal: the process's stdout and stderr.
pub(crate) fn terminal() -> Self {
Self::new(Box::new(io::stdout()), Box::new(io::stderr()))
}
}
impl LineSink for RawWriterSink {
fn line(&self, stream: Stream, line: &str) {
let writer = match stream {
Stream::Stdout => &self.stdout,
Stream::Stderr => &self.stderr,
};
if let Ok(mut out) = writer.lock() {
let _ = writeln!(out, "{line}");
}
}
}
// ---------------------------------------------------------------------------
// Host-tree snapshot and overlay upperdir sync
//
@@ -1854,6 +1897,34 @@ pub(crate) fn sync_upper_for_resume(session: &Session, host_parent: &Path) -> io
pub(crate) mod record_fixtures {
use super::*;
/// A writer copying everything it receives into a shared buffer:
/// lets tests observe what a `Box<dyn Write + Send>` sink wrote.
#[derive(Clone)]
pub(crate) struct SharedWriter(pub Arc<parking_lot::Mutex<Vec<u8>>>);
impl SharedWriter {
/// A fresh empty capture buffer.
pub(crate) fn new() -> Self {
Self(Arc::new(parking_lot::Mutex::new(Vec::new())))
}
/// What has been written so far.
pub(crate) fn contents(&self) -> String {
String::from_utf8_lossy(&self.0.lock()).into_owned()
}
}
impl std::io::Write for SharedWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub fn test_identity() -> SessionIdentity {
SessionIdentity {
package: "pkh".to_string(),
@@ -2739,6 +2810,33 @@ mod tests {
assert_eq!(out.len(), 0);
}
/// The raw writer sink routes each stream to its own writer,
/// newline-terminated: this is what keeps --verbose (and piped
/// runs) showing raw output under the record-log wrapper.
#[test]
fn raw_writer_sink_routes_streams_to_their_writers() {
use record_fixtures::SharedWriter;
let stdout = SharedWriter::new();
let stderr = SharedWriter::new();
let sink = RawWriterSink::new(Box::new(stdout.clone()), Box::new(stderr.clone()));
sink.line(Stream::Stdout, "from stdout");
sink.line(Stream::Stderr, "from stderr");
sink.line(Stream::Stdout, "second line");
assert_eq!(
stdout.contents(),
"from stdout\nsecond line\n",
"stdout lines land in order, newline-terminated"
);
assert_eq!(
stderr.contents(),
"from stderr\n",
"stderr lines land on their own writer only"
);
}
/// Ages render in the coarse units the list promises.
#[test]
fn age_rendering() {