// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "reporthandler.h" #include "typedatabase.h" #include "messages.h" #include #include #include #include #include #include #include using namespace Qt::StringLiterals; static const auto COLOR_END = "\033[0m"_ba; static const auto COLOR_YELLOW = "\033[1;33m"_ba; static const auto COLOR_GREEN = "\033[0;32m"_ba; static constexpr bool useTerminalColors() { return QOperatingSystemVersion::currentType() != QOperatingSystemVersion::Windows #ifdef NOCOLOR && false #endif ; } static bool m_silent = false; static int m_warningCount = 0; static int m_suppressedCount = 0; static ReportHandler::DebugLevel m_debugLevel = ReportHandler::NoDebug; static QSet m_reportedWarnings; static QString m_prefix; static bool m_withinProgress = false; static QByteArray m_progressMessage; static int m_step_warning = 0; static QElapsedTimer m_timer; Q_LOGGING_CATEGORY(lcShiboken, "qt.shiboken") Q_LOGGING_CATEGORY(lcShibokenDoc, "qt.shiboken.doc") void ReportHandler::install() { qInstallMessageHandler(ReportHandler::messageOutput); startTimer(); } void ReportHandler::startTimer() { m_timer.start(); } ReportHandler::DebugLevel ReportHandler::debugLevel() { return m_debugLevel; } void ReportHandler::setDebugLevel(ReportHandler::DebugLevel level) { m_debugLevel = level; } bool ReportHandler::setDebugLevelFromArg(const QString &level) { bool result = true; if (level == u"sparse") ReportHandler::setDebugLevel(ReportHandler::SparseDebug); else if (level == u"medium") ReportHandler::setDebugLevel(ReportHandler::MediumDebug); else if (level == u"full") ReportHandler::setDebugLevel(ReportHandler::FullDebug); else result = false; return result; } int ReportHandler::suppressedCount() { return m_suppressedCount; } int ReportHandler::warningCount() { return m_warningCount; } bool ReportHandler::isSilent() { return m_silent; } void ReportHandler::setSilent(bool silent) { m_silent = silent; } void ReportHandler::setPrefix(const QString &p) { m_prefix = p; } void ReportHandler::messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &text) { // Check for file location separator added by SourceLocation auto fileLocationPos = text.indexOf(u":\t"); if (type == QtWarningMsg) { if (m_silent || m_reportedWarnings.contains(text)) return; if (auto *db = TypeDatabase::instance()) { const bool suppressed = fileLocationPos >= 0 ? db->isSuppressedWarning(QStringView{text}.mid(fileLocationPos + 2)) : db->isSuppressedWarning(text); if (suppressed) { ++m_suppressedCount; return; } } ++m_warningCount; ++m_step_warning; m_reportedWarnings.insert(text); } QString message = m_prefix; if (!message.isEmpty()) message.append(u' '); const auto prefixLength = message.size(); message.append(text); // Replace file location tab by space if (fileLocationPos >= 0) message[prefixLength + fileLocationPos + 1] = u' '; fprintf(stderr, "%s\n", qPrintable(qFormatLogMessage(type, context, message))); } static QByteArray timeStamp() { const qint64 elapsed = m_timer.elapsed(); return elapsed > 5000 ? QByteArray::number(elapsed / 1000) + 's' : QByteArray::number(elapsed) + "ms"; } void ReportHandler::startProgress(const QByteArray& str) { if (m_silent) return; if (m_withinProgress) endProgress(); m_withinProgress = true; m_progressMessage = str; } static void indentStdout(qsizetype n) { for (qsizetype i = 0; i < n; ++i) fputc(' ', stdout); } void ReportHandler::endProgress() { if (m_silent) return; m_withinProgress = false; std::fputs(m_prefix.toUtf8().constData(), stdout); const auto ts = timeStamp(); if (ts.size() < 6) indentStdout(6 - ts.size()); std::fputs(" [", stdout); std::fputs(ts.constData(), stdout); std::fputs("] ", stdout); std::fputs(m_progressMessage.constData(), stdout); if (m_progressMessage.size() < 60) indentStdout(60 - m_progressMessage.size()); static const QByteArray ok = '[' + (useTerminalColors() ? COLOR_GREEN + "OK"_ba + COLOR_END : "OK"_ba) + "]\n"_ba; static const QByteArray warning = '[' + (useTerminalColors() ? COLOR_YELLOW + "WARNING"_ba + COLOR_END : "WARNING"_ba) + "]\n"_ba; std::fputs(m_step_warning == 0 ? ok.constData() : warning.constData(), stdout); std::fflush(stdout); m_progressMessage.clear(); m_step_warning = 0; } QByteArray ReportHandler::doneMessage() { QByteArray result = "Done, " + m_prefix.toUtf8() + ' ' + timeStamp(); if (m_warningCount) result += ", " + QByteArray::number(m_warningCount) + " warnings"; if (m_suppressedCount) result += " (" + QByteArray::number(m_suppressedCount) + " known issues)"; return result; } static QStringList generalMessages; void ReportHandler::addGeneralMessage(const QString &message) { generalMessages.append(message); } static const char generalLogFile[] = "mjb_shiboken.log"; void ReportHandler::writeGeneralLogFile(const QString &directory) { if (generalMessages.isEmpty()) return; QFile file(directory + u'/' + QLatin1StringView(generalLogFile)); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning(lcShiboken, "%s", qPrintable(msgCannotOpenForWriting(file))); return; } for (const auto &m : std::as_const(generalMessages)) { file.write(m.toUtf8()); file.putChar('\n'); } } void ReportHandler::dumpGeneralLogFile() { std::fprintf(stdout, "\n--- %s ---\n", generalLogFile); for (const auto &m : std::as_const(generalMessages)) { std::fputs(m.toUtf8().constData(), stdout); std::fputc('\n', stdout); } std::fprintf(stdout, "--- End of %s ---\n\n", generalLogFile); }