diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index 0e82ff92815a79dd3db224dba79376296f4c7be7..ca544dab18b811a73f99afb184ebb9270fc2c948 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -256,6 +256,9 @@ private slots: void testGnuMakeParserTaskMangling_data(); void testGnuMakeParserTaskMangling(); + void testXcodebuildParserParsing_data(); + void testXcodebuildParserParsing(); + void testMsvcOutputParsers_data(); void testMsvcOutputParsers(); diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 94e6d80b501908e49f04fbed01e6650feab358ec..a993fc79daf60b233468c37298285cc3c9f8c358 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -143,7 +143,8 @@ HEADERS += projectexplorer.h \ customparser.h \ customparserconfigdialog.h \ ipotentialkit.h \ - selectablefilesmodel.h + selectablefilesmodel.h \ + xcodebuildparser.h SOURCES += projectexplorer.cpp \ abi.cpp \ @@ -273,7 +274,8 @@ SOURCES += projectexplorer.cpp \ customparser.cpp \ customparserconfigdialog.cpp \ ipotentialkit.cpp \ - selectablefilesmodel.cpp + selectablefilesmodel.cpp \ + xcodebuildparser.cpp FORMS += processstep.ui \ editorsettingspropertiespage.ui \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 9ecac8b701f5569bfd9a85bd096a715b8167858e..884764ffc15a65299108647e0a13a7a49dd98b14 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -143,7 +143,8 @@ QtcPlugin { "toolchainmanager.cpp", "toolchainmanager.h", "toolchainoptionspage.cpp", "toolchainoptionspage.h", "unconfiguredprojectpanel.cpp", "unconfiguredprojectpanel.h", - "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h" + "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", + "xcodebuildparser.cpp", "xcodebuildparser.h" ] } diff --git a/src/plugins/projectexplorer/xcodebuildparser.cpp b/src/plugins/projectexplorer/xcodebuildparser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d6e280a95552792145a0c41b194409e933d928d --- /dev/null +++ b/src/plugins/projectexplorer/xcodebuildparser.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** 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 "xcodebuildparser.h" + +#include "projectexplorerconstants.h" +#include "task.h" + +#include <utils/qtcassert.h> + +#include <QCoreApplication> + +namespace ProjectExplorer { + +static const char failureRe[] = "\\*\\* BUILD FAILED \\*\\*$"; +static const char successRe[] = "\\*\\* BUILD SUCCEEDED \\*\\*$"; +static const char buildRe[] = "=== BUILD (AGGREGATE )?TARGET (.*) OF PROJECT (.*) WITH .* ===$"; + +XcodebuildParser::XcodebuildParser() : + m_fatalErrorCount(0), + m_xcodeBuildParserState(OutsideXcodebuild) +{ + setObjectName(QLatin1String("XcodeParser")); + m_failureRe.setPattern(QLatin1String(failureRe)); + QTC_CHECK(m_failureRe.isValid()); + m_successRe.setPattern(QLatin1String(successRe)); + QTC_CHECK(m_successRe.isValid()); + m_buildRe.setPattern(QLatin1String(buildRe)); + QTC_CHECK(m_buildRe.isValid()); +} + +bool XcodebuildParser::hasFatalErrors() const +{ + return (m_fatalErrorCount > 0) || IOutputParser::hasFatalErrors(); +} + +void XcodebuildParser::stdOutput(const QString &line) +{ + const QString lne = rightTrimmed(line); + if (m_buildRe.indexIn(lne) > -1) { + m_xcodeBuildParserState = InXcodebuild; + m_lastTarget = m_buildRe.cap(2); + m_lastProject = m_buildRe.cap(3); + return; + } + if (m_xcodeBuildParserState == InXcodebuild || m_xcodeBuildParserState == UnknownXcodebuildState) { + if (m_successRe.indexIn(lne) > -1) { + m_xcodeBuildParserState = OutsideXcodebuild; + return; + } + IOutputParser::stdError(line); + } else { + IOutputParser::stdOutput(line); + } +} + +void XcodebuildParser::stdError(const QString &line) +{ + const QString lne = rightTrimmed(line); + if (m_failureRe.indexIn(lne) > -1) { + ++m_fatalErrorCount; + m_xcodeBuildParserState = UnknownXcodebuildState; + // unfortunately the m_lastTarget, m_lastProject might not be in sync + Task task(Task::Error, + QCoreApplication::translate("ProjectExplorer::XcodebuildParser", + "Xcodebuild failed."), + Utils::FileName(), /* filename */ + -1, /* line */ + ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); + taskAdded(task); + return; + } + if (m_xcodeBuildParserState == OutsideXcodebuild) { // also forward if UnknownXcodebuildState ? + IOutputParser::stdError(line); + return; + } +} + +} // namespace ProjectExplorer + +// Unit tests: + +#ifdef WITH_TESTS +# include <QTest> + +# include "outputparser_test.h" +# include "projectexplorer.h" + +# include "metatypedeclarations.h" + +using namespace ProjectExplorer; + +Q_DECLARE_METATYPE(ProjectExplorer::XcodebuildParser::XcodebuildStatus) + +XcodebuildParserTester::XcodebuildParserTester(XcodebuildParser *p, QObject *parent) : + QObject(parent), + parser(p) +{ } + +void ProjectExplorerPlugin::testXcodebuildParserParsing_data() +{ + QTest::addColumn<ProjectExplorer::XcodebuildParser::XcodebuildStatus>("initialStatus"); + QTest::addColumn<QString>("input"); + QTest::addColumn<OutputParserTester::Channel>("inputChannel"); + QTest::addColumn<QString>("childStdOutLines"); + QTest::addColumn<QString>("childStdErrLines"); + QTest::addColumn<QList<Task> >("tasks"); + QTest::addColumn<QString>("outputLines"); + QTest::addColumn<ProjectExplorer::XcodebuildParser::XcodebuildStatus>("finalStatus"); + + QTest::newRow("outside pass-through stdout") + << XcodebuildParser::OutsideXcodebuild + << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT + << QString::fromLatin1("Sometext\n") << QString() + << QList<Task>() + << QString() + << XcodebuildParser::OutsideXcodebuild; + QTest::newRow("outside pass-through stderr") + << XcodebuildParser::OutsideXcodebuild + << QString::fromLatin1("Sometext") << OutputParserTester::STDERR + << QString() << QString::fromLatin1("Sometext\n") + << QList<Task>() + << QString() + << XcodebuildParser::OutsideXcodebuild; + QTest::newRow("inside pass stdout to stderr") + << XcodebuildParser::InXcodebuild + << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT + << QString() << QString::fromLatin1("Sometext\n") + << QList<Task>() + << QString() + << XcodebuildParser::InXcodebuild; + QTest::newRow("inside ignore stderr") + << XcodebuildParser::InXcodebuild + << QString::fromLatin1("Sometext") << OutputParserTester::STDERR + << QString() << QString() + << QList<Task>() + << QString() + << XcodebuildParser::InXcodebuild; + QTest::newRow("unknown pass stdout to stderr") + << XcodebuildParser::UnknownXcodebuildState + << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT + << QString() << QString::fromLatin1("Sometext\n") + << QList<Task>() + << QString() + << XcodebuildParser::UnknownXcodebuildState; + QTest::newRow("unknown ignore stderr (change?)") + << XcodebuildParser::UnknownXcodebuildState + << QString::fromLatin1("Sometext") << OutputParserTester::STDERR + << QString() << QString() + << QList<Task>() + << QString() + << XcodebuildParser::UnknownXcodebuildState; + QTest::newRow("switch outside->in->outside") + << XcodebuildParser::OutsideXcodebuild + << QString::fromLatin1("outside\n" + "=== BUILD AGGREGATE TARGET Qt Preprocess OF PROJECT testQQ WITH THE DEFAULT CONFIGURATION (Debug) ===\n" + "in xcodebuild\n" + "=== BUILD TARGET testQQ OF PROJECT testQQ WITH THE DEFAULT CONFIGURATION (Debug) ===\n" + "in xcodebuild2\n" + "** BUILD SUCCEEDED **\n" + "outside2") + << OutputParserTester::STDOUT + << QString::fromLatin1("outside\noutside2\n") << QString::fromLatin1("in xcodebuild\nin xcodebuild2\n") + << QList<Task>() + << QString() + << XcodebuildParser::OutsideXcodebuild; + QTest::newRow("switch Unknown->in->outside") + << XcodebuildParser::UnknownXcodebuildState + << QString::fromLatin1("unknown\n" + "=== BUILD TARGET testQQ OF PROJECT testQQ WITH THE DEFAULT CONFIGURATION (Debug) ===\n" + "in xcodebuild\n" + "** BUILD SUCCEEDED **\n" + "outside") + << OutputParserTester::STDOUT + << QString::fromLatin1("outside\n") << QString::fromLatin1("unknown\nin xcodebuild\n") + << QList<Task>() + << QString() + << XcodebuildParser::OutsideXcodebuild; + QTest::newRow("switch in->unknown") + << XcodebuildParser::InXcodebuild + << QString::fromLatin1("insideErr\n" + "** BUILD FAILED **\n" + "unknownErr") + << OutputParserTester::STDERR + << QString() << QString() + << (QList<ProjectExplorer::Task>() + << ProjectExplorer::Task( + Task::Error, + QCoreApplication::translate("ProjectExplorer::XcodebuildParser", + "Xcodebuild failed."), + Utils::FileName(), /* filename */ + -1, /* line */ + ProjectExplorer::Constants::TASK_CATEGORY_COMPILE)) + << QString() + << XcodebuildParser::UnknownXcodebuildState; + QTest::newRow("switch out->unknown") + << XcodebuildParser::OutsideXcodebuild + << QString::fromLatin1("outErr\n" + "** BUILD FAILED **\n" + "unknownErr") + << OutputParserTester::STDERR + << QString() << QString::fromLatin1("outErr\n") + << (QList<ProjectExplorer::Task>() + << ProjectExplorer::Task( + Task::Error, + QCoreApplication::translate("ProjectExplorer::XcodebuildParser", + "Xcodebuild failed."), + Utils::FileName(), /* filename */ + -1, /* line */ + ProjectExplorer::Constants::TASK_CATEGORY_COMPILE)) + << QString() + << XcodebuildParser::UnknownXcodebuildState; +} + +void ProjectExplorerPlugin::testXcodebuildParserParsing() +{ + OutputParserTester testbench; + XcodebuildParser *childParser = new XcodebuildParser; + XcodebuildParserTester *tester = new XcodebuildParserTester(childParser); + + testbench.appendOutputParser(childParser); + QFETCH(ProjectExplorer::XcodebuildParser::XcodebuildStatus, initialStatus); + QFETCH(QString, input); + QFETCH(OutputParserTester::Channel, inputChannel); + QFETCH(QString, childStdOutLines); + QFETCH(QString, childStdErrLines); + QFETCH(QList<Task>, tasks); + QFETCH(QString, outputLines); + QFETCH(ProjectExplorer::XcodebuildParser::XcodebuildStatus, finalStatus); + + childParser->m_xcodeBuildParserState = initialStatus; + testbench.testParsing(input, inputChannel, + tasks, childStdOutLines, childStdErrLines, + outputLines); + QCOMPARE(childParser->m_xcodeBuildParserState, finalStatus); + + delete tester; +} + +#endif + diff --git a/src/plugins/projectexplorer/xcodebuildparser.h b/src/plugins/projectexplorer/xcodebuildparser.h new file mode 100644 index 0000000000000000000000000000000000000000..53da4736e7910a5e3c492a70a61e6d807e2fbd1d --- /dev/null +++ b/src/plugins/projectexplorer/xcodebuildparser.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** 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 IOSXCODEPARSER_H +#define IOSXCODEPARSER_H + +#include "projectexplorer_export.h" +#include "ioutputparser.h" +#include "devicesupport/idevice.h" + +#include <QRegExp> +#include <QStringList> + +namespace ProjectExplorer { + +class PROJECTEXPLORER_EXPORT XcodebuildParser : public IOutputParser +{ + Q_OBJECT +public: + enum XcodebuildStatus { + InXcodebuild, + OutsideXcodebuild, + UnknownXcodebuildState + }; + + XcodebuildParser(); + + void stdOutput(const QString &line); + void stdError(const QString &line); + bool hasFatalErrors() const; +private: + int m_fatalErrorCount; + QRegExp m_failureRe; + QRegExp m_successRe; + QRegExp m_buildRe; + XcodebuildStatus m_xcodeBuildParserState; + QString m_lastTarget; + QString m_lastProject; +#if defined WITH_TESTS + friend class ProjectExplorerPlugin; +#endif +}; + +#if defined WITH_TESTS +class XcodebuildParserTester : public QObject +{ + Q_OBJECT +public: + explicit XcodebuildParserTester(XcodebuildParser *parser, QObject *parent = 0); + + XcodebuildParser *parser; +}; +#endif + +} // namespace ProjectExplorer + +#endif // IOSXCODEPARSER_H diff --git a/src/plugins/qmakeprojectmanager/makestep.cpp b/src/plugins/qmakeprojectmanager/makestep.cpp index d1b39bf2ed0aea173866feeefb570a1056669c30..067fdf53e81dcf3bb38e91d71cf93ba006524cbe 100644 --- a/src/plugins/qmakeprojectmanager/makestep.cpp +++ b/src/plugins/qmakeprojectmanager/makestep.cpp @@ -42,6 +42,7 @@ #include <projectexplorer/gnumakeparser.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/kitinformation.h> +#include <projectexplorer/xcodebuildparser.h> #include <utils/qtcprocess.h> #include <QDir> @@ -255,6 +256,8 @@ bool MakeStep::init() pp->resolveAll(); setOutputParser(new ProjectExplorer::GnuMakeParser()); + if (tc && tc->targetAbi().os() == Abi::MacOS) + appendOutputParser(new XcodebuildParser); IOutputParser *parser = target()->kit()->createOutputParser(); if (parser) appendOutputParser(parser);