Skip to content

Commit 94d98a4

Browse files
authored
Merge pull request #3435 from rami3l/feat/force-color-output
Support `RUSTUP_TERM_COLOR` as an override environment variable
2 parents 7fc79ef + b6cf380 commit 94d98a4

File tree

3 files changed

+88
-19
lines changed

3 files changed

+88
-19
lines changed

doc/user-guide/src/environment-variables.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
determines the directory that traces will be written too. Traces are of the
3030
form PID.trace. Traces can be read by the Catapult project [tracing viewer].
3131

32+
- `RUSTUP_TERM_COLOR` (default: `auto`) Controls whether colored output is used in the terminal.
33+
Set to `auto` to use colors only in tty streams, to `always` to always enable colors,
34+
or to `never` to disable colors.
35+
3236
- `RUSTUP_UNPACK_RAM` *unstable* (default free memory or 500MiB if unable to tell, min 210MiB) Caps the amount of
3337
RAM `rustup` will use for IO tasks while unpacking.
3438

src/currentprocess/filesource.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ impl Write for TestWriterLock<'_> {
198198
#[cfg(feature = "test")]
199199
pub(super) type TestWriterInner = Arc<Mutex<Vec<u8>>>;
200200
/// A thread-safe test file handle that pretends to be e.g. stdout.
201-
#[derive(Clone)]
201+
#[derive(Clone, Default)]
202202
#[cfg(feature = "test")]
203203
pub(super) struct TestWriter(TestWriterInner);
204204

src/currentprocess/terminalsource.rs

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ use termcolor::{ColorChoice, ColorSpec, StandardStream, StandardStreamLock, Writ
1111

1212
#[cfg(feature = "test")]
1313
use super::filesource::{TestWriter, TestWriterLock};
14-
use super::process;
14+
use super::{process, varsource::VarSource};
1515

1616
/// Select what stream to make a terminal on
1717
pub(super) enum StreamSelector {
1818
Stdout,
1919
Stderr,
2020
#[cfg(feature = "test")]
2121
TestWriter(TestWriter),
22+
#[cfg(feature = "test")]
23+
TestTtyWriter(TestWriter),
2224
}
2325

2426
impl StreamSelector {
@@ -36,6 +38,8 @@ impl StreamSelector {
3638
},
3739
#[cfg(feature = "test")]
3840
StreamSelector::TestWriter(_) => false,
41+
#[cfg(feature = "test")]
42+
StreamSelector::TestTtyWriter(_) => true,
3943
}
4044
}
4145
}
@@ -55,7 +59,7 @@ pub struct ColorableTerminal {
5559
enum TerminalInner {
5660
StandardStream(StandardStream, ColorSpec),
5761
#[cfg(feature = "test")]
58-
TestWriter(TestWriter),
62+
TestWriter(TestWriter, ColorChoice),
5963
}
6064

6165
pub struct ColorableTerminalLocked {
@@ -73,14 +77,20 @@ enum TerminalInnerLocked {
7377
}
7478

7579
impl ColorableTerminal {
76-
/// Construct a terminal for the selected stream. Colors written to the
77-
/// terminal will be discarded if the stream is not a tty.
80+
/// A terminal that supports colorisation of a stream.
81+
/// If `RUSTUP_TERM_COLOR` is set to `always`, or if the stream is a tty and
82+
/// `RUSTUP_TERM_COLOR` either unset or set to `auto`,
83+
/// then color commands will be sent to the stream.
84+
/// Otherwise color commands are discarded.
7885
pub(super) fn new(stream: StreamSelector) -> Self {
79-
let is_a_tty = stream.is_a_tty();
80-
let choice = if is_a_tty {
81-
ColorChoice::Auto
82-
} else {
83-
ColorChoice::Never
86+
let env_override = process()
87+
.var("RUSTUP_TERM_COLOR")
88+
.map(|it| it.to_lowercase());
89+
let choice = match env_override.as_deref() {
90+
Ok("always") => ColorChoice::Always,
91+
Ok("never") => ColorChoice::Never,
92+
_ if stream.is_a_tty() => ColorChoice::Auto,
93+
_ => ColorChoice::Never,
8494
};
8595
let inner = match stream {
8696
StreamSelector::Stdout => {
@@ -90,7 +100,9 @@ impl ColorableTerminal {
90100
TerminalInner::StandardStream(StandardStream::stderr(choice), ColorSpec::new())
91101
}
92102
#[cfg(feature = "test")]
93-
StreamSelector::TestWriter(w) => TerminalInner::TestWriter(w),
103+
StreamSelector::TestWriter(w) | StreamSelector::TestTtyWriter(w) => {
104+
TerminalInner::TestWriter(w, choice)
105+
}
94106
};
95107
ColorableTerminal {
96108
inner: Arc::new(Mutex::new(inner)),
@@ -118,7 +130,7 @@ impl ColorableTerminal {
118130
TerminalInnerLocked::StandardStream(locked)
119131
}
120132
#[cfg(feature = "test")]
121-
TerminalInner::TestWriter(w) => TerminalInnerLocked::TestWriter(w.lock()),
133+
TerminalInner::TestWriter(w, _) => TerminalInnerLocked::TestWriter(w.lock()),
122134
});
123135
// ColorableTerminalLocked { inner, guard, locked }
124136
uninit.assume_init()
@@ -132,7 +144,7 @@ impl ColorableTerminal {
132144
s.set_color(spec)
133145
}
134146
#[cfg(feature = "test")]
135-
TerminalInner::TestWriter(_) => Ok(()),
147+
TerminalInner::TestWriter(_, _) => Ok(()),
136148
}
137149
}
138150

@@ -143,7 +155,7 @@ impl ColorableTerminal {
143155
s.set_color(spec)
144156
}
145157
#[cfg(feature = "test")]
146-
TerminalInner::TestWriter(_) => Ok(()),
158+
TerminalInner::TestWriter(_, _) => Ok(()),
147159
}
148160
}
149161

@@ -157,23 +169,23 @@ impl ColorableTerminal {
157169
s.set_color(spec)
158170
}
159171
#[cfg(feature = "test")]
160-
TerminalInner::TestWriter(_) => Ok(()),
172+
TerminalInner::TestWriter(_, _) => Ok(()),
161173
}
162174
}
163175

164176
pub fn reset(&mut self) -> io::Result<()> {
165177
match self.inner.lock().unwrap().deref_mut() {
166178
TerminalInner::StandardStream(s, _color) => s.reset(),
167179
#[cfg(feature = "test")]
168-
TerminalInner::TestWriter(_) => Ok(()),
180+
TerminalInner::TestWriter(_, _) => Ok(()),
169181
}
170182
}
171183

172184
pub fn carriage_return(&mut self) -> io::Result<()> {
173185
match self.inner.lock().unwrap().deref_mut() {
174186
TerminalInner::StandardStream(s, _color) => s.write(b"\r")?,
175187
#[cfg(feature = "test")]
176-
TerminalInner::TestWriter(w) => w.write(b"\r")?,
188+
TerminalInner::TestWriter(w, _) => w.write(b"\r")?,
177189
};
178190
Ok(())
179191
}
@@ -190,15 +202,15 @@ impl io::Write for ColorableTerminal {
190202
match self.inner.lock().unwrap().deref_mut() {
191203
TerminalInner::StandardStream(s, _) => s.write(buf),
192204
#[cfg(feature = "test")]
193-
TerminalInner::TestWriter(w) => w.write(buf),
205+
TerminalInner::TestWriter(w, _) => w.write(buf),
194206
}
195207
}
196208

197209
fn flush(&mut self) -> std::result::Result<(), io::Error> {
198210
match self.inner.lock().unwrap().deref_mut() {
199211
TerminalInner::StandardStream(s, _) => s.flush(),
200212
#[cfg(feature = "test")]
201-
TerminalInner::TestWriter(w) => w.flush(),
213+
TerminalInner::TestWriter(w, _) => w.flush(),
202214
}
203215
}
204216
}
@@ -220,3 +232,56 @@ impl io::Write for ColorableTerminalLocked {
220232
}
221233
}
222234
}
235+
236+
#[cfg(test)]
237+
mod tests {
238+
use std::collections::HashMap;
239+
240+
use rustup_macros::unit_test as test;
241+
242+
use super::*;
243+
use crate::{currentprocess, test::Env};
244+
245+
#[test]
246+
fn term_color_choice() {
247+
fn assert_color_choice(env_val: &str, stream: StreamSelector, color_choice: ColorChoice) {
248+
let mut vars = HashMap::new();
249+
vars.env("RUSTUP_TERM_COLOR", env_val);
250+
let tp = currentprocess::TestProcess {
251+
vars,
252+
..Default::default()
253+
};
254+
currentprocess::with(tp.into(), || {
255+
let term = ColorableTerminal::new(stream);
256+
let inner = term.inner.lock().unwrap();
257+
assert!(matches!(
258+
&*inner,
259+
&TerminalInner::TestWriter(_, choice) if choice == color_choice
260+
));
261+
});
262+
}
263+
264+
assert_color_choice(
265+
"aLWayS",
266+
StreamSelector::TestWriter(Default::default()),
267+
ColorChoice::Always,
268+
);
269+
assert_color_choice(
270+
"neVer",
271+
StreamSelector::TestWriter(Default::default()),
272+
ColorChoice::Never,
273+
);
274+
// tty + `auto` enables the colors.
275+
assert_color_choice(
276+
"AutO",
277+
StreamSelector::TestTtyWriter(Default::default()),
278+
ColorChoice::Auto,
279+
);
280+
// non-tty + `auto` does not enable the colors.
281+
assert_color_choice(
282+
"aUTo",
283+
StreamSelector::TestWriter(Default::default()),
284+
ColorChoice::Never,
285+
);
286+
}
287+
}

0 commit comments

Comments
 (0)