/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "sbsv2parser.h" #include #include #include using namespace Qt4ProjectManager; using namespace ProjectExplorer; using namespace ProjectExplorer::Constants; /* * This parser takes a somewhat unusal approach of just eating most of its * input :-) * * Only when the XML-based log file generated by SBSv2 is announced it will * open that file and parse that. Tasks will then get generated by passing * any CDATA found in the XML file on to its child parsers (using STDERR). * * In additon and tags are reported, too. */ SbsV2Parser::SbsV2Parser() : m_hub(0) { setObjectName(QLatin1String("SbsV2Parser")); ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); m_hub = pm->getObject(); } void SbsV2Parser::stdOutput(const QString &line) { // Eat most output! if (line.startsWith(QLatin1String("sbs: build log in "))) { QString logfile = QDir::fromNativeSeparators(line.mid(18).trimmed()); parseLogFile(logfile); addTask(ProjectExplorer::Task(Task::Unknown, tr("SBSv2 build log"), logfile, -1, QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM))); } } void SbsV2Parser::stdError(const QString &line) { // Eat all output! Q_UNUSED(line); } void SbsV2Parser::taskAdded(const ProjectExplorer::Task &task) { // Fix pathes: ProjectExplorer::Task tmp(task); if (!tmp.file.isEmpty()) { QFileInfo fi(tmp.file); if (!fi.isAbsolute()) { if (m_currentSource.exists(tmp.file)) tmp.file = m_currentSource.absoluteFilePath(tmp.file); else if (m_currentTarget.exists(tmp.file)) tmp.file = m_currentTarget.absoluteFilePath(tmp.file); } } // Do not report tasks from our children via the normal channel: // We do not want them get registered with the Compile output window! m_hub->addTask(tmp); } void SbsV2Parser::parseLogFile(const QString &file) { QFile logFile(file); logFile.open(QIODevice::ReadOnly); m_log.setDevice(&logFile); if (m_log.readNextStartElement()) { if (m_log.name() == QLatin1String("buildlog")) readBuildLog(); else m_log.raiseError(tr("The file '%1' is not a SBSv2 log file.").arg(file)); } } void SbsV2Parser::readBuildLog() { Q_ASSERT(m_log.isStartElement() && m_log.name() == QLatin1String("buildlog")); while (m_log.readNextStartElement()) { if (m_log.name() == QLatin1String("error")) readError(); else if (m_log.name() == QLatin1String("warning")) readWarning(); else if (m_log.name() == QLatin1String("recipe")) readRecipe(); else m_log.skipCurrentElement(); } } void SbsV2Parser::readError() { Q_ASSERT(m_log.isStartElement() && m_log.name() == QLatin1String("error")); QString error = m_log.readElementText(); addTask(Task(Task::Error, error, QString(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM)); } void SbsV2Parser::readWarning() { Q_ASSERT(m_log.isStartElement() && m_log.name() == QLatin1String("warning")); QString warning = m_log.readElementText(); addTask(Task(Task::Warning, warning, QString(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM)); } void SbsV2Parser::readRecipe() { Q_ASSERT(m_log.isStartElement() && m_log.name() == QLatin1String("recipe")); const QString name = m_log.attributes().value(QLatin1String("name")).toString(); m_currentSource = QDir(m_log.attributes().value("source").toString()).absolutePath(); m_currentTarget = QDir(m_log.attributes().value("target").toString()).absolutePath(); int returnCode = 0; QString outputText; QXmlStreamReader::TokenType tokenType = QXmlStreamReader::Invalid; while ((tokenType = m_log.readNext()) != QXmlStreamReader::Invalid) { if (tokenType == QXmlStreamReader::Characters) { outputText.append(m_log.text()); } else if (tokenType == QXmlStreamReader::StartElement) { if (m_log.name() == QLatin1String("status")) { if (m_log.attributes().value(QLatin1String("exit")) == QLatin1String("failed")) returnCode = m_log.attributes().value(QLatin1String("code")).toString().toInt(); } } else if (tokenType == QXmlStreamReader::EndElement) { if (m_log.name() == QLatin1String("recipe")) break; } } QStringList output = outputText.split(QChar('\n')); outputText.clear(); foreach (const QString &line, output) { if (line.isEmpty()) continue; if (line.startsWith(QChar('+'))) { outputText.append(tr("Running command: %1\n").arg(line.mid(2))); continue; } outputText.append(line); outputText.append(QChar('\n')); if (name == QLatin1String("compile") || name == QLatin1String("qmake_extra_pre_targetdep")) IOutputParser::stdError(line); } if (returnCode != 0) { //: %1 is the SBSv2 build recipe name, %2 the return code of the failed command QString description = tr("Recipe %1 failed with exit code %2.").arg(name).arg(returnCode); m_hub->addTask(Task(Task::Error, description, QString(), -1, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); m_hub->addTask(Task(Task::Unknown, outputText, QString(), -1, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); } } // Unit tests: #ifdef WITH_TESTS # include # include "qt4projectmanagerplugin.h" # include "projectexplorer/outputparser_test.h" using namespace Qt4ProjectManager::Internal; void Qt4ProjectManagerPlugin::testSbsV2OutputParsers_data() { QTest::addColumn("input"); QTest::addColumn("inputChannel"); QTest::addColumn("childStdOutLines"); QTest::addColumn("childStdErrLines"); QTest::addColumn >("tasks"); QTest::addColumn("outputLines"); QTest::newRow("eat stdout") << QString::fromLatin1(" Sometext") << OutputParserTester::STDOUT << QString() << QString() << QList() << QString(); QTest::newRow("eat stderr") << QString::fromLatin1(" Sometext") << OutputParserTester::STDERR << QString() << QString() << QList() << QString(); QTest::newRow("build log") << QString::fromLatin1("sbs: build log in X:/epoc32/build/Makefile.2010-08-10-15-25-52.log") << OutputParserTester::STDOUT << QString() << QString() << (QList() << ProjectExplorer::Task(Task::Unknown, QLatin1String("SBSv2 build log"), QLatin1String("X:/epoc32/build/Makefile.2010-08-10-15-25-52.log"), -1, QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM))) << QString(); } void Qt4ProjectManagerPlugin::testSbsV2OutputParsers() { OutputParserTester testbench; testbench.appendOutputParser(new SbsV2Parser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(QList, tasks); QFETCH(QString, childStdOutLines); QFETCH(QString, childStdErrLines); QFETCH(QString, outputLines); testbench.testParsing(input, inputChannel, tasks, childStdOutLines, childStdErrLines, outputLines); } #endif