diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index c9fa99927..016c802da 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -178,6 +178,7 @@ if (NOT YOSYS_BUILD_PYTHON_ONLY) INCLUDE_DIRS ${pybind11_INCLUDE_DIR} LIBRARIES + fmt::fmt $<${YOSYS_ENABLE_READLINE}:PkgConfig::readline> $<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline> $<${YOSYS_ENABLE_TCL}:PkgConfig::tcl> diff --git a/kernel/driver.cc b/kernel/driver.cc index 10ebca84e..537a3976c 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -68,6 +68,9 @@ namespace py = pybind11; # include #endif +#include +#include + USING_YOSYS_NAMESPACE #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) @@ -116,6 +119,88 @@ namespace Yosys { extern bool log_stderr_sink_forced; }; +class ColorConsoleLogSink : public LogSink +{ +public: + explicit ColorConsoleLogSink(bool err, bool quiet) : error_output(err), quiet_warnings(quiet) {} + void log(const LogMessage &msg) override; + bool should_log(const LogMessage &msg) const override; + void flush() override; + FILE *file_handle() override { return error_output ? nullptr : stdout; } +private: + bool error_output; + bool quiet_warnings; +}; + +bool no_color = false; + +bool is_colored(FILE *file) +{ + if (no_color) + return false; + +#if defined(_WIN32) + HANDLE handle = reinterpret_cast(_get_osfhandle(_fileno(file))); + DWORD mode; + return handle != INVALID_HANDLE_VALUE && + GetConsoleMode(handle, &mode); +#else + return isatty(fileno(file)); +#endif +} + +void ColorConsoleLogSink::flush() +{ + fflush(stdout); + fflush(stderr); +} + +void ColorConsoleLogSink::log(const LogMessage &msg) +{ + FILE *f = (error_output || msg.severity == LogSeverity::Error) ? stderr : stdout; + + if (logger().get_log_time()) { + auto elapsed = msg.timestamp - logger().get_initial_time(); + auto us = std::chrono::duration_cast(elapsed); + std::string time_str = stringf("[%05d.%06d] ", int(us.count() / 1'000'000),int(us.count() % 1'000'000)); + fmt::print(f, fg(fmt::terminal_color::blue), "{}", time_str); + } + + switch (msg.severity) { + case LogSeverity::Warning: + fmt::print(f, fg(fmt::terminal_color::bright_yellow), "{}", msg.prefix); + fmt::print(f, fg(fmt::terminal_color::blue) | fmt::emphasis::bold, "{}", msg.message); + break; + + case LogSeverity::Error: + fmt::print(f, fg(fmt::terminal_color::bright_red), "{}", msg.prefix); + fmt::print(f, fg(fmt::terminal_color::blue) | fmt::emphasis::bold, "{}", msg.message); + break; + + case LogSeverity::Header: + fmt::print(f, fg(fmt::terminal_color::cyan) | fmt::emphasis::bold, "{}", msg.prefix); + fmt::print(f, fg(fmt::terminal_color::white) | fmt::emphasis::bold, "{}", msg.message); + break; + + case LogSeverity::Comment: + case LogSeverity::Debug: + fmt::print(f, fg(fmt::terminal_color::bright_black), "{}{}", msg.prefix, msg.message); + break; + + case LogSeverity::Info: + default: + fmt::print(f, "{}{}", msg.prefix, msg.message); + break; + } +} + +bool ColorConsoleLogSink::should_log(const LogMessage &msg) const +{ + return (!error_output) || (msg.severity == LogSeverity::Error || + (msg.severity == LogSeverity::Warning && !quiet_warnings) || + log_stderr_sink_forced); +} + #ifdef _WIN32 int wmain(int argc, wchar_t **wargv) { @@ -166,6 +251,7 @@ int main(int argc, char **argv) bool mode_q = false; bool quiet_warnings = false; bool log_stderr = false; + no_color = std::getenv("NO_COLOR") != nullptr; cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite"); options.set_width(SIZE_MAX); @@ -236,6 +322,7 @@ int main(int argc, char **argv) cxxopts::value>(), "") ("E,deps-file", "write a Makefile dependencies file with input and output file names", cxxopts::value(), "") + ("no-color", "disable colored output"); ; options.add_options("developer") ("X,trace", "enable tracing of core data structure changes. for debugging") @@ -339,6 +426,7 @@ int main(int argc, char **argv) logger().set_verbose_level(result["v"].as()); } if (result.count("t")) logger().set_log_time(true); + if (result.count("no-color")) no_color = true; if (result.count("d")) timing_details = true; for (const auto& key : {"s", "c"}) { if (result.count(key)) { @@ -408,11 +496,14 @@ int main(int argc, char **argv) Hasher::set_fudge((Hasher::hash_t)seed); } - if (!log_stderr) { - logger().add_sink(); - } else { - logger().add_sink(quiet_warnings); - } + if (!is_colored(stdout) || !is_colored(stderr)) { + if (!log_stderr) { + logger().add_sink(); + } else { + logger().add_sink(quiet_warnings); + } + } else + logger().add_sink(log_stderr, quiet_warnings); if (print_banner) yosys_banner(); diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 076390e23..91cde8cab 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -19,7 +19,7 @@ block() set(FETCHCONTENT_FULLY_DISCONNECTED ON) if(NOT YOSYS_USE_BUNDLED_LIBS) - find_package(fmt 12.2 QUIET) + find_package(fmt 12.2 QUIET GLOBAL) endif() if(fmt_FOUND) set(SLANG_USE_SYSTEM_FMT ON) @@ -31,6 +31,7 @@ block() SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/fmt ) FetchContent_MakeAvailable(fmt) + add_subdirectory(fmt) endif() if(NOT YOSYS_USE_BUNDLED_LIBS)