diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.cpp b/src/plugins/cmakeprojectmanager/cmakeparser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9242ee538bfaa93fe9e59fd5a95dbb13d95ada23 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeparser.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Axonian LLC. +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "cmakeparser.h" + +#include <projectexplorer/gnumakeparser.h> +#include <projectexplorer/projectexplorerconstants.h> + +using namespace CMakeProjectManager; +using namespace Internal; +using namespace ProjectExplorer; + +namespace { +const char * const COMMON_ERROR_PATTERN = "^CMake Error at (.*):([0-9]*) \\((.*)\\):"; +const char * const NEXT_SUBERROR_PATTERN = "^CMake Error in (.*):"; +} + +CMakeParser::CMakeParser() : + m_skippedFirstEmptyLine(false) +{ + m_commonError.setPattern(QLatin1String(COMMON_ERROR_PATTERN)); + m_commonError.setMinimal(true); + + m_nextSubError.setPattern(QLatin1String(NEXT_SUBERROR_PATTERN)); + m_nextSubError.setMinimal(true); + appendOutputParser(new GnuMakeParser()); +} + +void CMakeParser::stdError(const QString &line) +{ + QString trimmedLine = rightTrimmed(line); + if (trimmedLine.isEmpty() && !m_lastTask.isNull()) { + if (m_skippedFirstEmptyLine) { + doFlush(); + } else { + m_skippedFirstEmptyLine = true; + } + return; + } + if (m_skippedFirstEmptyLine) + m_skippedFirstEmptyLine= false; + + if (m_commonError.indexIn(trimmedLine) != -1) { + m_lastTask = Task(Task::Error, QString(), Utils::FileName::fromUserInput(m_commonError.cap(1)), + m_commonError.cap(2).toInt(), Core::Id(Constants::TASK_CATEGORY_COMPILE)); + return; + } else if (m_nextSubError.indexIn(trimmedLine) != -1) { + m_lastTask = Task(Task::Error, QString(), Utils::FileName::fromUserInput(m_nextSubError.cap(1)), -1, + Core::Id(Constants::TASK_CATEGORY_COMPILE)); + return; + } else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) { + if (!m_lastTask.description.isEmpty()) + m_lastTask.description.append(QLatin1Char(' ')); + m_lastTask.description.append(trimmedLine.trimmed()); + return; + } + + IOutputParser::stdError(line); +} + +void CMakeParser::doFlush() +{ + if (m_lastTask.isNull()) + return; + Task t = m_lastTask; + m_lastTask.clear(); + emit addTask(t); +} + +#ifdef WITH_TESTS +#include "cmakeprojectplugin.h" + +#include <projectexplorer/outputparser_test.h> + +#include <QTest> + +void CMakeProjectPlugin::testCMakeParser_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<OutputParserTester::Channel>("inputChannel"); + QTest::addColumn<QString>("childStdOutLines"); + QTest::addColumn<QString>("childStdErrLines"); + QTest::addColumn<QList<ProjectExplorer::Task> >("tasks"); + QTest::addColumn<QString>("outputLines"); + + const Core::Id categoryCompile = Core::Id(Constants::TASK_CATEGORY_COMPILE); + + // negative tests + QTest::newRow("pass-through stdout") + << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT + << QString::fromLatin1("Sometext\n") << QString() + << QList<ProjectExplorer::Task>() + << QString(); + QTest::newRow("pass-through stderr") + << QString::fromLatin1("Sometext") << OutputParserTester::STDERR + << QString() << QString::fromLatin1("Sometext\n") + << QList<ProjectExplorer::Task>() + << QString(); + + // positive tests + QTest::newRow("add custom target") + << QString::fromLatin1("CMake Error at src/1/app/CMakeLists.txt:70 (add_custom_target):\n" + " Cannot find source file:\n\n" + " unknownFile.qml\n\n" + " Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp\n" + " .hxx .in .txx\n\n\n" + "CMake Error in src/1/app/CMakeLists.txt:\n" + " Cannot find source file:\n\n" + " CMakeLists.txt2\n\n" + " Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp\n" + " .hxx .in .txx\n\n") + << OutputParserTester::STDERR + << QString() << QString() + << (QList<ProjectExplorer::Task>() + << Task(Task::Error, + QLatin1String("Cannot find source file: unknownFile.qml Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx"), + Utils::FileName::fromUserInput(QLatin1String("src/1/app/CMakeLists.txt")), 70, + categoryCompile) + << Task(Task::Error, + QLatin1String("Cannot find source file: CMakeLists.txt2 Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx"), + Utils::FileName::fromUserInput(QLatin1String("src/1/app/CMakeLists.txt")), -1, + categoryCompile)) + << QString(); + + QTest::newRow("add subdirectory") + << QString::fromLatin1("CMake Error at src/1/CMakeLists.txt:8 (add_subdirectory):\n" + " add_subdirectory given source \"app1\" which is not an existing directory.\n\n") + << OutputParserTester::STDERR + << QString() << QString() + << (QList<ProjectExplorer::Task>() + << Task(Task::Error, + QLatin1String("add_subdirectory given source \"app1\" which is not an existing directory."), + Utils::FileName::fromUserInput(QLatin1String("src/1/CMakeLists.txt")), 8, + categoryCompile)) + << QString(); + + QTest::newRow("unknown command") + << QString::fromLatin1("CMake Error at src/1/CMakeLists.txt:8 (i_am_wrong_command):\n" + " Unknown CMake command \"i_am_wrong_command\".\n\n") + << OutputParserTester::STDERR + << QString() << QString() + << (QList<ProjectExplorer::Task>() + << Task(Task::Error, + QLatin1String("Unknown CMake command \"i_am_wrong_command\"."), + Utils::FileName::fromUserInput(QLatin1String("src/1/CMakeLists.txt")), 8, + categoryCompile)) + << QString(); + + QTest::newRow("incorrect arguments") + << QString::fromLatin1("CMake Error at src/1/CMakeLists.txt:8 (message):\n" + " message called with incorrect number of arguments\n\n") + << OutputParserTester::STDERR + << QString() << QString() + << (QList<ProjectExplorer::Task>() + << Task(Task::Error, + QLatin1String("message called with incorrect number of arguments"), + Utils::FileName::fromUserInput(QLatin1String("src/1/CMakeLists.txt")), 8, + categoryCompile)) + << QString(); +} + +void CMakeProjectPlugin::testCMakeParser() +{ + OutputParserTester testbench; + testbench.appendOutputParser(new CMakeParser); + QFETCH(QString, input); + QFETCH(OutputParserTester::Channel, inputChannel); + QFETCH(QList<Task>, tasks); + QFETCH(QString, childStdOutLines); + QFETCH(QString, childStdErrLines); + QFETCH(QString, outputLines); + + testbench.testParsing(input, inputChannel, + tasks, childStdOutLines, childStdErrLines, + outputLines); +} + +#endif diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.h b/src/plugins/cmakeprojectmanager/cmakeparser.h new file mode 100644 index 0000000000000000000000000000000000000000..24b61f9863c68cfd1fc9646a0acddacdcec10778 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeparser.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Axonian LLC. +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CMAKEOUTPUTPARSER_H +#define CMAKEOUTPUTPARSER_H + +#include <projectexplorer/ioutputparser.h> +#include <projectexplorer/task.h> + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeParser : public ProjectExplorer::IOutputParser +{ + Q_OBJECT + +public: + explicit CMakeParser(); + void stdError(const QString &line); + +protected: + void doFlush(); + +private: + ProjectExplorer::Task m_lastTask; + QRegExp m_commonError; + QRegExp m_nextSubError; + bool m_skippedFirstEmptyLine; +}; + +} // namespace CMakeProjectManager +} // namespace Internal + +#endif // CMAKEOUTPUTPARSER_H diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro index 2df5d239a8c5ffde9be71248467655d2dc3e55c4..e58bd7878dc4ba24460956b05e1a9bce1add76dd 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -14,7 +14,8 @@ HEADERS = cmakeproject.h \ cmakehighlighter.h \ cmakelocatorfilter.h \ cmakefilecompletionassist.h \ - cmakevalidator.h + cmakevalidator.h \ + cmakeparser.h SOURCES = cmakeproject.cpp \ cmakeprojectplugin.cpp \ @@ -29,6 +30,8 @@ SOURCES = cmakeproject.cpp \ cmakehighlighter.cpp \ cmakelocatorfilter.cpp \ cmakefilecompletionassist.cpp \ - cmakevalidator.cpp + cmakevalidator.cpp \ + cmakeparser.cpp + RESOURCES += cmakeproject.qrc diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs index 22c32b4dcf8b2747706dc433a301ba3155aa7305..52cc38ac7d432e6b90a1217555ce452538d91826 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs @@ -34,6 +34,8 @@ QtcPlugin { "cmakelocatorfilter.h", "cmakeopenprojectwizard.cpp", "cmakeopenprojectwizard.h", + "cmakeparser.cpp", + "cmakeparser.h", "cmakeproject.cpp", "cmakeproject.h", "cmakeproject.qrc", diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h index 855ec1abd7b1f5c0eb4c03c8e332b298cc73fc87..f7a0e175534c9c476259bfad843e1b46e3ab73a0 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h @@ -50,6 +50,12 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage); void extensionsInitialized(); + +private slots: +#ifdef WITH_TESTS + void testCMakeParser_data(); + void testCMakeParser(); +#endif }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/makestep.cpp b/src/plugins/cmakeprojectmanager/makestep.cpp index c6be30d7005caeb0903f53fc24099281b9b3be01..9dad1172f6afd23d3f1b715531bd6ef52e2bda02 100644 --- a/src/plugins/cmakeprojectmanager/makestep.cpp +++ b/src/plugins/cmakeprojectmanager/makestep.cpp @@ -29,16 +29,16 @@ #include "makestep.h" +#include "cmakebuildconfiguration.h" +#include "cmakeparser.h" #include "cmakeprojectconstants.h" #include "cmakeproject.h" -#include "cmakebuildconfiguration.h" #include <projectexplorer/buildsteplist.h> #include <projectexplorer/deployconfiguration.h> -#include <projectexplorer/gnumakeparser.h> #include <projectexplorer/kitinformation.h> -#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectexplorer.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> @@ -197,7 +197,7 @@ bool MakeStep::init() pp->setArguments(arguments); pp->resolveAll(); - setOutputParser(new ProjectExplorer::GnuMakeParser()); + setOutputParser(new CMakeParser()); IOutputParser *parser = target()->kit()->createOutputParser(); if (parser) appendOutputParser(parser);