Index: llvm/trunk/docs/CommandGuide/FileCheck.rst =================================================================== --- llvm/trunk/docs/CommandGuide/FileCheck.rst +++ llvm/trunk/docs/CommandGuide/FileCheck.rst @@ -116,6 +116,10 @@ as old tests are migrated to the new non-overlapping ``CHECK-DAG:`` implementation. +.. option:: --color + + Use colors in output (autodetected by default). + EXIT STATUS ----------- Index: llvm/trunk/include/llvm/Support/WithColor.h =================================================================== --- llvm/trunk/include/llvm/Support/WithColor.h +++ llvm/trunk/include/llvm/Support/WithColor.h @@ -29,23 +29,49 @@ Macro, Error, Warning, - Note + Note, + Remark }; /// An RAII object that temporarily switches an output stream to a specific /// color. class WithColor { raw_ostream &OS; - /// Determine whether colors should be displayed. - bool colorsEnabled(raw_ostream &OS); + bool DisableColors; public: /// To be used like this: WithColor(OS, HighlightColor::String) << "text"; - WithColor(raw_ostream &OS, HighlightColor S); + /// @param OS The output stream + /// @param S Symbolic name for syntax element to color + /// @param DisableColors Whether to ignore color changes regardless of -color + /// and support in OS + WithColor(raw_ostream &OS, HighlightColor S, bool DisableColors = false); + /// To be used like this: WithColor(OS, raw_ostream::Black) << "text"; + /// @param OS The output stream + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold Bold/brighter text, default false + /// @param BG If true, change the background, default: change foreground + /// @param DisableColors Whether to ignore color changes regardless of -color + /// and support in OS + WithColor(raw_ostream &OS, + raw_ostream::Colors Color = raw_ostream::SAVEDCOLOR, + bool Bold = false, bool BG = false, bool DisableColors = false) + : OS(OS), DisableColors(DisableColors) { + changeColor(Color, Bold, BG); + } ~WithColor(); raw_ostream &get() { return OS; } operator raw_ostream &() { return OS; } + template WithColor &operator<<(T &O) { + OS << O; + return *this; + } + template WithColor &operator<<(const T &O) { + OS << O; + return *this; + } /// Convenience method for printing "error: " to stderr. static raw_ostream &error(); @@ -53,13 +79,36 @@ static raw_ostream &warning(); /// Convenience method for printing "note: " to stderr. static raw_ostream ¬e(); + /// Convenience method for printing "remark: " to stderr. + static raw_ostream &remark(); /// Convenience method for printing "error: " to the given stream. - static raw_ostream &error(raw_ostream &OS, StringRef Prefix = ""); + static raw_ostream &error(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); /// Convenience method for printing "warning: " to the given stream. - static raw_ostream &warning(raw_ostream &OS, StringRef Prefix = ""); + static raw_ostream &warning(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); /// Convenience method for printing "note: " to the given stream. - static raw_ostream ¬e(raw_ostream &OS, StringRef Prefix = ""); + static raw_ostream ¬e(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + /// Convenience method for printing "remark: " to the given stream. + static raw_ostream &remark(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + + /// Determine whether colors are displayed. + bool colorsEnabled(); + + /// Change the color of text that will be output from this point forward. + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold Bold/brighter text, default false + /// @param BG If true, change the background, default: change foreground + WithColor &changeColor(raw_ostream::Colors Color, bool Bold = false, + bool BG = false); + + /// Reset the colors to terminal defaults. Call this when you are done + /// outputting colored text, or before program exit. + WithColor &resetColor(); }; } // end namespace llvm Index: llvm/trunk/lib/Support/SourceMgr.cpp =================================================================== --- llvm/trunk/lib/Support/SourceMgr.cpp +++ llvm/trunk/lib/Support/SourceMgr.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SMLoc.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -370,65 +371,48 @@ return c & 0x80; } -void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, - bool ShowKindLabel) const { - // Display colors only if OS supports colors. - ShowColors &= S.has_colors(); - - if (ShowColors) - S.changeColor(raw_ostream::SAVEDCOLOR, true); - - if (ProgName && ProgName[0]) - S << ProgName << ": "; - - if (!Filename.empty()) { - if (Filename == "-") - S << ""; - else - S << Filename; - - if (LineNo != -1) { - S << ':' << LineNo; - if (ColumnNo != -1) - S << ':' << (ColumnNo+1); +void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, + bool ShowColors, bool ShowKindLabel) const { + { + WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors); + + if (ProgName && ProgName[0]) + S << ProgName << ": "; + + if (!Filename.empty()) { + if (Filename == "-") + S << ""; + else + S << Filename; + + if (LineNo != -1) { + S << ':' << LineNo; + if (ColumnNo != -1) + S << ':' << (ColumnNo + 1); + } + S << ": "; } - S << ": "; } if (ShowKindLabel) { switch (Kind) { case SourceMgr::DK_Error: - if (ShowColors) - S.changeColor(raw_ostream::RED, true); - S << "error: "; + WithColor::error(OS, "", !ShowColors); break; case SourceMgr::DK_Warning: - if (ShowColors) - S.changeColor(raw_ostream::MAGENTA, true); - S << "warning: "; + WithColor::warning(OS, "", !ShowColors); break; case SourceMgr::DK_Note: - if (ShowColors) - S.changeColor(raw_ostream::BLACK, true); - S << "note: "; + WithColor::note(OS, "", !ShowColors); break; case SourceMgr::DK_Remark: - if (ShowColors) - S.changeColor(raw_ostream::BLUE, true); - S << "remark: "; + WithColor::remark(OS, "", !ShowColors); break; } - - if (ShowColors) { - S.resetColor(); - S.changeColor(raw_ostream::SAVEDCOLOR, true); - } } - S << Message << '\n'; - - if (ShowColors) - S.resetColor(); + WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors) + << Message << '\n'; if (LineNo == -1 || ColumnNo == -1) return; @@ -439,7 +423,7 @@ // expanding them later, and bail out rather than show incorrect ranges and // misaligned fixits for any other odd characters. if (find_if(LineContents, isNonASCII) != LineContents.end()) { - printSourceLine(S, LineContents); + printSourceLine(OS, LineContents); return; } size_t NumColumns = LineContents.size(); @@ -473,29 +457,27 @@ // least. CaretLine.erase(CaretLine.find_last_not_of(' ')+1); - printSourceLine(S, LineContents); + printSourceLine(OS, LineContents); - if (ShowColors) - S.changeColor(raw_ostream::GREEN, true); + { + WithColor S(OS, raw_ostream::GREEN, true, false, !ShowColors); - // Print out the caret line, matching tabs in the source line. - for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { - if (i >= LineContents.size() || LineContents[i] != '\t') { - S << CaretLine[i]; - ++OutCol; - continue; - } + // Print out the caret line, matching tabs in the source line. + for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { + if (i >= LineContents.size() || LineContents[i] != '\t') { + S << CaretLine[i]; + ++OutCol; + continue; + } - // Okay, we have a tab. Insert the appropriate number of characters. - do { - S << CaretLine[i]; - ++OutCol; - } while ((OutCol % TabStop) != 0); + // Okay, we have a tab. Insert the appropriate number of characters. + do { + S << CaretLine[i]; + ++OutCol; + } while ((OutCol % TabStop) != 0); + } + S << '\n'; } - S << '\n'; - - if (ShowColors) - S.resetColor(); // Print out the replacement line, matching tabs in the source line. if (FixItInsertionLine.empty()) @@ -503,14 +485,14 @@ for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) { if (i >= LineContents.size() || LineContents[i] != '\t') { - S << FixItInsertionLine[i]; + OS << FixItInsertionLine[i]; ++OutCol; continue; } // Okay, we have a tab. Insert the appropriate number of characters. do { - S << FixItInsertionLine[i]; + OS << FixItInsertionLine[i]; // FIXME: This is trying not to break up replacements, but then to re-sync // with the tabs between replacements. This will fail, though, if two // fix-it replacements are exactly adjacent, or if a fix-it contains a @@ -521,5 +503,5 @@ ++OutCol; } while (((OutCol % TabStop) != 0) && i != e); } - S << '\n'; + OS << '\n'; } Index: llvm/trunk/lib/Support/WithColor.cpp =================================================================== --- llvm/trunk/lib/Support/WithColor.cpp +++ llvm/trunk/lib/Support/WithColor.cpp @@ -19,15 +19,10 @@ cl::desc("Use colors in output (default=autodetect)"), cl::init(cl::BOU_UNSET)); -bool WithColor::colorsEnabled(raw_ostream &OS) { - if (UseColor == cl::BOU_UNSET) - return OS.has_colors(); - return UseColor == cl::BOU_TRUE; -} - -WithColor::WithColor(raw_ostream &OS, HighlightColor Color) : OS(OS) { +WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors) + : OS(OS), DisableColors(DisableColors) { // Detect color from terminal type unless the user passed the --color option. - if (colorsEnabled(OS)) { + if (colorsEnabled()) { switch (Color) { case HighlightColor::Address: OS.changeColor(raw_ostream::YELLOW); @@ -56,6 +51,9 @@ case HighlightColor::Note: OS.changeColor(raw_ostream::BLACK, true); break; + case HighlightColor::Remark: + OS.changeColor(raw_ostream::BLUE, true); + break; } } } @@ -66,25 +64,58 @@ raw_ostream &WithColor::note() { return note(errs()); } -raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::remark() { return remark(errs()); } + +raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Error).get() << "error: "; + return WithColor(OS, HighlightColor::Error, DisableColors).get() + << "error: "; } -raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Warning).get() << "warning: "; + return WithColor(OS, HighlightColor::Warning, DisableColors).get() + << "warning: "; } -raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Note).get() << "note: "; + return WithColor(OS, HighlightColor::Note, DisableColors).get() << "note: "; } -WithColor::~WithColor() { - if (colorsEnabled(OS)) +raw_ostream &WithColor::remark(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { + if (!Prefix.empty()) + OS << Prefix << ": "; + return WithColor(OS, HighlightColor::Remark, DisableColors).get() + << "remark: "; +} + +bool WithColor::colorsEnabled() { + if (DisableColors) + return false; + if (UseColor == cl::BOU_UNSET) + return OS.has_colors(); + return UseColor == cl::BOU_TRUE; +} + +WithColor &WithColor::changeColor(raw_ostream::Colors Color, bool Bold, + bool BG) { + if (colorsEnabled()) + OS.changeColor(Color, Bold, BG); + return *this; +} + +WithColor &WithColor::resetColor() { + if (colorsEnabled()) OS.resetColor(); + return *this; } + +WithColor::~WithColor() { resetColor(); } Index: llvm/trunk/test/FileCheck/opt-color.txt =================================================================== --- llvm/trunk/test/FileCheck/opt-color.txt +++ llvm/trunk/test/FileCheck/opt-color.txt @@ -0,0 +1,22 @@ +; Create a case that produces a simple diagnostic. +; RUN: echo foo > %t.in +; CHECK: bar + +; Run without and with -color. In the former case, FileCheck should suppress +; color in its diagnostics because stderr is a file. +; RUN: not FileCheck %s < %t.in 2> %t.no-color +; RUN: not FileCheck -color %s < %t.in 2> %t.color + +; Check whether color was produced. +; RUN: FileCheck -check-prefix NO-COLOR %s < %t.no-color +; RUN: FileCheck -check-prefix COLOR %s < %t.color + +; Make sure our NO-COLOR and COLOR patterns are sane: they don't match the +; opposite cases. +; RUN: not FileCheck -check-prefix COLOR %s < %t.no-color +; RUN: not FileCheck -check-prefix NO-COLOR %s < %t.color + +; I don't know of a good way to check for ANSI color codes, so just make sure +; some new characters show up where those codes should appear. +; NO-COLOR: : error: CHECK: expected string not found in input +; COLOR: : {{.+}}error: {{.+}}CHECK: expected string not found in input Index: llvm/trunk/utils/FileCheck/FileCheck.cpp =================================================================== --- llvm/trunk/utils/FileCheck/FileCheck.cpp +++ llvm/trunk/utils/FileCheck/FileCheck.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/FileCheck.h" using namespace llvm; @@ -108,6 +109,10 @@ } int main(int argc, char **argv) { + // Enable use of ANSI color codes because FileCheck is using them to + // highlight text. + llvm::sys::Process::UseANSIEscapeCodes(true); + InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv);