This is an archive of the discontinued LLVM Phabricator instance.

Add support for the NO_COLOR environment variable
ClosedPublic

Authored by aaron.ballman on Jun 6 2023, 9:38 AM.

Details

Summary

Clang currently supports disabling color diagnostic output via -fno-color-diagnostics. However, there is a somewhat long-standing push to support use of an environment variable to override color output so that users can set up their terminal such that most color output is disabled (largely for accessibility reasons).

There are two competing de facto standards to accomplish this: NO_COLOR (https://no-color.org/) and CLICOLOR/CLICOLOR_FORCE (http://bixense.com/clicolors/).

This patch adds support for NO_COLOR as that appears to be the more commonly supported feature, at least when comparing issues and pull requests:
https://github.com/search?q=NO_COLOR&type=issues (2.2k issues, 35k pull requests)
https://github.com/search?q=CLICOLOR&type=issues (1k issues, 3k pull requests)

It's also the more straightforward and thoroughly-specified of the two options. If NO_COLOR is present as an environment variable (regardless of value), color output is suppressed unless the command line specifies use of color output (command line takes precedence over the environment variable).

Diff Detail

Event Timeline

aaron.ballman created this revision.Jun 6 2023, 9:38 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 6 2023, 9:38 AM
aaron.ballman requested review of this revision.Jun 6 2023, 9:38 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 6 2023, 9:38 AM

Thanks for the detailed description. I am generally conservative when it comes to new use cases for environment variables. However, NO_COLOR seems to become a standard and a large number of tools respect it, I think it is the right call to support it.

If we don't intend to support both standards, we can close https://github.com/llvm/llvm-project/issues/23983 (CLICOLOR) as a wontfix :)

clang/lib/Frontend/CompilerInvocation.cpp
2346

We should inspect that NO_COLOR is not empty.

Command-line software which adds ANSI color to its output by default should check for a NO_COLOR environment variable that, when present and not an empty string (regardless of its value), prevents the addition of ANSI color.

clang/test/Driver/no-color.c
8

when present and not an empty string, the value does not matter.

shafik added a subscriber: shafik.Jun 6 2023, 7:22 PM

CLICOLOR/CLICOLOR_FORCE seems a lot more complicated, NO-COLOR feels like a win for low cost and does not preclude supporting others later on if we feel there is a need. So LGTM but I would like to see more feedback.

jhasse added a subscriber: jhasse.Jun 7 2023, 2:56 AM

There's a valid usecase CLICOLOR_FORCE: Force color diagnostics. The "disable colors" part of https://bixense.com/clicolors/ is not that important to me, I could change it to point to NO_COLOR instead?

btw: I've tried to join the "standards" a few years ago: https://github.com/jcs/no_color/issues/28

Right now I think it would be best to drop CLICOLOR and have the following simple rules:

  • if NO_COLOR set: disable colors
  • if CLICOLOR_FORCE set: always set colors
  • else: isatty

What do you think?

aaron.ballman marked 2 inline comments as done.Jun 7 2023, 6:03 AM

If we don't intend to support both standards, we can close https://github.com/llvm/llvm-project/issues/23983 (CLICOLOR) as a wontfix :)

That was my plan.

There's a valid usecase CLICOLOR_FORCE: Force color diagnostics. The "disable colors" part of https://bixense.com/clicolors/ is not that important to me, I could change it to point to NO_COLOR instead?

btw: I've tried to join the "standards" a few years ago: https://github.com/jcs/no_color/issues/28

Right now I think it would be best to drop CLICOLOR and have the following simple rules:

  • if NO_COLOR set: disable colors
  • if CLICOLOR_FORCE set: always set colors
  • else: isatty

What do you think?

I don't think we should implement all of one standard and part of another. My thinking is: users will have to do NO_COLOR=1 and CLICOLOR_FORCE=0 to disable colors due to there being competing standards, so we need to support only one of these. CLICOLOR_FORCE is the more powerful option, but I don't know what compelling use case there is for forcing colors *on*, but forcing them *off* makes a lot of sense to me from an accessibility standpoint. So I think supporting NO_COLOR alone is sufficient until we know why users need to force-enable colors. If we are compelled by that reasoning, we could implement the other standard at that point (and I'd suggest we implement it fully). But I'd like to avoid that right now because then we have to think about what happens in circumstances like NO_COLOR=1 CLICOLOR_FORCE=1

clang/lib/Frontend/CompilerInvocation.cpp
2346

Good catch!

As I told Aaron, someone who disables color for accessibility reason is likely to have both variables(NO_COLOR/CLICOLOR) set. So supporting one is enough. Picking the one that is the simplest and most popular makes perfect sense.
This is great.

aaron.ballman marked an inline comment as done.

Update based on review feedback so that we check for an empty environment variable value.

This was a fun rabbit hole to fall into: lit is not a PTY, so our fallback of "automatically enable colors" means env NO_COLOR= still disables colors as far as lit tests are concerned. See https://github.com/llvm/llvm-project/blob/4418434c6de7a861e241ba2448ea4a12080cf08f/clang/test/Driver/color-diagnostics.c#L25 for another test running into this. What's more, Windows does not have a way to set an empty environment variable on the command line from what I can find, so testing this manually also doesn't work (at least for me). However, because this piggy-backs on the functionality for -fdiagnostics-color=auto, I think the test coverage I added is sufficient.

MaskRay accepted this revision.Jun 7 2023, 8:04 AM

LGTM. Glad that my comment on https://github.com/llvm/llvm-project/issues/23983 might bring attention to users.

I'd like to make my stance softer: if we find CLICOLOR_FORCE useful, we can implement it later.
I don't mind how we handle NO_COLOR=1 CLICOLOR_FORCE=1. This seems invalid input from the user and making either option win should be fine (I'd prefer that NO_COLOR wins.)

This revision is now accepted and ready to land.Jun 7 2023, 8:04 AM
cor3ntin added inline comments.Jun 7 2023, 8:07 AM
clang/lib/Frontend/CompilerInvocation.cpp
2348

NoColor->at(0) != '\0' seem very superfluous. do you have examples of scenario that would produce a null terminator?

MaskRay added inline comments.Jun 7 2023, 8:08 AM
clang/lib/Frontend/CompilerInvocation.cpp
2348

GetEnv returned std::string originates from a C string. Just !NoColor->empty() is sufficient.

2348

Also, basic_string::at may throw an exception, which may be undesired.

jhasse added a comment.Jun 7 2023, 8:14 AM

If we don't intend to support both standards, we can close https://github.com/llvm/llvm-project/issues/23983 (CLICOLOR) as a wontfix :)

That was my plan.

As that issue was more about adding a way to force colors and not about disabling them, please leave it open.

[...] I don't know what compelling use case there is for forcing colors *on*, [...] until we know why users need to force-enable colors.

The reason is that adding -fcolor-diagnostics to the command line is not always feasible, e.g. most build systems would rebuild everything in that case. Relying on tty detection also doesn't work as many build tools buffer the output (and some CIs, too).

I don't mind how we handle NO_COLOR=1 CLICOLOR_FORCE=1. This seems invalid input from the user and making either option win should be fine (I'd prefer that NO_COLOR wins.)

I'd prefer NO_COLOR winning, too. I can specify that case at https://bixense.com/clicolors/.

aaron.ballman marked 3 inline comments as done.Jun 7 2023, 8:51 AM

If we don't intend to support both standards, we can close https://github.com/llvm/llvm-project/issues/23983 (CLICOLOR) as a wontfix :)

That was my plan.

As that issue was more about adding a way to force colors and not about disabling them, please leave it open.

The UX we follow for most options is "last option wins", and CLICOLOR_FORCE doesn't follow that model because it overrides what is specified on the command line explicitly. We can leave the issue open, but I think it's worth making it clear that we're probably unlikely to implement it.

[...] I don't know what compelling use case there is for forcing colors *on*, [...] until we know why users need to force-enable colors.

The reason is that adding -fcolor-diagnostics to the command line is not always feasible, e.g. most build systems would rebuild everything in that case. Relying on tty detection also doesn't work as many build tools buffer the output (and some CIs, too).

Ah, that's an interesting use case, thank you for mentioning it! I'm not certain that's super compelling to me; color diagnostics are on by default, so if someone forcibly disables them in the build system (which seems to not really happen too much in practice: https://sourcegraph.com/search?q=context:global+-fno-color-diagnostics+file:.*Makefile.*&patternType=standard&sm=0&groupBy=repo), that's likely done for a reason.

I don't mind how we handle NO_COLOR=1 CLICOLOR_FORCE=1. This seems invalid input from the user and making either option win should be fine (I'd prefer that NO_COLOR wins.)

I'd prefer NO_COLOR winning, too. I can specify that case at https://bixense.com/clicolors/.

Thanks!

clang/lib/Frontend/CompilerInvocation.cpp
2348

Ah this was leftover from my flailing around trying to figure out why env NO_COLOR= %clang ... was not emitting colors. I'll remove the superfluous bit when landing.

cor3ntin accepted this revision.Jun 7 2023, 8:52 AM

LGTM!

This revision was landed with ongoing or failed builds.Jun 7 2023, 8:55 AM
This revision was automatically updated to reflect the committed changes.
aaron.ballman marked an inline comment as done.

[...] I don't know what compelling use case there is for forcing colors *on*, [...] until we know why users need to force-enable colors.

The reason is that adding -fcolor-diagnostics to the command line is not always feasible, e.g. most build systems would rebuild everything in that case. Relying on tty detection also doesn't work as many build tools buffer the output (and some CIs, too).

Ah, that's an interesting use case, thank you for mentioning it! I'm not certain that's super compelling to me; color diagnostics are on by default, so if someone forcibly disables them in the build system (which seems to not really happen too much in practice: https://sourcegraph.com/search?q=context:global+-fno-color-diagnostics+file:.*Makefile.*&patternType=standard&sm=0&groupBy=repo), that's likely done for a reason.

I had the same use case quite a few years ago: I wanted to have Jenkins output coloured diagnostics in the logs to make them slightly easier to read. since the output goes to a file rather than a terminal it defaults to no colours and there were too many different build systems involved that adding compiler flags was not feasible. In the end I added a local hack to clang to default to colours being on if a given environment var is set (https://github.com/CTSRD-CHERI/llvm-project/commit/a427ffe13c19276ae94cc757632439fda08d9dc6). I'd be happy if we ended up supporting CLICOLOR_FORCE=1 as a way of ignoring the isatty check but colours in Jenkins without modifying build systems is just a nice to have feature rather than something super useful and there are better ways of making the warnings more visible (e.g. the plugin that scans the build logs and aggregates results).