/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include #include #include #include #include #include #include #include #include "log.h" NEXTPNR_NAMESPACE_BEGIN NPNR_NORETURN void logv_error(const char *format, va_list ap) NPNR_ATTRIBUTE(noreturn); std::vector> log_streams; log_write_type log_write_function = nullptr; std::string log_last_error; void (*log_error_atexit)() = NULL; dict message_count_by_level; static int log_newline_count = 0; bool had_nonfatal_error = false; bool log_warn_as_error = false; std::string stringf(const char *fmt, ...) { std::string string; va_list ap; va_start(ap, fmt); string = vstringf(fmt, ap); va_end(ap); return string; } std::string vstringf(const char *fmt, va_list ap) { std::string string; char *str = NULL; #if defined(_WIN32) || defined(__CYGWIN__) int sz = 64 + strlen(fmt), rc; while (1) { va_list apc; va_copy(apc, ap); str = (char *)realloc(str, sz); rc = vsnprintf(str, sz, fmt, apc); va_end(apc); if (rc >= 0 && rc < sz) break; sz *= 2; } #else if (vasprintf(&str, fmt, ap) < 0) str = NULL; #endif if (str != NULL) { string = str; free(str); } return string; } void logv(const char *format, va_list ap, LogLevel level = LogLevel::LOG_MSG) { // // Trim newlines from the beginning while (format[0] == '\n' && format[1] != 0) { log_always("\n"); format++; } std::string str = vstringf(format, ap); if (str.empty()) return; size_t nnl_pos = str.find_last_not_of('\n'); if (nnl_pos == std::string::npos) log_newline_count += str.size(); else log_newline_count = str.size() - nnl_pos - 1; for (auto f : log_streams) if (f.second <= level) *f.first << str; if (log_write_function) log_write_function(str); } void log_with_level(LogLevel level, const char *format, ...) { message_count_by_level[level]++; va_list ap; va_start(ap, format); logv(format, ap, level); va_end(ap); } void logv_prefixed(const char *prefix, const char *format, va_list ap, LogLevel level) { std::string message = vstringf(format, ap); log_with_level(level, "%s%s", prefix, message.c_str()); log_flush(); } void logv_nonfatal_error(const char *format, va_list ap) { logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR_MSG); had_nonfatal_error = true; } void logv_error(const char *format, va_list ap) { logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR_MSG); if (log_error_atexit) log_error_atexit(); throw log_execution_error_exception(); } void log_always(const char *format, ...) { va_list ap; va_start(ap, format); logv(format, ap, LogLevel::ALWAYS_MSG); va_end(ap); } void log(const char *format, ...) { va_list ap; va_start(ap, format); logv(format, ap, LogLevel::LOG_MSG); va_end(ap); } void log_info(const char *format, ...) { va_list ap; va_start(ap, format); logv_prefixed("Info: ", format, ap, LogLevel::INFO_MSG); va_end(ap); } void log_warning(const char *format, ...) { va_list ap; va_start(ap, format); if (log_warn_as_error) logv_nonfatal_error(format, ap); else logv_prefixed("Warning: ", format, ap, LogLevel::WARNING_MSG); va_end(ap); } void log_error(const char *format, ...) { va_list ap; va_start(ap, format); logv_error(format, ap); } void log_break() { if (log_newline_count < 2) log("\n"); if (log_newline_count < 2) log("\n"); } void log_nonfatal_error(const char *format, ...) { va_list ap; va_start(ap, format); logv_nonfatal_error(format, ap); va_end(ap); } void log_flush() { for (auto f : log_streams) f.first->flush(); } NEXTPNR_NAMESPACE_END