/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, 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. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "sbsv2parser.h" #include #include #include #include #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 = 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