Skip to content

Commit c2ae2e0

Browse files
committed
Implement compiletest --new-output-capture, in stable Rust
1 parent 5d71a8a commit c2ae2e0

File tree

4 files changed

+76
-2
lines changed

4 files changed

+76
-2
lines changed

src/tools/compiletest/src/common.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,10 @@ pub struct Config {
667667
/// to avoid `!nocapture` double-negatives.
668668
pub nocapture: bool,
669669

670+
/// True if the experimental new output-capture implementation should be
671+
/// used, avoiding the need for `#![feature(internal_output_capture)]`.
672+
pub new_output_capture: bool,
673+
670674
/// Needed both to construct [`build_helper::git::GitConfig`].
671675
pub nightly_branch: String,
672676
pub git_merge_commit_email: String,
@@ -784,6 +788,7 @@ impl Config {
784788
builtin_cfg_names: Default::default(),
785789
supported_crate_types: Default::default(),
786790
nocapture: Default::default(),
791+
new_output_capture: Default::default(),
787792
nightly_branch: Default::default(),
788793
git_merge_commit_email: Default::default(),
789794
profiler_runtime: Default::default(),

src/tools/compiletest/src/executor.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,17 @@ enum CaptureKind {
168168
/// Use the old output-capture implementation, which relies on the unstable
169169
/// library feature `#![feature(internal_output_capture)]`.
170170
Old { buf: Arc<Mutex<Vec<u8>>> },
171+
172+
/// Use the new output-capture implementation, which only uses stable Rust.
173+
New { buf: output_capture::CaptureBuf },
171174
}
172175

173176
impl CaptureKind {
174177
fn for_config(config: &Config) -> Self {
175178
if config.nocapture {
176179
Self::None
180+
} else if config.new_output_capture {
181+
Self::New { buf: output_capture::CaptureBuf::new() }
177182
} else {
178183
// Create a capure buffer for `io::set_output_capture`.
179184
Self::Old { buf: Default::default() }
@@ -184,21 +189,30 @@ impl CaptureKind {
184189
match self {
185190
Self::None => false,
186191
Self::Old { .. } => true,
192+
Self::New { .. } => true,
187193
}
188194
}
189195

190196
fn stdout(&self) -> &dyn ConsoleOut {
191-
&output_capture::Stdout
197+
self.capture_buf_or(&output_capture::Stdout)
192198
}
193199

194200
fn stderr(&self) -> &dyn ConsoleOut {
195-
&output_capture::Stderr
201+
self.capture_buf_or(&output_capture::Stderr)
202+
}
203+
204+
fn capture_buf_or<'a>(&'a self, fallback: &'a dyn ConsoleOut) -> &'a dyn ConsoleOut {
205+
match self {
206+
Self::None | Self::Old { .. } => fallback,
207+
Self::New { buf } => buf,
208+
}
196209
}
197210

198211
fn into_inner(self) -> Option<Vec<u8>> {
199212
match self {
200213
Self::None => None,
201214
Self::Old { buf } => Some(buf.lock().unwrap_or_else(|e| e.into_inner()).to_vec()),
215+
Self::New { buf } => Some(buf.into_inner().into()),
202216
}
203217
}
204218
}

src/tools/compiletest/src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
178178
// FIXME: Temporarily retained so we can point users to `--no-capture`
179179
.optflag("", "nocapture", "")
180180
.optflag("", "no-capture", "don't capture stdout/stderr of tests")
181+
.optopt(
182+
"N",
183+
"new-output-capture",
184+
"enables or disables the new output-capture implementation",
185+
"off|on",
186+
)
181187
.optflag("", "profiler-runtime", "is the profiler runtime enabled for this target")
182188
.optflag("h", "help", "show this message")
183189
.reqopt("", "channel", "current Rust channel", "CHANNEL")
@@ -462,6 +468,14 @@ pub fn parse_config(args: Vec<String>) -> Config {
462468
supported_crate_types: OnceLock::new(),
463469

464470
nocapture: matches.opt_present("no-capture"),
471+
new_output_capture: {
472+
let value = matches
473+
.opt_str("new-output-capture")
474+
.or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok())
475+
.unwrap_or_else(|| "off".to_owned());
476+
parse_bool_option(&value)
477+
.unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given"))
478+
},
465479

466480
nightly_branch: matches.opt_str("nightly-branch").unwrap(),
467481
git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
@@ -477,6 +491,19 @@ pub fn parse_config(args: Vec<String>) -> Config {
477491
}
478492
}
479493

494+
/// Parses the same set of boolean values accepted by rustc command-line arguments.
495+
///
496+
/// Accepting all of these values is more complicated than just picking one
497+
/// pair, but has the advantage that contributors who are used to rustc
498+
/// shouldn't have to think about which values are legal.
499+
fn parse_bool_option(value: &str) -> Option<bool> {
500+
match value {
501+
"off" | "no" | "n" | "false" => Some(false),
502+
"on" | "yes" | "y" | "true" => Some(true),
503+
_ => None,
504+
}
505+
}
506+
480507
pub fn opt_str(maybestr: &Option<String>) -> &str {
481508
match *maybestr {
482509
None => "(none)",

src/tools/compiletest/src/output_capture.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt;
22
use std::panic::RefUnwindSafe;
3+
use std::sync::Mutex;
34

45
pub trait ConsoleOut: fmt::Debug + RefUnwindSafe {
56
fn write_fmt(&self, args: fmt::Arguments<'_>);
@@ -22,3 +23,30 @@ impl ConsoleOut for Stderr {
2223
eprint!("{args}");
2324
}
2425
}
26+
27+
pub(crate) struct CaptureBuf {
28+
inner: Mutex<String>,
29+
}
30+
31+
impl CaptureBuf {
32+
pub(crate) fn new() -> Self {
33+
Self { inner: Mutex::new(String::new()) }
34+
}
35+
36+
pub(crate) fn into_inner(self) -> String {
37+
self.inner.into_inner().unwrap_or_else(|e| e.into_inner())
38+
}
39+
}
40+
41+
impl fmt::Debug for CaptureBuf {
42+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43+
f.debug_struct("CaptureBuf").finish_non_exhaustive()
44+
}
45+
}
46+
47+
impl ConsoleOut for CaptureBuf {
48+
fn write_fmt(&self, args: fmt::Arguments<'_>) {
49+
let mut s = self.inner.lock().unwrap_or_else(|e| e.into_inner());
50+
<String as fmt::Write>::write_fmt(&mut s, args).unwrap();
51+
}
52+
}

0 commit comments

Comments
 (0)