summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--src/color.cc84
-rw-r--r--src/color.h71
-rw-r--r--src/config.h.in3
-rw-r--r--src/source-error-handler.cc16
-rw-r--r--src/source-error-handler.h6
-rw-r--r--test/README.md1
-rw-r--r--test/parse/force-color.txt14
-rwxr-xr-xtest/run-tests.py19
9 files changed, 208 insertions, 11 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ec0896f..1ae2b326 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,10 @@ check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF)
check_symbol_exists(sysconf "unistd.h" HAVE_SYSCONF)
check_symbol_exists(strcasecmp "strings.h" HAVE_STRCASECMP)
+if (WIN32)
+ check_symbol_exists(ENABLE_VIRTUAL_TERMINAL_PROCESSING "windows.h" HAVE_WIN32_VT100)
+endif ()
+
if (EMSCRIPTEN)
set(SIZEOF_SSIZE_T 4)
set(SIZEOF_SIZE_T 4)
@@ -254,6 +258,7 @@ add_library(libwabt STATIC
src/resolve-names.cc
src/binary.cc
+ src/color.cc
src/common.cc
src/config.cc
src/literal.cc
diff --git a/src/color.cc b/src/color.cc
new file mode 100644
index 00000000..f1657728
--- /dev/null
+++ b/src/color.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "color.h"
+
+#include <cstdlib>
+
+#include "common.h"
+
+#if _WIN32
+#include <io.h>
+#include <windows.h>
+#elif HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+namespace wabt {
+
+Color::Color(FILE* file, bool enabled) : file_(file) {
+ enabled_ = enabled && SupportsColor(file_);
+}
+
+// static
+bool Color::SupportsColor(FILE* file) {
+ char* force = getenv("FORCE_COLOR");
+ if (force) {
+ return atoi(force) != 0;
+ }
+
+#if _WIN32
+
+ {
+#if HAVE_WIN32_VT100
+ HANDLE handle;
+ if (file == stdout) {
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ } else if (file == stderr) {
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ } else {
+ return false;
+ }
+ DWORD mode;
+ if (!_isatty(_fileno(file)) || !GetConsoleMode(handle, mode) ||
+ !SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
+ return false;
+ }
+ return true;
+#else
+ // TODO(binji): Support older Windows by using SetConsoleTextAttribute?
+ return false;
+#endif
+ }
+
+#elif HAVE_UNISTD_H
+
+ return isatty(fileno(file));
+
+#else
+
+ return false;
+
+#endif
+}
+
+void Color::WriteCode(const char* code) const {
+ if (enabled_) {
+ fputs(code, file_);
+ }
+}
+
+} // namespace wabt
diff --git a/src/color.h b/src/color.h
new file mode 100644
index 00000000..ba64e5d2
--- /dev/null
+++ b/src/color.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WABT_COLOR_H_
+#define WABT_COLOR_H_
+
+#include <cstdio>
+
+namespace wabt {
+
+#define WABT_FOREACH_COLOR_CODE(V) \
+ V(Default, "\x1b[0m") \
+ V(Bold, "\x1b[1m") \
+ V(NoBold, "\x1b[22m") \
+ V(Black, "\x1b[30m") \
+ V(Red, "\x1b[31m") \
+ V(Green, "\x1b[32m") \
+ V(Yellow, "\x1b[33m") \
+ V(Blue, "\x1b[34m") \
+ V(Magenta, "\x1b[35m") \
+ V(Cyan, "\x1b[36m") \
+ V(White, "\x1b[37m")
+
+class Color {
+ public:
+ Color(FILE*, bool enabled = true);
+
+ // Write the given color to the file, if enabled.
+#define WABT_COLOR(Name, code) \
+ void Name() const { WriteCode(Name##Code()); }
+ WABT_FOREACH_COLOR_CODE(WABT_COLOR)
+#undef WABT_COLOR
+
+ // Get the color code as a string, if enabled.
+#define WABT_COLOR(Name, code) \
+ const char* Maybe##Name##Code() const { return enabled_ ? Name##Code() : ""; }
+ WABT_FOREACH_COLOR_CODE(WABT_COLOR)
+#undef WABT_COLOR
+
+ // Get the color code as a string.
+#define WABT_COLOR(Name, code) \
+ static const char* Name##Code() { return code; }
+ WABT_FOREACH_COLOR_CODE(WABT_COLOR)
+#undef WABT_COLOR
+
+ private:
+ static bool SupportsColor(FILE*);
+ void WriteCode(const char*) const;
+
+ FILE* file_;
+ bool enabled_;
+};
+
+#undef WABT_FOREACH_COLOR_CODE
+
+} // namespace wabt
+
+#endif // WABT_COLOR_H_
diff --git a/src/config.h.in b/src/config.h.in
index b2dc04b4..cb6af598 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -37,6 +37,9 @@
/* Whether strcasecmp is defined by strings.h */
#cmakedefine01 HAVE_STRCASECMP
+/* Whether ENABLE_VIRTUAL_TERMINAL_PROCESSING is defined by windows.h */
+#cmakedefine01 HAVE_WIN32_VT100
+
#cmakedefine01 COMPILER_IS_CLANG
#cmakedefine01 COMPILER_IS_GNU
#cmakedefine01 COMPILER_IS_MSVC
diff --git a/src/source-error-handler.cc b/src/source-error-handler.cc
index acd3641f..7424b973 100644
--- a/src/source-error-handler.cc
+++ b/src/source-error-handler.cc
@@ -24,6 +24,7 @@ SourceErrorHandler::SourceErrorHandler(Location::Type location_type)
: location_type_(location_type) {}
std::string SourceErrorHandler::DefaultErrorMessage(
+ const Color& color,
const Location* loc,
const std::string& error,
const std::string& source_line,
@@ -31,12 +32,14 @@ std::string SourceErrorHandler::DefaultErrorMessage(
int indent) {
std::string indent_str(indent, ' ');
std::string result = indent_str;
+ result += color.MaybeBoldCode();
if (location_type_ == Location::Type::Text) {
result += string_printf("%s:%d:%d: ", loc->filename, loc->line,
loc->first_column);
} else {
result += string_printf("%s:%" PRIzd ": ", loc->filename, loc->offset);
}
+ result += color.MaybeDefaultCode();
result += error;
result += '\n';
result += indent_str;
@@ -50,7 +53,10 @@ std::string SourceErrorHandler::DefaultErrorMessage(
num_carets = std::min(num_carets, source_line.size() - num_spaces);
num_carets = std::max<size_t>(num_carets, 1);
result.append(num_spaces, ' ');
+ result += color.MaybeBoldCode();
+ result += color.MaybeGreenCode();
result.append(num_carets, '^');
+ result += color.MaybeDefaultCode();
result += '\n';
}
return result;
@@ -69,7 +75,8 @@ SourceErrorHandlerFile::SourceErrorHandlerFile(FILE* file,
file_(file),
header_(header),
print_header_(print_header),
- source_line_max_length_(source_line_max_length) {}
+ source_line_max_length_(source_line_max_length),
+ color_(file) {}
bool SourceErrorHandlerFile::OnError(const Location* loc,
const std::string& error,
@@ -77,7 +84,7 @@ bool SourceErrorHandlerFile::OnError(const Location* loc,
size_t source_line_column_offset) {
PrintErrorHeader();
int indent = header_.empty() ? 0 : 2;
- std::string message = DefaultErrorMessage(loc, error, source_line,
+ std::string message = DefaultErrorMessage(color_, loc, error, source_line,
source_line_column_offset, indent);
fwrite(message.data(), 1, message.size(), file_);
return true;
@@ -105,13 +112,14 @@ SourceErrorHandlerBuffer::SourceErrorHandlerBuffer(
size_t source_line_max_length,
Location::Type location_type)
: SourceErrorHandler(location_type),
- source_line_max_length_(source_line_max_length) {}
+ source_line_max_length_(source_line_max_length),
+ color_(nullptr, false) {}
bool SourceErrorHandlerBuffer::OnError(const Location* loc,
const std::string& error,
const std::string& source_line,
size_t source_line_column_offset) {
- buffer_ += DefaultErrorMessage(loc, error, source_line,
+ buffer_ += DefaultErrorMessage(color_, loc, error, source_line,
source_line_column_offset, 0);
return true;
}
diff --git a/src/source-error-handler.h b/src/source-error-handler.h
index 45e049b0..5e09dc26 100644
--- a/src/source-error-handler.h
+++ b/src/source-error-handler.h
@@ -19,6 +19,7 @@
#include <string>
+#include "color.h"
#include "common.h"
namespace wabt {
@@ -38,7 +39,8 @@ class SourceErrorHandler {
// OnError will be called with with source_line trimmed to this length.
virtual size_t source_line_max_length() const = 0;
- std::string DefaultErrorMessage(const Location*,
+ std::string DefaultErrorMessage(const Color&,
+ const Location*,
const std::string& error,
const std::string& source_line,
size_t source_line_column_offset,
@@ -93,6 +95,7 @@ class SourceErrorHandlerFile : public SourceErrorHandler {
std::string header_;
PrintHeader print_header_;
size_t source_line_max_length_;
+ Color color_;
};
class SourceErrorHandlerBuffer : public SourceErrorHandler {
@@ -114,6 +117,7 @@ class SourceErrorHandlerBuffer : public SourceErrorHandler {
private:
size_t source_line_max_length_;
std::string buffer_;
+ Color color_;
};
} // namespace wabt
diff --git a/test/README.md b/test/README.md
index 64c452e0..e03508f4 100644
--- a/test/README.md
+++ b/test/README.md
@@ -103,6 +103,7 @@ The currently supported list of keys:
- `EXE`: the executable to run, defaults to out/wast2wasm
- `STDIN_FILE`: the file to use for STDIN instead of the contents of this file.
- `FLAGS`: additional flags to pass to the executable
+- `ENV`: environment variables to set, separated by spaces
- `ERROR`: the expected return value from the executable, defaults to 0
- `SLOW`: if defined, this test's timeout is doubled.
- `SKIP`: if defined, this test is not run. You can use the value as a comment.
diff --git a/test/parse/force-color.txt b/test/parse/force-color.txt
new file mode 100644
index 00000000..9ea7d587
--- /dev/null
+++ b/test/parse/force-color.txt
@@ -0,0 +1,14 @@
+;;; ERROR: 1
+;;; TOOL: wast2wasm
+;;; ENV: FORCE_COLOR=1
+(module
+ (func badname (param i32) (result badtype)
+ drop))
+(;; STDERR ;;;
+out/test/parse/force-color.txt:5:9: unexpected token "badname"
+ (func badname (param i32) (result badtype)
+ ^^^^^^^
+out/test/parse/force-color.txt:5:37: unexpected token "badtype"
+ (func badname (param i32) (result badtype)
+ ^^^^^^^
+;;; STDERR ;;)
diff --git a/test/run-tests.py b/test/run-tests.py
index 44de0525..eb459d19 100755
--- a/test/run-tests.py
+++ b/test/run-tests.py
@@ -171,7 +171,7 @@ class Cell(object):
return self.value[0]
-def RunCommandWithTimeout(command, cwd, timeout, console_out=False):
+def RunCommandWithTimeout(command, cwd, timeout, console_out=False, env=None):
process = None
is_timeout = Cell(False)
@@ -196,10 +196,10 @@ def RunCommandWithTimeout(command, cwd, timeout, console_out=False):
# http://stackoverflow.com/a/10012262: subprocess with a timeout
# http://stackoverflow.com/a/22582602: kill subprocess and children
- process = subprocess.Popen(command, cwd=cwd, stdout=None if console_out
- else subprocess.PIPE, stderr=None if console_out
- else subprocess.PIPE, universal_newlines=True,
- **kwargs)
+ process = subprocess.Popen(command, cwd=cwd, env=env,
+ stdout=None if console_out else subprocess.PIPE,
+ stderr=None if console_out else subprocess.PIPE,
+ universal_newlines=True, **kwargs)
timer = threading.Timer(timeout, KillProcess)
try:
timer.start()
@@ -231,6 +231,7 @@ class TestInfo(object):
self.tool = 'wast2wasm'
self.exe = '%(wast2wasm)s'
self.flags = []
+ self.env = {}
self.last_cmd = ''
self.expected_error = 0
self.slow = False
@@ -250,6 +251,7 @@ class TestInfo(object):
result.flags = ['--bindir', '%(bindir)s', '-v', '-o', '%(out_dir)s']
if fold_exprs:
result.flags.append('--fold-exprs')
+ result.env = self.env
result.expected_error = 0
result.slow = self.slow
result.skip = self.skip
@@ -311,6 +313,9 @@ class TestInfo(object):
self.tool = value
for tool_key, tool_value in TOOLS[value].items():
self.ParseDirective(tool_key, tool_value)
+ elif key == 'ENV':
+ # Pattern: FOO=1 BAR=stuff
+ self.env = dict(x.split('=') for x in value.split())
else:
raise Error('Unknown directive: %s' % key)
@@ -545,6 +550,8 @@ def RunTest(info, options, variables, verbose_level=0):
variables = dict(variables)
cwd = REPO_ROOT_DIR
+ env = dict(os.environ)
+ env.update(info.env)
gen_input_path = info.CreateInputFile()
rel_gen_input_path = os.path.relpath(gen_input_path, cwd)
@@ -562,7 +569,7 @@ def RunTest(info, options, variables, verbose_level=0):
print(' '.join(cmd))
try:
- return RunCommandWithTimeout(cmd, cwd, timeout, verbose_level > 0)
+ return RunCommandWithTimeout(cmd, cwd, timeout, verbose_level > 0, env)
except (Error, KeyboardInterrupt) as e:
return e