When available, use ANSI escapes to colorize diagnostic output.

Issue #245.
This commit is contained in:
Matt Pharr
2012-04-19 11:36:28 -07:00
parent cc26b66e99
commit e4b3d03da5
2 changed files with 108 additions and 14 deletions

View File

@@ -17,6 +17,10 @@ import shlex
import platform import platform
import tempfile import tempfile
# disable fancy error/warning printing with ANSI colors, so grepping for error
# messages doesn't get confused
os.environ["TERM"] = "dumb"
# This script is affected by http://bugs.python.org/issue5261 on OSX 10.5 Leopard # This script is affected by http://bugs.python.org/issue5261 on OSX 10.5 Leopard
# git history has a workaround for that issue. # git history has a workaround for that issue.

118
util.cpp
View File

@@ -90,6 +90,49 @@ lTerminalWidth() {
} }
static bool
lHaveANSIColors() {
static bool r = (getenv("TERM") != NULL &&
strcmp(getenv("TERM"), "dumb") != 0);
return r;
}
static const char *
lStartBold() {
if (lHaveANSIColors())
return "\e[1m";
else
return "";
}
static const char *
lStartRed() {
if (lHaveANSIColors())
return "\e[31m";
else
return "";
}
static const char *
lStartBlue() {
if (lHaveANSIColors())
return "\e[34m";
else
return "";
}
static const char *
lResetColor() {
if (lHaveANSIColors())
return "\e[0m";
else
return "";
}
/** Given a pointer into a string, find the end of the current word and /** Given a pointer into a string, find the end of the current word and
return a pointer to its last character. return a pointer to its last character.
*/ */
@@ -140,17 +183,43 @@ lPrintFileLineContext(SourcePos p) {
fclose(f); fclose(f);
} }
/** Counts the number of characters into the buf at which the numColons
colon character is found. Skips over ANSI escape sequences and doesn't
include their characters in the final count.
*/
static int
lFindIndent(int numColons, const char *buf) {
int indent = 0;
while (*buf != '\0') {
if (*buf == '\e') {
while (*buf != '\0' && *buf != 'm')
++buf;
if (*buf == 'm')
++buf;
}
else {
if (*buf == ':') {
if (--numColons == 0)
break;
}
++indent;
++buf;
}
}
return indent + 2;
}
/** Print the given string to the given FILE, assuming the given output /** Print the given string to the given FILE, assuming the given output
column width. Break words as needed to avoid words spilling past the column width. Break words as needed to avoid words spilling past the
last column. */ last column. */
static void static void
lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) { lPrintWithWordBreaks(const char *buf, int indent, int columnWidth, FILE *out) {
#ifdef ISPC_IS_WINDOWS #ifdef ISPC_IS_WINDOWS
fputs(buf, out); fputs(buf, out);
#else #else
int column = 0; int column = 0;
Assert(strchr(buf, ':') != NULL);
int indent = strchr(buf, ':') - buf + 2;
int width = std::max(40, columnWidth - 2); int width = std::max(40, columnWidth - 2);
// Collect everything into a string and print it all at once at the end // Collect everything into a string and print it all at once at the end
@@ -160,6 +229,15 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
const char *msgPos = buf; const char *msgPos = buf;
while (true) { while (true) {
if (*msgPos == '\e') {
// handle ANSI color escape: copy it to the output buffer
// without charging for the characters it uses
do {
outStr.push_back(*msgPos++);
} while (*msgPos != '\0' && *msgPos != 'm');
continue;
}
while (*msgPos != '\0' && isspace(*msgPos)) while (*msgPos != '\0' && isspace(*msgPos))
++msgPos; ++msgPos;
if (*msgPos == '\0') if (*msgPos == '\0')
@@ -171,8 +249,8 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
column = indent; column = indent;
outStr.push_back('\n'); outStr.push_back('\n');
// Indent to the same column as the ":" at the start of the // Indent to the same column as the ":" at the start of the
// message, unless doing so would be too far in. // message.
for (int i = 0; i < std::min(16, indent); ++i) for (int i = 0; i < indent; ++i)
outStr.push_back(' '); outStr.push_back(' ');
} }
@@ -225,26 +303,37 @@ asprintf(char **sptr, const char *fmt, ...)
@param args Arguments with values for format string % entries @param args Arguments with values for format string % entries
*/ */
static void static void
lPrint(const char *type, SourcePos p, const char *fmt, va_list args) { lPrint(const char *type, bool isError, SourcePos p, const char *fmt,
va_list args) {
char *errorBuf, *formattedBuf; char *errorBuf, *formattedBuf;
if (vasprintf(&errorBuf, fmt, args) == -1) { if (vasprintf(&errorBuf, fmt, args) == -1) {
fprintf(stderr, "vasprintf() unable to allocate memory!\n"); fprintf(stderr, "vasprintf() unable to allocate memory!\n");
abort(); abort();
} }
int indent = 0;
if (p.first_line == 0) { if (p.first_line == 0) {
// We don't have a valid SourcePos, so create a message without it // We don't have a valid SourcePos, so create a message without it
if (asprintf(&formattedBuf, "%s: %s\n", type, errorBuf) == -1) { if (asprintf(&formattedBuf, "%s%s%s%s%s: %s%s\n", lStartBold(),
isError ? lStartRed() : lStartBlue(), type,
lResetColor(), lStartBold(), errorBuf,
lResetColor()) == -1) {
fprintf(stderr, "asprintf() unable to allocate memory!\n"); fprintf(stderr, "asprintf() unable to allocate memory!\n");
exit(1); exit(1);
} }
indent = lFindIndent(1, formattedBuf);
} }
else { else {
// Create an error message that includes the file and line number // Create an error message that includes the file and line number
if (asprintf(&formattedBuf, "%s:%d:%d: %s: %s\n", p.name, if (asprintf(&formattedBuf, "%s%s:%d:%d: %s%s%s%s: %s%s\n",
p.first_line, p.first_column, type, errorBuf) == -1) { lStartBold(), p.name, p.first_line, p.first_column,
isError ? lStartRed() : lStartBlue(), type,
lResetColor(), lStartBold(), errorBuf,
lResetColor()) == -1) {
fprintf(stderr, "asprintf() unable to allocate memory!\n"); fprintf(stderr, "asprintf() unable to allocate memory!\n");
exit(1); exit(1);
} }
indent = lFindIndent(3, formattedBuf);
} }
// Now that we've done all that work, see if we've already printed the // Now that we've done all that work, see if we've already printed the
@@ -255,7 +344,7 @@ lPrint(const char *type, SourcePos p, const char *fmt, va_list args) {
return; return;
printed.insert(formattedBuf); printed.insert(formattedBuf);
lPrintWithWordBreaks(formattedBuf, lTerminalWidth(), stderr); lPrintWithWordBreaks(formattedBuf, indent, lTerminalWidth(), stderr);
lPrintFileLineContext(p); lPrintFileLineContext(p);
free(errorBuf); free(errorBuf);
@@ -271,7 +360,7 @@ Error(SourcePos p, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
lPrint("Error", p, fmt, args); lPrint("Error", true, p, fmt, args);
va_end(args); va_end(args);
} }
@@ -283,7 +372,7 @@ Debug(SourcePos p, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
lPrint("Debug", p, fmt, args); lPrint("Debug", false, p, fmt, args);
va_end(args); va_end(args);
} }
@@ -298,7 +387,8 @@ Warning(SourcePos p, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
lPrint(g->warningsAsErrors ? "Error" : "Warning", p, fmt, args); lPrint(g->warningsAsErrors ? "Error" : "Warning", g->warningsAsErrors,
p, fmt, args);
va_end(args); va_end(args);
} }
@@ -311,7 +401,7 @@ PerformanceWarning(SourcePos p, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
lPrint("Performance Warning", p, fmt, args); lPrint("Performance Warning", false, p, fmt, args);
va_end(args); va_end(args);
} }