diff options
author | Nico Weber <thakis@chromium.org> | 2012-01-19 00:09:16 (GMT) |
---|---|---|
committer | Nico Weber <thakis@chromium.org> | 2012-01-19 00:09:16 (GMT) |
commit | a2c4b6780dcf105821e4f2ec1f0b591adbeb6dca (patch) | |
tree | a62348131d053354b7d8d5fb1c3ca5b3d5527d67 /src | |
parent | 63013b3bd5ca79d454a95a6f985d98f16f21da1c (diff) | |
download | Ninja-a2c4b6780dcf105821e4f2ec1f0b591adbeb6dca.zip Ninja-a2c4b6780dcf105821e4f2ec1f0b591adbeb6dca.tar.gz Ninja-a2c4b6780dcf105821e4f2ec1f0b591adbeb6dca.tar.bz2 |
Strip ansi escape sequences from subcommand output when not writing to a smart terminal.
Diffstat (limited to 'src')
-rw-r--r-- | src/build.cc | 22 | ||||
-rw-r--r-- | src/util.cc | 28 | ||||
-rw-r--r-- | src/util.h | 3 | ||||
-rw-r--r-- | src/util_test.cc | 17 |
4 files changed, 68 insertions, 2 deletions
diff --git a/src/build.cc b/src/build.cc index 94c9d77..8d0abce 100644 --- a/src/build.cc +++ b/src/build.cc @@ -133,8 +133,26 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (!success) printf("FAILED: %s\n", edge->EvaluateCommand().c_str()); - if (!output.empty()) - printf("%s", output.c_str()); + // ninja sets stdout and stderr of subprocesses to a pipe, to be able to + // check if the output is empty. Some compilers, e.g. clang, check + // isatty(stderr) to decide if they should print colored output. + // To make it possible to use colored output with ninja, subprocesses should + // be run with a flag that forces them to always print color escape codes. + // To make sure these escape codes don't show up in a file if ninja's output + // is piped to a file, ninja strips ansi escape codes again if it's not + // writing to a |smart_terminal_|. + // (Launching subprocesses in pseudo ttys doesn't work because there are + // only a few hundred available on some systems, and ninja can launch + // thousands of parallel compile commands.) + // TODO: There should be a flag to disable escape code stripping. + string final_output; + if (!smart_terminal_) + final_output = StripAnsiEscapeCodes(output); + else + final_output = output; + + if (!final_output.empty()) + printf("%s", final_output.c_str()); } } diff --git a/src/util.cc b/src/util.cc index dbf7566..5f5d8dc 100644 --- a/src/util.cc +++ b/src/util.cc @@ -255,3 +255,31 @@ string GetLastErrorString() { return msg; } #endif + +static bool islatinalpha(int c) { + // isalpha() is locale-dependent. + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +string StripAnsiEscapeCodes(const string& in) { + string stripped; + stripped.reserve(in.size()); + + for (size_t i = 0; i < in.size(); ++i) { + if (in[i] != '\33') { + // Not an escape code. + stripped.push_back(in[i]); + continue; + } + + // Only strip CSIs for now. + if (i + 1 >= in.size()) break; + if (in[i + 1] != '[') continue; // Not a CSI. + i += 2; + + // Skip everything up to and including the next [a-zA-Z]. + while (i < in.size() && !islatinalpha(in[i])) + ++i; + } + return stripped; +} @@ -65,6 +65,9 @@ const char* SpellcheckStringV(const string& text, const vector<const char*>& wor /// Like SpellcheckStringV, but takes a NULL-terminated list. const char* SpellcheckString(const string& text, ...); +/// Removes all Ansi escape codes (http://www.termsys.demon.co.uk/vtansi.htm). +string StripAnsiEscapeCodes(const string& in); + #ifdef _WIN32 #define snprintf _snprintf #define fileno _fileno diff --git a/src/util_test.cc b/src/util_test.cc index 8c3d023..c44ab77 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -73,3 +73,20 @@ TEST(CanonicalizePath, AbsolutePath) { EXPECT_TRUE(CanonicalizePath(&path, &err)); EXPECT_EQ("/usr/include/stdio.h", path); } + +TEST(StripAnsiEscapeCodes, EscapeAtEnd) { + string stripped = StripAnsiEscapeCodes("foo\33"); + EXPECT_EQ("foo", stripped); + + stripped = StripAnsiEscapeCodes("foo\33["); + EXPECT_EQ("foo", stripped); +} + +TEST(StripAnsiEscapeCodes, StripColors) { + // An actual clang warning. + string input = "\33[1maffixmgr.cxx:286:15: \33[0m\33[0;1;35mwarning: " + "\33[0m\33[1musing the result... [-Wparentheses]\33[0m"; + string stripped = StripAnsiEscapeCodes(input); + EXPECT_EQ("affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]", + stripped); +} |