diff options
author | Joerg Bornemann <joerg.bornemann@nokia.com> | 2012-02-15 15:56:22 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2012-02-15 21:57:41 +0100 |
commit | 5672fa8fd3bd6b09125d9e143a7bb277cea2e87f (patch) | |
tree | c945b6ad76bd8c8f6af8b520cf3ce72ef298d4b5 /src | |
download | qbs-5672fa8fd3bd6b09125d9e143a7bb277cea2e87f.tar.gz |
Long live qbs!
Change-Id: I412793e868919a9ac99611616f292e7047f1ebcf
Diffstat (limited to 'src')
143 files changed, 33333 insertions, 0 deletions
diff --git a/src/app/app.pro b/src/app/app.pro new file mode 100644 index 000000000..ae37990db --- /dev/null +++ b/src/app/app.pro @@ -0,0 +1,6 @@ +TEMPLATE = subdirs +SUBDIRS =\ + qbs\ + graph \ + platforms \ + diff --git a/src/app/graph/graph.cpp b/src/app/graph/graph.cpp new file mode 100644 index 000000000..f93ebc057 --- /dev/null +++ b/src/app/graph/graph.cpp @@ -0,0 +1,465 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include <tools/logger.h> +#include <tools/options.h> +#include <buildgraph/artifact.h> +#include <buildgraph/buildgraph.h> +#include <tools/fileinfo.h> +#include <Qbs/oldsourceproject.h> + +#include <cassert> + +#include <QCoreApplication> +#include <QProcess> +#include <QDir> +#include <QFileInfo> +#include <QDebug> +#include <cstdlib> +#include <cstdio> +#include <cassert> +#include <cmath> + +#include <QGraphicsView> +#include <QWheelEvent> +#include <QGraphicsScene> +#include <QGraphicsItem> +#include <QApplication> +#include <QFile> +#include <QTextStream> +#include <QDebug> +#include <QProcess> +#include <QFont> +#include <QStyle> +#include <QLineEdit> +#include <QGridLayout> +#include <QAbstractListModel> +#include <QTimer> + +#include <qtconcurrent/runextensions.h> +#include <QtCore/QFuture> + +class QGV : public QGraphicsView +{ +public: + QGV(QGraphicsScene *scene) : QGraphicsView(scene) + { + setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + setDragMode(ScrollHandDrag); + scaleView(0.5); + } + +protected: + void wheelEvent ( QWheelEvent * event ) + { + scaleView(pow((double)2, event->delta() / 240.0)); + } + + void scaleView(qreal scaleFactor) + { + qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width(); + if (factor < 0.07 || factor > 100) + return; + + scale(scaleFactor, scaleFactor); + } +}; + +struct ArtifactC +{ + qbs::Artifact *artifact; + QString text; + QString label; + int grade; + QRectF rectangle; + int row; + int col; + QGraphicsRectItem *graphicsRectItem; +}; + +QHash<qbs::Artifact *, ArtifactC *> cartifacts; +const qreal SCALE = 100.0f; +const qreal VSPACE = 200.0f; + +class MainWaffl : public QWidget +{ +Q_OBJECT +public: + MainWaffl(QGraphicsScene *scene) + : grid(this) + , qgv(scene) + { + grid.addWidget(&qgv, 0,0,1,1); + grid.addWidget(&searchBox, 1,0,1,1); + + connect(&searchBox, SIGNAL(textChanged ( const QString &)), + this, SLOT(startSearchTimer())); + connect(&searchTimer, SIGNAL(timeout()), + this, SLOT(search())); + l = cartifacts.values(); + qgv.centerOn(0,0); + } +private slots: + void startSearchTimer() + { + searchTimer.start(100); + } + void search() + { + searchTimer.stop(); + foreach (ArtifactC *nc, currentHis) { + nc->graphicsRectItem->setBrush(QColor(Qt::white)); + } + currentHis.clear(); + + QString t = searchBox.text(); + if (t.isEmpty()) + return; + foreach (ArtifactC *nc, l) { + if (nc->label.contains(t)) { + currentHis.append(nc); + nc->graphicsRectItem->setBrush(QColor(Qt::red)); + qgv.ensureVisible(nc->graphicsRectItem); + } + } + } +private: + QList<ArtifactC *> currentHis; + QTimer searchTimer; + QGridLayout grid; + QLineEdit searchBox; + QGV qgv; + QList<ArtifactC *> l; +}; + +qbs::CommandLineOptions options; + +void targetToScene(QGraphicsScene *scene, qbs::BuildProduct *t); +int main(int argc, char *argv[]) +{ +//#ifndef Q_OS_WIN +// QApplication::setGraphicsSystem("opengl"); +//#endif + QApplication app(argc, argv); + app.setApplicationName("qbs"); + app.setOrganizationName("Nokia"); + app.setOrganizationDomain("qt.nokia.com"); + + // read commandline + QStringList arguments = app.arguments(); + arguments.removeFirst(); + + if (!options.readCommandLineArguments(arguments)) { + qbs::CommandLineOptions::printHelp(); + return 1; + } + if (options.isHelpSet()) { + qbs::CommandLineOptions::printHelp(); + return 0; + } + + qbs::SourceProject sourceProject(options.settings()); + sourceProject.setSearchPaths(options.searchPaths()); + sourceProject.loadPlugins(options.pluginPaths()); + QFuture<bool> loadProjectFuture = QtConcurrent::run(&qbs::SourceProject::loadProject, + &sourceProject, + options.projectFileName(), + options.buildConfigurations()); + loadProjectFuture.waitForFinished(); + foreach (const qbs::Error &error, sourceProject.errors()) { + qbsError() << error.toString(); + return 4; + } + + QGraphicsScene scene; + foreach (qbs::BuildProject::Ptr t, sourceProject.buildProjects()) { + foreach (qbs::BuildProduct::Ptr p, t->buildProducts()) { + targetToScene(&scene, p.data()); + } + } + QRectF sr = scene.sceneRect(); + sr.adjust(-50, -50, 50, 50); + scene.setSceneRect(sr); + MainWaffl waffl(&scene); + waffl.showMaximized(); + + return app.exec(); +} + + +QMap<int, QMap<int, ArtifactC *> > table; + +int startcol = 0; +int endcol = 0; +void step1(qbs::Artifact *artifact, int l) +{ + if (!cartifacts.contains(artifact)) { + ArtifactC *nc = new ArtifactC; + cartifacts.insert(artifact, nc); + nc->text = artifact->fileName; + nc->label = qbs::FileInfo::fileName(artifact->fileName); + nc->artifact = artifact; + QMap<int, ArtifactC *> & line = table[l]; + int col = startcol; + while (line.contains(col)) { + col++; + } + table[l][col] = nc; + endcol = qMax(endcol, col); + foreach (qbs::Artifact *n2, artifact->children) { + step1(n2, l + 1); + } + startcol = endcol + 1; + } +} + + +QList<ArtifactC *> ncl; +QMap<int, int> layerx; +QMap<int, int> layerC; +QMultiMap<ArtifactC *, ArtifactC *> edges; + +void targetToScene(QGraphicsScene *scene, qbs::BuildProduct *t) +{ + QFont sceneFont = scene->font(); + sceneFont.setPointSize(14); + scene->setFont(sceneFont); + + QPen pen = QColor(Qt::black); + QBrush brush = QColor(Qt::white); + + foreach (qbs::Artifact *targetArtifact, t->targetArtifacts) + step1(targetArtifact, 1); + startcol = endcol + 1; + QFontMetricsF metrics(sceneFont); + + // draw artifacts + pen.setWidth(1); + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::MiterJoin); + for (QMap<int, QMap<int, ArtifactC *> >::const_iterator i = table.constBegin(); i != table.constEnd(); i++) { + int row = i.key(); + QMap<int, ArtifactC *> line = i.value(); + for (QMap<int, ArtifactC *>::const_iterator j = line.constBegin(); j != line.constEnd(); j++) { + int col = j.key(); + ArtifactC *nc = j.value(); + QRectF textRect = metrics.boundingRect(nc->label); + nc->row = row; + nc->col = col; + int x = row * (400 + VSPACE); + int y = col * 200; + nc->rectangle = textRect; + nc->rectangle.setWidth(400); + nc->rectangle.moveCenter(QPointF(x , y)); + + QGraphicsRectItem *item = scene->addRect(nc->rectangle, pen, brush); + item->setToolTip(nc->text); + nc->graphicsRectItem = item; + + QGraphicsSimpleTextItem *slabel = scene->addSimpleText(nc->label); + slabel->setBrush(Qt::black); + slabel->setFont(sceneFont); + + QRect textrect = QStyle::alignedRect (Qt::LeftToRight, + Qt::AlignCenter, + textRect.size().toSize(), + nc->rectangle.toRect()); + slabel->setPos(textrect.topLeft()); + + // insert edges + foreach (qbs::Artifact *c, nc->artifact->children) { + edges.insert(nc, cartifacts[c]); + } + } + } + + // draw edges + pen.setWidth(3); + + for (QMap<ArtifactC *, ArtifactC *>::const_iterator i = edges.constBegin(); + i != edges.constEnd(); + i++) { + ArtifactC * k = i.key(); + ArtifactC * v = i.value(); + assert(v); + QPainterPath path; + + QPointF fromP; + QPointF toP; + + + if (k->row < v->row) { + fromP = QPointF(k->rectangle.x() + k->rectangle.width(), k->rectangle.y() + k->rectangle.height()/2); + toP = QPointF(v->rectangle.x(), v->rectangle.y() + v->rectangle.height()/2); + } else { + fromP = QPointF(k->rectangle.x() + k->rectangle.width(), k->rectangle.y() + k->rectangle.height()/2); + toP = QPointF(v->rectangle.x(), v->rectangle.y() + v->rectangle.height()/2); + } + /* + } else if (k->row > v->row) { + fromP = QPointF(k->rc.x() + k->rc.width()/2, k->rc.y()); + toP = QPointF(v->rc.x() + v->rc.width()/2, v->rc.y() + v->rc.height()); + } else if (k->row < v->row) { + fromP = QPointF(k->rc.x() + k->rc.width()/2, k->rc.y() + v->rc.height()); + toP = QPointF(v->rc.x() + v->rc.width()/2, v->rc.y() ); + } + */ + + path.moveTo(fromP); + + QPointF c1 = fromP; + c1.setX(c1.x() + VSPACE/2); + QPointF c2 = toP; + c2.setX(c2.x() - VSPACE/2); + + path.cubicTo(c1, c2, toP); + + scene->addPath(path)->setPen(pen); + + QPolygonF poly; + poly.append(QPointF(toP.x() - 4, toP.y())); + poly.append(QPointF(toP.x() + 4, toP.y())); + poly.append(QPointF(toP.x(), toP.y() - 12)); + + QGraphicsItem *arrow = scene->addPolygon(poly, pen, QBrush(Qt::black)); + arrow->setTransformOriginPoint(toP.x(), toP.y()); + arrow->setRotation(-path.angleAtPercent(1) + 90); + } + + + + +} + + +#if 0 + if (directive == QLatin1String("graph")) { + qreal scale, width, height; + in >> scale >> width >> height; + scene->setSceneRect(0, 0, width * SCALE, height * SCALE); + + } else if (directive == QLatin1String("artifact")) { + QString name, label, style, shape, color, fillcolor; + qreal x, y, width, height; + in >> name >> x >> y >> width >> height; + + unsigned char labelReadState = 0; + while (!in.atEnd() && labelReadState != 2) { + QString s = in.read(1); + switch (labelReadState) { + case 0: + if (s.at(0) == QLatin1Char('"')) + labelReadState = 1; + break; + case 1: + if (s == QLatin1String("\"")) { + labelReadState = 2; + } else { + label.append(s); + } + break; + } + } + in >> style >> shape >> color >> fillcolor; + + label.replace(QLatin1String("\\n"), QLatin1String("\n")); + QPen pen = QColor(color); + QBrush brush = QColor(fillcolor); + + QRectF rc; + rc.setSize(QSizeF(width * SCALE, height * SCALE)); + rc.moveCenter(QPointF(x * SCALE, toY(y * SCALE))); + + QGraphicsItem *item = 0; + if (shape == QLatin1String("ellipse")) + item = scene->addEllipse(rc, pen, brush); + else + item = scene->addRect(rc, pen, brush); + item->setToolTip(label); + + QGraphicsSimpleTextItem *slabel = scene->addSimpleText(label); + slabel->setBrush(Qt::black); + slabel->setFont(sceneFont); + + QRect textrect = QStyle::alignedRect (Qt::LeftToRight, + Qt::AlignCenter, + slabel->boundingRect().size().toSize(), + rc.toRect()); + slabel->setPos(textrect.topLeft()); + + } else if (directive == QLatin1String("edge")) { + QString tail, head; + int n; + QString color; + + in >> tail >> head >> n; + color = line.split(' ').last(); + + QVector<QPointF> points; + for (int i = 0; i < n; ++i) { + qreal x, y; + in >> x >> y; + points.append(QPointF(x * SCALE, toY(y * SCALE))); + } + + QPainterPath path = fromControlPoints(points); + + QPen pen; + pen.setWidth(2); + if (color != "black") { + pen = QPen(QColor(0x33,0x33,0xff)); + pen.setWidth(1); + } + + scene->addPath(path)->setPen(pen); + + QPointF endPt = points.last(); + + QPolygonF poly; + poly.append(QPointF(endPt.x() - 4, endPt.y())); + poly.append(QPointF(endPt.x() + 4, endPt.y())); + poly.append(QPointF(endPt.x(), endPt.y() - 12)); + + QGraphicsItem *arrow = scene->addPolygon(poly, pen, QBrush(color + == "black"? Qt::black : QColor(0x33,0x33,0xff))); + arrow->setTransformOriginPoint(endPt.x(), endPt.y()); + arrow->setRotation(-path.angleAtPercent(1) + 90); + +#endif + + +#include "graph.moc" diff --git a/src/app/graph/graph.pro b/src/app/graph/graph.pro new file mode 100644 index 000000000..49d8f1e2a --- /dev/null +++ b/src/app/graph/graph.pro @@ -0,0 +1,12 @@ +QT = core script gui + +TEMPLATE = app +TARGET = qbs-graph +DESTDIR = ../../../bin/ + +CONFIG += console +CONFIG -= app_bundle + +SOURCES += graph.cpp + +include(../../lib/use.pri) diff --git a/src/app/platforms/main.cpp b/src/app/platforms/main.cpp new file mode 100644 index 000000000..dbb22a986 --- /dev/null +++ b/src/app/platforms/main.cpp @@ -0,0 +1,299 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include <QtCore/QCoreApplication> +#include <QtCore/QProcess> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QtCore/QTextStream> +#include <QtCore/QSettings> + +#include <tools/platform.h> +#include <tools/settings.h> + +#ifdef Q_OS_UNIX +#include <iostream> +#include <termios.h> +#endif + +#ifdef Q_OS_WIN +#include <qt_windows.h> +#include <Shlobj.h> +#endif + +void showUsage() +{ + QTextStream s(stderr); + s << "platform [action]\n" + << "actions:\n" + << " s|shell <name> -- open a shell, setting up the named platform\n" + << " ls|list -- list available platforms\n" + << " mv|rename <from> <to> -- rename a platform\n" + << " rm|remove <name> -- irrevocably remove the given target\n" + << " config <name> [<key>] [<value>] -- show or change configuration\n" + << " probe -- probe the current environment\n" + << " and construct qbs platforms for each compiler found\n" + ; +} + +static int ask(const QString &msg, const QString &choices); +static QString prompt(const QString &msg); + +int probe (const QString &settingsPath, + QHash<QString, qbs::Platform*> &platforms, + int (* ask)(const QString &msg, const QString &choices), + QString ( *prompt)(const QString &msg) + ); + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QTextStream qstdout(stdout); + + qbs::Settings::Ptr settings = qbs::Settings::create(); + QString defaultPlatform = settings->value("defaults/platform").toString(); + +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + QString localSettingsPath = QDir::homePath() + "/.config/Nokia/qbs/platforms/"; +#elif defined(Q_OS_WIN) + QString localSettingsPath; + wchar_t wszPath[MAX_PATH]; + if (SHGetSpecialFolderPath(NULL, wszPath, CSIDL_APPDATA, TRUE)) + localSettingsPath = QString::fromUtf16(wszPath) + "/qbs/platforms"; +#else +#error port me! +#endif + QDir().mkpath(localSettingsPath); + + enum Action { + ListPlatform, + ProbePlatform, + RenamePlatform, + RemovePlatform, + ShellPlatform, + ConfigPlatform + }; + + Action action = ListPlatform; + + QStringList arguments = app.arguments(); + arguments.takeFirst(); + if (arguments.count()) { + QString cmd = arguments.takeFirst(); + if (cmd == "probe") { + action = ProbePlatform; + } else if (cmd == "rename" || cmd == "mv") { + action = RenamePlatform; + } else if (cmd == "rm" || cmd == "remove") { + action = RemovePlatform; + } else if (cmd == "shell" || cmd == "s") { + action = ShellPlatform; + } else if (cmd == "config") { + action = ConfigPlatform; + } else if (cmd == "list" || cmd == "ls") { + action = ListPlatform; + } else { + showUsage(); + return 3; + } + } + + QHash<QString, qbs::Platform*> targets; + QDirIterator i(localSettingsPath, QDir::Dirs | QDir::NoDotAndDotDot); + while (i.hasNext()) { + i.next(); + qbs::Platform *t = new qbs::Platform(i.fileName(), i.filePath()); + targets.insert(t->name, t); + } + + if (action == ListPlatform) { + qstdout << "Platforms:\n"; + foreach (qbs::Platform *t, targets.values()) { + qstdout << "\t- " << t->name; + if (t->name == defaultPlatform) + qstdout << " (default)"; + qstdout << " "<< t->settings.value("target-triplet").toString() << "\n"; + } + } else if (action == RenamePlatform) { + if (arguments.count() < 2) { + showUsage(); + return 3; + } + QString from = arguments.takeFirst(); + if (!targets.contains(from)) { + qDebug("cannot rename: no such target: %s", qPrintable(from)); + return 5; + } + QString to = arguments.takeFirst(); + if (targets.contains(to)) { + qDebug("cannot rename: already exists: %s", qPrintable(to)); + return 5; + } + if (!QFile(localSettingsPath + from).rename(localSettingsPath + to)) { + qDebug("file error moving %s to %s", + qPrintable(localSettingsPath + from), + qPrintable(localSettingsPath + to) + ); + return 5; + } + targets.insert(to, targets.take(from)); + } else if (action == RemovePlatform) { + if (arguments.count() < 1) { + showUsage(); + return 3; + } + QString targetName = arguments.takeFirst(); + if (!targets.contains(targetName)) { + qDebug("cannot remove: no such target: %s", qPrintable(targetName)); + return 5; + } + QDirIterator i1(localSettingsPath + targetName, + QDir::Files | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden, + QDirIterator::Subdirectories); + while (i1.hasNext()) { + i1.next(); + QFile(i1.filePath()).remove(); + } + QDirIterator i2(localSettingsPath + targetName, + QDir::Dirs| QDir::NoDotAndDotDot | QDir::System | QDir::Hidden, + QDirIterator::Subdirectories); + while (i2.hasNext()) { + i2.next(); + QDir().rmdir(i2.filePath()); + } + QDir().rmdir(localSettingsPath + targetName); + delete targets.take(targetName); + } else if (action == ConfigPlatform) { + if (arguments.count() < 1) { + showUsage(); + return 3; + } + QString targetName= arguments.takeFirst(); + if (!targets.contains(targetName)) { + qDebug("no such target: %s", qPrintable(targetName)); + return 5; + } + qbs::Platform *p = targets.value(targetName); + if (arguments.count()) { + QString key = arguments.takeFirst(); + if (arguments.count()) { + QString value = arguments.takeFirst(); + p->settings.setValue(key, value); + } + if (!p->settings.contains(key)) { + qDebug("no such configuration key: %s", qPrintable(key)); + return 7; + } + qstdout << p->settings.value(key).toString() << "\n"; + } else { + foreach (const QString &key, p->settings.allKeys()) { + qstdout << key << "=" << p->settings.value(key).toString() << "\n"; + } + } + + } else if (action == ProbePlatform) { + bool firstRun = targets.isEmpty(); + probe(localSettingsPath, targets, ask, prompt); + if (firstRun && !targets.isEmpty()) { + settings->setValue(qbs::Settings::Global, "defaults/platform", targets.values().at(0)->name); + } + } + return 0; +} + + +static int ask(const QString &msg, const QString &choices) +{ +#ifdef Q_OS_UNIX + termios stored_settings; + tcgetattr(0, &stored_settings); + termios new_settings = stored_settings; + new_settings.c_lflag &= (~ICANON); + new_settings.c_lflag &= (~ECHO); // don't echo the character + // apply the new settings + tcsetattr(0, TCSANOW, &new_settings); +#endif + + setvbuf ( stdin , NULL , _IONBF , 0 ); + + + QTextStream qstdout(stdout); + qstdout << msg << " ("; + + QHash<QChar, int> cs; + + for (int i = 0; i < choices.count(); i++) { + QChar c = choices.at(i); + cs.insert(c.toLower(), i); + if (i == 0) + qstdout << c.toUpper(); + else + qstdout << c.toLower(); + if (i != choices.count() -1) + qstdout << "/"; + } + qstdout << ") " << flush; + + while (true) { + QChar i = QChar(getc(stdin)).toLower(); + if (i == '\n' || i == '\r') { +#ifdef Q_OS_UNIX + tcsetattr(0, TCSANOW, &stored_settings); +#endif + qstdout << choices.at(0).toUpper() << "\n" << flush; + return 0; + } + if (cs.contains(i)) { +#ifdef Q_OS_UNIX + tcsetattr(0, TCSANOW, &stored_settings); +#endif + qstdout << i << "\n" << flush; + return cs.value(i); + } + } +} + +static QString prompt(const QString &msg) +{ + QTextStream qstdout(stdout); + qstdout << msg << " "<< flush; + QTextStream qstdin(stdin); + QString s; + qstdin >> s; + return s; +} diff --git a/src/app/platforms/platforms.pro b/src/app/platforms/platforms.pro new file mode 100644 index 000000000..7fa483cac --- /dev/null +++ b/src/app/platforms/platforms.pro @@ -0,0 +1,12 @@ +QT = core script gui + +TEMPLATE = app +TARGET = qbs-platforms +DESTDIR = ../../../bin/ + +CONFIG += console +CONFIG -= app_bundle + +SOURCES += main.cpp probe.cpp + +include(../../lib/use.pri) diff --git a/src/app/platforms/probe.cpp b/src/app/platforms/probe.cpp new file mode 100644 index 000000000..efd0a69e7 --- /dev/null +++ b/src/app/platforms/probe.cpp @@ -0,0 +1,393 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include <QtCore/QCoreApplication> +#include <QtCore/QProcess> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QtCore/QTextStream> +#include <QtCore/QSettings> + +#include <tools/platform.h> + +using namespace qbs; + +static QString searchPath(const QString &path, const QString &me) +{ + //TODO: use native seperator + foreach (const QString &ppath, path.split(":")) { + if (QFileInfo(ppath + "/" + me).exists()) { + return QDir::cleanPath(ppath + "/" + me); + } + } + return QString(); +} + +static QString qsystem(const QString &exe, const QStringList &args = QStringList()) +{ + QProcess p; + p.setProcessChannelMode(QProcess::MergedChannels); + p.start(exe, args); + p.waitForStarted(); + p.waitForFinished(); + return QString::fromLocal8Bit(p.readAll()); +} + +static int specific_probe(const QString &settingsPath, + QHash<QString, Platform*> &platforms, + int (* ask)(const QString &msg, const QString &choices), + QString ( *prompt)(const QString &msg), + QString cc, + bool printComfortingMessage = false + ) +{ + QTextStream qstdout(stdout); + + QString toolchainType; + if(cc.contains("clang")) + toolchainType = "clang"; + else if (cc.contains("gcc")) + toolchainType = "gcc"; + + QString path = QString::fromLocal8Bit(qgetenv("PATH")); + QString cxx = QString::fromLocal8Bit(qgetenv("CXX")); + QString ld = QString::fromLocal8Bit(qgetenv("LD")); + QString cflags = QString::fromLocal8Bit(qgetenv("CFLAGS")); + QString cxxflags = QString::fromLocal8Bit(qgetenv("CXXFLAGS")); + QString ldflags = QString::fromLocal8Bit(qgetenv("LDFLAGS")); + QString cross = QString::fromLocal8Bit(qgetenv("CROSS_COMPILE")); + QString arch = QString::fromLocal8Bit(qgetenv("ARCH")); + + QString pathToGcc; + QString architecture; + QString endianness; + + QString name; + QString sysroot; + + bool isACrossCompiler = false; + + QString uname = qsystem("uname", QStringList() << "-m").simplified(); + + if (arch.isEmpty()) + arch = uname; + +#ifdef Q_OS_MAC + // HACK: "uname -m" reports "i386" but "gcc -dumpmachine" reports "i686" on MacOS. + if (arch == "i386") + arch = "i686"; +#endif + + if (ld.isEmpty()) + ld = "ld"; + if (cxx.isEmpty()) { + if (toolchainType == "gcc") + cxx = "g++"; + else if(toolchainType == "clang") + cxx = "clang++"; + } + if(!cross.isEmpty() && !cc.startsWith("/")) { + pathToGcc = searchPath(path, cross + cc); + if (QFileInfo(pathToGcc).exists()) { + if (!cc.contains(cross)) + cc.prepend(cross); + if (!cxx.contains(cross)) + cxx.prepend(cross); + if (!ld.contains(cross)) + ld.prepend(cross); + } + } + if (cc.startsWith("/")) + pathToGcc = cc; + else + pathToGcc = searchPath(path, cc); + + if (!QFileInfo(pathToGcc).exists()) { + fprintf(stderr, "Cannot find %s.", qPrintable(cc)); + if (printComfortingMessage) + fprintf(stderr, " But that's not a problem. I've already found other platforms.\n"); + else + fprintf(stderr, "\n"); + return 1; + } + + Platform *s = 0; + foreach (Platform *p, platforms.values()) { + QString path = p->settings.value("ev/completeccpath").toString(); + if (path == pathToGcc) { + name = p->name; + s = p; + name = s->name; + break; + } + } + + QString compilerTriplet = qsystem(pathToGcc, QStringList() << "-dumpmachine").simplified(); + QStringList compilerTripletl = compilerTriplet.split('-'); + if (compilerTripletl.count() < 2 || + !(compilerTripletl.at(0).contains(QRegExp(".86")) || + compilerTripletl.at(0).contains("arm") ) + ) { + qDebug("detected %s , but i don't understand it's architecture: %s", + qPrintable(pathToGcc), qPrintable(compilerTriplet)); + return 12; + } + + architecture = compilerTripletl.at(0); + if (architecture.contains("arm")) { + endianness = "big-endian"; + } else { + endianness = "little-endian"; + } + + QStringList pathToGccL = pathToGcc.split('/'); + QString compilerName = pathToGccL.takeLast().replace(cc, cxx); + + if (architecture != arch) { + isACrossCompiler = true; + } + if (!cross.isEmpty()) { + isACrossCompiler = true; + } + + if (isACrossCompiler && !cross.contains(compilerTriplet)) { + if (s) { + compilerTriplet = s->settings.value("ev/target-triplet").toString(); + } else { + qDebug("==> detected %s (%s), but it doesn't seem to fit your cross target (%s)", + qPrintable(pathToGcc), + qPrintable(compilerTriplet), + qPrintable(cross) + ); + if (ask(" assume this compiler produces "+cross+" and carry on?", "ny") == 0) + return 2; + + compilerTriplet = cross; + } + compilerTripletl = compilerTriplet.split('-'); + architecture = compilerTripletl.at(0); + } + + if ( + (isACrossCompiler) && + (!cflags.contains("--sysroot")) + ) { + qDebug("==> detected cross compiler %s (%s), but CFLAGS don't contain a --syroot option " + "\n If you did not want to cross-compiler, or you are in a chroot that is not arch %s," + "\n try overriding uname -m by setting ARCH=%s in the environment and/or unset CROSS_COMPILE" + "\n ", + qPrintable(pathToGcc), qPrintable(compilerTriplet), qPrintable(arch), qPrintable(architecture)); + return 3; + } else { + } + + if (cflags.contains("--sysroot")) { + QStringList flagl = cflags.split(' '); + + bool nextitis = false; + foreach (const QString &flag, flagl) { + if (nextitis) { + sysroot = flag; + break; + } + if (flag == "--sysroot") { + nextitis = true; + } + } + } + + + + qstdout << "==> " << (s?"reconfiguring " + name :"detected") + << " " << (isACrossCompiler ? "cross compiler" : "native compiler") + << " " << pathToGcc << "\n" + << " triplet: " << compilerTriplet << "\n" + << " arch: " << architecture << "\n" + << " bin: " << pathToGccL.join("/") << "\n" + << " cc: " << cc << "\n" + ; + + if (!cxx.isEmpty()) + qstdout << " cxx: " << cxx << "\n"; + if (!ld.isEmpty()) + qstdout << " ld: " << ld << "\n"; + + if (!sysroot.isEmpty()) + qstdout << " sysroot: " << sysroot << "\n"; + if (!cflags.isEmpty()) + qstdout << " CFLAGS: " << cflags << "\n"; + if (!cxxflags.isEmpty()) + qstdout << " CXXFLAGS: " << cxxflags << "\n"; + if (!ldflags.isEmpty()) + qstdout << " CXXFLAGS: " << ldflags << "\n"; + + qstdout << flush; + + if (!s) { + while (name.isEmpty()) + name = prompt("give a name to this platform:"); + s = new Platform(name, settingsPath + "/" + name); + } + + // fixme should be cpp.toolchain + // also there is no toolchain:clang + s->settings.setValue("toolchain", "gcc"); + s->settings.setValue("ev/completeccpath", pathToGcc); + s->settings.setValue("ev/target-triplet", compilerTriplet); + s->settings.setValue("architecture", architecture); + s->settings.setValue("endianness", endianness); + +#if defined(Q_OS_MAC) + s->settings.setValue("targetOS", "mac"); +#elif defined(Q_OS_LINUX) + s->settings.setValue("targetOS", "linux"); +#else + s->settings.setValue("targetOS", "unknown"); //fixme +#endif + + if (compilerName.contains('-')) { + QStringList nl = compilerName.split('-'); + s->settings.setValue("cpp/compilerName", nl.takeLast()); + s->settings.setValue("cpp/toolchainPrefix", nl.join("-") + '-'); + } else { + s->settings.setValue("cpp/compilerName", compilerName); + } + s->settings.setValue("cpp/toolchainInstallPath", pathToGccL.join("/")); + + s->settings.setValue("environment/PATH", s->settings.value("cpp/toolchainInstallPath")); + if (!cross.isEmpty()) + s->settings.setValue("environment/CROSS_COMPILE", cross); + if (!cflags.isEmpty()) + s->settings.setValue("environment/CFLAGS", cflags); + if (!cxxflags.isEmpty()) + s->settings.setValue("environment/CXXFLAGS", cxxflags); + if (!ldflags.isEmpty()) + s->settings.setValue("environment/LDFLAGS", ldflags); + + platforms.insert(s->name, s); + + s->settings.sync(); + return 0; +} + +#ifdef Q_OS_WIN +static void msvc_probe(const QString &settingsPath, + QHash<QString, Platform*> &platforms) +{ + QTextStream qstdout(stdout); + + QString vcInstallDir = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv("VCINSTALLDIR"))); + if (vcInstallDir.endsWith('/')) + vcInstallDir.chop(1); + if (vcInstallDir.isEmpty()) + return; + QString winSDKPath = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv("WindowsSdkDir"))); + if (winSDKPath.endsWith('/')) + winSDKPath.chop(1); + QString clOutput = qsystem(vcInstallDir + "/bin/cl.exe"); + if (clOutput.isEmpty()) + return; + + QRegExp rex("C/C\\+\\+ Optimizing Compiler Version ((\\d|\\.)+) for ((x|\\d)+)"); + int idx = rex.indexIn(clOutput); + if (idx < 0) + return; + + QStringList clVersion = rex.cap(1).split("."); + if (clVersion.isEmpty()) + return; + QString clArch = rex.cap(3); + QString msvcVersion = "msvc"; + switch (clVersion.first().toInt()) { + case 14: + msvcVersion += "2005"; + break; + case 15: + msvcVersion += "2008"; + break; + case 16: + msvcVersion += "2010"; + break; + default: + return; + } + + Platform *s = platforms.value(msvcVersion); + if (!s) + s = new Platform(msvcVersion, settingsPath + "/" + msvcVersion); + + QString vsInstallDir = vcInstallDir; + idx = vsInstallDir.lastIndexOf("/"); + if (idx < 0) + return; + vsInstallDir.truncate(idx); + + s->settings.setValue("targetOS", "windows"); + s->settings.setValue("cpp/toolchainInstallPath", vsInstallDir); + s->settings.setValue("toolchain", "msvc"); + s->settings.setValue("cpp/windowsSDKPath", winSDKPath); + platforms.insert(s->name, s); + qstdout << "Detected platform " << msvcVersion << " for " << clArch << ".\n"; + qstdout << "When building projects, the architecture can be chosen by passing\narchitecture:x86 or architecture:x86_64 to qbs.\n"; + s->settings.sync(); +} +#endif + +int probe (const QString &settingsPath, + QHash<QString, Platform*> &platforms, + int (* ask)(const QString &msg, const QString &choices), + QString ( *prompt)(const QString &msg) + ) +{ +#ifdef Q_OS_WIN + Q_UNUSED(prompt); + Q_UNUSED(ask); + msvc_probe(settingsPath, platforms); +#else + QString cc = QString::fromLocal8Bit(qgetenv("CC")); + if (cc.isEmpty()) { + bool somethingFound = false; + if (specific_probe(settingsPath, platforms, ask, prompt, "gcc") == 0) + somethingFound = true; + specific_probe(settingsPath, platforms, ask, prompt, "clang", somethingFound); + } else { + specific_probe(settingsPath, platforms, ask, prompt, cc); + } +#endif + return 0; +} diff --git a/src/app/qbs/application.cpp b/src/app/qbs/application.cpp new file mode 100644 index 000000000..d91b99cb2 --- /dev/null +++ b/src/app/qbs/application.cpp @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "application.h" +#include "ctrlchandler.h" +#include <QtCore/QDebug> + +Application::Application(int &argc, char **argv) + : QCoreApplication(argc, argv) +{ + // ### TODO reactivate the Ctrl-C handler + //installCtrlCHandler(); +} + +void Application::init() +{ + setApplicationName("qbs"); + setOrganizationName("Nokia"); + setOrganizationDomain("qt.nokia.com"); + + connect(&m_buildProjectFutureWatcher, SIGNAL(finished()), this, SLOT(quit()), Qt::QueuedConnection); +} + +void Application::userInterrupt() +{ + fprintf(stderr, "qbs terminated by user (pid=%u)\n", (uint)QCoreApplication::applicationPid()); + m_buildProjectFutureWatcher.cancel(); +} diff --git a/src/app/qbs/application.h b/src/app/qbs/application.h new file mode 100644 index 000000000..247dd813d --- /dev/null +++ b/src/app/qbs/application.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef APPLICATION_H +#define APPLICATION_H + +#include <Qbs/buildexecutor.h> +#include <QtCore/QCoreApplication> +#include <QtCore/QFutureWatcher> + +class Application : public QCoreApplication +{ + Q_OBJECT +public: + Application(int &argc, char **argv); + + void init(); + + Qbs::BuildExecutor *buildExecutor() { return &m_buildExecutor; } + QFutureWatcher<bool> *buildProjectFutureWatcher() { return &m_buildProjectFutureWatcher; } + +public slots: + void userInterrupt(); + +private: + Qbs::BuildExecutor m_buildExecutor; + QFutureWatcher<bool> m_buildProjectFutureWatcher; +}; + +#endif // APPLICATION_H diff --git a/src/app/qbs/ctrlchandler.cpp b/src/app/qbs/ctrlchandler.cpp new file mode 100644 index 000000000..5932c8bb0 --- /dev/null +++ b/src/app/qbs/ctrlchandler.cpp @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include <QtCore/qglobal.h> +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QTimer> + +#if defined(Q_OS_WIN) && defined(Q_CC_MSVC) + +#include <qt_windows.h> + +static BOOL WINAPI consoleCtrlHandlerRoutine(__in DWORD dwCtrlType) +{ + Q_UNUSED(dwCtrlType); + QTimer::singleShot(0, qApp, SLOT(userInterrupt())); + return TRUE; +} + +void installCtrlCHandler() +{ + SetConsoleCtrlHandler(&consoleCtrlHandlerRoutine, TRUE); +} + +#else + +#include <csignal> + +static void sigIntHandler(int sig) +{ + Q_UNUSED(sig); + QTimer::singleShot(0, qApp, SLOT(userInterrupt())); +} + +void installCtrlCHandler() +{ + signal(SIGINT, sigIntHandler); +} + +#endif diff --git a/src/app/qbs/ctrlchandler.h b/src/app/qbs/ctrlchandler.h new file mode 100644 index 000000000..75c9c12b0 --- /dev/null +++ b/src/app/qbs/ctrlchandler.h @@ -0,0 +1,43 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef CTRLCHANDLER_H +#define CTRLCHANDLER_H + +void installCtrlCHandler(); + +#endif // CTRLCHANDLER_H diff --git a/src/app/qbs/main.cpp b/src/app/qbs/main.cpp new file mode 100644 index 000000000..6c86741d0 --- /dev/null +++ b/src/app/qbs/main.cpp @@ -0,0 +1,304 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "application.h" +#include <Qbs/oldsourceproject.h> +#include <Qbs/mainthreadcommunication.h> +#include <tools/logger.h> +#include <tools/options.h> +#include <buildgraph/buildgraph.h> +#include <buildgraph/executor.h> +#include <tools/runenvironment.h> +#include <tools/fakeconcurrent.h> +#include <tools/fileinfo.h> +#include <tools/persistence.h> +#include <tools/logsink.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QProcess> +#include <QtCore/QScopedPointer> +#include <QtCore/QDebug> +#include <QtCore/QDir> + +#if defined(Q_OS_UNIX) +#include <errno.h> +#endif + +enum ExitCodes +{ + ExitCodeOK = 0, + ExitCodeErrorParsingCommandLine = 1, + ExitCodeErrorCommandNotImplemented = 2, + ExitCodeErrorExecutionFailed = 3, + ExitCodeErrorLoadingProjectFailed = 4, + ExitCodeErrorBuildFailure = 5 +}; + +int main(int argc, char *argv[]) +{ + Application app(argc, argv); + app.init(); + + qbs::CommandLineOptions options; + qbs::ConsolePrintLogSink *logSink = new qbs::ConsolePrintLogSink; + logSink->setColoredOutputEnabled(options.configurationValue("defaults/useColoredOutput", true).toBool()); + Qbs::ILogSink::setGlobalLogSink(logSink); + Qbs::MainThreadCommunication::registerMetaType(); + QStringList arguments = app.arguments(); + arguments.removeFirst(); + + if (arguments.count()) { +#if defined(Q_OS_UNIX) + qputenv("PATH", QCoreApplication::applicationDirPath().toLocal8Bit() + ':' + QByteArray(qgetenv("PATH"))); +#elif defined(Q_OS_WIN) + qputenv("PATH", QCoreApplication::applicationDirPath().toLocal8Bit() + ';' + QByteArray(qgetenv("PATH"))); +#endif + QStringList args = app.arguments(); + args.takeFirst(); + QString app = args.takeFirst(); + if (!app.startsWith('-')) { +#if defined(Q_OS_UNIX) + char **argvp = new char*[args.count() + 2]; + QList<QByteArray> bargs; + bargs.append("qbs-" + app.toLocal8Bit()); + argvp[0] = bargs.last().data(); + int i = 1; + foreach (const QString &s, args) { + bargs.append(s.toLocal8Bit()); + argvp[i++] = bargs.last().data(); + } + argvp[i] = 0; + + execvp(argvp[0], argvp); + if (errno != ENOENT) { + perror("execvp"); + return errno; + } +#else + int r = QProcess::execute ( "qbs-" + app, args ); + if (r != -2) + return r; +#endif + } + } + + // read commandline + if (!options.readCommandLineArguments(arguments)) { + qbs::CommandLineOptions::printHelp(); + return ExitCodeErrorParsingCommandLine; + } + + if (options.isHelpSet()) { + qbs::CommandLineOptions::printHelp(); + return 0; + } + + try { + if (options.command() == qbs::CommandLineOptions::ConfigCommand) { + options.configure(); + return 0; + } + } catch (qbs::Error &e) { + fputs("qbs config: ", stderr); + fputs(qPrintable(e.toString()), stderr); + fputs("\n", stderr); + return ExitCodeErrorParsingCommandLine; + } + + if (options.projectFileName().isEmpty()) { + qbsError("No project file found."); + return ExitCodeErrorParsingCommandLine; + } else { + qbsInfo() << qbs::DontPrintLogLevel << "Found project file " << qPrintable(QDir::toNativeSeparators(options.projectFileName())); + } + + if (options.command() == qbs::CommandLineOptions::CleanCommand) { + // ### TODO: take selected products into account! + QString errorMessage; + + const QString buildPath = qbs::FileInfo::resolvePath(QDir::currentPath(), QLatin1String("build")); + qbs::removeDirectoryWithContents(buildPath, &errorMessage); + + if (!errorMessage.isEmpty()) { + qbsError() << errorMessage; + return ExitCodeErrorExecutionFailed; + } + return 0; + } + + // some sanity checks + foreach (const QString &searchPath, options.searchPaths()) { + if (!qbs::FileInfo::exists(searchPath)) { + qbsError("search path '%s' does not exist.\n" + "run 'qbs config --global paths/cubes $QBS_SOURCE_TREE/share/qbs'", + qPrintable(searchPath)); + + return ExitCodeErrorParsingCommandLine; + } + } + foreach (const QString &pluginPath, options.pluginPaths()) { + if (!qbs::FileInfo::exists(pluginPath)) { + qbsError("plugin path '%s' does not exist.\n" + "run 'qbs config --global paths/plugins $QBS_BUILD_TREE/plugins'", + qPrintable(pluginPath)); + return ExitCodeErrorParsingCommandLine; + } + } + + qbs::SourceProject sourceProject(options.settings()); + sourceProject.setSearchPaths(options.searchPaths()); + sourceProject.loadPlugins(options.pluginPaths()); + QFuture<bool> loadProjectFuture = qbs::FakeConcurrent::run(&qbs::SourceProject::loadProject, + &sourceProject, + options.projectFileName(), + options.buildConfigurations()); + loadProjectFuture.waitForFinished(); + foreach (const qbs::Error &error, sourceProject.errors()) { + qbsError() << error.toString(); + return ExitCodeErrorLoadingProjectFailed; + } + + if (options.command() == qbs::CommandLineOptions::StartShellCommand) { + qbs::BuildProject::Ptr buildProject = sourceProject.buildProjects().first(); + qbs::BuildProduct::Ptr buildProduct = *buildProject->buildProducts().begin(); + qbs::RunEnvironment run(buildProduct->rProduct); + return run.runShell(); + } + if (options.isDumpGraphSet()) { + foreach (qbs::BuildProject::Ptr buildPrj, sourceProject.buildProjects()) + foreach (qbs::BuildProduct::Ptr buildProduct, buildPrj->buildProducts()) + qbs::BuildGraph().dump(buildProduct); + return 0; + } + + // execute the build graph + Qbs::BuildExecutor *buildExecutor = app.buildExecutor(); + buildExecutor->setMaximumJobs(options.jobs()); + buildExecutor->setRunOnceAndForgetModeEnabled(true); + buildExecutor->setKeepGoingEnabled(options.isKeepGoingSet()); + buildExecutor->setDryRunEnabled(options.isDryRunSet()); + + QDir currentDir; + QStringList absoluteNamesChangedFiles; + foreach (const QString &fileName, options.changedFiles()) + absoluteNamesChangedFiles += QDir::fromNativeSeparators(currentDir.absoluteFilePath(fileName)); + + int result = 0; + QFuture<bool> buildProjectFuture = qbs::FakeConcurrent::run(&Qbs::BuildExecutor::executeBuildProjects, buildExecutor, + sourceProject.buildProjects(), absoluteNamesChangedFiles, options.selectedProductNames()); + app.buildProjectFutureWatcher()->setFuture(buildProjectFuture); + result = app.exec(); + + // store the projects on disk + try { + foreach (qbs::BuildProject::Ptr project, sourceProject.buildProjects()) + project->store(); + } catch (qbs::Error &e) { + qbsError() << e.toString(); + return ExitCodeErrorExecutionFailed; + } + + if (buildExecutor->state() == Qbs::BuildExecutor::ExecutorError) + return ExitCodeErrorBuildFailure; + + if (options.command() == qbs::CommandLineOptions::RunCommand) { + qbs::BuildProject::Ptr project = sourceProject.buildProjects().first(); + qbs::BuildProduct::Ptr productToRun; + QString productFileName; + QSet<QString> runnableFileTags; +#ifdef Q_OS_MAC + runnableFileTags << "applicationbundle"; +#else + runnableFileTags << "application"; +#endif +#ifdef Q_OS_MAC + bool openProduct = false; +#endif // Q_OS_MAC + if (options.runTargetName().isEmpty()) { + foreach (qbs::BuildProduct::Ptr product, project->buildProducts()) { + foreach (qbs::Artifact *targetArtifact, product->targetArtifacts) { + if (!targetArtifact->fileTags.intersect(runnableFileTags).isEmpty()) { + productToRun = product; + productFileName = targetArtifact->fileName; +#ifdef Q_OS_MAC + if (targetArtifact->fileTags.contains("applicationbundle")) + openProduct = true; +#endif // Q_OS_MAC + break; + } + } + if (productToRun) + break; + } + } else { + foreach (qbs::BuildProduct::Ptr product, project->buildProducts()) { + if (product->rProduct->name == options.runTargetName()) { + productToRun = product; + foreach (qbs::Artifact *targetArtifact, product->targetArtifacts) { + if (!targetArtifact->fileTags.intersect(runnableFileTags).isEmpty()) { + productToRun = product; + productFileName = targetArtifact->fileName; + break; + } + } + break; + } + } + } + + if (!productToRun) { + if (options.runTargetName().isEmpty()) + qbsError() << QObject::tr("Can't find a suitable product to run."); + else + qbsError() << QObject::tr("No such product: '%1'").arg(options.runTargetName()); + return ExitCodeErrorBuildFailure; + } + + qbs::RunEnvironment run(productToRun->rProduct); +#ifdef Q_OS_MAC + if (openProduct) { + QStringList runArgs = options.runArgs(); + QString appBundleName = productFileName; // TODO: make appBundleName be the app bundle dir + runArgs.prepend(appBundleName); + return run.runTarget("/usr/bin/open", runArgs); + } +#endif // Q_OS_MAC + return run.runTarget(productFileName, options.runArgs()); + } + + return result; +} diff --git a/src/app/qbs/qbs.pro b/src/app/qbs/qbs.pro new file mode 100644 index 000000000..0a15fb139 --- /dev/null +++ b/src/app/qbs/qbs.pro @@ -0,0 +1,16 @@ +QT = core script +TEMPLATE = app +TARGET = qbs +DESTDIR = ../../../bin + +CONFIG += console +CONFIG -= app_bundle + +SOURCES += main.cpp \ + ctrlchandler.cpp \ + application.cpp +HEADERS += \ + ctrlchandler.h \ + application.h + +include(../../lib/use.pri) diff --git a/src/lib/Qbs/Qbs.pri b/src/lib/Qbs/Qbs.pri new file mode 100644 index 000000000..d981359fd --- /dev/null +++ b/src/lib/Qbs/Qbs.pri @@ -0,0 +1,37 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/sourceproject.h \ + $$PWD/oldsourceproject.h \ + $$PWD/buildproject.h \ + $$PWD/error.h \ + $$PWD/buildproduct.h \ + $$PWD/buildexecutor.h \ + $$PWD/processoutput.h \ + $$PWD/ilogsink.h \ + $$PWD/globals.h \ + $$PWD/mainthreadcommunication.h \ + $$PWD/logmessageevent.h \ + $$PWD/processoutputevent.h + +SOURCES += \ + $$PWD/sourceproject.cpp \ + $$PWD/oldsourceproject.cpp \ + $$PWD/buildproject.cpp \ + $$PWD/qbserror.cpp \ + $$PWD/buildproduct.cpp \ + $$PWD/buildexecutor.cpp \ + $$PWD/processoutput.cpp \ + $$PWD/ilogsink.cpp \ + $$PWD/mainthreadcommunication.cpp \ + $$PWD/logmessageevent.cpp \ + $$PWD/processoutputevent.cpp + + + + + + + + + diff --git a/src/lib/Qbs/buildexecutor.cpp b/src/lib/Qbs/buildexecutor.cpp new file mode 100644 index 000000000..b1a99063a --- /dev/null +++ b/src/lib/Qbs/buildexecutor.cpp @@ -0,0 +1,156 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#include "buildexecutor.h" + +#include <buildgraph/executor.h> +#include <Qbs/buildproduct.h> +#include <Qbs/buildproject.h> + +namespace Qbs { + +BuildExecutor::BuildExecutor() + : m_maximumJobs(1), + m_runOnceAndForgetModeEnabled(false), + m_dryRun(false), + m_keepGoing(false), + m_state(ExecutorIdle) +{ +} + + +void BuildExecutor::setMaximumJobs(int numberOfJobs) +{ + QWriteLocker locker(&m_lock); + m_maximumJobs = numberOfJobs; +} + +int BuildExecutor::maximumJobs() const +{ + QReadLocker locker(&m_lock); + return m_maximumJobs; +} + +void BuildExecutor::setRunOnceAndForgetModeEnabled(bool enable) +{ + QWriteLocker locker(&m_lock); + m_runOnceAndForgetModeEnabled = enable; +} + +bool BuildExecutor::runOnceAndForgetModeIsEnabled() const +{ + QReadLocker locker(&m_lock); + return m_runOnceAndForgetModeEnabled; +} + +void BuildExecutor::setDryRunEnabled(bool enable) +{ + QWriteLocker locker(&m_lock); + m_dryRun = enable; +} + +bool BuildExecutor::isDryRunEnabled() const +{ + QReadLocker locker(&m_lock); + return m_dryRun; +} + +void BuildExecutor::setKeepGoingEnabled(bool enable) +{ + QWriteLocker locker(&m_lock); + m_keepGoing = enable; +} + +bool BuildExecutor::isKeepGoingEnabled() const +{ + QReadLocker locker(&m_lock); + return m_keepGoing; +} + +void BuildExecutor::executeBuildProject(QFutureInterface<bool> &futureInterface, const BuildProject &buildProject) +{ + QList<qbs::BuildProject::Ptr> internalBuildProjects; + internalBuildProjects.append(buildProject.internalBuildProject()); + QStringList changedFiles; // ### populate + QStringList selectedProductNames; // ### populate + executeBuildProjects(futureInterface, internalBuildProjects, changedFiles, selectedProductNames); +} + +/*! + Starts the build for the given list of projects. + + Args: + changedFiles - absolute file names of changed files (optional) + */ +void BuildExecutor::executeBuildProjects(QFutureInterface<bool> &futureInterface, const QList<QSharedPointer<qbs::BuildProject> > internalBuildProjects, + QStringList changedFiles, QStringList selectedProductNames) +{ + QEventLoop eventLoop; + + m_state = ExecutorRunning; + qbs::Executor executor(maximumJobs()); + executor.setRunOnceAndForgetModeEnabled(runOnceAndForgetModeIsEnabled()); + executor.setKeepGoing(isDryRunEnabled()); + executor.setDryRun(isDryRunEnabled()); + + QObject::connect(&executor, SIGNAL(finished()), &eventLoop, SLOT(quit()), Qt::QueuedConnection); + QObject::connect(&executor, SIGNAL(error()), &eventLoop, SLOT(quit()), Qt::QueuedConnection); + + executor.build(internalBuildProjects, changedFiles, selectedProductNames, futureInterface); + + eventLoop.exec(); + + setState(static_cast<ExecutorState>(executor.state())); + + if (futureInterface.resultCount() == 0) + futureInterface.reportResult(true); +} + +BuildExecutor::ExecutorState BuildExecutor::state() const +{ + QReadLocker locker(&m_lock); + return m_state; +} + +void BuildExecutor::setState(const BuildExecutor::ExecutorState &state) +{ + QWriteLocker locker(&m_lock); + m_state = state; +} + +} // namespace Qbs diff --git a/src/lib/Qbs/buildexecutor.h b/src/lib/Qbs/buildexecutor.h new file mode 100644 index 000000000..67047e1ff --- /dev/null +++ b/src/lib/Qbs/buildexecutor.h @@ -0,0 +1,94 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBS_BUILDEXECUTOR_H +#define QBS_BUILDEXECUTOR_H + +#include "buildproduct.h" + +#include <QtCore/QFutureInterface> +#include <QtCore/QReadWriteLock> + +namespace qbs { + class Executor; + class BuildProject; +} + +namespace Qbs { + +class BuildExecutor +{ +public: + BuildExecutor(); + + enum ExecutorState { + ExecutorIdle, + ExecutorRunning, + ExecutorError + }; + + void setMaximumJobs(int numberOfJobs); + int maximumJobs() const; + + void setRunOnceAndForgetModeEnabled(bool enable); + bool runOnceAndForgetModeIsEnabled() const; + void setDryRunEnabled(bool enable); + bool isDryRunEnabled() const; + void setKeepGoingEnabled(bool enable); + bool isKeepGoingEnabled() const; + + void executeBuildProject(QFutureInterface<bool> &futureInterface, const BuildProject &buildProject); + void executeBuildProjects(QFutureInterface<bool> &futureInterface, const QList<QSharedPointer<qbs::BuildProject> > internalBuildProjects, QStringList changedFiles, QStringList selectedProductNames); + + ExecutorState state() const; + +private: //functions + void setState(const ExecutorState &state); + +private: // variables + mutable QReadWriteLock m_lock; + int m_maximumJobs; + bool m_runOnceAndForgetModeEnabled; + bool m_dryRun; + bool m_keepGoing; + ExecutorState m_state; +}; + +} // namespace Qbs + +#endif // QBS_BUILDEXECUTOR_H diff --git a/src/lib/Qbs/buildproduct.cpp b/src/lib/Qbs/buildproduct.cpp new file mode 100644 index 000000000..82733beb5 --- /dev/null +++ b/src/lib/Qbs/buildproduct.cpp @@ -0,0 +1,121 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#include "buildproduct.h" + +#include <buildgraph/buildgraph.h> +#include <tools/scripttools.h> + +namespace Qbs { + +BuildProduct::BuildProduct() +{ +} + +BuildProduct::~BuildProduct() +{ +} + +BuildProduct::BuildProduct(const BuildProduct &other) + : m_internalBuildProduct(other.m_internalBuildProduct) +{ +} + +BuildProduct &BuildProduct::operator =(const BuildProduct &other) +{ + m_internalBuildProduct = other.m_internalBuildProduct; + + return *this; +} + +QVector<SourceFile> BuildProduct::sourceFiles() const +{ + QVector<SourceFile> artifactList; + artifactList.reserve(m_internalBuildProduct->rProduct->sources.size()); + + if (m_internalBuildProduct) { + foreach (const qbs::SourceArtifact::Ptr &artifact, m_internalBuildProduct->rProduct->sources) + artifactList.append(SourceFile(artifact->absoluteFilePath, artifact->fileTags)); + } + + return artifactList; +} + +static QStringList findProjectIncludePathsRecursive(const QVariantMap &variantMap) +{ + QStringList includePathList; + QMapIterator<QString, QVariant> mapIternator(variantMap); + + while (mapIternator.hasNext()) { + mapIternator.next(); + if (mapIternator.key() == QLatin1String("includePaths")) { + includePathList.append(mapIternator.value().toStringList()); + } else if (mapIternator.value().userType() == QVariant::Map) { + includePathList.append(findProjectIncludePathsRecursive(mapIternator.value().toMap())); + } + } + + return includePathList; +} + +QStringList BuildProduct::projectIncludePaths() const +{ + return findProjectIncludePathsRecursive(m_internalBuildProduct->rProduct->configuration->value()); +} + +BuildProduct::BuildProduct(const QSharedPointer<qbs::BuildProduct> &internalBuildProduct) + : m_internalBuildProduct(internalBuildProduct) +{ +} + +QString BuildProduct::displayName() const +{ + return m_internalBuildProduct->rProduct->name; +} + +QString BuildProduct::filePath() const +{ + return m_internalBuildProduct->rProduct->qbsFile; +} + +SourceFile::SourceFile(const QString &fileNameArg, const QSet<QString> &tagsArg) + : fileName(fileNameArg), tags(tagsArg) +{ +} + +} // namespace Qbs diff --git a/src/lib/Qbs/buildproduct.h b/src/lib/Qbs/buildproduct.h new file mode 100644 index 000000000..08b111548 --- /dev/null +++ b/src/lib/Qbs/buildproduct.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBS_QBSBUILDPRODUCT_H +#define QBS_QBSBUILDPRODUCT_H + +#include <QtCore/QSharedPointer> +#include <QtCore/QStringList> +#include <QtCore/QSet> + +namespace qbs { + class BuildProduct; +} + +namespace Qbs { + +class BuildProject; + +class SourceFile +{ +public: + SourceFile(const QString &fileName = QString(), const QSet<QString> &tags = QSet<QString>()); + QString fileName; + QSet<QString> tags; +}; + + +class BuildProduct +{ + friend class BuildProject; +public: + BuildProduct(); + ~BuildProduct(); + BuildProduct(const BuildProduct &other); + BuildProduct &operator =(const BuildProduct &other); + + QString displayName() const; + QString filePath() const; + QVector<SourceFile> sourceFiles() const; + QStringList projectIncludePaths() const; + +private: // functions + BuildProduct(const QSharedPointer<qbs::BuildProduct> &internalBuildProduct); + +private: // variables + QSharedPointer<qbs::BuildProduct> m_internalBuildProduct; +}; + +} // namespace Qbs + +#endif // QBS_QBSBUILDPRODUCT_H diff --git a/src/lib/Qbs/buildproject.cpp b/src/lib/Qbs/buildproject.cpp new file mode 100644 index 000000000..abbd693d2 --- /dev/null +++ b/src/lib/Qbs/buildproject.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#include "buildproject.h" + +#include <QSharedData> + +#include <buildgraph/buildgraph.h> +#include <tools/scripttools.h> + +namespace Qbs { + + + +BuildProject::BuildProject() +{ +} + +BuildProject::~BuildProject() +{ +} + +BuildProject::BuildProject(const BuildProject &other) + : m_internalBuildProject(other.m_internalBuildProject) +{ +} + +BuildProject &BuildProject::operator =(const BuildProject &other) +{ + m_internalBuildProject = other.m_internalBuildProject; + + return *this; +} + +QVector<BuildProduct> BuildProject::buildProducts() const +{ + QVector<BuildProduct> buildProductList; + buildProductList.reserve(m_internalBuildProject->buildProducts().size()); + + foreach (const qbs::BuildProduct::Ptr &internalBuildProduct, m_internalBuildProject->buildProducts()) + buildProductList.append(BuildProduct(internalBuildProduct)); + + return buildProductList; +} + +QString BuildProject::buildDirectory() const +{ + return QString(m_internalBuildProject->buildGraph()->buildDirectoryRoot() + + m_internalBuildProject->resolvedProject()->id + + QLatin1String("/")); +} + +QString BuildProject::displayName() const +{ + return m_internalBuildProject->resolvedProject()->id; +} + +void BuildProject::storeBuildGraph() +{ + m_internalBuildProject->store(); +} + +bool BuildProject::isValid() const +{ + return m_internalBuildProject.data(); +} + +QString BuildProject::qtSourcePath() const +{ + return qbs::getConfigProperty(m_internalBuildProject->resolvedProject()->configuration->value(), + QStringList() << "qt/core" << "qtPath").toString(); +} + +BuildProject::BuildProject(const QSharedPointer<qbs::BuildProject> &internalBuildProject) + : m_internalBuildProject(internalBuildProject) +{ +} + +qbs::BuildProject::Ptr BuildProject::internalBuildProject() const +{ + return m_internalBuildProject; +} + +bool BuildProject::removeBuildDirectory() +{ + bool filesRemoved = false; + +#if defined(Q_OS_LINUX) + QStringList arguments; + arguments.append("-rf"); + arguments.append(buildDirectory()); + filesRemoved = !QProcess::execute("/bin/rm", arguments); +#elif defined(Q_OS_WIN) + QString command = QString("rd /s /q \"%1\"").arg(QDir::toNativeSeparators(buildDirectory())); + QStringList arguments; + arguments.append("/c"); + arguments.append(command); + filesRemoved = !QProcess::execute("cmd", arguments); +#endif + + return filesRemoved; +} + +} // namespace Qbs diff --git a/src/lib/Qbs/buildproject.h b/src/lib/Qbs/buildproject.h new file mode 100644 index 000000000..3521226b9 --- /dev/null +++ b/src/lib/Qbs/buildproject.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBS_QBSBUILDPROJECT_H +#define QBS_QBSBUILDPROJECT_H + +#include "buildproduct.h" + +#include <QtCore/QSharedPointer> + +namespace qbs { + class BuildProject; +} + +namespace Qbs { + +class SourceProject; + +class BuildProject +{ + friend class SourceProject; +public: + BuildProject(); + ~BuildProject(); + BuildProject(const BuildProject &other); + BuildProject &operator =(const BuildProject &other); + + QVector<BuildProduct> buildProducts() const; + + QString buildDirectory() const; + QString displayName() const; + + void storeBuildGraph(); + + bool isValid() const; + + QString qtSourcePath() const; + + QSharedPointer<qbs::BuildProject> internalBuildProject() const; + + bool removeBuildDirectory(); + + +private: // functions + BuildProject(const QSharedPointer<qbs::BuildProject> &internalBuildProject); + +private: // variables + QSharedPointer<qbs::BuildProject> m_internalBuildProject; +}; + +} // namespace Qbs + +#endif // QBS_QBSBUILDPROJECT_H diff --git a/src/lib/Qbs/error.h b/src/lib/Qbs/error.h new file mode 100644 index 000000000..6e4ff4d57 --- /dev/null +++ b/src/lib/Qbs/error.h @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBS_ERROR_H +#define QBS_ERROR_H + +#include <QtCore/QSharedPointer> + +namespace qbs { + class Error; +} + +namespace Qbs { + +class SourceProject; + +class Error +{ + friend class SourceProject; +public: + Error(); + ~Error(); + + Error(const Error &other); + Error &operator=(const Error &other); + + QString toString() const; + +private: + Error(const qbs::Error &error); + + +private: + QSharedPointer<qbs::Error> m_error; +}; + +} // namespace Qbs + +#endif // QBS_ERROR_H diff --git a/src/lib/Qbs/globals.h b/src/lib/Qbs/globals.h new file mode 100644 index 000000000..e420ee211 --- /dev/null +++ b/src/lib/Qbs/globals.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBSGLOBALS_H +#define QBSGLOBALS_H + + +namespace Qbs { + +enum LoggerLevel +{ + LoggerFatal = 0, + LoggerError, + LoggerWarning, + LoggerInfo, + LoggerDebug, + LoggerTrace, + LoggerMaxLevel = LoggerTrace +}; + +} + + +namespace qbs { +using namespace Qbs; +} + +#endif // QBSGLOBALS_H diff --git a/src/lib/Qbs/ilogsink.cpp b/src/lib/Qbs/ilogsink.cpp new file mode 100644 index 000000000..573bb7d25 --- /dev/null +++ b/src/lib/Qbs/ilogsink.cpp @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include <tools/logger.h> + +namespace Qbs { + +void ILogSink::setGlobalLogSink(ILogSink *logSink) +{ + qbs::Logger::instance().setLogSink(logSink); +} + +void ILogSink::cleanupGlobalLogSink() +{ + qbs::Logger::instance().setLogSink(0); +} + +} diff --git a/src/lib/Qbs/ilogsink.h b/src/lib/Qbs/ilogsink.h new file mode 100644 index 000000000..85a11617c --- /dev/null +++ b/src/lib/Qbs/ilogsink.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBS_ILOGSINK_H +#define QBS_ILOGSINK_H + +#include "globals.h" +#include "processoutput.h" +#include <tools/coloredoutput.h> + +namespace qbs { +class Logger; +} + +namespace Qbs { + +enum LogOutputChannel +{ + LogOutputStdOut, + LogOutputStdErr +}; + +struct LogMessage +{ + LogMessage() + : printLogLevel(true) + , outputChannel(LogOutputStdErr) + , textColor(qbs::TextColorDefault) + , backgroundColor(qbs::TextColorDefault) + {} + + QByteArray data; + bool printLogLevel; + LogOutputChannel outputChannel; + qbs::TextColor textColor; + qbs::TextColor backgroundColor; +}; + +class ILogSink +{ + friend class qbs::Logger; +public: + virtual ~ILogSink() { } + + static void setGlobalLogSink(ILogSink *logSink); + static void cleanupGlobalLogSink(); + +protected: + virtual void outputLogMessage(LoggerLevel /*level*/, const LogMessage &/*logMessage*/) {} + virtual void processOutput(const Qbs::ProcessOutput &/*processOutput*/) {} +}; + +} // namespace Qbs + +#endif // QBS_ILOGSINK_H diff --git a/src/lib/Qbs/logmessageevent.cpp b/src/lib/Qbs/logmessageevent.cpp new file mode 100644 index 000000000..1ba591203 --- /dev/null +++ b/src/lib/Qbs/logmessageevent.cpp @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "logmessageevent.h" + +namespace Qbs { + +int LogMessageEvent::s_eventType = QEvent::registerEventType(); + + +LogMessageEvent::LogMessageEvent(LoggerLevel level, const QString &message) + : QEvent(static_cast<QEvent::Type>(s_eventType)), + m_level(level), + m_message(message) +{ +} + +LogMessageEvent *LogMessageEvent::toLogMessageEvent(QEvent *event) +{ + Q_ASSERT(isLogMessageEvent(event)); + return static_cast<LogMessageEvent*>(event); +} + +LoggerLevel LogMessageEvent::level() const +{ + return m_level; +} + +QString LogMessageEvent::message() const +{ + return m_message; +} + +bool LogMessageEvent::isLogMessageEvent(QEvent *event) +{ + return static_cast<QEvent::Type>(s_eventType) == event->type(); +} + +} // namespace Qbs diff --git a/src/lib/Qbs/logmessageevent.h b/src/lib/Qbs/logmessageevent.h new file mode 100644 index 000000000..cb979baef --- /dev/null +++ b/src/lib/Qbs/logmessageevent.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef QBS_QBSLOGMESSAGEEVENT_H +#define QBS_QBSLOGMESSAGEEVENT_H + +#include "globals.h" + +#include <QtCore/QEvent> +#include <QtCore/QString> + +namespace Qbs { + +class LogMessageEvent : public QEvent +{ +public: + LogMessageEvent(LoggerLevel level, const QString &message); + + static LogMessageEvent *toLogMessageEvent(QEvent *event); + static bool isLogMessageEvent(QEvent *event); + + LoggerLevel level() const; + QString message() const; + + +private: + static int s_eventType; + LoggerLevel m_level; + QString m_message; +}; + +} // namespace Qbs + +#endif // QBS_QBSLOGMESSAGEEVENT_H diff --git a/src/lib/Qbs/mainthreadcommunication.cpp b/src/lib/Qbs/mainthreadcommunication.cpp new file mode 100644 index 000000000..b8c705868 --- /dev/null +++ b/src/lib/Qbs/mainthreadcommunication.cpp @@ -0,0 +1,123 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "mainthreadcommunication.h" + +#include <tools/logger.h> + +#include "logmessageevent.h" +#include "processoutputevent.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QReadLocker> +#include <QtCore/QWriteLocker> + +namespace Qbs { + +typedef QWeakPointer<QObject> ObjectPointer; + +MainThreadCommunication::MainThreadCommunication() +{ + qbs::Logger::instance().setLevel(LoggerMaxLevel); + setGlobalLogSink(this); +} + +MainThreadCommunication::~MainThreadCommunication() +{ + cleanupGlobalLogSink(); +} + +MainThreadCommunication &MainThreadCommunication::instance() +{ + static MainThreadCommunication instance; + return instance; +} + +void MainThreadCommunication::addLogEventReceiver(QObject *object) +{ + QWriteLocker locker(&m_lock); + if (!m_logReceiverList.contains(ObjectPointer(object))) + m_logReceiverList.append(ObjectPointer(object)); +} + +void MainThreadCommunication::removeLogEventReceiver(QObject *object) +{ + QReadLocker locker(&m_lock); + m_logReceiverList.removeOne(ObjectPointer(object)); +} + +void MainThreadCommunication::addProcessOutputEventReceiver(QObject *object) +{ + QWriteLocker locker(&m_lock); + if (!m_processOutputReceiverList.contains(ObjectPointer(object))) + m_processOutputReceiverList.append(ObjectPointer(object));} + +void MainThreadCommunication::removeProcessOutputEventReceiver(QObject *object) +{ + QReadLocker locker(&m_lock); + m_processOutputReceiverList.append(ObjectPointer(object)); +} + +void MainThreadCommunication::registerMetaType() +{ + qRegisterMetaType<Qbs::ProcessOutput>("Qbs::ProcessOutput"); + qRegisterMetaTypeStreamOperators<Qbs::ProcessOutput>("Qbs::ProcessOutput"); + qRegisterMetaType<Qbs::LoggerLevel>("Qbs::LoggerLevel"); +} + +void MainThreadCommunication::outputLogMessage(LoggerLevel level, const LogMessage &message) +{ + QReadLocker locker(&m_lock); + foreach (const ObjectPointer &logReceiver, m_logReceiverList) { + if (logReceiver.data()) { + LogMessageEvent *logMessageEvent = new LogMessageEvent(level, QString::fromLocal8Bit(message.data)); + QCoreApplication::postEvent(logReceiver.data(), logMessageEvent); + } + } +} + +void MainThreadCommunication::processOutput(const Qbs::ProcessOutput &processOutput) +{ + QReadLocker locker(&m_lock); + foreach (const ObjectPointer &processOutputReceiver, m_processOutputReceiverList) { + if (processOutputReceiver.data()) { + ProcessOutputEvent *processOutputEvent = new ProcessOutputEvent(processOutput); + QCoreApplication::postEvent(processOutputReceiver.data(), processOutputEvent); + } + } +} + +} // namespace Qbs diff --git a/src/lib/Qbs/mainthreadcommunication.h b/src/lib/Qbs/mainthreadcommunication.h new file mode 100644 index 000000000..1cbc6c81b --- /dev/null +++ b/src/lib/Qbs/mainthreadcommunication.h @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef QBS_MAINTHREADCOMMUNICATION_H +#define QBS_MAINTHREADCOMMUNICATION_H + +#include "globals.h" +#include "processoutput.h" +#include "ilogsink.h" + +#include <QtCore/QWeakPointer> +#include <QtCore/QReadWriteLock> + +namespace Qbs { + +class MainThreadCommunication : public ILogSink +{ +public: + MainThreadCommunication(); + ~MainThreadCommunication(); + + static MainThreadCommunication &instance(); + + void addLogEventReceiver(QObject *object); + void removeLogEventReceiver(QObject *object); + + void addProcessOutputEventReceiver(QObject *object); + void removeProcessOutputEventReceiver(QObject *object); + + static void registerMetaType(); + +public: + void outputLogMessage(LoggerLevel level, const LogMessage &message); + void processOutput(const Qbs::ProcessOutput &processOutput); + +private: + QReadWriteLock m_lock; + QList<QWeakPointer<QObject> > m_logReceiverList; + QList<QWeakPointer<QObject> > m_processOutputReceiverList; + +}; + +} // namespace Qbs + +#endif // QBS_MAINTHREADCOMMUNICATION_H diff --git a/src/lib/Qbs/oldsourceproject.cpp b/src/lib/Qbs/oldsourceproject.cpp new file mode 100644 index 000000000..95ba074a4 --- /dev/null +++ b/src/lib/Qbs/oldsourceproject.cpp @@ -0,0 +1,193 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "oldsourceproject.h" + +#include <buildgraph/buildgraph.h> +#include <buildgraph/executor.h> +#include <language/loader.h> +#include <tools/runenvironment.h> +#include <tools/fileinfo.h> +#include <tools/persistence.h> +#include <tools/logger.h> +#include <tools/scannerpluginmanager.h> +#include <tools/scripttools.h> +#include <tools/platform.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QProcess> +#include <QtCore/QSettings> +#include <QtCore/QScopedPointer> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QElapsedTimer> + +namespace qbs { + +SourceProject::SourceProject(qbs::Settings::Ptr settings) + : m_settings(settings) +{ +} + +void SourceProject::setSearchPaths(const QStringList &searchPaths) +{ + m_searchPaths = searchPaths; +} + +void SourceProject::loadPlugins(const QStringList &pluginPaths) +{ + static bool alreadyCalled = false; + if (alreadyCalled) { + qbsWarning("qbs::SourceProject::loadPlugins was called more than once."); + } + + alreadyCalled = true; + + foreach (const QString &pluginPath, pluginPaths) + QCoreApplication::addLibraryPath(pluginPath); + + qbs::ScannerPluginManager::instance()->loadPlugins(pluginPaths); +} + +void SourceProject::loadProject(QFutureInterface<bool> &futureInterface, QString projectFileName, QList<QVariantMap> buildConfigs) +{ + QHash<QString, qbs::Platform::Ptr > platforms = Platform::platforms(); + if (platforms.isEmpty()) { + qbsFatal("no platforms configured. maybe you want to run 'qbs platforms probe' first."); + futureInterface.reportResult(false); + return; + } + if (buildConfigs.isEmpty()) { + qbsFatal("SourceProject::loadProject: no build configuration given."); + futureInterface.reportResult(false); + return; + } + QList<qbs::Configuration::Ptr> configurations; + foreach (QVariantMap buildCfg, buildConfigs) { + if (!buildCfg.value("platform").isValid()) { + if (!m_settings->value("defaults/platform").isValid()) { + qbsFatal("SourceProject::loadProject: no platform given and no default set."); + continue; + } + buildCfg.insert("platform", m_settings->value("defaults/platform").toString()); + } + Platform::Ptr platform = platforms.value(buildCfg.value("platform").toString()); + if (platform.isNull()) { + qbsFatal("SourceProject::loadProject: unknown platform: %s", qPrintable(buildCfg.value("platform").toString())); + continue; + } + foreach (const QString &key, platform->settings.allKeys()) { + buildCfg.insert(QString(key).replace('/','.'), + platform->settings.value(key)); + } + + if (!buildCfg.value("buildVariant").isValid()) { + qbsFatal("SourceProject::loadProject: property 'buildVariant' missing in build configuration."); + continue; + } + qbs::Configuration::Ptr configure(new qbs::Configuration); + configurations.append(configure); + + foreach (const QString &property, buildCfg.keys()) { + QStringList nameElements = property.split('.'); + if (nameElements.count() == 1) + nameElements.prepend("qbs"); + QVariantMap configValue = configure->value(); + qbs::setConfigProperty(configValue, nameElements, buildCfg.value(property)); + configure->setValue(configValue); + } + } + + qbs::Loader loader; + loader.setSearchPaths(m_searchPaths); + m_buildGraph = QSharedPointer<qbs::BuildGraph>(new qbs::BuildGraph); + m_buildGraph->setOutputDirectoryRoot(QDir::currentPath()); + const QString buildDirectoryRoot = m_buildGraph->buildDirectoryRoot(); + + try { + foreach (const qbs::Configuration::Ptr &configure, configurations) { + qbs::BuildProject::Ptr bProject; + const qbs::FileTime projectFileTimeStamp = qbs::FileInfo(projectFileName).lastModified(); + bProject = qbs::BuildProject::load(m_buildGraph.data(), projectFileTimeStamp, configure, m_searchPaths); + if (!bProject) { + QElapsedTimer timer; + timer.start(); + if (!loader.hasLoaded()) + loader.loadProject(projectFileName); + qbs::ResolvedProject::Ptr rProject = loader.resolveProject(buildDirectoryRoot, configure, futureInterface); + if (rProject->products.isEmpty()) + throw qbs::Error(QString("'%1' does not contain products.").arg(projectFileName)); + qDebug() << "loading project took: " << timer.elapsed() << "ms"; + timer.start(); + bProject = m_buildGraph->resolveProject(rProject, futureInterface); + qDebug() << "build graph took: " << timer.elapsed() << "ms"; + } + + m_buildProjects.append(bProject); + + printf("for %s:\n", qPrintable(bProject->resolvedProject()->id)); + foreach (qbs::ResolvedProduct::Ptr p, bProject->resolvedProject()->products) { + printf(" - [%s] %s as %s\n" + ,qPrintable(p->fileTags.join(", ")) + ,qPrintable(p->name) + ,qPrintable(p->project->id) + ); + } + printf("\n"); + } + + } catch (qbs::Error &e) { + m_errors.append(e); + futureInterface.reportResult(false); + return; + } + + futureInterface.reportResult(true); +} + +QList<BuildProject::Ptr> SourceProject::buildProjects() const +{ + return m_buildProjects; +} + +QList<Error> SourceProject::errors() const +{ + return m_errors; +} + +} // namespace qbs + diff --git a/src/lib/Qbs/oldsourceproject.h b/src/lib/Qbs/oldsourceproject.h new file mode 100644 index 000000000..98a30a8b2 --- /dev/null +++ b/src/lib/Qbs/oldsourceproject.h @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QBSINTERFACE_H +#define QBSINTERFACE_H + +#include <language/language.h> +#include <buildgraph/buildgraph.h> + +#include <QtCore/QFutureInterface> + +namespace qbs { + +class SourceProject +{ +public: + SourceProject(qbs::Settings::Ptr settings); + + void setSearchPaths(const QStringList &searchPaths); + void loadPlugins(const QStringList &pluginPaths); + void loadProject(QFutureInterface<bool> &futureInterface, QString projectFileName, QList<QVariantMap> buildConfigs); + + QList<qbs::BuildProject::Ptr> buildProjects() const; + + QList<qbs::Error> errors() const; + +private: + QStringList m_searchPaths; + QList<qbs::Configuration::Ptr> m_configurations; + + QSharedPointer<qbs::BuildGraph> m_buildGraph; + QList<qbs::BuildProject::Ptr> m_buildProjects; + + qbs::Settings::Ptr m_settings; + QList<qbs::Error> m_errors; +}; + +} // namespace qbs + +#endif diff --git a/src/lib/Qbs/processoutput.cpp b/src/lib/Qbs/processoutput.cpp new file mode 100644 index 000000000..f633576f4 --- /dev/null +++ b/src/lib/Qbs/processoutput.cpp @@ -0,0 +1,124 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#include "processoutput.h" +#include <QSharedData> + +#include <QtDebug> + +namespace Qbs { + +class ProcessOutputData : public QSharedData +{ +public: + QByteArray standardOutput; + QByteArray standardError; + QString commandLine; +}; + +ProcessOutput::ProcessOutput() + : data(new ProcessOutputData) +{ +} + +ProcessOutput::ProcessOutput(const ProcessOutput &other) + : data(other.data) +{ +} + +ProcessOutput &ProcessOutput::operator=(const ProcessOutput &other) +{ + if (this != &other) + data = other.data; + + return *this; +} + +ProcessOutput::~ProcessOutput() +{ +} + +void ProcessOutput::setStandardOutput(const QByteArray &standardOutput) +{ + data->standardOutput = standardOutput; +} + +QByteArray ProcessOutput::standardOutput() const +{ + return data->standardOutput; +} + +void ProcessOutput::setStandardError(const QByteArray &standardError) +{ + data->standardError = standardError; +} + +QByteArray ProcessOutput::standardError() const +{ + return data->standardError; +} + +void ProcessOutput::setCommandLine(const QString &commandLine) +{ + data->commandLine = commandLine; +} + +QString ProcessOutput::commandLine() const +{ + return data->commandLine; +} + +QDataStream &operator<<(QDataStream &out, const ProcessOutput &processOutput) +{ + out << processOutput.commandLine(); + out << processOutput.standardOutput(); + out << processOutput.standardError(); + + return out; +} + +QDataStream &operator>>(QDataStream &in, ProcessOutput &processOutput) +{ + in >> processOutput.data->commandLine; + in >> processOutput.data->standardOutput; + in >> processOutput.data->standardError; + + return in; +} + +} // namespace Qbs diff --git a/src/lib/Qbs/processoutput.h b/src/lib/Qbs/processoutput.h new file mode 100644 index 000000000..ca9f8bd16 --- /dev/null +++ b/src/lib/Qbs/processoutput.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#ifndef QBS_PROCESSOUTPUT_H +#define QBS_PROCESSOUTPUT_H + +#include <QtCore/QByteArray> +#include <QtCore/QSharedDataPointer> +#include <QtCore/QString> +#include <QtCore/QMetaType> + +namespace Qbs { + +class ProcessOutputData; + +class ProcessOutput +{ + friend QDataStream &operator>>(QDataStream &in, ProcessOutput &processOutput); +public: + ProcessOutput(); + ProcessOutput(const ProcessOutput &other); + ProcessOutput &operator=(const ProcessOutput &other); + ~ProcessOutput(); + + void setStandardOutput(const QByteArray &standardOutput); + QByteArray standardOutput() const; + + void setStandardError(const QByteArray &standardError); + QByteArray standardError() const; + + void setCommandLine(const QString &commandLine); + QString commandLine() const; + +private: + QSharedDataPointer<ProcessOutputData> data; +}; + +QDataStream &operator<<(QDataStream &out, const ProcessOutput &processOutput); +QDataStream &operator>>(QDataStream &in, ProcessOutput &processOutput); + +} // namespace Qbs + +Q_DECLARE_METATYPE(Qbs::ProcessOutput) + +#endif // QBS_PROCESSOUTPUT_H diff --git a/src/lib/Qbs/processoutputevent.cpp b/src/lib/Qbs/processoutputevent.cpp new file mode 100644 index 000000000..e3576f619 --- /dev/null +++ b/src/lib/Qbs/processoutputevent.cpp @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "processoutputevent.h" + +namespace Qbs { + +int ProcessOutputEvent::s_eventType = QEvent::registerEventType(); + +ProcessOutputEvent::ProcessOutputEvent(const ProcessOutput &processOutput) + : QEvent(static_cast<QEvent::Type>(s_eventType)), + m_processOutput(processOutput) +{ +} + +ProcessOutputEvent *ProcessOutputEvent::toProcessOutputEvent(QEvent *event) +{ + Q_ASSERT(isProcessOutputEvent(event)); + return static_cast<ProcessOutputEvent*>(event); +} + +ProcessOutput ProcessOutputEvent::processOutput() const +{ + return m_processOutput; +} + +bool ProcessOutputEvent::isProcessOutputEvent(QEvent *event) +{ + return static_cast<QEvent::Type>(s_eventType) == event->type(); +} + +} // namespace Qbs diff --git a/src/lib/Qbs/processoutputevent.h b/src/lib/Qbs/processoutputevent.h new file mode 100644 index 000000000..74ebd24aa --- /dev/null +++ b/src/lib/Qbs/processoutputevent.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef QBS_PROCESSOUTPUTEVENT_H +#define QBS_PROCESSOUTPUTEVENT_H + +#include <QEvent> +#include "processoutput.h" + +namespace Qbs { + +class ProcessOutputEvent : public QEvent +{ +public: + ProcessOutputEvent(const ProcessOutput &processOutput); + + static ProcessOutputEvent *toProcessOutputEvent(QEvent *event); + static bool isProcessOutputEvent(QEvent *event); + + ProcessOutput processOutput() const; + +private: + static int s_eventType; + ProcessOutput m_processOutput; +}; + +} // namespace Qbs + +#endif // QBS_PROCESSOUTPUTEVENT_H diff --git a/src/lib/Qbs/qbserror.cpp b/src/lib/Qbs/qbserror.cpp new file mode 100644 index 000000000..2ccfe0884 --- /dev/null +++ b/src/lib/Qbs/qbserror.cpp @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "error.h" + +#include <tools/error.h> + +namespace Qbs { + +Error::Error() +{ +} + +Error::Error(const Error &other) + : m_error(other.m_error) +{ +} + +Error &Error::operator =(const Error &other) +{ + m_error = other.m_error; + + return *this; +} + +QString Error::toString() const +{ + return m_error->toString(); +} + +Error::~Error() +{ +} + +Error::Error(const qbs::Error &error) + : m_error(new qbs::Error(error)) +{ + +} + + +} // namespace Qbs diff --git a/src/lib/Qbs/sourceproject.cpp b/src/lib/Qbs/sourceproject.cpp new file mode 100644 index 000000000..bc61220b5 --- /dev/null +++ b/src/lib/Qbs/sourceproject.cpp @@ -0,0 +1,255 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "sourceproject.h" + +#include <language/loader.h> +#include <tools/logger.h> +#include <tools/scannerpluginmanager.h> +#include <tools/scripttools.h> +#include <tools/platform.h> + +#include <QtCore/QDebug> + +using namespace qbs; + +namespace Qbs { + +class SourceProjectPrivate : public QSharedData +{ +public: + QStringList searchPaths; + QList<qbs::Configuration::Ptr> configurations; + + QSharedPointer<qbs::BuildGraph> buildGraph; + QVector<Qbs::BuildProject> buildProjects; + + qbs::Settings::Ptr settings; + QList<Error> errors; + + QString buildDirectoryRoot; + +}; + +SourceProject::SourceProject() + : d(new SourceProjectPrivate) +{ + d->settings = qbs::Settings::create(); // fix it +} + +SourceProject::~SourceProject() +{ +} + +SourceProject::SourceProject(const SourceProject &other) + : d(other.d) +{ +} + +SourceProject &SourceProject::operator =(const SourceProject &other) +{ + d = other.d; + + return *this; +} + +void SourceProject::setSearchPaths(const QStringList &searchPaths) +{ + d->searchPaths = searchPaths; +} + +void SourceProject::loadPlugins(const QStringList &pluginPaths) +{ + static bool alreadyCalled = false; + if (alreadyCalled) { + qbsWarning("qbs::SourceProject::loadPlugins was called more than once."); + } + + alreadyCalled = true; + + foreach (const QString &pluginPath, pluginPaths) + QCoreApplication::addLibraryPath(pluginPath); + + qbs::ScannerPluginManager::instance()->loadPlugins(pluginPaths); +} + +void SourceProject::loadProject(QFutureInterface<bool> &futureInterface, + const QString projectFileName, + const QList<QVariantMap> buildConfigs) +{ + QHash<QString, qbs::Platform::Ptr > platforms = Platform::platforms(); + if (platforms.isEmpty()) { + qbsFatal("no platforms configured. maybe you want to run 'qbs platforms probe' first."); + futureInterface.reportResult(false); + return; + } + if (buildConfigs.isEmpty()) { + qbsFatal("SourceProject::loadProject: no build configuration given."); + futureInterface.reportResult(false); + return; + } + QList<qbs::Configuration::Ptr> configurations; + foreach (QVariantMap buildConfig, buildConfigs) { + if (!buildConfig.value("platform").isValid()) { + if (!d->settings->value("defaults/platform").isValid()) { + qbsFatal("SourceProject::loadProject: no platform given and no default set."); + continue; + } + buildConfig.insert("platform", d->settings->value("defaults/platform").toString()); + } + Platform::Ptr platform = platforms.value(buildConfig.value("platform").toString()); + if (platform.isNull()) { + qbsFatal("SourceProject::loadProject: unknown platform: %s", qPrintable(buildConfig.value("platform").toString())); + continue; + } + foreach (const QString &key, platform->settings.allKeys()) { + buildConfig.insert(QString(key).replace('/','.'), + platform->settings.value(key)); + } + + if (!buildConfig.value("buildVariant").isValid()) { + qbsFatal("SourceProject::loadProject: property 'buildVariant' missing in build configuration."); + continue; + } + qbs::Configuration::Ptr configuration(new qbs::Configuration); + configurations.append(configuration); + + foreach (const QString &property, buildConfig.keys()) { + QStringList nameElements = property.split('.'); + if (nameElements.count() == 1) + nameElements.prepend("qbs"); + QVariantMap configValue = configuration->value(); + qbs::setConfigProperty(configValue, nameElements, buildConfig.value(property)); + configuration->setValue(configValue); + } + } + + qbs::Loader loader; + loader.setSearchPaths(d->searchPaths); + d->buildGraph = QSharedPointer<qbs::BuildGraph>(new qbs::BuildGraph); + if (buildDirectoryRoot().isEmpty()) { + QByteArray buildDirectoryRootFromEnvironment = qgetenv("QBS_BUILD_ROOT_DIRECTORY"); + if (buildDirectoryRootFromEnvironment.isEmpty()) { + d->buildGraph->setOutputDirectoryRoot(QDir::currentPath()); + } else { + d->buildGraph->setOutputDirectoryRoot(buildDirectoryRootFromEnvironment); + } + } else { + d->buildGraph->setOutputDirectoryRoot(d->buildDirectoryRoot); + } + + const QString buildDirectoryRoot = d->buildGraph->buildDirectoryRoot(); + + + try { + int productCount = 0; + foreach (const qbs::Configuration::Ptr &configure, configurations) { + qbs::BuildProject::Ptr buildProject; + const qbs::FileTime projectFileTimeStamp = qbs::FileInfo(projectFileName).lastModified(); + buildProject = qbs::BuildProject::load(d->buildGraph.data(), projectFileTimeStamp, configure, d->searchPaths); + if (!buildProject) { + if (!loader.hasLoaded()) + loader.loadProject(projectFileName); + productCount += loader.productCount(configure); + } + } + futureInterface.setProgressRange(0, productCount * 2); + + + foreach (const qbs::Configuration::Ptr &configure, configurations) { + qbs::BuildProject::Ptr buildProject; + const qbs::FileTime projectFileTimeStamp = qbs::FileInfo(projectFileName).lastModified(); + buildProject = qbs::BuildProject::load(d->buildGraph.data(), projectFileTimeStamp, configure, d->searchPaths); + if (!buildProject) { + if (!loader.hasLoaded()) + loader.loadProject(projectFileName); + qbs::ResolvedProject::Ptr rProject = loader.resolveProject(buildDirectoryRoot, configure, futureInterface); + if (rProject->products.isEmpty()) + throw qbs::Error(QString("'%1' does not contain products.").arg(projectFileName)); + buildProject = d->buildGraph->resolveProject(rProject, futureInterface); + } + + d->buildProjects.append(BuildProject(buildProject)); + + printf("for %s:\n", qPrintable(buildProject->resolvedProject()->id)); + foreach (qbs::ResolvedProduct::Ptr p, buildProject->resolvedProject()->products) { + printf(" - [%s] %s as %s\n" + ,qPrintable(p->fileTags.join(", ")) + ,qPrintable(p->name) + ,qPrintable(p->project->id) + ); + } + printf("\n"); + } + + } catch (qbs::Error &error) { + d->errors.append(Error(error)); + futureInterface.reportResult(false); + return; + } + + futureInterface.reportResult(true); +} + +void SourceProject::storeBuildProjectsBuildGraph() +{ + foreach (BuildProject buildProject, d->buildProjects) + buildProject.storeBuildGraph(); +} + +void SourceProject::setBuildDirectoryRoot(const QString &buildDirectoryRoot) +{ + d->buildDirectoryRoot = buildDirectoryRoot; +} + +QString SourceProject::buildDirectoryRoot() const +{ + return d->buildDirectoryRoot; +} + +QVector<BuildProject> SourceProject::buildProjects() const +{ + return d->buildProjects; +} + +QList<Error> SourceProject::errors() const +{ + return d->errors; +} + +} + diff --git a/src/lib/Qbs/sourceproject.h b/src/lib/Qbs/sourceproject.h new file mode 100644 index 000000000..fef7ad187 --- /dev/null +++ b/src/lib/Qbs/sourceproject.h @@ -0,0 +1,95 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QBSINTERFACE_H +#define QBSINTERFACE_H + +#include <language/language.h> +#include <buildgraph/buildgraph.h> + +#include "buildproject.h" +#include "error.h" + +#include <QFutureInterface> + +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QSharedPointer> + + +namespace qbs { + class BuildProject; +} + + +namespace Qbs { + +class SourceProjectPrivate; + +class SourceProject +{ + friend class BuildProject; +public: + SourceProject(); + ~SourceProject(); + + SourceProject(const SourceProject &other); + SourceProject &operator=(const SourceProject &other); + + + void setSearchPaths(const QStringList &searchPaths); + void loadPlugins(const QStringList &pluginPaths); + void loadProject(QFutureInterface<bool> &futureInterface, + const QString projectFileName, + const QList<QVariantMap> buildConfigs); + void storeBuildProjectsBuildGraph(); + + void setBuildDirectoryRoot(const QString &buildDirectoryRoot); + QString buildDirectoryRoot() const; + + QVector<BuildProject> buildProjects() const; + + QList<Qbs::Error> errors() const; + +private: // functions + QList<QSharedPointer<qbs::BuildProject> > toInternalBuildProjectList(const QVector<Qbs::BuildProject> &buildProjects) const; + +private: + QExplicitlySharedDataPointer<SourceProjectPrivate> d; +}; + +} +#endif diff --git a/src/lib/buildgraph/artifact.cpp b/src/lib/buildgraph/artifact.cpp new file mode 100644 index 000000000..d7cf5e60d --- /dev/null +++ b/src/lib/buildgraph/artifact.cpp @@ -0,0 +1,97 @@ +/************************************************************************* +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "artifact.h" +#include "transformer.h" +#include "buildgraph.h" + +QT_BEGIN_NAMESPACE + +static QDataStream &operator >>(QDataStream &s, qbs::Artifact::ArtifactType &t) +{ + int i; + s >> i; + t = static_cast<qbs::Artifact::ArtifactType>(i); + return s; +} + +static QDataStream &operator <<(QDataStream &s, const qbs::Artifact::ArtifactType &t) +{ + return s << (int)t; +} + +QT_END_NAMESPACE + +namespace qbs { + +Artifact::Artifact(BuildProject *p) + : project(p) + , product(0) + , transformer(0) + , artifactType(Unknown) + , buildState(Untouched) + , outOfDateCheckPerformed(false) + , isOutOfDate(false) + , isExistingFile(false) +{ +} + +Artifact::~Artifact() +{ +} + +void Artifact::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + fileName = pool.idLoadString(s); + fileTags = pool.idLoadStringSet(s); + configuration = pool.idLoadS<Configuration>(s); + transformer = pool.idLoadS<Transformer>(s); + s >> artifactType; + product = pool.idLoadS<BuildProduct>(s).data(); +} + +void Artifact::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << pool.storeString(fileName); + s << pool.storeStringSet(fileTags); + s << pool.store(configuration); + s << pool.store(transformer); + s << artifactType; + s << pool.store(product); +} + +} // namespace qbs diff --git a/src/lib/buildgraph/artifact.h b/src/lib/buildgraph/artifact.h new file mode 100644 index 000000000..7663410fe --- /dev/null +++ b/src/lib/buildgraph/artifact.h @@ -0,0 +1,112 @@ +/************************************************************************* +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef ARTIFACT_H +#define ARTIFACT_H + +#include "artifactlist.h" +#include <language/language.h> + +#include <QtCore/QSet> +#include <QtCore/QString> + +namespace qbs { + +class BuildProduct; +class BuildProject; +class Transformer; + +class Artifact : public PersistentObject +{ +public: + Artifact(BuildProject *p = 0); + ~Artifact(); + + ArtifactList parents; + ArtifactList children; + ArtifactList fileDependencies; + ArtifactList sideBySideArtifacts; /// all artifacts that have been produced by the same rule + QString fileName; + QSet<QString> fileTags; + BuildProject *project; + BuildProduct *product; // Note: file dependency artifacts don't belong to a product. + QSharedPointer<Transformer> transformer; + Configuration::Ptr configuration; + + enum ArtifactType + { + Unknown, + SourceFile, + Generated, + FileDependency + }; + ArtifactType artifactType; + + enum BuildState + { + Untouched = 0, + Buildable, + Building, + Built + }; + BuildState buildState; + + bool outOfDateCheckPerformed : 1; + bool isOutOfDate : 1; + bool isExistingFile : 1; + +private: + void load(PersistentPool &pool, PersistentObjectData &data); + void store(PersistentPool &pool, PersistentObjectData &data) const; +}; + +// debugging helper +inline QString toString(Artifact::BuildState s) +{ + switch (s) { + case Artifact::Buildable: + return "Initialized"; + case Artifact::Building: + return "Processing"; + case Artifact::Built: + return "Finished"; + default: + return "Unknown"; + } +} + +} // namespace qbs + +#endif // ARTIFACT_H diff --git a/src/lib/buildgraph/artifactlist.cpp b/src/lib/buildgraph/artifactlist.cpp new file mode 100644 index 000000000..f9e80b232 --- /dev/null +++ b/src/lib/buildgraph/artifactlist.cpp @@ -0,0 +1,57 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "artifactlist.h" +#include <algorithm> + +namespace qbs { + +ArtifactList::ArtifactList() +{} + +ArtifactList::ArtifactList(const ArtifactList &other) + : m_data(other.m_data) +{} + +void ArtifactList::remove(Artifact *artifact) +{ + iterator it = m_data.find(artifact); + if (it != m_data.end()) + m_data.erase(it); +} + +} // namespace qbs diff --git a/src/lib/buildgraph/artifactlist.h b/src/lib/buildgraph/artifactlist.h new file mode 100644 index 000000000..018547740 --- /dev/null +++ b/src/lib/buildgraph/artifactlist.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef ARTIFACTLIST_H +#define ARTIFACTLIST_H + +#include <set> + +namespace qbs { + +class Artifact; + +/** + * List that holds a bunch of build graph artifacts. + * This is faster than QSet when iterating over the container. + */ +class ArtifactList +{ +public: + ArtifactList(); + ArtifactList(const ArtifactList &other); + + typedef std::set<Artifact *>::const_iterator const_iterator; + typedef std::set<Artifact *>::iterator iterator; + typedef Artifact * value_type; + + iterator begin() { return m_data.begin(); } + iterator end() { return m_data.end(); } + const_iterator begin() const { return m_data.begin(); } + const_iterator end() const { return m_data.end(); } + + void insert(Artifact *artifact) + { + m_data.insert(artifact); + } + + void operator +=(Artifact *artifact) + { + insert(artifact); + } + + void remove(Artifact *artifact); + + bool contains(Artifact *artifact) const + { + return m_data.find(artifact) != m_data.end(); + } + + void clear() + { + m_data.clear(); + } + + bool isEmpty() const + { + return m_data.empty(); + } + + int count() const + { + return m_data.size(); + } + + void reserve(int) + { + // no-op + } + +private: + mutable std::set<Artifact *> m_data; +}; + +} // namespace qbs + +#endif // ARTIFACTLIST_H diff --git a/src/lib/buildgraph/automoc.cpp b/src/lib/buildgraph/automoc.cpp new file mode 100644 index 000000000..3a7f03ffa --- /dev/null +++ b/src/lib/buildgraph/automoc.cpp @@ -0,0 +1,273 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "automoc.h" +#include "scanresultcache.h" +#include <buildgraph/artifact.h> +#include <tools/error.h> +#include <tools/logger.h> +#include <tools/scannerpluginmanager.h> + +namespace qbs { + +AutoMoc::AutoMoc() + : m_scanResultCache(0) +{ +} + +void AutoMoc::setScanResultCache(ScanResultCache *scanResultCache) +{ + m_scanResultCache = scanResultCache; +} + +void AutoMoc::apply(BuildProduct::Ptr product) +{ + if (scanners().isEmpty()) + throw Error("C++ scanner cannot be loaded."); + + QList<QPair<Artifact *, FileType> > artifactsToMoc; + QSet<QString> includedMocCppFiles; + QHash<QString, Artifact *>::const_iterator it = product->artifacts.begin(); + for (; it != product->artifacts.end(); ++it) { + Artifact *artifact = it.value(); + FileType fileType = UnknownFileType; + if (artifact->fileTags.contains("hpp")) + fileType = HppFileType; + if (artifact->fileTags.contains("cpp")) + fileType = CppFileType; + if (fileType == UnknownFileType) + continue; + QString mocFileTag; + bool alreadyMocced = isVictimOfMoc(artifact, fileType, mocFileTag); + bool hasQObjectMacro; + apply(artifact, hasQObjectMacro, includedMocCppFiles); + if (hasQObjectMacro && !alreadyMocced) { + artifactsToMoc += qMakePair(artifact, fileType); + } else if (!hasQObjectMacro && alreadyMocced) { + unmoc(artifact, mocFileTag); + } + } + + QMap<QString, QSet<Artifact *> > artifactsPerFileTag; + for (int i = artifactsToMoc.count(); --i >= 0;) { + const QPair<Artifact *, FileType> &p = artifactsToMoc.at(i); + Artifact * const artifact = p.first; + FileType fileType = p.second; + QString fileTag; + if (fileType == CppFileType) { + fileTag = "moc_cpp"; + } else if (fileType == HppFileType) { + QString mocFileName = generateMocFileName(artifact, fileType); + if (includedMocCppFiles.contains(mocFileName)) + fileTag = "moc_hpp_inc"; + else + fileTag = "moc_hpp"; + } + artifactsPerFileTag[fileTag].insert(artifact); + } + + BuildGraph *buildGraph = product->project->buildGraph(); + if (!artifactsPerFileTag.isEmpty()) { + qbsInfo() << DontPrintLogLevel << "Applying moc rules for '" << product->rProduct->name << "'."; + buildGraph->applyRules(product.data(), artifactsPerFileTag); + } + buildGraph->updateNodesThatMustGetNewTransformer(); +} + +QString AutoMoc::generateMocFileName(Artifact *artifact, FileType fileType) +{ + QString mocFileName; + switch (fileType) { + case UnknownFileType: + break; + case HppFileType: + mocFileName = "moc_" + FileInfo::baseName(artifact->fileName) + ".cpp"; + break; + case CppFileType: + mocFileName = FileInfo::baseName(artifact->fileName) + ".moc"; + break; + } + return mocFileName; +} + +void AutoMoc::apply(Artifact *artifact, bool &hasQObjectMacro, QSet<QString> &includedMocCppFiles) +{ + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[AUTOMOC] checks " << fileName(artifact); + + hasQObjectMacro = false; + const int numFileTags = artifact->fileTags.count(); + char **cFileTags = createCFileTags(artifact->fileTags); + + foreach (ScannerPlugin *scanner, scanners()) { + void *opaq = scanner->open(artifact->fileName.utf16(), cFileTags, numFileTags); + if (!opaq || !scanner->additionalFileTags) + continue; + + // HACK: misuse the file dependency scanner as provider for file tags + int length = 0; + const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length); + if (szFileTagsFromScanner && length > 0) { + for (int i=length; --i >= 0;) { + const QString fileTagFromScanner = QString::fromLocal8Bit(szFileTagsFromScanner[i]); + artifact->fileTags.insert(fileTagFromScanner); + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[AUTOMOC] finds Q_OBJECT macro"; + if (fileTagFromScanner.startsWith("moc")) + hasQObjectMacro = true; + } + } + + scanner->close(opaq); + + ScanResultCache::Result scanResult; + if (m_scanResultCache) + scanResult = m_scanResultCache->value(artifact->fileName); + if (!scanResult.visited) { + scanResult.visited = true; + opaq = scanner->open(artifact->fileName.utf16(), 0, 0); + if (!opaq) + continue; + + forever { + int flags = 0; + const char *szOutFilePath = scanner->next(opaq, &length, &flags); + if (szOutFilePath == 0) + break; + QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length); + if (includedFilePath.isEmpty()) + continue; + bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG); + scanResult.deps.insert(includedFilePath, isLocalInclude); + } + + scanner->close(opaq); + if (m_scanResultCache) + m_scanResultCache->insert(artifact->fileName, scanResult); + } + + for (QHash<QString, bool>::const_iterator it = scanResult.deps.constBegin(); it != scanResult.deps.constEnd(); ++it) { + const QString &includedFilePath = it.key(); + if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) { + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[AUTOMOC] finds included file: " << includedFilePath; + includedMocCppFiles += includedFilePath; + } + } + } + + freeCFileTags(cFileTags, numFileTags); +} + +bool AutoMoc::isVictimOfMoc(Artifact *artifact, FileType fileType, QString &foundMocFileTag) +{ + foundMocFileTag.clear(); + switch (fileType) { + case UnknownFileType: + break; + case HppFileType: + if (artifact->fileTags.contains("moc_hpp")) + foundMocFileTag = "moc_hpp"; + else if (artifact->fileTags.contains("moc_hpp_inc")) + foundMocFileTag = "moc_hpp_inc"; + break; + case CppFileType: + if (artifact->fileTags.contains("moc_cpp")) + foundMocFileTag = "moc_cpp"; + break; + } + return !foundMocFileTag.isEmpty(); +} + +void AutoMoc::unmoc(Artifact *artifact, const QString &mocFileTag) +{ + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[AUTOMOC] unmoc'ing " << fileName(artifact); + + artifact->fileTags.remove(mocFileTag); + + Artifact *generatedMocArtifact = 0; + foreach (Artifact *parent, artifact->parents) { + foreach (const QString &fileTag, parent->fileTags) { + if (fileTag == "hpp" || fileTag == "cpp") { + generatedMocArtifact = parent; + break; + } + } + } + + if (!generatedMocArtifact) { + qbsTrace() << "[AUTOMOC] generated moc artifact could not be found"; + return; + } + + BuildGraph *buildGraph = artifact->project->buildGraph(); + if (mocFileTag == "moc_hpp") { + Artifact *mocObjArtifact = 0; + foreach (Artifact *parent, generatedMocArtifact->parents) { + foreach (const QString &fileTag, parent->fileTags) { + if (fileTag == "obj" || fileTag == "fpicobj") { + mocObjArtifact = parent; + break; + } + } + } + + if (!mocObjArtifact) { + qbsTrace() << "[AUTOMOC] generated moc obj artifact could not be found"; + } else { + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[AUTOMOC] removing moc obj artifact " << fileName(mocObjArtifact); + buildGraph->remove(mocObjArtifact); + } + } + + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[AUTOMOC] removing generated artifact " << fileName(generatedMocArtifact); + buildGraph->remove(generatedMocArtifact); + delete generatedMocArtifact; +} + +QList<ScannerPlugin *> AutoMoc::scanners() const +{ + if (m_scanners.isEmpty()) + m_scanners = ScannerPluginManager::scannersForFileTag("hpp"); + + return m_scanners; +} + +} // namespace qbs diff --git a/src/lib/buildgraph/automoc.h b/src/lib/buildgraph/automoc.h new file mode 100644 index 000000000..ac0025dcc --- /dev/null +++ b/src/lib/buildgraph/automoc.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef AUTOMOC_H +#define AUTOMOC_H + +#include "buildgraph.h" + +struct ScannerPlugin; + +namespace qbs { + +class ScanResultCache; + +/** + * Scans cpp and hpp files for the Q_OBJECT / Q_GADGET macro and + * applies the corresponding rule then. + * Also scans the files for moc_XXX.cpp files to find out if we must + * compile and link a moc_XXX.cpp file or not. + * + * This whole thing is an ugly hack, I know. + */ +class AutoMoc +{ +public: + AutoMoc(); + + void setScanResultCache(ScanResultCache *scanResultCache); + void apply(BuildProduct::Ptr product); + +private: + enum FileType + { + UnknownFileType, + HppFileType, + CppFileType + }; + +private: + static QString generateMocFileName(Artifact *artifact, FileType fileType); + void apply(Artifact *artifact, bool &hasQObjectMacro, QSet<QString> &includedMocCppFiles); + bool isVictimOfMoc(Artifact *artifact, FileType fileType, QString &foundMocFileTag); + void unmoc(Artifact *artifact, const QString &mocFileTag); + QList<ScannerPlugin *> scanners() const; +private: + mutable QList<ScannerPlugin *> m_scanners; + ScanResultCache *m_scanResultCache; +}; + +} // namespace qbs + +#endif // AUTOMOC_H diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp new file mode 100644 index 000000000..b9225881d --- /dev/null +++ b/src/lib/buildgraph/buildgraph.cpp @@ -0,0 +1,1384 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "buildgraph.h" +#include "artifact.h" +#include "command.h" +#include "rulegraph.h" +#include "transformer.h" + +#include <language/loader.h> +#include <tools/fileinfo.h> +#include <tools/persistence.h> +#include <tools/scannerpluginmanager.h> +#include <tools/logger.h> +#include <tools/scripttools.h> + +#include <QFileInfo> +#include <QDebug> +#include <QDir> +#include <QtCore/QDirIterator> +#include <QDataStream> +#include <QtCore/QElapsedTimer> +#include <QtCore/QMutex> +#include <QtScript/QScriptProgram> +#include <QtScript/QScriptValueIterator> + +namespace qbs { + +BuildProduct::BuildProduct() + : project(0) +{ +} + +BuildProduct::~BuildProduct() +{ + qDeleteAll(artifacts); +} + +const QList<Rule::Ptr> &BuildProduct::topSortedRules() const +{ + if (m_topSortedRules.isEmpty()) { + RuleGraph ruleGraph; + ruleGraph.build(rProduct->rules, rProduct->fileTags); +// ruleGraph.dump(); + m_topSortedRules = ruleGraph.topSorted(); +// int i=0; +// foreach (Rule::Ptr r, m_topSortedRules) +// qDebug() << ++i << r->toString() << (void*)r.data(); + } + return m_topSortedRules; +} + +BuildGraph::BuildGraph() +{ + ProcessCommand::setupForJavaScript(&m_scriptEngine); + JavaScriptCommand::setupForJavaScript(&m_scriptEngine); +} + +BuildGraph::~BuildGraph() +{ +} + +static void internalDump(BuildProduct *product, Artifact *n, QByteArray indent) +{ + Artifact *artifactInProduct = product->artifacts.value(n->fileName); + if (artifactInProduct && artifactInProduct != n) { + fprintf(stderr,"\ntree corrupted. %p ('%s') resolves to %p ('%s')\n", + n, qPrintable(n->fileName), product->artifacts.value(n->fileName), + qPrintable(product->artifacts.value(n->fileName)->fileName)); + + abort(); + } + printf("%s", indent.constData()); + printf("Artifact (%p) ", n); + printf("%s%s %s [%s]", + qPrintable(QString(toString(n->buildState).at(0))), + artifactInProduct ? "" : " SBS", // SBS == side-by-side artifact from other product + qPrintable(n->fileName), + qPrintable(QStringList(n->fileTags.toList()).join(","))); + printf("\n"); + indent.append(" "); + foreach (Artifact *child, n->children) { + internalDump(product, child, indent); + } +} + +void BuildGraph::dump(BuildProduct::Ptr product) const +{ + Q_ASSERT(product->artifacts.uniqueKeys() == product->artifacts.keys()); + + foreach (Artifact *n, product->artifacts) + if (n->parents.isEmpty()) + internalDump(product.data(), n, QByteArray()); +} + +void BuildGraph::insert(BuildProduct::Ptr product, Artifact *n) const +{ + insert(product.data(), n); +} + +void BuildGraph::insert(BuildProduct *product, Artifact *n) const +{ + Q_ASSERT(n->product == 0); + Q_ASSERT(!n->fileName.isEmpty()); + Q_ASSERT(!product->artifacts.contains(n->fileName)); +#ifdef QT_DEBUG + foreach (BuildProduct::Ptr otherProduct, product->project->buildProducts()) { + if (otherProduct->artifacts.contains(n->fileName)) { + if (n->artifactType == Artifact::Generated) { + QString pl; + pl.append(QString(" - %1 \n").arg(product->rProduct->name)); + foreach (BuildProduct::Ptr p, product->project->buildProducts()) { + if (p->artifacts.contains(n->fileName)) { + pl.append(QString(" - %1 \n").arg(p->rProduct->name)); + } + } + throw Error(QString ("BUG: already inserted in this project: %1\n%2" + ) + .arg(n->fileName) + .arg(pl) + ); + } + } + } +#endif + product->artifacts.insert(n->fileName, n); + n->product = product; + product->project->markDirty(); + + if (qbsLogLevel(LoggerTrace)) + qbsTrace("[BG] insert artifact '%s'", qPrintable(n->fileName)); +} + +void BuildGraph::setupScriptEngineForProduct(QScriptEngine *scriptEngine, ResolvedProduct::Ptr product, Rule::Ptr rule, BuildGraph *bg) +{ + ResolvedProduct *lastSetupProduct = (ResolvedProduct *)scriptEngine->property("lastSetupProduct").toULongLong(); + + QScriptValue productScriptValue; + if (lastSetupProduct != product.data()) { + scriptEngine->setProperty("lastSetupProduct", QVariant((qulonglong)product.data())); + productScriptValue = scriptEngine->toScriptValue(product->configuration->value()); + productScriptValue.setProperty("name", product->name); + QString destinationDirectory = product->destinationDirectory; + if (destinationDirectory.isEmpty()) + destinationDirectory = "."; + productScriptValue.setProperty("destinationDirectory", destinationDirectory); + scriptEngine->globalObject().setProperty("product", productScriptValue, QScriptValue::ReadOnly); + } else { + productScriptValue = scriptEngine->globalObject().property("product"); + } + + // If the Rule is in a Module, set up the 'module' property + if (!rule->module->name.isEmpty()) + productScriptValue.setProperty("module", productScriptValue.property("modules").property(rule->module->name)); + + if (rule) { + for (JsImports::const_iterator it = rule->jsImports.begin(); it != rule->jsImports.end(); ++it) { + foreach (const QString &fileName, it.value()) { + QScriptValue jsImportValue; + if (bg) + jsImportValue = bg->m_jsImportCache.value(fileName, scriptEngine->undefinedValue()); + if (jsImportValue.isUndefined()) { +// qDebug() << "CACHE MISS" << fileName; + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) + throw Error(QString("Cannot open '%1'.").arg(fileName)); + const QString sourceCode = QTextStream(&file).readAll(); + QScriptProgram program(sourceCode, fileName); + addJSImport(scriptEngine, program, jsImportValue); + addJSImport(scriptEngine, jsImportValue, it.key()); + if (bg) + bg->m_jsImportCache.insert(fileName, jsImportValue); + } else { +// qDebug() << "CACHE HIT" << fileName; + addJSImport(scriptEngine, jsImportValue, it.key()); + } + } + } + } else { + // ### TODO remove the imports we added before + } +} + +void BuildGraph::setupScriptEngineForArtifact(BuildProduct *product, Artifact *artifact) +{ + QString inFileName = FileInfo::fileName(artifact->fileName); + QString inBaseName = FileInfo::baseName(artifact->fileName); + QString inCompleteBaseName = FileInfo::completeBaseName(artifact->fileName); + + QString basedir; + if (artifact->artifactType == Artifact::SourceFile) { + QDir sourceDir(product->rProduct->sourceDirectory); + basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->fileName)); + } else { + QDir buildDir(product->project->buildGraph()->buildDirectoryRoot() + product->project->resolvedProject()->id); + basedir = FileInfo::path(buildDir.relativeFilePath(artifact->fileName)); + } + + QScriptValue modulesScriptValue = artifact->configuration->cachedScriptValue(&m_scriptEngine); + if (!modulesScriptValue.isValid()) { + modulesScriptValue = m_scriptEngine.toScriptValue(artifact->configuration->value()); + artifact->configuration->cacheScriptValue(&m_scriptEngine, modulesScriptValue); + } + modulesScriptValue = modulesScriptValue.property("modules"); + + // expose per file properties we want to use in an Artifact within a Rule + QScriptValue scriptValue = m_scriptEngine.newObject(); + scriptValue.setProperty("fileName", inFileName); + scriptValue.setProperty("baseName", inBaseName); + scriptValue.setProperty("completeBaseName", inCompleteBaseName); + scriptValue.setProperty("baseDir", basedir); + scriptValue.setProperty("modules", modulesScriptValue); + + QScriptValue globalObj = m_scriptEngine.globalObject(); + globalObj.setProperty("input", scriptValue); +} + +void BuildGraph::applyRules(BuildProduct *product, QMap<QString, QSet<Artifact *> > &artifactsPerFileTag) +{ + foreach (Rule::Ptr rule, product->topSortedRules()) + applyRule(product, artifactsPerFileTag, rule); +} + +/*! + * Runs a cycle detection on the BG and throws an exception if there is one. + */ +void BuildGraph::detectCycle(BuildProject *project) +{ + QElapsedTimer *t = 0; + if (qbsLogLevel(LoggerTrace)) { + t = new QElapsedTimer; + qbsTrace() << "[BG] running cycle detection on project '" + project->resolvedProject()->id + "'"; + } + + foreach (BuildProduct::Ptr product, project->buildProducts()) + foreach (Artifact *artifact, product->targetArtifacts) + detectCycle(artifact); + + if (qbsLogLevel(LoggerTrace)) { + qint64 elapsed = t->elapsed(); + qbsTrace() << "[BG] cycle detection for project '" + project->resolvedProject()->id + "' took " << elapsed << " ms"; + delete t; + } +} + +void BuildGraph::detectCycle(Artifact *a) +{ + QSet<Artifact *> done, currentBranch; + detectCycle(a, done, currentBranch); +} + +void BuildGraph::detectCycle(Artifact *v, QSet<Artifact *> &done, QSet<Artifact *> ¤tBranch) +{ + currentBranch += v; + for (ArtifactList::const_iterator it = v->children.begin(); it != v->children.end(); ++it) { + Artifact *u = *it; + if (currentBranch.contains(u)) + throw Error("Cycle in build graph detected."); + if (!done.contains(u)) + detectCycle(u, done, currentBranch); + } + currentBranch -= v; + done += v; +} + +static AbstractCommand *createCommandFromScriptValue(const QScriptValue &scriptValue) +{ + if (scriptValue.isUndefined() || !scriptValue.isValid()) + return 0; + AbstractCommand *cmdBase = 0; + QString className = scriptValue.property("className").toString(); + if (className == "Command") + cmdBase = new ProcessCommand; + else if (className == "JavaScriptCommand") + cmdBase = new JavaScriptCommand; + if (cmdBase) + cmdBase->fillFromScriptValue(&scriptValue); + return cmdBase; +} + +void BuildGraph::applyRule(BuildProduct *product, QMap<QString, QSet<Artifact *> > &artifactsPerFileTag, + Rule::Ptr rule) +{ + setupScriptEngineForProduct(&m_scriptEngine, product->rProduct, rule, this); + + if (rule->isMultiplexRule()) { + // apply the rule once for a set of inputs + + QSet<Artifact*> inputArtifacts; + foreach (const QString &fileTag, rule->inputs) + inputArtifacts.unite(artifactsPerFileTag.value(fileTag)); + + if (!inputArtifacts.isEmpty()) + applyRule(product, artifactsPerFileTag, rule, inputArtifacts); + } else { + // apply the rule once for each input + + QSet<Artifact*> inputArtifacts; + foreach (const QString &fileTag, rule->inputs) { + foreach (Artifact *inputArtifact, artifactsPerFileTag.value(fileTag)) { + inputArtifacts.insert(inputArtifact); + applyRule(product, artifactsPerFileTag, rule, inputArtifacts); + inputArtifacts.clear(); + } + } + } +} + +void BuildGraph::createOutputArtifact( + BuildProduct *product, + const Rule::Ptr &rule, const RuleArtifact::Ptr &ruleArtifact, + const QSet<Artifact *> &inputArtifacts, + QList< QPair<RuleArtifact*, Artifact *> > *ruleArtifactArtifactMap, + QList<Artifact *> *outputArtifacts, + QSharedPointer<Transformer> &transformer) +{ + QScriptValue scriptValue = m_scriptEngine.evaluate(ruleArtifact->fileScript); + if (scriptValue.isError() || m_scriptEngine.hasUncaughtException()) + throw Error("Error in Rule.Artifact fileName: " + scriptValue.toString()); + QString outputPath = scriptValue.toString(); + outputPath.replace("..", "dotdot"); // don't let the output artifact "escape" its build dir + outputPath = resolveOutPath(outputPath, product); + + Artifact *outputArtifact = product->artifacts.value(outputPath); + if (outputArtifact) { + if (outputArtifact->transformer && outputArtifact->transformer != transformer) { + // This can happen when applying rules after scanning for additional file tags. + // We just regenerate the transformer. + if (qbsLogLevel(LoggerTrace)) + qbsTrace("[BG] regenerating transformer for '%s'", qPrintable(fileName(outputArtifact))); + transformer = outputArtifact->transformer; + transformer->inputs += inputArtifacts; + + if (transformer->inputs.count() > 1 && !rule->isMultiplexRule()) { + QString th = "[" + QStringList(outputArtifact->fileTags.toList()).join(", ") + "]"; + QString e = tr("Conflicting rules for producing %1 %2 \n").arg(outputArtifact->fileName, th); + th = "[" + rule->inputs.join(", ") + + "] -> [" + QStringList(outputArtifact->fileTags.toList()).join(", ") + "]"; + + e += QString(" while trying to apply: %1:%2:%3 %4\n") + .arg(rule->script->location.fileName) + .arg(rule->script->location.line) + .arg(rule->script->location.column) + .arg(th); + + e += QString(" was already defined in: %1:%2:%3 %4\n") + .arg(outputArtifact->transformer->rule->script->location.fileName) + .arg(outputArtifact->transformer->rule->script->location.line) + .arg(outputArtifact->transformer->rule->script->location.column) + .arg(th); + throw Error(e); + } + } + outputArtifact->fileTags += ruleArtifact->fileTags.toSet(); + } else { + outputArtifact = new Artifact(product->project); + outputArtifact->artifactType = Artifact::Generated; + outputArtifact->fileName = outputPath; + outputArtifact->fileTags = ruleArtifact->fileTags.toSet(); + insert(product, outputArtifact); + } + + if (rule->isMultiplexRule()) + outputArtifact->configuration = product->rProduct->configuration; + else + outputArtifact->configuration = (*inputArtifacts.constBegin())->configuration; + + foreach (Artifact *inputArtifact, inputArtifacts) { + Q_ASSERT(outputArtifact != inputArtifact); + loggedConnect(outputArtifact, inputArtifact); + } + ruleArtifactArtifactMap->append(qMakePair(ruleArtifact.data(), outputArtifact)); + outputArtifacts->append(outputArtifact); + + // create transformer if not already done so + if (!transformer) { + transformer = QSharedPointer<Transformer>(new Transformer); + transformer->rule = rule; + transformer->inputs = inputArtifacts; + } + outputArtifact->transformer = transformer; +} + +void BuildGraph::applyRule(BuildProduct *product, QMap<QString, QSet<Artifact *> > &artifactsPerFileTag, Rule::Ptr rule, const QSet<Artifact *> &inputArtifacts) +{ + if (qbsLogLevel(LoggerDebug)) + qbsDebug() << "[BG] apply rule " << rule->toString() << " " << toStringList(inputArtifacts).join(",\n "); + + QList< QPair<RuleArtifact*, Artifact *> > ruleArtifactArtifactMap; + QList<Artifact *> outputArtifacts; + + QSet<Artifact *> usingArtifacts; + foreach (BuildProduct *dep, product->usings) { + foreach (Artifact *targetArtifact, dep->targetArtifacts) { + ArtifactList sbsArtifacts = targetArtifact->sideBySideArtifacts; + sbsArtifacts.insert(targetArtifact); + foreach (Artifact *artifact, sbsArtifacts) { + QString matchingTag; + foreach (const QString &tag, rule->usings) { + if (artifact->fileTags.contains(tag)) { + matchingTag = tag; + break; + } + } + if (matchingTag.isEmpty()) + continue; + usingArtifacts.insert(artifact); + } + } + } + + // create the output artifacts from the set of input artifacts + QSharedPointer<Transformer> transformer; + foreach (RuleArtifact::Ptr ruleArtifact, rule->artifacts) { + if (!rule->isMultiplexRule()) { + foreach (Artifact *inputArtifact, inputArtifacts) { + setupScriptEngineForArtifact(product, inputArtifact); + QSet<Artifact *> oneInputArtifact; + oneInputArtifact.insert(inputArtifact); + createOutputArtifact(product, rule, ruleArtifact, oneInputArtifact, + &ruleArtifactArtifactMap, &outputArtifacts, transformer); + } + } else { + createOutputArtifact(product, rule, ruleArtifact, inputArtifacts, + &ruleArtifactArtifactMap, &outputArtifacts, transformer); + } + } + + foreach (Artifact *outputArtifact, outputArtifacts) { + // insert the output artifacts into the pool of artifacts + foreach (const QString &fileTag, outputArtifact->fileTags) + artifactsPerFileTag[fileTag].insert(outputArtifact); + + // connect artifacts that match the file tags in explicitlyDependsOn + foreach (const QString &fileTag, rule->explicitlyDependsOn) + foreach (Artifact *dependency, artifactsPerFileTag.value(fileTag)) + loggedConnect(outputArtifact, dependency); + + // Transformer setup + transformer->outputs.insert(outputArtifact); + for (QSet<Artifact *>::const_iterator it = usingArtifacts.constBegin(); it != usingArtifacts.constEnd(); ++it) { + Artifact *dep = *it; + loggedConnect(outputArtifact, dep); + transformer->inputs.insert(dep); + foreach (Artifact *sideBySideDep, dep->sideBySideArtifacts) { + loggedConnect(outputArtifact, sideBySideDep); + transformer->inputs.insert(sideBySideDep); + } + } + + m_artifactsThatMustGetNewTransformers -= outputArtifact; + } + + // setup side-by-side artifacts + if (outputArtifacts.count() > 1) + foreach (Artifact *sbs1, outputArtifacts) + foreach (Artifact *sbs2, outputArtifacts) + if (sbs1 != sbs2) + sbs1->sideBySideArtifacts.insert(sbs2); + + transformer->setupInputs(&m_scriptEngine, m_scriptEngine.globalObject()); + + // change the transformer outputs according to the bindings in Artifact + QScriptValue scriptValue; + for (int i=ruleArtifactArtifactMap.count(); --i >= 0;) { + RuleArtifact *ra = ruleArtifactArtifactMap.at(i).first; + if (ra->bindings.isEmpty()) + continue; + + // expose attributes of this artifact + Artifact *outputArtifact = ruleArtifactArtifactMap.at(i).second; + outputArtifact->configuration = Configuration::Ptr(new Configuration(*outputArtifact->configuration)); + + // ### clean m_scriptEngine first? + m_scriptEngine.globalObject().setProperty("fileName", m_scriptEngine.toScriptValue(outputArtifact->fileName), QScriptValue::ReadOnly); + m_scriptEngine.globalObject().setProperty("fileTags", toScriptValue(&m_scriptEngine, outputArtifact->fileTags), QScriptValue::ReadOnly); + + QVariantMap artifactModulesCfg = outputArtifact->configuration->value().value("modules").toMap(); + for (int i=0; i < ra->bindings.count(); ++i) { + const QStringList &name = ra->bindings.at(i).first; + const QString &code = ra->bindings.at(i).second; + scriptValue = m_scriptEngine.evaluate(code); + if (scriptValue.isError()) + throw Error(QLatin1String("evaluating rule bindings: ") + scriptValue.toString()); + setConfigProperty(artifactModulesCfg, name, scriptValue.toVariant()); + } + QVariantMap outputArtifactConfiguration = outputArtifact->configuration->value(); + outputArtifactConfiguration.insert("modules", artifactModulesCfg); + outputArtifact->configuration->setValue(outputArtifactConfiguration); + } + + transformer->setupOutputs(&m_scriptEngine, m_scriptEngine.globalObject()); + + // setup transform properties + { + const QVariantMap overriddenTransformProperties = product->rProduct->configuration->value().value("modules").toMap().value(rule->module->name).toMap().value(rule->objectId).toMap(); + /* + overriddenTransformProperties contains the rule's transform properties that have been overridden in the project file. + For example, if you set cpp.compiler.defines in your project file, that property appears here. + */ + + QMap<QString, QScriptProgram>::const_iterator it = rule->transformProperties.begin(); + for (; it != rule->transformProperties.end(); ++it) + { + const QString &propertyName = it.key(); + QScriptValue sv; + if (overriddenTransformProperties.contains(propertyName)) { + sv = m_scriptEngine.toScriptValue(overriddenTransformProperties.value(propertyName)); + } else { + const QScriptProgram &myProgram = it.value(); + sv = m_scriptEngine.evaluate(myProgram); + if (m_scriptEngine.hasUncaughtException()) { + CodeLocation errorLocation; + errorLocation.fileName = m_scriptEngine.uncaughtExceptionBacktrace().join("\n"); + errorLocation.line = m_scriptEngine.uncaughtExceptionLineNumber(); + throw Error(QLatin1String("transform property evaluation: ") + m_scriptEngine.uncaughtException().toString(), errorLocation); + } else if (sv.isError()) { + CodeLocation errorLocation(myProgram.fileName(), myProgram.firstLineNumber()); + throw Error(QLatin1String("transform property evaluation: ") + sv.toString(), errorLocation); + } + } + m_scriptEngine.globalObject().setProperty(propertyName, sv); + } + } + + createTransformerCommands(rule->script, transformer.data()); + if (transformer->commands.isEmpty()) + throw Error(QString("There's a rule without commands: %1.").arg(rule->toString()), rule->script->location); +} + +void BuildGraph::createTransformerCommands(RuleScript::Ptr script, Transformer *transformer) +{ + QScriptProgram &scriptProgram = m_scriptProgramCache[script->script]; + if (scriptProgram.isNull()) + scriptProgram = QScriptProgram(script->script); + + QScriptValue scriptValue = m_scriptEngine.evaluate(scriptProgram); + if (m_scriptEngine.hasUncaughtException()) + throw Error("evaluating prepare script: " + m_scriptEngine.uncaughtException().toString(), + script->location); + + QList<AbstractCommand*> commands; + if (scriptValue.isArray()) { + const int count = scriptValue.property("length").toInt32(); + for (qint32 i=0; i < count; ++i) { + QScriptValue item = scriptValue.property(i); + if (item.isValid() && !item.isUndefined()) { + AbstractCommand *cmd = createCommandFromScriptValue(item); + if (cmd) + commands += cmd; + } + } + } else { + AbstractCommand *cmd = createCommandFromScriptValue(scriptValue); + if (cmd) + commands += cmd; + } + + transformer->commands = commands; +} + +QString BuildGraph::buildDirectoryRoot() const +{ + Q_ASSERT(!m_outputDirectoryRoot.isEmpty()); + QString path = FileInfo::resolvePath(m_outputDirectoryRoot, QLatin1String("build")); + if (!path.endsWith('/')) + path.append(QLatin1Char('/')); + return path; +} + +/* + * c must be built before p + * p ----> c + * p.children = c + * c.parents = p + * + * also: children means i depend on or i am produced by + * parent means "produced by me" or "depends on me" + */ +void BuildGraph::connect(Artifact *p, Artifact *c) +{ + Q_ASSERT(p != c); + p->children.insert(c); + c->parents.insert(p); + p->project->markDirty(); +} + +void BuildGraph::loggedConnect(Artifact *u, Artifact *v) +{ + Q_ASSERT(u != v); + if (qbsLogLevel(LoggerTrace)) + qbsTrace("[BG] connect '%s' -> '%s'", + qPrintable(fileName(u)), + qPrintable(fileName(v))); + connect(u, v); +} + +static bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path) +{ + if (u == v) { + path.append(v); + return true; + } + + for (ArtifactList::const_iterator it = u->children.begin(); it != u->children.end(); ++it) { + if (findPath(*it, v, path)) { + path.prepend(u); + return true; + } + } + + return false; +} + +static bool existsPath(Artifact *u, Artifact *v) +{ + if (u == v) + return true; + + for (ArtifactList::const_iterator it = u->children.begin(); it != u->children.end(); ++it) + if (existsPath(*it, v)) + return true; + + return false; +} + +bool BuildGraph::safeConnect(Artifact *u, Artifact *v) +{ + Q_ASSERT(u != v); + if (qbsLogLevel(LoggerTrace)) + qbsTrace("[BG] safeConnect: '%s' '%s'", + qPrintable(fileName(u)), + qPrintable(fileName(v))); + + if (existsPath(v, u)) { + QList<Artifact *> circle; + findPath(v, u, circle); + qbsTrace() << "[BG] safeConnect: circle detected " << toStringList(circle); + return false; + } + + connect(u, v); + return true; +} + +void BuildGraph::disconnect(Artifact *u, Artifact *v) +{ + u->children.remove(v); + v->parents.remove(u); +} + +QSet<Artifact *> BuildGraph::disconnect(Artifact *n) const +{ + QSet<Artifact *> r; + if (n->children.count() == 1) { + Artifact * c = *(n->children.begin()); + c->parents.remove(n); + n->children.clear(); + r += n; + foreach (Artifact * p, n->parents) { + r += disconnect(p); + p->children.remove(n); + if (p->transformer) + p->transformer->inputs.remove(n); + } + } + return r; +} + +void BuildGraph::remove(Artifact *artifact) const +{ + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[BG] remove artifact " << fileName(artifact); + + if (artifact->artifactType == Artifact::Generated) + QFile::remove(artifact->fileName); + artifact->product->artifacts.remove(artifact->fileName); + artifact->product->targetArtifacts.remove(artifact); + foreach (Artifact *parent, artifact->parents) { + parent->children.remove(artifact); + if (parent->transformer) { + parent->transformer->inputs.remove(artifact); + m_artifactsThatMustGetNewTransformers += parent; + } + } + foreach (Artifact *child, artifact->children) { + child->parents.remove(artifact); + } + artifact->children.clear(); + artifact->parents.clear(); + artifact->project->markDirty(); +} + +/** + * Removes the artifact and all the artifacts that depend exclusively on it. + * Example: if you remove a cpp artifact then the obj artifact is removed but + * not the resulting application (if there's more then one cpp artifact). + */ +void BuildGraph::removeArtifactAndExclusiveDependents(Artifact *artifact, QList<Artifact*> *removedArtifacts) +{ + if (removedArtifacts) + removedArtifacts->append(artifact); + foreach (Artifact *parent, artifact->parents) { + if (parent->children.count() == 1) + removeArtifactAndExclusiveDependents(parent, removedArtifacts); + } + remove(artifact); +} + +BuildProject::Ptr BuildGraph::resolveProject(ResolvedProject::Ptr rProject, QFutureInterface<bool> &futureInterface) +{ + BuildProject::Ptr project = BuildProject::Ptr(new BuildProject(this)); + project->setResolvedProject(rProject); + foreach (ResolvedProduct::Ptr rProduct, rProject->products) { + resolveProduct(project.data(), rProduct, futureInterface); + } + detectCycle(project.data()); + return project; +} + +BuildProduct::Ptr BuildGraph::resolveProduct(BuildProject *project, ResolvedProduct::Ptr rProduct, QFutureInterface<bool> &futureInterface) +{ + BuildProduct::Ptr product = m_productCache.value(rProduct); + if (product) + return product; + + futureInterface.setProgressValue(futureInterface.progressValue() + 1); + product = BuildProduct::Ptr(new BuildProduct); + m_productCache.insert(rProduct, product); + product->project = project; + product->rProduct = rProduct; + QMap<QString, QSet<Artifact *> > artifactsPerFileTag; + + foreach (ResolvedProduct::Ptr t2, rProduct->uses) { + if (t2 == rProduct) { + throw Error(tr("circular using")); + } + BuildProduct::Ptr referencedProduct = resolveProduct(project, t2, futureInterface); + product->usings.append(referencedProduct.data()); + } + + //add qbsFile artifact + Artifact *qbsFileArtifact = product->artifacts.value(rProduct->qbsFile); + if (!qbsFileArtifact) { + qbsFileArtifact = new Artifact(project); + qbsFileArtifact->artifactType = Artifact::SourceFile; + qbsFileArtifact->fileName = rProduct->qbsFile; + qbsFileArtifact->configuration = rProduct->configuration; + insert(product, qbsFileArtifact); + } + qbsFileArtifact->fileTags.insert("qbs"); + artifactsPerFileTag["qbs"].insert(qbsFileArtifact); + + // read sources + foreach (SourceArtifact::Ptr sourceArtifact, rProduct->sources) { + QString filePath = sourceArtifact->absoluteFilePath; + if (product->artifacts.contains(filePath)) { + // ignore duplicate artifacts + continue; + } + + Artifact *artifact = createArtifact(product, sourceArtifact); + + foreach (const QString &fileTag, artifact->fileTags) + artifactsPerFileTag[fileTag].insert(artifact); + } + + // read manually added transformers + QList<Artifact *> transformerOutputs; + foreach (const ResolvedTransformer::Ptr rtrafo, rProduct->transformers) { + QList<Artifact *> inputArtifacts; + foreach (const QString &inputFileName, rtrafo->inputs) { + Artifact *artifact = product->artifacts.value(inputFileName); + if (!artifact) + throw Error(QString("Can't find artifact '%0' in the list of source files.").arg(inputFileName)); + if (artifact->fileTags.isEmpty()) + artifact->fileTags += "unknown"; + inputArtifacts += artifact; + } + QSharedPointer<Transformer> transformer(new Transformer); + transformer->inputs = inputArtifacts.toSet(); + transformer->rule = Rule::Ptr(new Rule); + transformer->rule->inputs = rtrafo->inputs; + transformer->rule->jsImports = rtrafo->jsImports; + transformer->rule->module = ResolvedModule::Ptr(new ResolvedModule); + transformer->rule->module->name = rtrafo->module->name; + transformer->rule->script = rtrafo->transform; + foreach (SourceArtifact::Ptr sourceArtifact, rtrafo->outputs) { + Artifact *outputArtifact = createArtifact(product, sourceArtifact); + outputArtifact->artifactType = Artifact::Generated; + outputArtifact->transformer = transformer; + transformer->outputs += outputArtifact; + transformerOutputs += outputArtifact; + foreach (Artifact *inputArtifact, inputArtifacts) + safeConnect(outputArtifact, inputArtifact); + foreach (const QString &fileTag, outputArtifact->fileTags) + artifactsPerFileTag[fileTag].insert(outputArtifact); + + RuleArtifact::Ptr ruleArtifact(new RuleArtifact); + ruleArtifact->fileScript = outputArtifact->fileName; + ruleArtifact->fileTags = outputArtifact->fileTags.toList(); + transformer->rule->artifacts += ruleArtifact; + } + setupScriptEngineForProduct(&m_scriptEngine, rProduct, transformer->rule, this); + transformer->setupInputs(&m_scriptEngine, m_scriptEngine.globalObject()); + transformer->setupOutputs(&m_scriptEngine, m_scriptEngine.globalObject()); + createTransformerCommands(rtrafo->transform, transformer.data()); + if (transformer->commands.isEmpty()) + throw Error(QString("There's a transformer without commands."), rtrafo->transform->location); + } + + applyRules(product.data(), artifactsPerFileTag); + + QSet<Artifact *> productArtifactCandidates; + for (int i=0; i < product->rProduct->fileTags.count(); ++i) + foreach (Artifact *artifact, artifactsPerFileTag.value(product->rProduct->fileTags.at(i))) + if (artifact->artifactType == Artifact::Generated) + productArtifactCandidates += artifact; + + if (productArtifactCandidates.isEmpty()) { + // this should already be catched in the rule graph + throw Error("The impossible happenend! The rules generate no product."); + } + + foreach (Artifact *productArtifact, productArtifactCandidates) { + product->targetArtifacts.insert(productArtifact); + project->addBuildProduct(product); + + foreach (Artifact *trafoOutputArtifact, transformerOutputs) + if (productArtifact != trafoOutputArtifact) + loggedConnect(productArtifact, trafoOutputArtifact); + } + + return product; +} + +void BuildGraph::onProductChanged(BuildProduct::Ptr product, ResolvedProduct::Ptr changedProduct) +{ + qbsDebug() << "[BG] product '" << product->rProduct->name << "' changed."; + + QMap<QString, QSet<Artifact *> > artifactsPerFileTag; + QList<Artifact *> addedArtifacts, artifactsToRemove; + QHash<QString, SourceArtifact::Ptr> oldArtifacts, newArtifacts; + foreach (SourceArtifact::Ptr a, product->rProduct->sources) + oldArtifacts.insert(a->absoluteFilePath, a); + foreach (SourceArtifact::Ptr a, changedProduct->sources) { + newArtifacts.insert(a->absoluteFilePath, a); + if (!oldArtifacts.contains(a->absoluteFilePath)) { + // artifact added + qbsDebug() << "[BG] artifact '" << a->absoluteFilePath << "' added to product " << product->rProduct->name; + product->rProduct->sources.insert(a); + addedArtifacts += createArtifact(product, a); + } + } + foreach (SourceArtifact::Ptr a, product->rProduct->sources) { + SourceArtifact::Ptr changedArtifact = newArtifacts.value(a->absoluteFilePath); + if (!changedArtifact) { + // artifact removed + qbsDebug() << "[BG] artifact '" << a->absoluteFilePath << "' removed from product " << product->rProduct->name; + Artifact *artifact = product->artifacts.value(a->absoluteFilePath); + Q_ASSERT(artifact); + removeArtifactAndExclusiveDependents(artifact, &artifactsToRemove); + continue; + } + if (changedArtifact->fileTags != a->fileTags) { + // artifact's filetags have changed + qbsDebug() << "[BG] filetags have changed for artifact '" << a->absoluteFilePath + << "' from " << a->fileTags << " to " << changedArtifact->fileTags; + Artifact *artifact = product->artifacts.value(a->absoluteFilePath); + Q_ASSERT(artifact); + + // handle added filetags + foreach (const QString &addedFileTag, changedArtifact->fileTags - a->fileTags) + artifactsPerFileTag[addedFileTag] += artifact; + + // handle removed filetags + foreach (const QString &removedFileTag, a->fileTags - changedArtifact->fileTags) { + artifact->fileTags -= removedFileTag; + foreach (Artifact *parent, artifact->parents) { + if (parent->transformer && parent->transformer->rule->inputs.contains(removedFileTag)) { + // this parent has been created because of the removed filetag + removeArtifactAndExclusiveDependents(parent, &artifactsToRemove); + } + } + } + } + if (changedArtifact->configuration->value() != a->configuration->value()) // ### TODO + { + qWarning("Some properties changed. Consider rebuild or fix QBS-7. File name: %s", qPrintable(changedArtifact->absoluteFilePath)); + QVariantMap m = a->configuration->value(); + + for (QVariantMap::iterator it = m.begin(); it != m.end(); ++it) { + if (it.value() != changedArtifact->configuration->value().value(it.key())) { + qDebug() << " old:" << it.value(); + qDebug() << " new:" << changedArtifact->configuration->value().value(it.key()); + } + } + } + } + + // apply rules for new artifacts + foreach (Artifact *artifact, addedArtifacts) + foreach (const QString &ft, artifact->fileTags) + artifactsPerFileTag[ft] += artifact; + applyRules(product.data(), artifactsPerFileTag); + + // parents of removed artifacts must update their transformers + foreach (Artifact *removedArtifact, artifactsToRemove) + foreach (Artifact *parent, removedArtifact->parents) + m_artifactsThatMustGetNewTransformers += parent; + updateNodesThatMustGetNewTransformer(); + + // delete all removed artifacts physically from the disk + foreach (Artifact *artifact, artifactsToRemove) { + if (artifact->artifactType == Artifact::Generated) { + qbsDebug() << "[BG] deleting stale artifact " << artifact->fileName; + QFile::remove(artifact->fileName); + } + delete artifact; + } +} + +void BuildGraph::updateNodesThatMustGetNewTransformer() +{ + foreach (Artifact *artifact, m_artifactsThatMustGetNewTransformers) + updateNodeThatMustGetNewTransformer(artifact); + m_artifactsThatMustGetNewTransformers.clear(); +} + +void BuildGraph::updateNodeThatMustGetNewTransformer(Artifact *artifact) +{ + Q_CHECK_PTR(artifact->transformer); + + if (qbsLogLevel(LoggerDebug)) + qbsDebug() << "[BG] updating transformer for " << fileName(artifact); + + Rule::Ptr rule = artifact->transformer->rule; + artifact->product->project->markDirty(); + artifact->transformer = QSharedPointer<Transformer>(); + + QMap<QString, QSet<Artifact *> > artifactsPerFileTag; + foreach (Artifact *input, artifact->children) + foreach (const QString &fileTag, input->fileTags) + artifactsPerFileTag[fileTag] += input; + + applyRule(artifact->product, artifactsPerFileTag, rule); +} + +Artifact *BuildGraph::createArtifact(BuildProduct::Ptr product, SourceArtifact::Ptr sourceArtifact) +{ + Artifact *artifact = new Artifact(product->project); + artifact->artifactType = Artifact::SourceFile; + artifact->fileName = sourceArtifact->absoluteFilePath; + artifact->fileTags = sourceArtifact->fileTags; + artifact->configuration = sourceArtifact->configuration; + insert(product, artifact); + return artifact; +} + +QString BuildGraph::resolveOutPath(const QString &path, BuildProduct *product) const +{ + QString result; + QString buildDir = product->rProduct->buildDirectory; + result = FileInfo::resolvePath(buildDir, path); + + Q_ASSERT(result.startsWith(buildDir)); + result = QDir::cleanPath(result); + return result; +} + +void Transformer::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + rule = pool.idLoadS<Rule>(s); + loadContainer(inputs, s, pool); + loadContainer(outputs, s, pool); + int count, cmdType; + s >> count; + commands.reserve(count); + while (--count >= 0) { + s >> cmdType; + AbstractCommand *cmd = AbstractCommand::createByType(static_cast<AbstractCommand::CommandType>(cmdType)); + cmd->load(s); + commands += cmd; + } +} + +void Transformer::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << pool.store(rule); + storeContainer(inputs, s, pool); + storeContainer(outputs, s, pool); + s << commands.count(); + foreach (AbstractCommand *cmd, commands) { + s << int(cmd->type()); + cmd->store(s); + } +} + +void BuildProduct::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + int i, count; + + // artifacts + artifacts.clear(); + s >> count; + for (i = count; --i >= 0;) { + QString key; + s >> key; + artifacts.insert(key, pool.idLoad<Artifact>(s)); + } + + // edges + for (i = count; --i >= 0;) { + Artifact *artifact = pool.idLoad<Artifact>(s); + int count2, j; + s >> count2; + artifact->parents.clear(); + artifact->parents.reserve(count2); + for (j = count2; --j >= 0;) + artifact->parents.insert(pool.idLoad<Artifact>(s)); + + s >> count2; + artifact->children.clear(); + artifact->children.reserve(count2); + for (j = count2; --j >= 0;) + artifact->children.insert(pool.idLoad<Artifact>(s)); + + s >> count2; + artifact->fileDependencies.clear(); + artifact->fileDependencies.reserve(count2); + for (j = count2; --j >= 0;) + artifact->fileDependencies.insert(pool.idLoad<Artifact>(s)); + + s >> count2; + artifact->sideBySideArtifacts.clear(); + artifact->sideBySideArtifacts.reserve(count2); + for (j = count2; --j >= 0;) + artifact->sideBySideArtifacts.insert(pool.idLoad<Artifact>(s)); + } + + // other data + rProduct = pool.idLoadS<ResolvedProduct>(s); + loadContainer(targetArtifacts, s, pool); + loadContainer(usings, s, pool); +} + +void BuildProduct::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << artifacts.count(); + + //artifacts + for (QHash<QString, Artifact *>::const_iterator i = artifacts.constBegin(); i != artifacts.constEnd(); i++) { + s << i.key(); + PersistentObjectId artifactId = pool.store(i.value()); + s << artifactId; + } + + // edges + for (QHash<QString, Artifact *>::const_iterator i = artifacts.constBegin(); i != artifacts.constEnd(); i++) { + Artifact * artifact = i.value(); + s << pool.store(artifact); + + s << artifact->parents.count(); + foreach (Artifact * n, artifact->parents) + s << pool.store(n); + s << artifact->children.count(); + foreach (Artifact * n, artifact->children) + s << pool.store(n); + s << artifact->fileDependencies.count(); + foreach (Artifact * n, artifact->fileDependencies) + s << pool.store(n); + s << artifact->sideBySideArtifacts.count(); + foreach (Artifact *n, artifact->sideBySideArtifacts) + s << pool.store(n); + } + + // other data + s << pool.store(rProduct); + storeContainer(targetArtifacts, s, pool); + storeContainer(usings, s, pool); +} + +BuildProject::BuildProject(BuildGraph *bg) + : m_buildGraph(bg) + , m_dirty(false) +{ +} + +BuildProject::~BuildProject() +{ + qDeleteAll(m_dependencyArtifacts); +} + +static bool isConfigCompatible(const QVariantMap &userCfg, const QVariantMap &projectCfg) +{ + QVariantMap::const_iterator it = userCfg.begin(); + for (; it != userCfg.end(); ++it) { + if (it.value().type() == QVariant::Map) { + if (!isConfigCompatible(it.value().toMap(), projectCfg.value(it.key()).toMap())) + return false; + } else { + QVariant value = projectCfg.value(it.key()); + if (!value.isNull() && value != it.value()) { + return false; + } + } + } + return true; +} + +BuildProject::Ptr BuildProject::load(BuildGraph *bg, const FileTime &minTimeStamp, Configuration::Ptr cfg, const QStringList &loaderSearchPaths) +{ + PersistentPool pool; + QString fileName; + QStringList bgFiles = storedProjectFiles(bg); + foreach (const QString &fn, bgFiles) { + if (!pool.load(fn, PersistentPool::LoadHeadData)) + continue; + PersistentPool::HeadData headData = pool.headData(); + if (isConfigCompatible(cfg->value(), headData.projectConfig)) { + fileName = fn; + break; + } + } + if (fileName.isNull()) { + qbsDebug() << "[BG] No stored build graph found that's compatible to the desired build configuration."; + return BuildProject::Ptr(); + } + + BuildProject::Ptr project; + qbsDebug() << "[BG] trying to load: " << fileName; + FileInfo bgfi(fileName); + if (!bgfi.exists()) { + qbsDebug() << "[BG] stored build graph file does not exist"; + return project; + } + if (!pool.load(fileName)) + throw Error("Cannot load stored build graph."); + project = BuildProject::Ptr(new BuildProject(bg)); + PersistentObjectData data = pool.getData(0); + project->load(pool, data); + project->resolvedProject()->configuration = Configuration::Ptr(new Configuration); + project->resolvedProject()->configuration->setValue(pool.headData().projectConfig); + qbsDebug() << "[BG] stored project loaded."; + + bool projectFileChanged = false; + if (bgfi.lastModified() < minTimeStamp) { + projectFileChanged = true; + } + + QList<BuildProduct::Ptr> changedProducts; + foreach (BuildProduct::Ptr product, project->buildProducts()) { + FileInfo pfi(product->rProduct->qbsFile); + if (!pfi.exists()) + throw Error(QString("The product file '%1' is gone.").arg(product->rProduct->qbsFile)); + if (bgfi.lastModified() < pfi.lastModified()) + changedProducts += product; + } + + if (projectFileChanged || !changedProducts.isEmpty()) { + + Loader ldr; + ldr.setSearchPaths(loaderSearchPaths); + ldr.loadProject(project->resolvedProject()->qbsFile); + QFutureInterface<bool> dummyFutureInterface; + ResolvedProject::Ptr changedProject = ldr.resolveProject(bg->buildDirectoryRoot(), cfg, dummyFutureInterface); + if (!changedProject) { + QString msg("Trying to load '%1' failed."); + throw Error(msg.arg(project->resolvedProject()->qbsFile)); + } + + if (projectFileChanged) { + qWarning("[BG] project file changed: %s", qPrintable(project->resolvedProject()->qbsFile)); + qWarning("[BG] ### HANDLING THAT PROPERLY IS NOT YET IMPLEMENTED"); + qWarning("[BG] ### CONSIDER DELETING THE STORED BUILD GRAPH"); + } + + QMap<QString, ResolvedProduct::Ptr> changedProductsMap; + foreach (BuildProduct::Ptr product, changedProducts) { + if (changedProductsMap.isEmpty()) + foreach (ResolvedProduct::Ptr cp, changedProject->products) + changedProductsMap.insert(cp->name, cp); + bg->onProductChanged(product, changedProductsMap.value(product->rProduct->name)); + } + + BuildGraph::detectCycle(project.data()); + } + + return project; +} + +void BuildProject::store() +{ + if (!dirty()) { + qbsDebug() << "[BG] build graph is unchanged in project " << resolvedProject()->id << "."; + return; + } + const QString fileName = storedProjectFilePath(buildGraph(), resolvedProject()->id); + qbsDebug() << "[BG] storing: " << fileName; + PersistentPool pool; + PersistentPool::HeadData headData; + headData.projectConfig = resolvedProject()->configuration->value(); + pool.setHeadData(headData); + PersistentObjectData data; + store(pool, data); + pool.setData(0, data); + pool.store(fileName); +} + +QString BuildProject::storedProjectFilePath(BuildGraph *bg, const QString &projectId) +{ + return bg->buildDirectoryRoot() + projectId + ".bg"; +} + +QStringList BuildProject::storedProjectFiles(BuildGraph *bg) +{ + QStringList result; + QDirIterator dirit(bg->buildDirectoryRoot(), QStringList() << "*.bg", QDir::Files); + while (dirit.hasNext()) + result += dirit.next(); + return result; +} + +void BuildProject::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + + setResolvedProject(pool.idLoadS<ResolvedProject>(s)); + + int count, i; + s >> count; + for (i = count; --i >= 0;) { + BuildProduct::Ptr product = pool.idLoadS<BuildProduct>(s); + product->project = this; + foreach (Artifact *artifact, product->artifacts) + artifact->project = this; + addBuildProduct(product); + } + + s >> count; + m_dependencyArtifacts.clear(); + m_dependencyArtifacts.reserve(count); + for (i = count; --i >= 0;) { + Artifact *artifact = pool.idLoad<Artifact>(s); + artifact->project = this; + m_dependencyArtifacts.insert(artifact->fileName, artifact); + } +} + +void BuildProject::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + + s << pool.store(resolvedProject()); + storeContainer(m_buildProducts, s, pool); + storeHashContainer(m_dependencyArtifacts, s, pool); +} + +char **createCFileTags(const QSet<QString> &fileTags) +{ + if (fileTags.isEmpty()) + return 0; + + char **buf = new char*[fileTags.count()]; + size_t i = 0; + foreach (const QString &fileTag, fileTags) { + buf[i] = qstrdup(fileTag.toLocal8Bit().data()); + ++i; + } + return buf; +} + +void freeCFileTags(char **cFileTags, int numFileTags) +{ + if (!cFileTags) + return; + for (int i = numFileTags; --i >= 0;) + delete[] cFileTags[i]; + delete[] cFileTags; +} + +BuildGraph * BuildProject::buildGraph() const +{ + return m_buildGraph; +} + +ResolvedProject::Ptr BuildProject::resolvedProject() const +{ + return m_resolvedProject; +} + +QSet<BuildProduct::Ptr> BuildProject::buildProducts() const +{ + return m_buildProducts; +} + +QHash<QString, Artifact *> &BuildProject::dependencyArtifacts() +{ + return m_dependencyArtifacts; +} + +bool BuildProject::dirty() const +{ + return m_dirty; +} + +Artifact *BuildProject::findArtifact(const QString &filePath) const +{ + Artifact *artifact = m_dependencyArtifacts.value(filePath); + if (!artifact) { + foreach (const BuildProduct::Ptr &product, m_buildProducts) { + artifact = product->artifacts.value(filePath); + if (artifact) + break; + } + } + return artifact; +} + +void BuildProject::markDirty() +{ + m_dirty = true; +} + +void BuildProject::addBuildProduct(const BuildProduct::Ptr &product) +{ + m_buildProducts.insert(product); +} + +void BuildProject::setResolvedProject(const ResolvedProject::Ptr &resolvedProject) +{ + m_resolvedProject = resolvedProject; +} + +QString fileName(Artifact *n) +{ + class BuildGraph *bg = n->project->buildGraph(); + QString str = n->fileName; + if (str.startsWith(bg->outputDirectoryRoot())) + str.remove(0, bg->outputDirectoryRoot().count()); + if (str.startsWith('/')) + str.remove(0, 1); + return str; +} + +} // namespace qbs diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h new file mode 100644 index 000000000..3c9992748 --- /dev/null +++ b/src/lib/buildgraph/buildgraph.h @@ -0,0 +1,216 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef BUILDGRAPH_H +#define BUILDGRAPH_H + +#include "artifactlist.h" + +#include <language/language.h> +#include <tools/error.h> +#include <tools/persistence.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QSet> +#include <QtCore/QSharedPointer> +#include <QtCore/QStringList> + +#include <QtCore/QDir> +#include <QtCore/QVector> +#include <QtCore/QVariant> +#include <QtScript/QScriptEngine> +#include <QtCore/QFutureInterface> + +namespace qbs { + +class Artifact; +class Transformer; +class BuildProject; + +class BuildProduct : public PersistentObject +{ +public: + typedef QSharedPointer<BuildProduct> Ptr; + + BuildProduct(); + ~BuildProduct(); + + const QList<Rule::Ptr> &topSortedRules() const; + + BuildProject *project; + ResolvedProduct::Ptr rProduct; + QSet<Artifact *> targetArtifacts; + QList<BuildProduct *> usings; + QHash<QString, Artifact *> artifacts; + +private: + void load(PersistentPool &pool, PersistentObjectData &data); + void store(PersistentPool &pool, PersistentObjectData &data) const; + +private: + mutable QList<Rule::Ptr> m_topSortedRules; +}; + +class BuildGraph; + +class BuildProject : public PersistentObject +{ + friend class BuildGraph; +public: + typedef QSharedPointer<BuildProject> Ptr; + + BuildProject(BuildGraph *bg); + ~BuildProject(); + + static BuildProject::Ptr load(BuildGraph *bg, const FileTime &minTimeStamp, Configuration::Ptr cfg, const QStringList &loaderSearchPaths); + void store(); + + BuildGraph *buildGraph() const; + ResolvedProject::Ptr resolvedProject() const; + QSet<BuildProduct::Ptr> buildProducts() const; + QHash<QString, Artifact *> &dependencyArtifacts(); + bool dirty() const; + Artifact *findArtifact(const QString &filePath) const; + +private: + static QString storedProjectFilePath(BuildGraph *bg, const QString &configId); + static QStringList storedProjectFiles(BuildGraph *bg); + void load(PersistentPool &pool, PersistentObjectData &data); + void store(PersistentPool &pool, PersistentObjectData &data) const; + void markDirty(); + void addBuildProduct(const BuildProduct::Ptr &product); + void setResolvedProject(const ResolvedProject::Ptr & resolvedProject); + +private: + BuildGraph *m_buildGraph; + ResolvedProject::Ptr m_resolvedProject; + QSet<BuildProduct::Ptr> m_buildProducts; + QHash<QString, Artifact *> m_dependencyArtifacts; + bool m_dirty; +}; + +class BuildGraphListener; + +/** + * N artifact, T transformer, parent -> child + * parent depends on child, child is a dependency of parent, + * parent is a dependent of child. + * + * N a.out -> N main.o -> N main.cpp + * + * Every artifact can point to a transformer which contains the commands. + * Multiple artifacts can point to the same transformer. + */ +class BuildGraph +{ + Q_DECLARE_TR_FUNCTIONS(BuildGraph) +public: + BuildGraph(); + ~BuildGraph(); + + BuildProject::Ptr resolveProject(ResolvedProject::Ptr, QFutureInterface<bool> &futureInterface); + BuildProduct::Ptr resolveProduct(BuildProject *, ResolvedProduct::Ptr, QFutureInterface<bool> &futureInterface); + + void dump(BuildProduct::Ptr) const; + void applyRules(BuildProduct *product, QMap<QString, QSet<Artifact *> > &artifactsPerFileTag); + static void detectCycle(BuildProject *project); + static void detectCycle(Artifact *a); + + void setOutputDirectoryRoot(const QString &buildDirectoryRoot) { m_outputDirectoryRoot = buildDirectoryRoot; } + const QString &outputDirectoryRoot() const { return m_outputDirectoryRoot; } + QString buildDirectoryRoot() const; + QString resolveOutPath(const QString &path, BuildProduct *) const; + + void connect(Artifact *p, Artifact *c); + void loggedConnect(Artifact *u, Artifact *v); + bool safeConnect(Artifact *u, Artifact *v); + void insert(BuildProduct::Ptr target, Artifact *n) const; + void insert(BuildProduct *target, Artifact *n) const; + void remove(Artifact *artifact) const; + void removeArtifactAndExclusiveDependents(Artifact *artifact, QList<Artifact*> *removedArtifacts = 0); + + void createOutputArtifact(BuildProduct *product, + const Rule::Ptr &rule, const RuleArtifact::Ptr &ruleArtifact, + const QSet<Artifact *> &inputArtifacts, + QList<QPair<RuleArtifact *, Artifact *> > *ruleArtifactArtifactMap, + QList<Artifact *> *outputArtifacts, + QSharedPointer<Transformer> &transformer); + + void onProductChanged(BuildProduct::Ptr product, ResolvedProduct::Ptr changedProduct); + void updateNodesThatMustGetNewTransformer(); + + static void setupScriptEngineForProduct(QScriptEngine *scriptEngine, ResolvedProduct::Ptr product, Rule::Ptr rule, BuildGraph *bg = 0); + +private: + Artifact *createArtifact(BuildProduct::Ptr product, SourceArtifact::Ptr sourceArtifact); + void applyRule(BuildProduct *product, QMap<QString, QSet<Artifact *> > &artifactsPerFileTag, Rule::Ptr rule); + void applyRule(BuildProduct *product, QMap<QString, QSet<Artifact *> > &artifactsPerFileTag, Rule::Ptr rule, const QSet<Artifact *> &inputArtifacts); + void createTransformerCommands(RuleScript::Ptr script, Transformer *transformer); + static void disconnect(Artifact *u, Artifact *v); + QSet<Artifact *> disconnect(Artifact *n) const; + void setupScriptEngineForArtifact(BuildProduct *product, Artifact *artifact); + void updateNodeThatMustGetNewTransformer(Artifact *artifact); + static void detectCycle(Artifact *v, QSet<Artifact *> &done, QSet<Artifact *> ¤tBranch); + +private: + QString m_outputDirectoryRoot; /// The directory where the 'build' and 'targets' subdirectories end up. + QScriptEngine m_scriptEngine; + QHash<ResolvedProduct::Ptr, BuildProduct::Ptr> m_productCache; + QHash<QString, QScriptValue> m_jsImportCache; + QHash<QString, QScriptProgram> m_scriptProgramCache; + mutable QSet<Artifact *> m_artifactsThatMustGetNewTransformers; +}; + +// debugging helper +QString fileName(Artifact *n); + +// debugging helper +template <typename T> +static QStringList toStringList(const T &artifactContainer) +{ + QStringList l; + foreach (Artifact *n, artifactContainer) + l.append(fileName(n)); + return l; +} + +char **createCFileTags(const QSet<QString> &fileTags); +void freeCFileTags(char **cFileTags, int numFileTags); + +} // namespace qbs + +#endif // BUILDGRAPH_H diff --git a/src/lib/buildgraph/buildgraph.pri b/src/lib/buildgraph/buildgraph.pri new file mode 100644 index 000000000..779994a74 --- /dev/null +++ b/src/lib/buildgraph/buildgraph.pri @@ -0,0 +1,26 @@ +INCLUDEPATH += $$PWD +SOURCES += \ + $$PWD/automoc.cpp\ + $$PWD/buildgraph.cpp\ + $$PWD/executor.cpp\ + $$PWD/executorjob.cpp\ + $$PWD/rulegraph.cpp\ + $$PWD/scanresultcache.cpp \ + $$PWD/artifactlist.cpp \ + $$PWD/command.cpp \ + $$PWD/commandexecutor.cpp \ + $$PWD/transformer.cpp \ + $$PWD/artifact.cpp + +HEADERS += \ + $$PWD/automoc.h\ + $$PWD/buildgraph.h\ + $$PWD/executor.h\ + $$PWD/executorjob.h\ + $$PWD/rulegraph.h\ + $$PWD/scanresultcache.h \ + $$PWD/artifactlist.h \ + $$PWD/command.h \ + $$PWD/commandexecutor.h \ + $$PWD/transformer.h \ + $$PWD/artifact.h diff --git a/src/lib/buildgraph/command.cpp b/src/lib/buildgraph/command.cpp new file mode 100644 index 000000000..189f04b50 --- /dev/null +++ b/src/lib/buildgraph/command.cpp @@ -0,0 +1,231 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "command.h" + +#include <QDebug> +#include <QScriptEngine> +#include <QtScript/QScriptValueIterator> + +namespace qbs { + +AbstractCommand::AbstractCommand() + : m_silent(true) +{ +} + +AbstractCommand::~AbstractCommand() +{ +} + +AbstractCommand *AbstractCommand::createByType(AbstractCommand::CommandType commandType) +{ + switch (commandType) { + case qbs::AbstractCommand::AbstractCommandType: + break; + case qbs::AbstractCommand::ProcessCommandType: + return new ProcessCommand; + case qbs::AbstractCommand::JavaScriptCommandType: + return new JavaScriptCommand; + } + return 0; +} + +void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue) +{ + m_description = scriptValue->property("description").toString(); + m_highlight = scriptValue->property("highlight").toString(); + m_silent = scriptValue->property("silent").toBool(); +} + +void AbstractCommand::load(QDataStream &s) +{ + s >> m_description >> m_highlight >> m_silent; +} + +void AbstractCommand::store(QDataStream &s) +{ + s << m_description << m_highlight << m_silent; +} + +static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engine) +{ + static AbstractCommand commandPrototype; + QScriptValue cmd = context->thisObject(); + cmd.setProperty("description", engine->toScriptValue(commandPrototype.description())); + cmd.setProperty("highlight", engine->toScriptValue(commandPrototype.highlight())); + cmd.setProperty("silent", engine->toScriptValue(commandPrototype.isSilent())); + return cmd; +} + +static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() != 2) { + return context->throwError(QScriptContext::SyntaxError, + "Command c'tor expects 2 arguments"); + } + + static ProcessCommand commandPrototype; + + QScriptValue program = context->argument(0); + if (program.isUndefined()) + program = engine->toScriptValue(commandPrototype.program()); + QScriptValue arguments = context->argument(1); + if (arguments.isUndefined()) + arguments = engine->toScriptValue(commandPrototype.arguments()); + QScriptValue cmd = js_CommandBase(context, engine); + cmd.setProperty("className", engine->toScriptValue(QString("Command"))); + cmd.setProperty("program", program); + cmd.setProperty("arguments", arguments); + cmd.setProperty("workingDir", engine->toScriptValue(commandPrototype.workingDir())); + cmd.setProperty("maxExitCode", engine->toScriptValue(commandPrototype.maxExitCode())); + cmd.setProperty("stdoutFilterFunction", engine->toScriptValue(commandPrototype.stdoutFilterFunction())); + cmd.setProperty("stderrFilterFunction", engine->toScriptValue(commandPrototype.stderrFilterFunction())); + cmd.setProperty("responseFileThreshold", engine->toScriptValue(commandPrototype.responseFileThreshold())); + cmd.setProperty("responseFileUsagePrefix", engine->toScriptValue(commandPrototype.responseFileUsagePrefix())); + return cmd; +} + + +void ProcessCommand::setupForJavaScript(QScriptEngine *engine) +{ + QScriptValue ctor = engine->newFunction(js_Command, 2); + engine->globalObject().setProperty("Command", ctor); +} + +ProcessCommand::ProcessCommand() + : m_maxExitCode(0), + m_responseFileThreshold(32000) +{ +} + +void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue) +{ + AbstractCommand::fillFromScriptValue(scriptValue); + m_program = scriptValue->property("program").toString(); + m_arguments = scriptValue->property("arguments").toVariant().toStringList(); + m_workingDir = scriptValue->property("workingDirectory").toString(); + m_maxExitCode = scriptValue->property("maxExitCode").toInt32(); + m_stdoutFilterFunction = scriptValue->property("stdoutFilterFunction").toString(); + m_stderrFilterFunction = scriptValue->property("stderrFilterFunction").toString(); + m_responseFileThreshold = scriptValue->property("responseFileThreshold").toInt32(); + m_responseFileUsagePrefix = scriptValue->property("responseFileUsagePrefix").toString(); +} + +void ProcessCommand::load(QDataStream &s) +{ + AbstractCommand::load(s); + s >> m_program + >> m_arguments + >> m_workingDir + >> m_maxExitCode + >> m_stdoutFilterFunction + >> m_stderrFilterFunction + >> m_responseFileThreshold + >> m_responseFileUsagePrefix; +} + +void ProcessCommand::store(QDataStream &s) +{ + AbstractCommand::store(s); + s << m_program + << m_arguments + << m_workingDir + << m_maxExitCode + << m_stdoutFilterFunction + << m_stderrFilterFunction + << m_responseFileThreshold + << m_responseFileUsagePrefix; +} + +static QScriptValue js_JavaScriptCommand(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() != 0) { + return context->throwError(QScriptContext::SyntaxError, + "JavaScriptCommand c'tor doesn't take arguments."); + } + + static JavaScriptCommand commandPrototype; + QScriptValue cmd = js_CommandBase(context, engine); + cmd.setProperty("className", engine->toScriptValue(QString("JavaScriptCommand"))); + cmd.setProperty("sourceCode", engine->toScriptValue(commandPrototype.sourceCode())); + return cmd; +} + +void JavaScriptCommand::setupForJavaScript(QScriptEngine *engine) +{ + QScriptValue ctor = engine->newFunction(js_JavaScriptCommand, 0); + engine->globalObject().setProperty("JavaScriptCommand", ctor); +} + +JavaScriptCommand::JavaScriptCommand() +{ +} + +void JavaScriptCommand::fillFromScriptValue(const QScriptValue *scriptValue) +{ + AbstractCommand::fillFromScriptValue(scriptValue); + QScriptValue sourceCode = scriptValue->property("sourceCode"); + if (sourceCode.isFunction()) { + m_sourceCode = "(" + sourceCode.toString() + ")()"; + } else { + m_sourceCode = sourceCode.toString(); + } + static QSet<QString> predefinedProperties = QSet<QString>() + << "description" << "highlight" << "silent" << "className" << "sourceCode"; + + QScriptValueIterator it(*scriptValue); + while (it.hasNext()) { + it.next(); + if (predefinedProperties.contains(it.name())) + continue; + m_properties.insert(it.name(), it.value().toVariant()); + } +} + +void JavaScriptCommand::load(QDataStream &s) +{ + AbstractCommand::load(s); + s >> m_sourceCode >> m_properties; +} + +void JavaScriptCommand::store(QDataStream &s) +{ + AbstractCommand::store(s); + s << m_sourceCode << m_properties; +} + +} // namespace qbs diff --git a/src/lib/buildgraph/command.h b/src/lib/buildgraph/command.h new file mode 100644 index 000000000..14bd11f00 --- /dev/null +++ b/src/lib/buildgraph/command.h @@ -0,0 +1,154 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef COMMAND_H +#define COMMAND_H + +#include <QtCore/QStringList> +#include <QtCore/QVariantMap> + +QT_BEGIN_NAMESPACE +class QScriptEngine; +class QScriptValue; +QT_END_NAMESPACE + +namespace qbs { + +class AbstractCommand +{ +public: + AbstractCommand(); + virtual ~AbstractCommand(); + + enum CommandType { + AbstractCommandType, + ProcessCommandType, + JavaScriptCommandType + }; + + static AbstractCommand *createByType(CommandType commandType); + + virtual CommandType type() const { return AbstractCommandType; } + virtual void fillFromScriptValue(const QScriptValue *scriptValue); + virtual void load(QDataStream &s); + virtual void store(QDataStream &s); + + const QString description() const { return m_description; } + void setDescription(const QString &str) { m_description = str; } + + const QString highlight() const { return m_highlight; } + void setHighlight(const QString &str) { m_highlight = str; } + + bool isSilent() const { return m_silent; } + void setSilent(bool b) { m_silent = b; } + +private: + QString m_description; + QString m_highlight; + bool m_silent; +}; + +class ProcessCommand : public AbstractCommand +{ +public: + static void setupForJavaScript(QScriptEngine *engine); + + ProcessCommand(); + + CommandType type() const { return ProcessCommandType; } + void fillFromScriptValue(const QScriptValue *scriptValue); + void load(QDataStream &s); + void store(QDataStream &s); + + const QString program() const { return m_program; } + void setProgram(const QString &str) { m_program = str; } + + const QStringList arguments() const { return m_arguments; } + void setArguments(const QStringList &l) { m_arguments = l; } + + const QString workingDir() const { return m_workingDir; } + void setWorkingDir(const QString &str) { m_workingDir = str; } + + int maxExitCode() const { return m_maxExitCode; } + void setMaxExitCode(int n) { m_maxExitCode = n; } + + QString stdoutFilterFunction() const { return m_stdoutFilterFunction; } + void setStdoutFilterFunction(const QString &filter) { m_stdoutFilterFunction = filter; } + + QString stderrFilterFunction() const { return m_stderrFilterFunction; } + void setStderrFilterFunction(const QString &filter) { m_stderrFilterFunction = filter; } + + int responseFileThreshold() const { return m_responseFileThreshold; } + void setResponseFileThreshold(int n) { m_responseFileThreshold = n; } + + QString responseFileUsagePrefix() const { return m_responseFileUsagePrefix; } + void setResponseFileUsagePrefix(const QString &function) { m_responseFileUsagePrefix = function; } + +private: + QString m_program; + QStringList m_arguments; + QString m_workingDir; + int m_maxExitCode; + QString m_stdoutFilterFunction; + QString m_stderrFilterFunction; + int m_responseFileThreshold; // When to use response files? In bytes of (program name + arguments). + QString m_responseFileUsagePrefix; +}; + +class JavaScriptCommand : public AbstractCommand +{ +public: + static void setupForJavaScript(QScriptEngine *engine); + + JavaScriptCommand(); + + virtual CommandType type() const { return JavaScriptCommandType; } + void fillFromScriptValue(const QScriptValue *scriptValue); + void load(QDataStream &s); + void store(QDataStream &s); + + const QString &sourceCode() const { return m_sourceCode; } + void setSourceCode(const QString &str) { m_sourceCode = str; } + const QVariantMap &properties() const { return m_properties; } + +private: + QString m_sourceCode; + QVariantMap m_properties; +}; + +} // namespace qbs + +#endif // COMMAND_H diff --git a/src/lib/buildgraph/commandexecutor.cpp b/src/lib/buildgraph/commandexecutor.cpp new file mode 100644 index 000000000..7a72a6c98 --- /dev/null +++ b/src/lib/buildgraph/commandexecutor.cpp @@ -0,0 +1,376 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "commandexecutor.h" +#include "command.h" +#include "buildgraph.h" +#include "processoutput.h" + +#include <buildgraph/artifact.h> +#include <buildgraph/transformer.h> +#include <tools/logger.h> + +#include <QtConcurrentRun> +#include <QDebug> +#include <QFutureWatcher> +#include <QProcess> +#include <QScriptEngine> +#include <QThread> +#include <QTemporaryFile> + +namespace qbs { + +struct JavaScriptCommandFutureResult +{ + bool success; + QString errorMessage; +}; + +class JavaScriptCommandFutureWatcher : public QFutureWatcher<JavaScriptCommandFutureResult> +{ +public: + JavaScriptCommandFutureWatcher(QObject *parent) + : QFutureWatcher<JavaScriptCommandFutureResult>(parent) + {} +}; + +CommandExecutor::CommandExecutor(QObject *parent) + : QObject(parent) + , m_processCommand(0) + , m_process(0) + , m_mainThreadScriptEngine(0) + , m_transformer(0) + , m_jsCommand(0) + , m_jsFutureWatcher(0) +{ + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onProcessError(QProcess::ProcessError))); + connect(&m_process, SIGNAL(finished(int)), this, SLOT(onProcessFinished(int))); +} + +void CommandExecutor::setProcessEnvironment(const QProcessEnvironment &processEnvironment) +{ + m_process.setProcessEnvironment(processEnvironment); +} + +void CommandExecutor::waitForFinished() +{ + if (m_process.state() == QProcess::Running) + m_process.waitForFinished(-1); + if (m_jsFutureWatcher && m_jsFutureWatcher->isRunning()) + m_jsFutureWatcher->waitForFinished(); +} + +void CommandExecutor::start(Transformer *transformer, AbstractCommand *cmd) +{ + m_processCommand = 0; + m_jsCommand = 0; + + switch (cmd->type()) { + case AbstractCommand::AbstractCommandType: + qWarning("CommandExecutor can't execute abstract commands."); + return; + case AbstractCommand::ProcessCommandType: + m_processCommand = static_cast<ProcessCommand*>(cmd); + startProcessCommand(); + return; + case AbstractCommand::JavaScriptCommandType: + m_jsCommand = static_cast<JavaScriptCommand*>(cmd); + m_transformer = transformer; + startJavaScriptCommand(); + return; + } + + emit error("CommandExecutor: unknown command type."); + return; +} + +static QHash<QString, TextColor> setupColorTable() +{ + QHash<QString, TextColor> colorTable; + colorTable["compiler"] = TextColorDefault; + colorTable["linker"] = TextColorDarkGreen; + colorTable["codegen"] = TextColorDarkYellow; + return colorTable; +} + +void CommandExecutor::printCommandInfo(AbstractCommand *cmd) +{ + if (!cmd->description().isEmpty()) { + static QHash<QString, TextColor> colorTable = setupColorTable(); + qbsInfo() << DontPrintLogLevel << LogOutputStdOut + << colorTable.value(cmd->highlight(), TextColorDefault) + << cmd->description(); + } +} + +void CommandExecutor::startProcessCommand() +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + printCommandInfo(m_processCommand); + if (!m_processCommand->isSilent()) { + QString commandLine = m_processCommand->program() + QLatin1Char(' ') + m_processCommand->arguments().join(" "); + qbsInfo() << DontPrintLogLevel << LogOutputStdOut << commandLine; + } + if (qbsLogLevel(LoggerDebug)) { + qbsDebug() << "[EXEC] " << m_processCommand->program() + QLatin1Char(' ') + m_processCommand->arguments().join(" "); + } + + // Automatically use response files, if the command line gets to long. + QStringList arguments = m_processCommand->arguments(); + if (!m_processCommand->responseFileUsagePrefix().isEmpty()) { + int commandLineLength = m_processCommand->program().length() + 1; + for (int i = m_processCommand->arguments().count(); --i >= 0;) + commandLineLength += m_processCommand->arguments().at(i).length(); + if (m_processCommand->responseFileThreshold() >= 0 && commandLineLength > m_processCommand->responseFileThreshold()) { + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] Using response file. Threshold is %d. Command line length %d.", m_processCommand->responseFileThreshold(), commandLineLength); + + // The QTemporaryFile keeps a handle on the file, even if closed. + // On Windows, some commands (e.g. msvc link.exe) won't accept that. + // We need to delete the file manually, later. + QTemporaryFile responseFile; + responseFile.setAutoRemove(false); + responseFile.setFileTemplate(QDir::tempPath() + "/qbsresp"); + if (!responseFile.open()) { + QString errorMessage = "Cannot create response file."; + emit error(errorMessage); + return; + } + for (int i = 0; i < m_processCommand->arguments().count(); ++i) { + responseFile.write(m_processCommand->arguments().at(i).toLocal8Bit()); + responseFile.write("\n"); + } + responseFile.close(); + arguments.clear(); + arguments += QDir::toNativeSeparators(m_processCommand->responseFileUsagePrefix() + responseFile.fileName()); + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] command line with response file: %s %s", qPrintable(m_processCommand->program()), qPrintable(arguments.join(" "))); + } + } + + m_process.setWorkingDirectory(m_processCommand->workingDir()); + m_process.start(m_processCommand->program(), arguments); +} + +QByteArray CommandExecutor::filterProcessOutput(const QByteArray &output, const QString &filterFunctionSource) +{ + if (filterFunctionSource.isEmpty()) + return output; + + QScriptValue filterFunction = m_mainThreadScriptEngine->evaluate("var f = " + filterFunctionSource + "; f"); + if (!filterFunction.isFunction()) { + emit error(QString("Error in filter function: %1.\n%2").arg(filterFunctionSource, filterFunction.toString())); + return output; + } + + QScriptValue outputArg = m_mainThreadScriptEngine->newArray(1); + outputArg.setProperty(0, m_mainThreadScriptEngine->toScriptValue(QString::fromLatin1(output))); + QScriptValue filteredOutput = filterFunction.call(m_mainThreadScriptEngine->undefinedValue(), outputArg); + if (filteredOutput.isError()) { + emit error(QString("Error when calling ouput filter function: %1").arg(filteredOutput.toString())); + return output; + } + + return filteredOutput.toString().toLocal8Bit(); +} + +void CommandExecutor::sendProcessOutput(bool logCommandLine) +{ + QString commandLine = m_processCommand->program(); + if (!m_processCommand->arguments().isEmpty()) { + commandLine += ' '; + commandLine += m_processCommand->arguments().join(" "); + } + + QByteArray processStdOut = filterProcessOutput(m_process.readAllStandardOutput(), m_processCommand->stdoutFilterFunction()); + QByteArray processStdErr = filterProcessOutput(m_process.readAllStandardError(), m_processCommand->stderrFilterFunction()); + + bool processOutputEmpty = processStdOut.isEmpty() && processStdErr.isEmpty(); + if (logCommandLine || !processOutputEmpty) { + qbsInfo() << DontPrintLogLevel << commandLine << (processOutputEmpty ? "" : "\n") + << processStdOut << processStdErr; + } + + ProcessOutput processOutput; + processOutput.setCommandLine(commandLine); + processOutput.setStandardOutput(processStdOut); + processOutput.setStandardError(processStdErr); + Logger::instance().sendProcessOutput(processOutput); +} + +void CommandExecutor::onProcessError(QProcess::ProcessError processError) +{ + removeResponseFile(); + sendProcessOutput(true); + QString errorMessage; + switch (processError) { + case QProcess::FailedToStart: + errorMessage = "Process could not be started."; + break; + case QProcess::Crashed: + errorMessage = "Process crashed."; + break; + case QProcess::Timedout: + errorMessage = "Process timed out."; + break; + case QProcess::ReadError: + errorMessage = "Error when reading process output."; + break; + case QProcess::WriteError: + errorMessage = "Error when writing to process."; + break; + default: + errorMessage = "Unknown process error."; + break; + } + emit error(errorMessage); +} + +void CommandExecutor::onProcessFinished(int exitCode) +{ + removeResponseFile(); + bool errorOccurred = exitCode > m_processCommand->maxExitCode(); + sendProcessOutput(errorOccurred); + if (errorOccurred) { + QString msg = "Process failed with exit code %1."; + emit error(msg.arg(exitCode)); + return; + } + + emit finished(); +} + +class JSRunner +{ +public: + typedef JavaScriptCommandFutureResult result_type; + + JSRunner(JavaScriptCommand *jsCommand) + : m_jsCommand(jsCommand) + {} + + JavaScriptCommandFutureResult operator() (Transformer *transformer) + { + result_type result; + result.success = true; + QThread *currentThread = QThread::currentThread(); + QScriptEngine *scriptEngine = m_scriptEnginesPerThread.value(currentThread); + if (!scriptEngine) { + scriptEngine = new QScriptEngine(); + m_scriptEnginesPerThread.insert(currentThread, scriptEngine); + + // import script extension plugins + foreach (const QString &name, scriptEngine->availableExtensions()) { + if (!name.startsWith("qbs")) + continue; + QScriptValue e = scriptEngine->importExtension(name); + if (e.isError()) { + qbsWarning("JS thread %x, unable to load %s into QScriptEngine: %s", + (void*)currentThread, + qPrintable(name), + qPrintable(e.toString())); + } + qbsDebug("JS thread %x, script plugin loaded: %s", (void*)currentThread, qPrintable(name)); + } + } + + QString trafoPtrStr = QString::number((qulonglong)transformer); + if (scriptEngine->globalObject().property("_qbs_transformer_ptr").toString() != trafoPtrStr) { + scriptEngine->globalObject().setProperty("_qbs_transformer_ptr", scriptEngine->toScriptValue(trafoPtrStr)); + + Artifact *someOutputArtifact = *transformer->outputs.begin(); + if (someOutputArtifact->product) { + ResolvedProduct::Ptr product = someOutputArtifact->product->rProduct; + BuildGraph::setupScriptEngineForProduct(scriptEngine, product, transformer->rule); + } + transformer->setupInputs(scriptEngine, scriptEngine->globalObject()); + transformer->setupOutputs(scriptEngine, scriptEngine->globalObject()); + } + + scriptEngine->pushContext(); + for (QVariantMap::const_iterator it = m_jsCommand->properties().constBegin(); it != m_jsCommand->properties().constEnd(); ++it) + scriptEngine->currentContext()->activationObject().setProperty(it.key(), scriptEngine->toScriptValue(it.value())); + scriptEngine->evaluate(m_jsCommand->sourceCode()); + if (scriptEngine->hasUncaughtException()) { + result.success = false; + result.errorMessage = scriptEngine->uncaughtException().toString(); + } + scriptEngine->popContext(); + return result; + } + +private: + static QHash<QThread *, QScriptEngine *> m_scriptEnginesPerThread; + JavaScriptCommand *m_jsCommand; +}; + +QHash<QThread *, QScriptEngine *> JSRunner::m_scriptEnginesPerThread; + +void CommandExecutor::startJavaScriptCommand() +{ + printCommandInfo(m_jsCommand); + QFuture<JSRunner::result_type> future = QtConcurrent::run(JSRunner(m_jsCommand), m_transformer); + if (!m_jsFutureWatcher) { + m_jsFutureWatcher = new JavaScriptCommandFutureWatcher(this); + connect(m_jsFutureWatcher, SIGNAL(finished()), SLOT(onJavaScriptCommandFinished())); + } + m_jsFutureWatcher->setFuture(future); +} + +void CommandExecutor::onJavaScriptCommandFinished() +{ + JavaScriptCommandFutureResult result = m_jsFutureWatcher->future().result(); + if (result.success) { + emit finished(); + } else { + qbsInfo() << DontPrintLogLevel << "JS context:\n" << m_jsCommand->properties(); + qbsInfo() << DontPrintLogLevel << "JS code:\n" << m_jsCommand->sourceCode(); + QString msg = "Error while executing JavaScriptCommand: "; + msg += result.errorMessage; + emit error(msg); + } +} + +void CommandExecutor::removeResponseFile() +{ + if (m_responseFileName.isEmpty()) + return; + QFile::remove(m_responseFileName); + m_responseFileName.clear(); +} + +} // namespace qbs diff --git a/src/lib/buildgraph/commandexecutor.h b/src/lib/buildgraph/commandexecutor.h new file mode 100644 index 000000000..3b5a1273f --- /dev/null +++ b/src/lib/buildgraph/commandexecutor.h @@ -0,0 +1,106 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef COMMANDEXECUTOR_H +#define COMMANDEXECUTOR_H + +#include <QObject> +#include <QProcess> + +QT_BEGIN_NAMESPACE +class QProcess; +class QScriptEngine; +QT_END_NAMESPACE + +namespace qbs { + +class AbstractCommand; +class ProcessCommand; +class JavaScriptCommand; +class JavaScriptCommandFutureWatcher; +class Transformer; + +class CommandExecutor : public QObject +{ + Q_OBJECT +public: + explicit CommandExecutor(QObject *parent = 0); + + void setMainThreadScriptEngine(QScriptEngine *engine) { m_mainThreadScriptEngine = engine; } + void setDryRunEnabled(bool enabled) { dryRun = enabled; } + void setProcessEnvironment(const QProcessEnvironment &processEnvironment); + void waitForFinished(); + +signals: + void error(QString errorString); + void finished(); + +public slots: + void start(Transformer *transformer, AbstractCommand *cmd); + +protected: + void printCommandInfo(AbstractCommand *cmd); + void startProcessCommand(); + QByteArray filterProcessOutput(const QByteArray &output, const QString &filterFunctionSource); + void sendProcessOutput(bool logCommandLine = false); + void startJavaScriptCommand(); + +protected slots: + void onProcessError(QProcess::ProcessError); + void onProcessFinished(int exitCode); + void onJavaScriptCommandFinished(); + +private: + void removeResponseFile(); + +private: + bool dryRun; + + // members for executing ProcessCommand + ProcessCommand *m_processCommand; + QProcess m_process; + QString m_responseFileName; + QScriptEngine *m_mainThreadScriptEngine; + + // members for executing JavaScriptCommand members + Transformer *m_transformer; + JavaScriptCommand *m_jsCommand; + JavaScriptCommandFutureWatcher *m_jsFutureWatcher; +}; + +} // namespace qbs + +#endif // COMMANDEXECUTOR_H diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp new file mode 100644 index 000000000..4b3e8b38b --- /dev/null +++ b/src/lib/buildgraph/executor.cpp @@ -0,0 +1,885 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "executor.h" +#include "executorjob.h" +#include "scanresultcache.h" +#include "automoc.h" + +#include <buildgraph/transformer.h> +#include <language/language.h> +#include <tools/fileinfo.h> +#include <tools/logger.h> +#include <tools/scannerpluginmanager.h> + +#ifdef Q_OS_WIN32 +#include <Windows.h> +#endif + +namespace qbs { + +static QHashDummyValue hashDummy; + +Executor::Executor(int maxJobs) + : m_scriptEngine(0) + , m_runOnceAndForgetMode(false) + , m_state(ExecutorIdle) + , m_keepGoing(false) + , m_maximumJobNumber(maxJobs) + , m_futureInterface(0) +{ + m_autoMoc = new AutoMoc; + m_autoMoc->setScanResultCache(&m_scanResultCache); + if (!m_runOnceAndForgetMode) { + connect(this, SIGNAL(finished()), SLOT(resetArtifactsToUntouched())); + } + qbsDebug("[EXEC] preparing executor for %d jobs in parallel", maxJobs); + addExecutorJobs(maxJobs); +} + +Executor::~Executor() +{ + // jobs must be destroyed before deleting the shared scan result cache + foreach (ExecutorJob *job, m_availableJobs) + delete job; + foreach (ExecutorJob *job, m_processingJobs.keys()) + delete job; + delete m_autoMoc; +} + +void Executor::build(const QList<BuildProject::Ptr> projectsToBuild, const QStringList &changedFiles, const QStringList &selectedProductNames, + QFutureInterface<bool> &futureInterface) +{ + Q_ASSERT(m_state != ExecutorRunning); + m_leaves.clear(); + m_futureInterface = &futureInterface; + bool success = true; + + setState(ExecutorRunning); + Artifact::BuildState initialBuildState = changedFiles.isEmpty() ? Artifact::Buildable : Artifact::Built; + + if (!m_scriptEngine) { + m_scriptEngine = new QScriptEngine(this); + foreach (ExecutorJob *job, findChildren<ExecutorJob *>()) + job->setMainThreadScriptEngine(m_scriptEngine); + } + + // determine the products we want to build + m_projectsToBuild = projectsToBuild; + if (selectedProductNames.isEmpty()) { + // Use all products we have in the build graph. + m_productsToBuild.clear(); + foreach (BuildProject::Ptr project, m_projectsToBuild) + foreach (BuildProduct::Ptr product, project->buildProducts()) + m_productsToBuild += product; + } else { + // Try to find the selected products and their dependencies. + QHash<QString, BuildProduct::Ptr> productsPerName; + foreach (BuildProject::Ptr project, m_projectsToBuild) + foreach (BuildProduct::Ptr product, project->buildProducts()) + productsPerName.insert(product->rProduct->name.toLower(), product); + + QSet<BuildProduct::Ptr> selectedProducts; + foreach (const QString &productName, selectedProductNames) { + BuildProduct::Ptr product = productsPerName.value(productName.toLower()); + if (!product) { + qbsWarning() << "Selected product " << productName << " not found."; + continue; + } + selectedProducts += product; + } + QSet<BuildProduct::Ptr> s = selectedProducts; + do { + QSet<BuildProduct::Ptr> t; + foreach (const BuildProduct::Ptr &product, s) + foreach (BuildProduct *dependency, product->usings) + t += productsPerName.value(dependency->rProduct->name.toLower()); + selectedProducts += t; + s = t; + } while (!s.isEmpty()); + m_productsToBuild = selectedProducts.toList(); + } + + QList<Artifact *> changedArtifacts; + foreach (const QString &filePath, changedFiles) { + Artifact *artifact = 0; + foreach (BuildProject::Ptr project, m_projectsToBuild) { + artifact = project->findArtifact(filePath); + if (artifact) + break; + } + if (!artifact) { + qbsWarning() << QString("Out of date file '%1' provided but not found.").arg(QDir::toNativeSeparators(filePath)); + continue; + } + changedArtifacts += artifact; + } + + // prepare products + const QProcessEnvironment systemEnvironment = QProcessEnvironment::systemEnvironment(); + foreach (BuildProduct::Ptr product, m_productsToBuild) { + try { + product->rProduct->setupBuildEnvironment(m_scriptEngine, systemEnvironment); + } catch (Error &e) { + setError(e.toString()); + return; + } + foreach (Artifact *artifact, product->artifacts) + if (artifact->artifactType == Artifact::SourceFile) + artifact->buildState = initialBuildState; + } + + // find the root nodes + m_roots.clear(); + foreach (BuildProduct::Ptr product, m_productsToBuild) + foreach (Artifact *rootArtifact, product->targetArtifacts) + m_roots += rootArtifact; + + // mark the artifacts we want to build + prepareBuildGraph(initialBuildState); + + // determine which artifacts are out of date + const bool changedFilesProvided = !changedFiles.isEmpty(); + if (!changedFilesProvided) { + doOutOfDateCheck(); + } + + if (success) + success = runAutoMoc(); + if (success) { + if (changedFilesProvided) { + foreach (Artifact *artifact, changedArtifacts) + scanFileDependencies(artifact); + } else { + doDependencyScanTopDown(); + updateBuildGraph(initialBuildState); + } + initLeaves(changedArtifacts); + } + + m_futureInterface->setProgressRange(0 , m_leaves.count()); + + if (success) { + bool stillArtifactsToExecute = run(futureInterface); + + if (!stillArtifactsToExecute) + finish(); + } +} + +void Executor::setDryRun(bool b) +{ + foreach (ExecutorJob *job, m_availableJobs) + job->setDryRun(b); +} + +void Executor::setMaximumJobs(int numberOfJobs) +{ + if (numberOfJobs == m_maximumJobNumber) + return; + + m_maximumJobNumber = numberOfJobs; + int actualJobNumber = m_availableJobs.count() + m_processingJobs.count(); + if (actualJobNumber > m_maximumJobNumber) { + removeExecutorJobs(actualJobNumber - m_maximumJobNumber); + } else { + addExecutorJobs(m_maximumJobNumber - actualJobNumber); + } +} + +int Executor::maximumJobs() const +{ + return m_maximumJobNumber; +} + +bool Executor::isLeaf(Artifact *artifact) +{ + foreach (Artifact *child, artifact->children) + if (child->buildState != Artifact::Built) + return false; + return true; +} + +static void markAsOutOfDateBottomUp(Artifact *artifact) +{ + if (artifact->buildState == Artifact::Untouched) + return; + artifact->buildState = Artifact::Buildable; + artifact->outOfDateCheckPerformed = true; + artifact->isOutOfDate = true; + artifact->isExistingFile = FileInfo(artifact->fileName).exists(); + foreach (Artifact *parent, artifact->parents) + markAsOutOfDateBottomUp(parent); +} + +void Executor::initLeaves(const QList<Artifact *> &changedArtifacts) +{ + if (changedArtifacts.isEmpty()) { + QSet<Artifact *> seenArtifacts; + foreach (Artifact *root, m_roots) + initLeavesTopDown(root, seenArtifacts); + } else { + foreach (Artifact *artifact, changedArtifacts) { + m_leaves.insert(artifact, hashDummy); + markAsOutOfDateBottomUp(artifact); + } + } +} + +void Executor::initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts) +{ + if (seenArtifacts.contains(artifact)) + return; + seenArtifacts += artifact; + + + if (artifact->children.isEmpty()) { + m_leaves.insert(artifact, hashDummy); + } else { + foreach (Artifact *child, artifact->children) + initLeavesTopDown(child, seenArtifacts); + } +} + +/** + * Returns true if there are still artifacts to traverse. + */ +bool Executor::run(QFutureInterface<bool> &futureInterface) +{ + while (m_state == ExecutorRunning) { + if (m_futureInterface->isCanceled()) { + qbsInfo() << "Build canceled."; + cancelJobs(); + return false; + } + + futureInterface.setProgressValue(futureInterface.progressValue() + 1); + if (m_leaves.isEmpty()) + return !m_processingJobs.isEmpty(); + + Artifact *leaf = m_leaves.begin().key(); + if (!execute(leaf)) + return true; + } + return false; +} + +FileTime Executor::timeStamp(Artifact *artifact) +{ + FileTime result = m_timeStampCache.value(artifact); + if (result.isValid()) + return result; + + FileInfo fi(artifact->fileName); + if (!fi.exists()) + return FileTime::currentTime(); + + result = fi.lastModified(); + foreach (Artifact *child, artifact->children) { + const FileTime childTime = timeStamp(child); + if (result < childTime) + result = childTime; + } + foreach (Artifact *fileDependency, artifact->fileDependencies) { + const FileTime ft = timeStamp(fileDependency); + if (result < ft) + result = ft; + } + + m_timeStampCache.insert(artifact, result); + return result; +} + +bool Executor::isOutOfDate(Artifact *artifact, bool &fileExists) +{ + FileInfo fi(artifact->fileName); + fileExists = fi.exists(); + if (!fileExists) + return true; + + FileTime artifactTimeStamp = fi.lastModified(); + foreach (Artifact *child, artifact->children) { + if (artifactTimeStamp < timeStamp(child)) + return true; + } + + return false; +} + +/** + * Returns false if the artifact cannot be executed right now + * and should be looked at later. + */ +bool Executor::execute(Artifact *artifact) +{ + if (qbsLogLevel(LoggerDebug)) + qbsDebug() << "[EXEC] " << fileName(artifact); + +// artifact->project->buildGraph()->dump(*artifact->products.begin()); + + if (m_availableJobs.isEmpty()) { + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] No jobs available. Trying later."); + return false; + } + + if (!artifact->outOfDateCheckPerformed) + doOutOfDateCheck(artifact); + bool fileExists = artifact->isExistingFile; + bool isDirty = artifact->isOutOfDate; + m_leaves.remove(artifact); + + if (!fileExists && artifact->artifactType == Artifact::SourceFile) { + setError(QString("Can't find source file '%1'.").arg(artifact->fileName)); + return true; + } + + if (!artifact->transformer) { + if (!fileExists) + qbsWarning() << tr("No transformer builds '%1'").arg(QDir::toNativeSeparators(artifact->fileName)); + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] No transformer. Skipping."); + finishArtifact(artifact); + return true; + } else if (!isDirty) { + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] Up to date. Skipping."); + finishArtifact(artifact); + return true; + } else { + foreach (Artifact *sideBySideArtifact, artifact->sideBySideArtifacts) { + if (sideBySideArtifact->transformer != artifact->transformer) + continue; + switch (sideBySideArtifact->buildState) + { + case Artifact::Untouched: + case Artifact::Buildable: + break; + case Artifact::Built: + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] Side by side artifact already finished. Skipping."); + finishArtifact(artifact); + return true; + case Artifact::Building: + if (qbsLogLevel(LoggerDebug)) + qbsDebug("[EXEC] Side by side artifact processing. Skipping."); + artifact->buildState = Artifact::Building; + return true; + } + } + + foreach (Artifact *output, artifact->transformer->outputs) { + // create the output directories + QDir outDir = QFileInfo(output->fileName).absoluteDir(); + if (!outDir.exists()) + outDir.mkpath("."); + } + + ExecutorJob *job = m_availableJobs.takeFirst(); + artifact->buildState = Artifact::Building; + m_processingJobs.insert(job, artifact); + + if (!artifact->product) + qbsError() << QString("BUG: Generated artifact %1 belongs to no product.").arg(QDir::toNativeSeparators(artifact->fileName)); + + job->run(artifact->transformer.data(), artifact->product); + } + + return true; +} + +void Executor::finishArtifact(Artifact *leaf) +{ + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[EXEC] finishArtifact " << fileName(leaf); + + leaf->buildState = Artifact::Built; + foreach (Artifact *parent, leaf->parents) { + if (parent->buildState != Artifact::Buildable) + continue; + + if (isLeaf(parent)) { + m_leaves.insert(parent, hashDummy); + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[EXEC] finishArtifact adds leaf " << fileName(parent) << " " << toString(parent->buildState); + } + } + + foreach (Artifact *sideBySideArtifact, leaf->sideBySideArtifacts) + if (leaf->transformer == sideBySideArtifact->transformer && sideBySideArtifact->buildState == Artifact::Building) + finishArtifact(sideBySideArtifact); +} + +void Executor::handleDependencies(Artifact *processedArtifact, Artifact *scannedArtifact, const QSet<QString> &resolvedDependencies) +{ + qbsTrace() << "[DEPSCAN] dependencies found for '" << fileName(scannedArtifact) + << "' while processing '" << fileName(processedArtifact) << "'"; + + foreach (const QString &dependencyFilePath, resolvedDependencies) + handleDependency(processedArtifact, dependencyFilePath); +} + +void Executor::handleDependency(Artifact *processedArtifact, const QString &dependencyFilePath) +{ + BuildProduct *product = processedArtifact->product; + Artifact *dependency = 0; + bool insertIntoProduct = true; + Q_ASSERT(processedArtifact->artifactType == Artifact::Generated); + Q_CHECK_PTR(processedArtifact->product); + if ((dependency = product->artifacts.value(dependencyFilePath))) { + qbsTrace("[DEPSCAN] ok in product '%s'", qPrintable(dependencyFilePath)); + insertIntoProduct = false; + } else if ((dependency = processedArtifact->project->dependencyArtifacts().value(dependencyFilePath))) { + qbsTrace("[DEPSCAN] ok in deps '%s'", qPrintable(dependencyFilePath)); + } else { + // try to find the dependency in other products of this project + foreach (BuildProduct::Ptr otherProduct, product->project->buildProducts()) { + if (otherProduct == product) + continue; + if ((dependency = otherProduct->artifacts.value(dependencyFilePath))) { + insertIntoProduct = false; + if (qbsLogLevel(LoggerTrace)) + qbsTrace("[DEPSCAN] found in product '%s': '%s'", qPrintable(otherProduct->rProduct->name), qPrintable(dependencyFilePath)); + break; + } + } + } + + // dependency not found in the whole build graph, thus create a new artifact + if (!dependency) { + qbsTrace("[DEPSCAN] + '%s'", qPrintable(dependencyFilePath)); + dependency = new Artifact(processedArtifact->project); + dependency->artifactType = Artifact::FileDependency; + dependency->configuration = processedArtifact->configuration; + dependency->fileName = dependencyFilePath; + processedArtifact->project->dependencyArtifacts().insert(dependencyFilePath, dependency); + } + + if (processedArtifact == dependency) + return; + + if (dependency->artifactType == Artifact::FileDependency) { + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[DEPSCAN] new file dependency " << fileName(dependency); + processedArtifact->fileDependencies.insert(dependency); + } else { + if (processedArtifact->children.contains(dependency)) + return; + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[DEPSCAN] new artifact dependency " << fileName(dependency); + BuildGraph *buildGraph = product->project->buildGraph(); + if (insertIntoProduct && !product->artifacts.contains(dependency->fileName)) + buildGraph->insert(product, dependency); + buildGraph->safeConnect(processedArtifact, dependency); + } +} + +void Executor::cancelJobs() +{ + QList<ExecutorJob *> jobs = m_processingJobs.keys(); + foreach (ExecutorJob *job, jobs) + job->cancel(); + foreach (ExecutorJob *job, jobs) + job->waitForFinished(); +} + +void Executor::addExecutorJobs(int jobNumber) +{ + if (jobNumber < 1) + qbsError() << tr("Maximum job number must be larger than zero."); + for (int i = 1; i <= jobNumber; i++) { + ExecutorJob *job = new ExecutorJob(this); + job->setObjectName(QString(QLatin1String("J%1")).arg(i)); + m_availableJobs.append(job); + connect(job, SIGNAL(error(QString)), + this, SLOT(onProcessError(QString))); + connect(job, SIGNAL(success()), + this, SLOT(onProcessSuccess())); + } +} + +void Executor::removeExecutorJobs(int jobNumber) +{ + if (jobNumber >= m_availableJobs.count()) { + qDeleteAll(m_availableJobs); + m_availableJobs.clear(); + } else { + for (int i = 1; i <= jobNumber; i++) { + delete m_availableJobs.takeLast(); + } + } +} + +bool Executor::runAutoMoc() +{ + bool autoMocApplied = false; + foreach (const BuildProduct::Ptr &product, m_productsToBuild) { + // HACK call the automoc thingy here only if we have use qt/core module + foreach (ResolvedModule::Ptr m, product->rProduct->modules) { + if (m->name == "qt/core") { + try { + autoMocApplied = true; + m_autoMoc->apply(product); + } catch (Error &e) { + setError(e.toString()); + return false; + } + break; + } + } + } + if (autoMocApplied) + foreach (BuildProject::Ptr prj, m_projectsToBuild) + BuildGraph::detectCycle(prj.data()); + + return true; +} + +static bool scanWithScannerPlugin(ScannerPlugin *scannerPlugin, + const QString &filePathToBeScanned, + ScanResultCache *scanResultCache, + ScanResultCache::Result *scanResult) +{ + void *scannerHandle = scannerPlugin->open(filePathToBeScanned.utf16(), 0, 0); + if (!scannerHandle) + return false; + while (true) { + int flags = 0; + int length = 0; + const char *szOutFilePath = scannerPlugin->next(scannerHandle, &length, &flags); + if (szOutFilePath == 0) + break; + QString outFilePath = QString::fromLocal8Bit(szOutFilePath, length); + if (outFilePath.isEmpty()) + continue; + bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG); + scanResult->deps.insert(outFilePath, isLocalInclude); + } + scannerPlugin->close(scannerHandle); + scanResult->visited = true; + scanResultCache->insert(filePathToBeScanned, *scanResult); + return true; +} + +static void collectIncludePaths(const QVariantMap &modules, QSet<QString> *collectedPaths) +{ + QMapIterator<QString, QVariant> iterator(modules); + while (iterator.hasNext()) { + iterator.next(); + if (iterator.key() == "cpp") { + QVariant includePathsVariant = iterator .value().toMap().value("includePaths"); + if (includePathsVariant.isValid()) + collectedPaths->unite(QSet<QString>::fromList(includePathsVariant.toStringList())); + } else { + collectIncludePaths(iterator .value().toMap().value("modules").toMap(), collectedPaths); + } + } +} + +static QStringList collectIncludePaths(const QVariantMap &modules) +{ + QSet<QString> collectedPaths; + + collectIncludePaths(modules, &collectedPaths); + return QStringList(collectedPaths.toList()); +} + +void Executor::scanFileDependencies(Artifact *processedArtifact) +{ + if (!processedArtifact->transformer) + return; + + foreach (Artifact *output, processedArtifact->transformer->outputs) { + // clear the file dependencies - they will be regenerated + output->fileDependencies.clear(); + } + + foreach (Artifact *inputArtifact, processedArtifact->transformer->inputs) { + QStringList includePaths; + foreach (const QString &fileTag, inputArtifact->fileTags) { + QList<ScannerPlugin *> scanners = ScannerPluginManager::scannersForFileTag(fileTag); + foreach (ScannerPlugin *scanner, scanners) { + if (includePaths.isEmpty()) + includePaths = collectIncludePaths(inputArtifact->configuration->value().value("modules").toMap()); + + scanForFileDependencies(scanner, includePaths, processedArtifact, inputArtifact); + } + } + } +} + +void Executor::scanForFileDependencies(ScannerPlugin *scannerPlugin, const QStringList &includePaths, Artifact *processedArtifact, Artifact *inputArtifact) +{ + qbsDebug("scanning %s [%s]", qPrintable(inputArtifact->fileName), scannerPlugin->fileTag); + qbsDebug(" from %s", qPrintable(processedArtifact->fileName)); + + QSet<QString> resolvedDependencies; + QSet<QString> visitedFilePaths; + QStringList filePathsToScan; + filePathsToScan.append(inputArtifact->fileName); + + while (!filePathsToScan.isEmpty()) { + const QString filePathToBeScanned = filePathsToScan.takeFirst(); + if (visitedFilePaths.contains(filePathToBeScanned)) + continue; + visitedFilePaths.insert(filePathToBeScanned); + + ScanResultCache::Result scanResult = m_scanResultCache.value(filePathToBeScanned); + if (!scanResult.visited) { + bool canScan = scanWithScannerPlugin(scannerPlugin, filePathToBeScanned, &m_scanResultCache, &scanResult); + if (!canScan) + continue; + } + + resolveScanResultDependencies(includePaths, inputArtifact, scanResult, filePathToBeScanned, &resolvedDependencies, &filePathsToScan); + } + + handleDependencies(processedArtifact, inputArtifact, resolvedDependencies); +} + +static bool resolveWithIncludePath(const QString &includePath, QString &outFilePath, BuildProduct *buildProduct) +{ + QString filePath = FileInfo::resolvePath(includePath, outFilePath); + if (buildProduct->artifacts.contains(filePath)) { + outFilePath = filePath; + return true; + } else if (FileInfo::exists(filePath)) { + outFilePath = filePath; + return true; + } + return false; +} + +void Executor::resolveScanResultDependencies(const QStringList &includePaths, + Artifact *inputArtifact, + const ScanResultCache::Result &scanResult, + const QString &filePathToBeScanned, + QSet<QString> *dependencies, + QStringList *filePathsToScan) +{ + QString baseDirOfInFilePath; + for (QHash<QString, bool>::const_iterator iterator = scanResult.deps.constBegin(); iterator != scanResult.deps.constEnd(); ++iterator) { + QString outFilePath = iterator.key(); + const bool isLocalInclude = iterator.value(); + bool resolved = FileInfo::isAbsolute(outFilePath); + + if (!resolved && isLocalInclude) { + // try base directory of source file + if (baseDirOfInFilePath.isNull()) + baseDirOfInFilePath = FileInfo::path(filePathToBeScanned); + resolved = resolveWithIncludePath(baseDirOfInFilePath, outFilePath, inputArtifact->product); + } + + if (!resolved) { + // try include paths + foreach (const QString &includePath, includePaths) { + if (resolveWithIncludePath(includePath, outFilePath, inputArtifact->product)) { + resolved = true; + break; + } + } + } + + if (resolved) { + outFilePath = QDir::cleanPath(outFilePath); + dependencies->insert(outFilePath); + filePathsToScan->append(outFilePath); + } + } +} + +void Executor::onProcessError(QString errorString) +{ + if (m_keepGoing) { + qbsWarning() << tr("ignoring error: %1").arg(errorString); + onProcessSuccess(); + } else { + setError(errorString); + cancelJobs(); + finish(); + } +} + +void Executor::onProcessSuccess() +{ + ExecutorJob *job = qobject_cast<ExecutorJob *>(sender()); + Q_ASSERT(job); + Artifact *processedArtifact = m_processingJobs.value(job); + Q_ASSERT(processedArtifact); + m_processingJobs.remove(job); + m_availableJobs.append(job); + finishArtifact(processedArtifact); + + if (m_state == ExecutorRunning && !run(*m_futureInterface)) + finish(); +} + +void Executor::finish() +{ + if (m_state == ExecutorIdle) + return; + + QStringList unbuiltProductNames; + foreach (BuildProduct::Ptr buildProduct, m_productsToBuild) { + foreach (Artifact *artifact, buildProduct->targetArtifacts) { + if (artifact->buildState != Artifact::Built) { + unbuiltProductNames += buildProduct->rProduct->name; + break; + } + } + } + if (unbuiltProductNames.isEmpty()) { + qbsInfo() << DontPrintLogLevel << LogOutputStdOut << TextColorGreen + << "Build done."; + } else { + setError(tr("The following products could not be built: %1.").arg(unbuiltProductNames.join(", "))); + qbsInfo() << DontPrintLogLevel << LogOutputStdOut << TextColorRed + << "Build failed."; + return; + } + + setState(ExecutorIdle); + emit finished(); +} + +/** + * Resets the state of all artifacts in the graph to "untouched". + * This must be done before doing another build. + */ +void Executor::resetArtifactsToUntouched() +{ + foreach (const BuildProduct::Ptr &product, m_productsToBuild) { + foreach (Artifact *artifact, product->artifacts) { + artifact->buildState = Artifact::Untouched; + artifact->isExistingFile = false; + artifact->isOutOfDate = false; + } + } +} + +void Executor::prepareBuildGraph(Artifact::BuildState buildState) +{ + foreach (Artifact *root, m_roots) + prepareBuildGraph_impl(root, buildState); +} + +void Executor::prepareBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState) +{ + if (artifact->buildState != Artifact::Untouched) + return; + + artifact->buildState = buildState; + + foreach (Artifact *child, artifact->children) + prepareBuildGraph_impl(child, buildState); +} + +void Executor::updateBuildGraph(Artifact::BuildState buildState) +{ + QSet<Artifact *> seenArtifacts; + foreach (Artifact *root, m_roots) + updateBuildGraph_impl(root, buildState, seenArtifacts); +} + +void Executor::updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts) +{ + if (seenArtifacts.contains(artifact)) + return; + + seenArtifacts += artifact; + artifact->buildState = buildState; + + foreach (Artifact *child, artifact->children) + updateBuildGraph_impl(child, buildState, seenArtifacts); +} + +void Executor::doOutOfDateCheck() +{ + foreach (Artifact *root, m_roots) + doOutOfDateCheck(root); +} + +void Executor::doOutOfDateCheck(Artifact *artifact) +{ + if (artifact->outOfDateCheckPerformed) + return; + bool fileExists; + if (isOutOfDate(artifact, fileExists)) + artifact->isOutOfDate = true; + artifact->isExistingFile = fileExists; + artifact->outOfDateCheckPerformed = true; + foreach (Artifact *child, artifact->children) + doOutOfDateCheck(child); +} + +void Executor::doDependencyScanTopDown() +{ + qbsInfo() << DontPrintLogLevel << "Scanning for file dependencies..."; + QSet<Artifact *> seenArtifacts; + foreach (Artifact *root, m_roots) + doDependencyScan_impl(root, seenArtifacts); +} + +void Executor::doDependencyScan_impl(Artifact *artifact, QSet<Artifact *> &seenArtifacts) +{ + if (!artifact->transformer || seenArtifacts.contains(artifact)) + return; + seenArtifacts += artifact; + if (!artifact->outOfDateCheckPerformed) + doOutOfDateCheck(artifact); + if (artifact->isOutOfDate) + scanFileDependencies(artifact); + foreach (Artifact *child, artifact->children) + doDependencyScan_impl(child, seenArtifacts); +} + +void Executor::setState(ExecutorState s) +{ + if (m_state == s) + return; + m_state = s; + emit stateChanged(s); +} + +void Executor::setError(const QString &errorMessage) +{ + setState(ExecutorError); + qbsError() << errorMessage; + emit error(); +} + +} // namespace qbs diff --git a/src/lib/buildgraph/executor.h b/src/lib/buildgraph/executor.h new file mode 100644 index 000000000..45e86280b --- /dev/null +++ b/src/lib/buildgraph/executor.h @@ -0,0 +1,147 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef BUILDGRAPHEXECUTOR_H +#define BUILDGRAPHEXECUTOR_H + +#include "buildgraph.h" + +#include <buildgraph/artifact.h> +#include <Qbs/processoutput.h> +#include <tools/settings.h> +#include <tools/scannerpluginmanager.h> +#include <buildgraph/scanresultcache.h> + +#include <QtCore/QObject> +#include <QtCore/QVariant> + +namespace qbs { + +class AutoMoc; +class ExecutorJob; +class ScanResultCache; + +class Executor : public QObject +{ + Q_OBJECT +public: + Executor(int maxJobs = 1); + ~Executor(); + + void build(const QList<BuildProject::Ptr> projectsToBuild, const QStringList &changedFiles, const QStringList &selectedProductNames, QFutureInterface<bool> &futureInterface); + + enum ExecutorState { + ExecutorIdle, + ExecutorRunning, + ExecutorError + }; + + void setRunOnceAndForgetModeEnabled(bool enabled) { m_runOnceAndForgetMode = enabled; } + void setDryRun(bool b); + void setKeepGoing(bool b) { m_keepGoing = b; } + bool isKeepGoingSet() const { return m_keepGoing; } + ExecutorState state() const { return m_state; } + + void setMaximumJobs(int numberOfJobs); + int maximumJobs() const; + +signals: + void error(); + void finished(); + void stateChanged(ExecutorState); + void progress(int jobsToDo, int jobCount, const QString &description); + void newProcessOutput(const Qbs::ProcessOutput &processOutput); + +protected slots: + void onProcessError(QString errorString); + void onProcessSuccess(); + void resetArtifactsToUntouched(); + +protected: + void prepareBuildGraph(Artifact::BuildState buildState); + void prepareBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState); + void updateBuildGraph(Artifact::BuildState buildState); + void updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts); + void doOutOfDateCheck(); + void doOutOfDateCheck(Artifact *root); + void doDependencyScanTopDown(); + void doDependencyScan_impl(Artifact *artifact, QSet<Artifact *> &seenArtifacts); + static bool isLeaf(Artifact *artifact); + void initLeaves(const QList<Artifact *> &changedArtifacts); + void initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts); + bool run(QFutureInterface<bool> &futureInterface); + qbs::FileTime timeStamp(Artifact *artifact); + bool isOutOfDate(Artifact *artifact, bool &fileExists); + bool execute(Artifact *artifact); + void finishArtifact(Artifact *artifact); + void finish(); + void setState(ExecutorState); + void setError(const QString &errorMessage); + void addExecutorJobs(int jobNumber); + void removeExecutorJobs(int jobNumber); + bool runAutoMoc(); + void scanFileDependencies(Artifact *processedArtifact); + void scanForFileDependencies(ScannerPlugin *scannerPlugin, const QStringList &includePaths, Artifact *processedArtifact, Artifact *inputArtifact); + static void resolveScanResultDependencies(const QStringList &includePaths, Artifact *inputArtifact, const ScanResultCache::Result &scanResult, + const QString &filePathToBeScanned, QSet<QString> *dependencies, QStringList *filePathsToScan); + void handleDependencies(Artifact *processedArtifact, Artifact *scannedArtifact, const QSet<QString> &resolvedDependencies); + void handleDependency(Artifact *processedArtifact, const QString &dependencyFilePath); + void cancelJobs(); + +private: + QScriptEngine *m_scriptEngine; + bool m_runOnceAndForgetMode; // This is true for the command line version. + QList<ExecutorJob*> m_availableJobs; + QHash<ExecutorJob*, Artifact *> m_processingJobs; + ExecutorState m_state; + bool m_keepGoing; + QList<BuildProject::Ptr> m_projectsToBuild; + QList<BuildProduct::Ptr> m_productsToBuild; + QList<Artifact *> m_roots; + QMap<Artifact *, QHashDummyValue> m_leaves; + QHash<Artifact *, qbs::FileTime> m_timeStampCache; + ScanResultCache m_scanResultCache; + AutoMoc *m_autoMoc; + + friend class ExecutorJob; + int m_maximumJobNumber; + QFutureInterface<bool> *m_futureInterface; // TODO: its a hack +}; + +} // namespace qbs + +#endif // BUILDGRAPHEXECUTOR_H diff --git a/src/lib/buildgraph/executorjob.cpp b/src/lib/buildgraph/executorjob.cpp new file mode 100644 index 000000000..b7a16828a --- /dev/null +++ b/src/lib/buildgraph/executorjob.cpp @@ -0,0 +1,128 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "executorjob.h" +#include "executor.h" +#include "commandexecutor.h" +#include <buildgraph/transformer.h> +#include <QtCore/QThread> + +namespace qbs { + +ExecutorJob::ExecutorJob(Executor *parent) + : QObject(parent) + , m_executor(parent) + , m_commandExecutor(new CommandExecutor(this)) + , m_transformer(0) + , m_lastUsedProduct(0) + , m_currentCommandIdx(-1) +{ + connect(m_commandExecutor, SIGNAL(error(QString)), SLOT(onCommandError(QString))); + connect(m_commandExecutor, SIGNAL(finished()), SLOT(onCommandFinished())); +} + +ExecutorJob::~ExecutorJob() +{ +} + +void ExecutorJob::setMainThreadScriptEngine(QScriptEngine *engine) +{ + m_commandExecutor->setMainThreadScriptEngine(engine); +} + +void ExecutorJob::setDryRun(bool enabled) +{ + m_commandExecutor->setDryRunEnabled(enabled); +} + +void ExecutorJob::run(Transformer *t, BuildProduct *buildProduct) +{ + Q_ASSERT(m_currentCommandIdx == -1); + + if (t->commands.isEmpty()) { + emit success(); + return; + } + + if (m_lastUsedProduct != buildProduct) + m_commandExecutor->setProcessEnvironment(buildProduct->rProduct->buildEnvironment); + + m_transformer = t; + runNextCommand(); +} + +void ExecutorJob::cancel() +{ + if (!m_transformer) + return; + m_currentCommandIdx = m_transformer->commands.count(); +} + +void ExecutorJob::waitForFinished() +{ + m_commandExecutor->waitForFinished(); +} + +void ExecutorJob::runNextCommand() +{ + ++m_currentCommandIdx; + if (m_currentCommandIdx >= m_transformer->commands.count()) { + m_transformer = 0; + m_currentCommandIdx = -1; + emit success(); + return; + } + + AbstractCommand *command = m_transformer->commands.at(m_currentCommandIdx); + m_commandExecutor->start(m_transformer, command); +} + +void ExecutorJob::onCommandError(QString errorString) +{ + m_transformer = 0; + m_currentCommandIdx = -1; + emit error(errorString); +} + +void ExecutorJob::onCommandFinished() +{ + if (!m_transformer) + return; + runNextCommand(); +} + +} // namespace qbs diff --git a/src/lib/buildgraph/executorjob.h b/src/lib/buildgraph/executorjob.h new file mode 100644 index 000000000..db3073a9c --- /dev/null +++ b/src/lib/buildgraph/executorjob.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef EXECUTORJOB_H +#define EXECUTORJOB_H + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QScriptEngine; +QT_END_NAMESPACE + +namespace qbs { + +class CommandExecutor; +class BuildProduct; +class Executor; +class Transformer; + +class ExecutorJob : public QObject +{ + Q_OBJECT +public: + ExecutorJob(Executor *parent); + ~ExecutorJob(); + + void setMainThreadScriptEngine(QScriptEngine *engine); + void setDryRun(bool enabled); + void run(Transformer *t, BuildProduct *buildProduct); + void cancel(); + void waitForFinished(); + +protected slots: + void runNextCommand(); + void onCommandError(QString errorString); + void onCommandFinished(); + +signals: + void error(QString errorString); + void success(); + +private: + Executor *m_executor; + CommandExecutor *m_commandExecutor; + Transformer *m_transformer; + BuildProduct *m_lastUsedProduct; + int m_currentCommandIdx; +}; + +} // namespace qbs + +#endif // EXECUTORJOB_H diff --git a/src/lib/buildgraph/rulegraph.cpp b/src/lib/buildgraph/rulegraph.cpp new file mode 100644 index 000000000..11d7712e8 --- /dev/null +++ b/src/lib/buildgraph/rulegraph.cpp @@ -0,0 +1,201 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "rulegraph.h" +#include <tools/error.h> +#include <QtCore/QDebug> + +namespace qbs { + +RuleGraph::RuleGraph() +{ +} + +void RuleGraph::build(const QSet<Rule::Ptr> &rules, const QStringList &productFileTags) +{ + QMap<QString, QList<Rule*> > inputFileTagToRule; + m_artifacts.reserve(rules.count()); + foreach (const Rule::Ptr &rule, rules) { + foreach (const QString &fileTag, rule->outputFileTags()) + m_outputFileTagToRule[fileTag].append(rule.data()); + insert(rule); + } + + m_parents.resize(rules.count()); + m_children.resize(rules.count()); + + foreach (Rule::Ptr rule, m_artifacts) { + QStringList inFileTags = rule->inputs; + inFileTags += rule->explicitlyDependsOn; + foreach (const QString &fileTag, inFileTags) { + inputFileTagToRule[fileTag].append(rule.data()); + foreach (Rule *consumingRule, m_outputFileTagToRule.value(fileTag)) { + connect(rule.data(), consumingRule); + } + } + } + + QList<Rule*> productRules; + for (int i=0; i < productFileTags.count(); ++i) { + QList<Rule*> rules = m_outputFileTagToRule.value(productFileTags.at(i)); + productRules += rules; + //### check: the rule graph must be a in valid shape! + } + foreach (Rule *r, productRules) + m_rootRules += r->ruleGraphId; +} + +QList<Rule::Ptr> RuleGraph::topSorted() +{ + QSet<int> rootRules = m_rootRules; + QList<Rule::Ptr> result; + foreach (int rootIndex, rootRules) { + Rule::Ptr rule = m_artifacts.at(rootIndex); + result.append(topSort(rule)); + } + + // remove duplicates from the result of our post-order traversal + QSet<Rule*> seenRules; + seenRules.reserve(result.count()); + for (int i = 0; i < result.count();) { + Rule *rule = result.at(i).data(); + if (seenRules.contains(rule)) + result.removeAt(i); + else + ++i; + seenRules.insert(rule); + } + + return result; +} + +void RuleGraph::dump() const +{ + QByteArray indent; + printf("---rule graph dump:\n"); + QSet<int> rootRules; + foreach (Rule::Ptr rule, m_artifacts) + if (m_parents[rule->ruleGraphId].isEmpty()) + rootRules += rule->ruleGraphId; + foreach (int idx, rootRules) { + dump_impl(indent, idx); + } +} + +void RuleGraph::dump_impl(QByteArray &indent, int rootIndex) const +{ + Rule::Ptr r = m_artifacts[rootIndex]; + printf("%s", indent.constData()); + printf("%s", qPrintable(r->toString())); + printf("\n"); + + indent.append(" "); + foreach (int childIndex, m_children[rootIndex]) + dump_impl(indent, childIndex); + indent.chop(2); +} + +int RuleGraph::insert(Rule::Ptr rule) +{ + rule->ruleGraphId = m_artifacts.count(); + m_artifacts.append(rule); + return rule->ruleGraphId; +} + +void RuleGraph::connect(Rule *creatingRule, Rule *consumingRule) +{ + int maxIndex = qMax(creatingRule->ruleGraphId, consumingRule->ruleGraphId); + if (m_parents.count() <= maxIndex) { + const int c = maxIndex + 1; + m_parents.resize(c); + m_children.resize(c); + } + m_parents[consumingRule->ruleGraphId].append(creatingRule->ruleGraphId); + m_children[creatingRule->ruleGraphId].append(consumingRule->ruleGraphId); +} + +void RuleGraph::remove(Rule *rule) +{ + m_parents[rule->ruleGraphId].clear(); + m_children[rule->ruleGraphId].clear(); + m_artifacts[rule->ruleGraphId] = Rule::Ptr(); + rule->ruleGraphId = -1; +} + +void RuleGraph::removeParents(Rule *rule) +{ + foreach (int parentIndex, m_parents[rule->ruleGraphId]) { + Rule::Ptr parent = m_artifacts.at(parentIndex); + removeParents(parent.data()); + remove(parent.data()); + } + m_parents[rule->ruleGraphId].clear(); +} + +void RuleGraph::removeSiblings(Rule *rule) +{ + foreach (int childIndex, m_children[rule->ruleGraphId]) { + Rule::Ptr child = m_artifacts.at(childIndex); + QList<int> toRemove; + foreach (int siblingIndex, m_parents.at(child->ruleGraphId)) { + Rule::Ptr sibling = m_artifacts.at(siblingIndex); + if (sibling == rule) + continue; + toRemove.append(sibling->ruleGraphId); + remove(sibling.data()); + } + QVector<int> &parents = m_parents[child->ruleGraphId]; + qSort(parents); + foreach (int id, toRemove) { + QVector<int>::iterator it = qBinaryFind(parents.begin(), parents.end(), id); + if (it != parents.end()) + parents.erase(it); + } + } +} + +QList<Rule::Ptr> RuleGraph::topSort(Rule::Ptr rule) +{ + QList<Rule::Ptr> result; + foreach (int childIndex, m_children.at(rule->ruleGraphId)) + result.append(topSort(m_artifacts.at(childIndex))); + + result.append(rule); + return result; +} + +} // namespace qbs diff --git a/src/lib/buildgraph/rulegraph.h b/src/lib/buildgraph/rulegraph.h new file mode 100644 index 000000000..d5a88b940 --- /dev/null +++ b/src/lib/buildgraph/rulegraph.h @@ -0,0 +1,77 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef RULEGRAPH_H +#define RULEGRAPH_H + +#include <language.h> + +#include <QtCore/QSet> +#include <QtCore/QVector> + +namespace qbs { + +class RuleGraph +{ +public: + RuleGraph(); + + void build(const QSet<qbs::Rule::Ptr> &rules, const QStringList &productFileTag); + QList<qbs::Rule::Ptr> topSorted(); + + void dump() const; + +private: + void dump_impl(QByteArray &indent, int rootIndex) const; + int insert(qbs::Rule::Ptr rule); + void connect(qbs::Rule *creatingRule, qbs::Rule *consumingRule); + void remove(qbs::Rule *rule); + void removeParents(qbs::Rule *rule); + void removeSiblings(qbs::Rule *rule); + QList<qbs::Rule::Ptr> topSort(qbs::Rule::Ptr rule); + +private: + QMap<QString, QList<qbs::Rule*> > m_outputFileTagToRule; + QVector<qbs::Rule::Ptr> m_artifacts; + QVector< QVector<int> > m_parents; + QVector< QVector<int> > m_children; + QSet<int> m_rootRules; +}; + +} // namespace qbs + +#endif // RULEGRAPH_H diff --git a/src/lib/buildgraph/scanresultcache.cpp b/src/lib/buildgraph/scanresultcache.cpp new file mode 100644 index 000000000..70d683337 --- /dev/null +++ b/src/lib/buildgraph/scanresultcache.cpp @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#include "scanresultcache.h" + +namespace qbs { + +ScanResultCache::Result ScanResultCache::value(const QString &fileName) const +{ + m_mutex.lock(); + Result result = m_data.value(fileName); + m_mutex.unlock(); + return result; +} + +void ScanResultCache::insert(const QString &fileName, const ScanResultCache::Result &value) +{ + m_mutex.lock(); + m_data.insert(fileName, value); + m_mutex.unlock(); +} + +} // namespace qbs diff --git a/src/lib/buildgraph/scanresultcache.h b/src/lib/buildgraph/scanresultcache.h new file mode 100644 index 000000000..23e7d811f --- /dev/null +++ b/src/lib/buildgraph/scanresultcache.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef SCANRESULTCACHE_H +#define SCANRESULTCACHE_H + +#include <QtCore/QHash> +#include <QtCore/QMutex> +#include <QtCore/QSet> +#include <QtCore/QString> + +namespace qbs { + +class ScanResultCache +{ +public: + + struct Result + { + Result() + : visited(false) + {} + + QHash<QString, bool> deps; + bool visited; + }; + + Result value(const QString &fileName) const; + void insert(const QString &fileName, const Result &value); + +private: + mutable QMutex m_mutex; + QHash<QString, Result> m_data; +}; + +} // namespace qbs + +#endif // SCANRESULTCACHE_H diff --git a/src/lib/buildgraph/transformer.cpp b/src/lib/buildgraph/transformer.cpp new file mode 100644 index 000000000..e9327849a --- /dev/null +++ b/src/lib/buildgraph/transformer.cpp @@ -0,0 +1,119 @@ +/************************************************************************* +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "transformer.h" +#include "artifact.h" +#include "command.h" + +namespace qbs { + +Transformer::Transformer() +{ +} + +Transformer::~Transformer() +{ + qDeleteAll(commands); +} + +QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artifact *artifact, const QString &defaultModuleName) +{ + QScriptValue config = artifact->configuration->cachedScriptValue(scriptEngine); + if (!config.isValid()) { + config = scriptEngine->toScriptValue(artifact->configuration->value()); + artifact->configuration->cacheScriptValue(scriptEngine, config); + } + + QScriptValue artifactConfig = scriptEngine->newObject(); + artifactConfig.setPrototype(config); + artifactConfig.setProperty(QLatin1String("fileName"), artifact->fileName); + QStringList fileTags = artifact->fileTags.toList(); + artifactConfig.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags)); + if (!defaultModuleName.isEmpty()) + artifactConfig.setProperty(QLatin1String("module"), config.property("modules").property(defaultModuleName)); + return artifactConfig; +} + +QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const QSet<Artifact*> &artifacts, const QString &defaultModuleName) +{ + typedef QMap<QString, QList<Artifact*> > TagArtifactsMap; + TagArtifactsMap tagArtifactsMap; + foreach (Artifact *artifact, artifacts) + foreach (const QString &fileTag, artifact->fileTags) + tagArtifactsMap[fileTag].append(artifact); + + QScriptValue jsTagFiles = scriptEngine->newObject(); + for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) { + const QList<Artifact*> &artifactList = tag.value(); + QScriptValue jsFileConfig = scriptEngine->newArray(artifactList.count()); + int i=0; + foreach (Artifact *artifact, artifactList) { + jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact, defaultModuleName)); + } + jsTagFiles.setProperty(tag.key(), jsFileConfig); + } + + return jsTagFiles; +} + +void Transformer::setupInputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue) +{ + const QString &defaultModuleName = rule->module->name; + QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName); + targetScriptValue.setProperty("inputs", scriptValue, QScriptValue::ReadOnly); + if (inputs.count() == 1) { + Artifact *input = *inputs.begin(); + const QSet<QString> &fileTags = input->fileTags; + QScriptValue inputsForFileTag = scriptValue.property(*fileTags.begin()); + QScriptValue inputScriptValue = inputsForFileTag.property(0); + targetScriptValue.setProperty("input", inputScriptValue, QScriptValue::ReadOnly); + } +} + +void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue) +{ + const QString &defaultModuleName = rule->module->name; + QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName); + targetScriptValue.setProperty("outputs", scriptValue, QScriptValue::ReadOnly); + if (outputs.count() == 1) { + Artifact *output = *outputs.begin(); + const QSet<QString> &fileTags = output->fileTags; + QScriptValue outputsForFileTag = scriptValue.property(*fileTags.begin()); + QScriptValue outputScriptValue = outputsForFileTag.property(0); + targetScriptValue.setProperty("output", outputScriptValue, QScriptValue::ReadOnly); + } +} + +} // namespace qbs diff --git a/src/lib/buildgraph/transformer.h b/src/lib/buildgraph/transformer.h new file mode 100644 index 000000000..d52d9dd3b --- /dev/null +++ b/src/lib/buildgraph/transformer.h @@ -0,0 +1,85 @@ +/************************************************************************* +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef TRANSFORMER_H +#define TRANSFORMER_H + +#include "tools/persistence.h" + +#include <QtCore/QSet> +#include <QtCore/QSharedPointer> +#include <QtScript/QScriptEngine> + +QT_BEGIN_NAMESPACE + +QT_END_NAMESPACE + +namespace qbs { + +class Artifact; +class AbstractCommand; +class Rule; + +class Transformer : public PersistentObject +{ +public: + typedef QSharedPointer<Transformer> Ptr; + + Transformer(); + ~Transformer(); + + QSet<Artifact*> inputs; // can be different from "children of all outputs" + QSet<Artifact*> outputs; + QSharedPointer<Rule> rule; + QList<AbstractCommand *> commands; + + static QScriptValue translateFileConfig(QScriptEngine *scriptEngine, + Artifact *artifact, + const QString &defaultModuleName); + static QScriptValue translateInOutputs(QScriptEngine *scriptEngine, + const QSet<Artifact*> &artifacts, + const QString &defaultModuleName); + + void setupInputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue); + void setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue); + +private: + void load(PersistentPool &pool, PersistentObjectData &data); + void store(PersistentPool &pool, PersistentObjectData &data) const; +}; + +} // namespace qbs + +#endif // TRANSFORMER_H diff --git a/src/lib/language/language.cpp b/src/lib/language/language.cpp new file mode 100644 index 000000000..4640ecdc4 --- /dev/null +++ b/src/lib/language/language.cpp @@ -0,0 +1,453 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "language.h" +#include <tools/scripttools.h> +#include <QtCore/QCryptographicHash> +#include <QtScript/QScriptEngine> +#include <QtScript/QScriptValue> +#include <algorithm> + +namespace qbs { + +QMutex Configuration::m_scriptValueCacheMutex; + +void Configuration::setValue(const QVariantMap &map) +{ + m_value = map; + m_scriptValueCache.clear(); +} + +QScriptValue Configuration::cachedScriptValue(QScriptEngine *scriptEngine) const +{ + m_scriptValueCacheMutex.lock(); + const QScriptValue result = m_scriptValueCache.value(scriptEngine); + m_scriptValueCacheMutex.unlock(); + Q_ASSERT(!result.isValid() || result.engine() == scriptEngine); + return result; +} + +void Configuration::cacheScriptValue(QScriptEngine *scriptEngine, const QScriptValue &scriptValue) +{ + Q_ASSERT(scriptEngine == scriptValue.engine()); + m_scriptValueCacheMutex.lock(); + m_scriptValueCache.insert(scriptEngine, scriptValue); + m_scriptValueCacheMutex.unlock(); +} + +void Configuration::load(PersistentPool &, PersistentObjectData &data) +{ + QDataStream s(data); + s >> m_value; +} + +void Configuration::store(PersistentPool &, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << m_value; +} + +void FileTagger::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + artifactExpression = pool.idLoadString(s); + fileTags = pool.idLoadStringList(s); +} + +void FileTagger::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << pool.storeString(artifactExpression); + s << pool.storeStringList(fileTags); +} + +void SourceArtifact::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + s >> absoluteFilePath; + s >> fileTags; + configuration = pool.idLoadS<Configuration>(s); +} + +void SourceArtifact::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << absoluteFilePath; + s << fileTags; + s << pool.store(configuration); +} + +void RuleArtifact::load(PersistentPool &pool, PersistentObjectData &data) +{ + Q_UNUSED(pool); + QDataStream s(data); + s >> fileScript; + s >> fileTags; + s >> bindings; +} + +void RuleArtifact::store(PersistentPool &pool, PersistentObjectData &data) const +{ + Q_UNUSED(pool); + QDataStream s(&data, QIODevice::WriteOnly); + s << fileScript; + s << fileTags; + s << bindings; +} + +void RuleScript::load(PersistentPool &, PersistentObjectData &data) +{ + QDataStream s(data); + s >> script; + s >> location; +} + +void RuleScript::store(PersistentPool &, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << script; + s << location; +} + +void ResolvedModule::load(PersistentPool &pool, PersistentObjectData &data) +{ + Q_UNUSED(pool); + QDataStream s(data); + name = pool.idLoadString(s); + moduleDependencies = pool.idLoadStringList(s); + setupBuildEnvironmentScript = pool.idLoadString(s); + setupRunEnvironmentScript = pool.idLoadString(s); + s >> jsImports + >> setupBuildEnvironmentScript + >> setupRunEnvironmentScript; +} + +void ResolvedModule::store(PersistentPool &pool, PersistentObjectData &data) const +{ + Q_UNUSED(pool); + QDataStream s(&data, QIODevice::WriteOnly); + s << pool.storeString(name) + << pool.storeStringList(moduleDependencies) + << pool.storeString(setupBuildEnvironmentScript) + << pool.storeString(setupRunEnvironmentScript) + << jsImports + << setupBuildEnvironmentScript + << setupRunEnvironmentScript; +} + +QString Rule::toString() const +{ + return "[" + inputs.join(",") + " -> " + outputFileTags().join(",") + "]"; +} + +QStringList Rule::outputFileTags() const +{ + QStringList result; + foreach (RuleArtifact::Ptr artifact, artifacts) + result.append(artifact->fileTags); + result.sort(); + std::unique(result.begin(), result.end()); + return result; +} + +void Rule::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + script = pool.idLoadS<RuleScript>(s); + module = pool.idLoadS<ResolvedModule>(s); + s >> jsImports + >> inputs + >> usings + >> explicitlyDependsOn + >> multiplex + >> objectId + >> transformProperties; + + loadContainerS(artifacts, s, pool); +} + +void Rule::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << pool.store(script) + << pool.store(module) + << jsImports + << inputs + << usings + << explicitlyDependsOn + << multiplex + << objectId + << transformProperties; + + storeContainer(artifacts, s, pool); +} + +ResolvedProduct::ResolvedProduct() + : project(0) +{ +} + +QSet<QString> ResolvedProduct::fileTagsForFileName(const QString &fileName) const +{ + QSet<QString> result; + foreach (FileTagger::Ptr tagger, fileTaggers) { + if (FileInfo::globMatches(tagger->artifactExpression, fileName)) { + result.unite(tagger->fileTags.toSet()); + } + } + return result; +} + +void ResolvedProduct::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + s >> fileTags + >> name + >> buildDirectory + >> sourceDirectory + >> destinationDirectory + >> qbsFile; + + configuration = pool.idLoadS<Configuration>(s); + loadContainerS(sources, s, pool); + loadContainerS(rules, s, pool); + loadContainerS(uses, s, pool); + loadContainerS(fileTaggers, s, pool); + loadContainerS(modules, s, pool); +} + +void ResolvedProduct::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << fileTags + << name + << buildDirectory + << sourceDirectory + << destinationDirectory + << qbsFile; + + s << pool.store(configuration); + storeContainer(sources, s, pool); + storeContainer(rules, s, pool); + storeContainer(uses, s, pool); + storeContainer(fileTaggers, s, pool); + storeContainer(modules, s, pool); +} + +QList<ResolvedModule*> topSortModules(const QHash<ResolvedModule*, QList<ResolvedModule*> > &moduleChildren, + const QList<ResolvedModule*> &modules, + QSet<QString> &seenModuleNames) +{ + QList<ResolvedModule*> result; + foreach (ResolvedModule *m, modules) { + result.append(topSortModules(moduleChildren, moduleChildren.value(m), seenModuleNames)); + if (!seenModuleNames.contains(m->name)) { + seenModuleNames.insert(m->name); + result.append(m); + } + } + return result; +} + +static QScriptValue js_getenv(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() < 1) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("getenv expects 1 argument")); + QVariant v = engine->property("_qbs_procenv"); + QProcessEnvironment *procenv = reinterpret_cast<QProcessEnvironment*>(v.value<void*>()); + return engine->toScriptValue(procenv->value(context->argument(0).toString())); +} + +static QScriptValue js_putenv(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() < 2) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("putenv expects 2 arguments")); + QVariant v = engine->property("_qbs_procenv"); + QProcessEnvironment *procenv = reinterpret_cast<QProcessEnvironment*>(v.value<void*>()); + procenv->insert(context->argument(0).toString(), context->argument(1).toString()); + return engine->undefinedValue(); +} + +enum EnvType +{ + BuildEnv, RunEnv +}; + +static QProcessEnvironment getProcessEnvironment(QScriptEngine *scriptEngine, EnvType envType, + const QList<ResolvedModule::Ptr> &modules, + const Configuration::Ptr &productConfiguration, + const QProcessEnvironment &systemEnvironment) +{ + QProcessEnvironment procenv = systemEnvironment; + QMap<QString, ResolvedModule*> moduleMap; + foreach (ResolvedModule::Ptr module, modules) + moduleMap.insert(module->name, module.data()); + + QHash<ResolvedModule*, QList<ResolvedModule*> > moduleParents; + QHash<ResolvedModule*, QList<ResolvedModule*> > moduleChildren; + foreach (ResolvedModule::Ptr module, modules) { + foreach (const QString &moduleName, module->moduleDependencies) { + ResolvedModule *depmod = moduleMap.value(moduleName); + moduleParents[depmod].append(module.data()); + moduleChildren[module.data()].append(depmod); + } + } + + QList<ResolvedModule*> rootModules; + foreach (ResolvedModule::Ptr module, modules) + if (moduleParents.value(module.data()).isEmpty()) + rootModules.append(module.data()); + + { + QVariant v; + v.setValue<void*>(&procenv); + scriptEngine->setProperty("_qbs_procenv", v); + } + + QSet<QString> seenModuleNames; + QList<ResolvedModule*> topSortedModules = topSortModules(moduleChildren, rootModules, seenModuleNames); + foreach (ResolvedModule *module, topSortedModules) { + if ((envType == BuildEnv && module->setupBuildEnvironmentScript.isEmpty()) || + (envType == RunEnv && module->setupBuildEnvironmentScript.isEmpty() && module->setupRunEnvironmentScript.isEmpty())) + continue; + + // expose functions + scriptEngine->globalObject().setProperty("getenv", scriptEngine->newFunction(js_getenv, 1)); + scriptEngine->globalObject().setProperty("putenv", scriptEngine->newFunction(js_putenv, 2)); + + QScriptContext *ctx = scriptEngine->pushContext(); + + // handle imports + QScriptValue scriptValue; + for (JsImports::const_iterator it = module->jsImports.begin(); it != module->jsImports.end(); ++it) { + foreach (const QString &fileName, it.value()) { + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) + throw Error(QString("Can't open '%1'.").arg(fileName)); + QScriptProgram program(file.readAll(), fileName); + scriptValue = addJSImport(scriptEngine, program, it.key()); + if (scriptValue.isError()) + throw Error(scriptValue.toString()); + } + } + + // expose properties of direct module dependencies + QScriptValue activationObject = ctx->activationObject(); + QVariantMap productModules = productConfiguration->value().value("modules").toMap(); + foreach (ResolvedModule *depmod, moduleChildren.value(module)) { + scriptValue = scriptEngine->newObject(); + QVariantMap moduleCfg = productModules.value(depmod->name).toMap(); + for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it) + scriptValue.setProperty(it.key(), scriptEngine->toScriptValue(it.value())); + activationObject.setProperty(depmod->name, scriptValue); + } + + // expose the module's properties + QVariantMap moduleCfg = productModules.value(module->name).toMap(); + for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it) + activationObject.setProperty(it.key(), scriptEngine->toScriptValue(it.value())); + + QString setupScript; + if (envType == BuildEnv) { + setupScript = module->setupBuildEnvironmentScript; + } else { + if (module->setupRunEnvironmentScript.isEmpty()) { + setupScript = module->setupBuildEnvironmentScript; + } else { + setupScript = module->setupRunEnvironmentScript; + } + } + scriptValue = scriptEngine->evaluate(setupScript); + if (scriptValue.isError() || scriptEngine->hasUncaughtException()) { + QString envTypeStr = (envType == BuildEnv ? "build" : "run"); + throw Error(QString("Error while setting up %1 environment: %2").arg(envTypeStr, scriptValue.toString())); + } + + scriptEngine->popContext(); + } + + scriptEngine->setProperty("_qbs_procenv", QVariant()); + return procenv; +} + +void ResolvedProduct::setupBuildEnvironment(QScriptEngine *scriptEngine, const QProcessEnvironment &systemEnvironment) const +{ + if (!buildEnvironment.isEmpty()) + return; + + buildEnvironment = getProcessEnvironment(scriptEngine, BuildEnv, modules, configuration, systemEnvironment); +} + +void ResolvedProduct::setupRunEnvironment(QScriptEngine *scriptEngine, const QProcessEnvironment &systemEnvironment) const +{ + if (!runEnvironment.isEmpty()) + return; + + runEnvironment = getProcessEnvironment(scriptEngine, RunEnv, modules, configuration, systemEnvironment); +} + +void ResolvedProject::load(PersistentPool &pool, PersistentObjectData &data) +{ + QDataStream s(data); + s >> id; + s >> qbsFile; + + int count, i; + s >> count; + products.clear(); + products.reserve(count); + for (i = count; --i >= 0;) { + ResolvedProduct::Ptr rProduct = pool.idLoadS<ResolvedProduct>(s); + rProduct->project = this; + products.insert(rProduct); + } +} + +void ResolvedProject::store(PersistentPool &pool, PersistentObjectData &data) const +{ + QDataStream s(&data, QIODevice::WriteOnly); + s << id; + s << qbsFile; + + s << products.count(); + foreach (ResolvedProduct::Ptr product, products) + s << pool.store(product); +} + +} // namespace qbs diff --git a/src/lib/language/language.h b/src/lib/language/language.h new file mode 100644 index 000000000..255f4c369 --- /dev/null +++ b/src/lib/language/language.h @@ -0,0 +1,264 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef L2_LANGUAGE_HPP +#define L2_LANGUAGE_HPP + +#include <tools/codelocation.h> +#include <tools/persistence.h> +#include <tools/settings.h> +#include <tools/fileinfo.h> + +#include <QtCore/QString> +#include <QtCore/QDataStream> +#include <QtCore/QProcessEnvironment> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QSharedPointer> +#include <QtCore/QVariant> +#include <QtCore/QMutex> +#include <QtScript/QScriptProgram> +#include <QtScript/QScriptValue> + +QT_BEGIN_NAMESPACE +class QScriptEngine; +QT_END_NAMESPACE + +namespace qbs { + +/** + * Represents JavaScript import. + * Key: scope name, Value: list of JS file names. + * There can be several filenames per scope + * if we import a whole directory. + */ +typedef QHash<QString, QStringList> JsImports; + +class Rule; + +class Configuration: public qbs::PersistentObject +{ +public: + typedef QSharedPointer<Configuration> Ptr; + + const QVariantMap &value() const { return m_value; } + void setValue(const QVariantMap &value); + QScriptValue cachedScriptValue(QScriptEngine *scriptEngine) const; + void cacheScriptValue(QScriptEngine *scriptEngine, const QScriptValue &scriptValue); + +private: + void load(qbs::PersistentPool &, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &, qbs::PersistentObjectData &data) const; + +private: + QVariantMap m_value; + QHash<QScriptEngine *, QScriptValue> m_scriptValueCache; + static QMutex m_scriptValueCacheMutex; +}; + +struct FileTagger : public qbs::PersistentObject +{ + typedef QSharedPointer<FileTagger> Ptr; + QString artifactExpression; + QStringList fileTags; + +private: + void load(qbs::PersistentPool &, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &, qbs::PersistentObjectData &data) const; +}; + +class RuleArtifact : public qbs::PersistentObject +{ +public: + typedef QSharedPointer<RuleArtifact> Ptr; + QString fileScript; + QStringList fileTags; + QList<QPair<QStringList, QString> > bindings; + +private: + void load(qbs::PersistentPool &pool, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &pool, qbs::PersistentObjectData &data) const; +}; + +class SourceArtifact : public qbs::PersistentObject +{ +public: + typedef QSharedPointer<SourceArtifact> Ptr; + QString absoluteFilePath; + QSet<QString> fileTags; + Configuration::Ptr configuration; + +private: + void load(qbs::PersistentPool &pool, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &pool, qbs::PersistentObjectData &data) const; +}; + +class RuleScript: public qbs::PersistentObject +{ +public: + typedef QSharedPointer<RuleScript> Ptr; + QString script; + qbs::CodeLocation location; + +private: + void load(qbs::PersistentPool &, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &, qbs::PersistentObjectData &data) const; +}; + +class ResolvedModule : public qbs::PersistentObject +{ +public: + typedef QSharedPointer<ResolvedModule> Ptr; + QString name; + QStringList moduleDependencies; + JsImports jsImports; + QString setupBuildEnvironmentScript; + QString setupRunEnvironmentScript; + +private: + void load(qbs::PersistentPool &pool, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &pool, qbs::PersistentObjectData &data) const; +}; + +/** + * Per default each rule is a "non-multiplex rule". + * + * A "multiplex rule" creates one transformer that takes all + * input artifacts with the matching input file tag and creates + * one or more artifacts. (e.g. linker rule) + * + * A "non-multiplex rule" creates one transformer per matching input file. + */ +class Rule : public qbs::PersistentObject +{ +public: + typedef QSharedPointer<Rule> Ptr; + ResolvedModule::Ptr module; + JsImports jsImports; + RuleScript::Ptr script; + QStringList inputs; + QStringList usings; + QStringList explicitlyDependsOn; + bool multiplex; + QString objectId; + QList<RuleArtifact::Ptr> artifacts; + QMap<QString, QScriptProgram> transformProperties; + + // members that we don't need to save + int ruleGraphId; + + Rule() + : multiplex(false) + , ruleGraphId(-1) + {} + + QString toString() const; + QStringList outputFileTags() const; + + inline bool isMultiplexRule() const + { + return multiplex; + } + +private: + void load(qbs::PersistentPool &pool, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &pool, qbs::PersistentObjectData &data) const; +}; + +class ResolvedTransformer +{ +public: + typedef QSharedPointer<ResolvedTransformer> Ptr; + + ResolvedModule::Ptr module; + QStringList inputs; + QList<SourceArtifact::Ptr> outputs; + RuleScript::Ptr transform; + JsImports jsImports; +}; + +class ResolvedProject; +class ResolvedProduct: public qbs::PersistentObject +{ +public: + typedef QSharedPointer<ResolvedProduct> Ptr; + ResolvedProduct(); + + QStringList fileTags; + QString name; + QString buildDirectory; + QString sourceDirectory; + QString destinationDirectory; + QString qbsFile; + ResolvedProject *project; + Configuration::Ptr configuration; + QSet<SourceArtifact::Ptr> sources; + QSet<Rule::Ptr> rules; + QSet<ResolvedProduct::Ptr> uses; + QSet<FileTagger::Ptr> fileTaggers; + QList<ResolvedModule::Ptr> modules; + QList<ResolvedTransformer::Ptr> transformers; + + mutable QProcessEnvironment buildEnvironment; // must not be saved + mutable QProcessEnvironment runEnvironment; // must not be saved + + QSet<QString> fileTagsForFileName(const QString &fileName) const; + void setupBuildEnvironment(QScriptEngine *scriptEngine, const QProcessEnvironment &systemEnvironment) const; + void setupRunEnvironment(QScriptEngine *scriptEngine, const QProcessEnvironment &systemEnvironment) const; + +private: + void load(qbs::PersistentPool &pool, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &pool, qbs::PersistentObjectData &data) const; +}; + +class ResolvedProject: public qbs::PersistentObject +{ +public: + typedef QSharedPointer<ResolvedProject> Ptr; + QString id; + QString qbsFile; + QSet<ResolvedProduct::Ptr> products; + Configuration::Ptr configuration; + +private: + void load(qbs::PersistentPool &pool, qbs::PersistentObjectData &data); + void store(qbs::PersistentPool &pool, qbs::PersistentObjectData &data) const; +}; + +} // namespace qbs + +#endif diff --git a/src/lib/language/language.pri b/src/lib/language/language.pri new file mode 100644 index 000000000..d2b5bdf83 --- /dev/null +++ b/src/lib/language/language.pri @@ -0,0 +1,9 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/loader.h \ + $$PWD/language.h + +SOURCES += \ + $$PWD/loader.cpp \ + $$PWD/language.cpp diff --git a/src/lib/language/loader.cpp b/src/lib/language/loader.cpp new file mode 100644 index 000000000..4fcd4abb3 --- /dev/null +++ b/src/lib/language/loader.cpp @@ -0,0 +1,2647 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + + +#include "loader.h" + +#include "language.h" +#include <tools/error.h> +#include <tools/settings.h> +#include <tools/fileinfo.h> +#include <tools/runenvironment.h> +#include <tools/scripttools.h> +#include <tools/logger.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QSettings> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtCore/QDirIterator> + +#include <QtScript/QScriptEngine> +#include <QtScript/QScriptProgram> +#include <QtScript/QScriptValueIterator> + +#include <parser/qmljsparser_p.h> +#include <parser/qmljsnodepool_p.h> +#include <parser/qmljsengine_p.h> +#include <parser/qmljslexer_p.h> + +QT_BEGIN_NAMESPACE +static uint qHash(const QStringList &list) +{ + uint hash = 0; + foreach (const QString &n, list) + hash ^= qHash(n); + return hash; +} +QT_END_NAMESPACE + +using namespace QmlJS::AST; + + +namespace qbs { + +const QString dumpIndent(" "); + +PropertyDeclaration::PropertyDeclaration() + : type(UnknownType) + , flags(DefaultFlags) +{ +} + +PropertyDeclaration::PropertyDeclaration(const QString &name, Type type, Flags flags) + : name(name) + , type(type) + , flags(flags) +{ +} + +PropertyDeclaration::~PropertyDeclaration() +{ +} + +LanguageObject::LanguageObject(ProjectFile *owner) + : file(owner) +{ + file->registerLanguageObject(this); +} + +LanguageObject::LanguageObject(const LanguageObject &other) + : id(other.id) + , prototype(other.prototype) + , prototypeFileName(other.prototypeFileName) + , prototypeLocation(other.prototypeLocation) + , file(other.file) + , bindings(other.bindings) + , functions(other.functions) + , propertyDeclarations(other.propertyDeclarations) +{ + file->registerLanguageObject(this); + children.reserve(other.children.size()); + for (int i = 0; i < other.children.size(); ++i) + children.append(new LanguageObject(*other.children.at(i))); +} + +LanguageObject::~LanguageObject() +{ + if (!file->isDestructing()) + file->registerLanguageObject(this); +} + +ScopeChain::ScopeChain(QScriptEngine *engine, const QSharedPointer<Scope> &root) + : QScriptClass(engine) +{ + m_value = engine->newObject(this); + m_globalObject = engine->globalObject(); + if (root) + m_scopes.append(root); +} + +ScopeChain::~ScopeChain() +{ +} + +ScopeChain *ScopeChain::clone() const +{ + ScopeChain *s = new ScopeChain(engine(), m_scopes.last()); + s->m_scopes = m_scopes; + return s; +} + +QScriptValue ScopeChain::value() +{ + return m_value; +} + +Scope::Ptr ScopeChain::first() const +{ + return m_scopes.first(); +} + +Scope::Ptr ScopeChain::last() const +{ + return m_scopes.last(); +} + +ScopeChain *ScopeChain::prepend(const QSharedPointer<Scope> &newTop) +{ + if (!newTop) + return this; + m_scopes.prepend(newTop); + return this; +} + +QSharedPointer<Scope> ScopeChain::findNonEmpty(const QString &name) const +{ + foreach (const Scope::Ptr &scope, m_scopes) { + if (scope->name() == name && !scope->properties.isEmpty()) + return scope; + } + return Scope::Ptr(); +} + +QSharedPointer<Scope> ScopeChain::find(const QString &name) const +{ + foreach (const Scope::Ptr &scope, m_scopes) { + if (scope->name() == name) + return scope; + } + return Scope::Ptr(); +} + +Property ScopeChain::lookupProperty(const QString &name) const +{ + foreach (const Scope::Ptr &scope, m_scopes) { + Property p = scope->properties.value(name); + if (p.isValid()) + return p; + } + return Property(); +} + +ScopeChain::QueryFlags ScopeChain::queryProperty(const QScriptValue &object, const QScriptString &name, + QueryFlags flags, uint *id) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(id); + return (HandlesReadAccess | HandlesWriteAccess) & flags; +} + +QScriptValue ScopeChain::property(const QScriptValue &object, const QScriptString &name, uint id) +{ + Q_UNUSED(object); + Q_UNUSED(id); + QScriptValue value; + foreach (const Scope::Ptr &scope, m_scopes) { + value = scope->value.property(name); + if (value.isError()) { + engine()->clearExceptions(); + } else if (value.isValid()) { + return value; + } + } + value = m_globalObject.property(name); + if (!value.isValid() || (value.isUndefined() && name.toString() != QLatin1String("undefined"))) { + QString msg("Undefined property '%1'"); + value = engine()->currentContext()->throwError(msg.arg(name.toString())); + } + return value; +} + +void ScopeChain::setProperty(QScriptValue &, const QScriptString &name, uint, const QScriptValue &) +{ + QString msg("Removing or setting property '%1' in a binding is invalid."); + engine()->currentContext()->throwError(msg.arg(name.toString())); +} + +Property::Property(EvaluationObject * object) + : scope(object->scope) +{ +} + +Property::Property(const QScriptValue &scriptValue) + : value(scriptValue) +{ +} + +static QScriptValue evaluate(QScriptEngine *engine, const QScriptProgram &expression) +{ + QScriptValue result = engine->evaluate(expression); + if (engine->hasUncaughtException()) { + QString errorMessage = engine->uncaughtException().toString(); + int errorLine = engine->uncaughtExceptionLineNumber(); + engine->clearExceptions(); + throw Error(errorMessage, expression.fileName(), errorLine); + } + if (result.isError()) + throw Error(result.toString()); + return result; +} + +std::set<Scope *> Scope::scopesWithEvaluatedProperties; + +Scope::Scope(QScriptEngine *engine, const QString &name) + : QScriptClass(engine) + , m_name(name) +{ +} + +QSharedPointer<Scope> Scope::create(QScriptEngine *engine, const QString &name, ProjectFile *owner) +{ + QSharedPointer<Scope> obj(new Scope(engine, name)); + obj->value = engine->newObject(obj.data()); + owner->registerScope(obj); + return obj; +} + +Scope::~Scope() +{ +} + +QString Scope::name() const +{ + return m_name; +} + +static const bool debugProperties = false; + +Scope::QueryFlags Scope::queryProperty(const QScriptValue &object, const QScriptString &name, + QueryFlags flags, uint *id) +{ + const QString nameString = name.toString(); + if (properties.contains(nameString)) { + *id = 0; + return (HandlesReadAccess | HandlesWriteAccess) & flags; + } + if (fallbackScope && fallbackScope.data()->queryProperty(object, name, flags, id)) { + *id = 1; + return (HandlesReadAccess | HandlesWriteAccess) & flags; + } + + QScriptValue proto = value.prototype(); + if (proto.isValid()) { + QScriptValue v = proto.property(name); + if (!v.isValid()) { + *id = 2; + return (HandlesReadAccess | HandlesWriteAccess) & flags; + } + } + + if (debugProperties) + qbsTrace() << "PROPERTIES: we don't handle " << name.toString(); + return 0; +} + +QScriptValue Scope::property(const QScriptValue &object, const QScriptString &name, uint id) +{ + if (id == 1) + return fallbackScope.data()->property(object, name, 0); + else if (id == 2) { + QString msg = "Property %0.%1 is undefined."; + return engine()->currentContext()->throwError(msg.arg(m_name, name)); + } + + const QString nameString = name.toString(); + + Property property = properties.value(nameString); + + if (debugProperties) + qbsTrace() << "PROPERTIES: evaluating " << nameString; + + if (!property.isValid()) { + if (debugProperties) + qbsTrace() << " : no such property"; + return QScriptValue(); // does this raise an error? + } + + if (property.scope) { + if (debugProperties) + qbsTrace() << " : object property"; + return property.scope->value; + } + + if (property.value.isValid()) { + if (debugProperties) + qbsTrace() << " : pre-evaluated property: " << property.value.toVariant(); + return property.value; + } + + // evaluate now + if (debugProperties) + qbsTrace() << " : evaluating now: " << property.valueSource.sourceCode(); + QScriptContext *context = engine()->currentContext(); + const QScriptValue oldActivation = context->activationObject(); + const QString &sourceCode = property.valueSource.sourceCode(); + + // evaluate base properties + QLatin1String baseValueName("base"); + const bool usesBaseProperty = sourceCode.contains(baseValueName); + if (usesBaseProperty) { + foreach (const Property &baseProperty, property.baseProperties) { + context->setActivationObject(baseProperty.scopeChain->value()); + QScriptValue baseValue; + try { + baseValue = evaluate(engine(), baseProperty.valueSource); + } + catch (Error &e) + { + baseValue = engine()->currentContext()->throwError("error while evaluating:\n" + e.toString()); + } + engine()->globalObject().setProperty(baseValueName, baseValue); + } + } + + context->setActivationObject(property.scopeChain->value()); + + QLatin1String oldValueName("outer"); + const bool usesOldProperty = fallbackScope && sourceCode.contains(oldValueName); + if (usesOldProperty) { + QScriptValue oldValue = fallbackScope.data()->value.property(name); + if (oldValue.isValid() && !oldValue.isError()) + engine()->globalObject().setProperty(oldValueName, oldValue); + } + + QScriptValue result; + // Do not throw exceptions through the depths of the script engine. + try { + result = evaluate(engine(), property.valueSource); + } + catch (Error &e) + { + result = engine()->currentContext()->throwError("error while evaluating:\n" + e.toString()); + } + + if (debugProperties) { + qbsTrace() << "PROPERTIES: evaluated " << nameString << " to " << result.toVariant() << " " << result.toString(); + if (result.isError()) + qbsTrace() << " was error!"; + } + + Scope::scopesWithEvaluatedProperties.insert(this); + property.value = result; + properties.insert(nameString, property); + + if (usesOldProperty) + engine()->globalObject().setProperty(oldValueName, engine()->undefinedValue()); + if (usesBaseProperty) + engine()->globalObject().setProperty(baseValueName, engine()->undefinedValue()); + context->setActivationObject(oldActivation); + + return result; +} + +QScriptValue Scope::property(const QString &name) const +{ + QScriptValue result = value.property(name); + if (result.isError()) + throw Error(result.toString()); + return result; +} + +bool Scope::boolValue(const QString &name, bool defaultValue) const +{ + QScriptValue scriptValue = property(name); + if (scriptValue.isBool()) + return scriptValue.toBool(); + return defaultValue; +} + +QString Scope::stringValue(const QString &name) const +{ + QScriptValue scriptValue = property(name); + if (scriptValue.isString()) + return scriptValue.toString(); + QVariant v = scriptValue.toVariant(); + if (v.type() == QVariant::String) { + return v.toString(); + } else if (v.type() == QVariant::StringList) { + const QStringList lst = v.toStringList(); + if (lst.count() == 1) + return lst.first(); + } + return QString(); +} + +QStringList Scope::stringListValue(const QString &name) const +{ + QScriptValue scriptValue = property(name); + if (scriptValue.isString()) { + return QStringList(scriptValue.toString()); + } else if (scriptValue.isArray()) { + QStringList lst; + int i=0; + forever { + QScriptValue item = scriptValue.property(i++); + if (!item.isValid()) + break; + if (!item.isString()) + continue; + lst.append(item.toString()); + } + return lst; + } + return QStringList(); +} + +QString Scope::verbatimValue(const QString &name) const +{ + const Property &property = properties.value(name); + return property.valueSource.sourceCode(); +} + +void Scope::dump(const QByteArray &aIndent) const +{ + QByteArray indent = aIndent; + printf("%sScope: {\n", indent.constData()); + indent.append(dumpIndent); + printf("%sName: '%s'\n", indent.constData(), qPrintable(m_name)); + if (!properties.isEmpty()) { + printf("%sProperties: [\n", indent.constData()); + indent.append(dumpIndent); + foreach (const QString &propertyName, properties.keys()) { + QScriptValue scriptValue = property(propertyName); + QString propertyValue; + if (scriptValue.isString()) + propertyValue = stringValue(propertyName); + else if (scriptValue.isArray()) + propertyValue = stringListValue(propertyName).join(", "); + else if (scriptValue.isBool()) + propertyValue = boolValue(propertyName) ? "true" : "false"; + else + propertyValue = verbatimValue(propertyName); + printf("%s'%s': %s\n", indent.constData(), qPrintable(propertyName), qPrintable(propertyValue)); + } + indent.chop(dumpIndent.length()); + printf("%s]\n", indent.constData()); + } + if (!declarations.isEmpty()) + printf("%sPropertyDeclarations: [%s]\n", indent.constData(), qPrintable(QStringList(declarations.keys()).join(", "))); + + indent.chop(dumpIndent.length()); + printf("%s}\n", indent.constData()); +} + +EvaluationObject::EvaluationObject(LanguageObject *instantiatingObject) +{ + instantiatingObject->file->registerEvaluationObject(this); + objects.append(instantiatingObject); +} + +EvaluationObject::~EvaluationObject() +{ + ProjectFile *file = instantiatingObject()->file; + if (!file->isDestructing()) + file->unregisterEvaluationObject(this); +} + +LanguageObject *EvaluationObject::instantiatingObject() const +{ + return objects.first(); +} + +void EvaluationObject::dump(QByteArray &indent) +{ + printf("%sEvaluationObject: {\n", indent.constData()); + indent.append(dumpIndent); + printf("%sProtoType: '%s'\n", indent.constData(), qPrintable(prototype)); + if (!modules.isEmpty()) { + printf("%sModules: [\n", indent.constData()); + indent.append(dumpIndent); + foreach (const QSharedPointer<Module> module, modules) + module->dump(indent); + indent.chop(dumpIndent.length()); + printf("%s]\n", indent.constData()); + } + scope->dump(indent); + foreach (EvaluationObject *child, children) + child->dump(indent); + indent.chop(dumpIndent.length()); + printf("%s}\n", indent.constData()); +} + +Module::Module() + : object(0) +{ +} + +Module::~Module() +{ +} + +ProjectFile *Module::file() const +{ + return object->instantiatingObject()->file; +} + +void Module::dump(QByteArray &indent) +{ + printf("%s'%s': %s\n", indent.constData(), qPrintable(name), qPrintable(dependsLocation.fileName)); +} + +static QStringList resolvePaths(const QStringList &paths, const QString &base) +{ + QStringList resolved; + foreach (const QString &path, paths) { + QString resolvedPath = FileInfo::resolvePath(base, path); + resolvedPath = QDir::cleanPath(resolvedPath); + resolved += resolvedPath; + } + return resolved; +} + + +static const char szLoaderPropertyName[] = "qbs_loader_ptr"; +static const QLatin1String name_FileTagger("FileTagger"); +static const QLatin1String name_Rule("Rule"); +static const QLatin1String name_Transformer("Transformer"); +static const QLatin1String name_TransformProperties("TransformProperties"); +static const QLatin1String name_Artifact("Artifact"); +static const QLatin1String name_Group("Group"); +static const QLatin1String name_Project("Project"); +static const QLatin1String name_Product("Product"); +static const QLatin1String name_ProductModule("ProductModule"); +static const QLatin1String name_Module("Module"); +static const QLatin1String name_Properties("Properties"); +static const QLatin1String name_PropertyOptions("PropertyOptions"); +static const QLatin1String name_Depends("Depends"); +static const QLatin1String name_moduleSearchPaths("moduleSearchPaths"); +static const uint hashName_FileTagger = qHash(name_FileTagger); +static const uint hashName_Rule = qHash(name_Rule); +static const uint hashName_Transformer = qHash(name_Transformer); +static const uint hashName_TransformProperties = qHash(name_TransformProperties); +static const uint hashName_Artifact = qHash(name_Artifact); +static const uint hashName_Group = qHash(name_Group); +static const uint hashName_Project = qHash(name_Project); +static const uint hashName_Product = qHash(name_Product); +static const uint hashName_ProductModule = qHash(name_ProductModule); +static const uint hashName_Module = qHash(name_Module); +static const uint hashName_Properties = qHash(name_Properties); +static const uint hashName_PropertyOptions = qHash(name_PropertyOptions); +static const uint hashName_Depends = qHash(name_Depends); +QHash<QString, PropertyDeclaration> Loader::m_dependsPropertyDeclarations; + +static const QLatin1String name_productPropertyScope("product property scope"); +static const QLatin1String name_projectPropertyScope("project property scope"); + +Loader::Loader() +{ + m_settings = Settings::create(); + + QVariant v; + v.setValue(static_cast<void*>(this)); + m_engine.setProperty(szLoaderPropertyName, v); + m_engine.pushContext(); // this preserves the original global object + + m_jsFunction_getHostOS = m_engine.newFunction(js_getHostOS, 0); + m_jsFunction_getHostDefaultArchitecture = m_engine.newFunction(js_getHostDefaultArchitecture, 0); + m_jsFunction_configurationValue = m_engine.newFunction(js_configurationValue, 2); + + if (m_dependsPropertyDeclarations.isEmpty()) { + QList<PropertyDeclaration> depends; + depends += PropertyDeclaration("name", PropertyDeclaration::String); + depends += PropertyDeclaration("submodules", PropertyDeclaration::Variant); + depends += PropertyDeclaration("condition", PropertyDeclaration::Boolean); + depends += PropertyDeclaration("required", PropertyDeclaration::Boolean); + depends += PropertyDeclaration("failureMessage", PropertyDeclaration::String); + foreach (const PropertyDeclaration &pd, depends) + m_dependsPropertyDeclarations.insert(pd.name, pd); + } +} + +Loader::~Loader() +{ +} + +static bool compare(const QStringList &list, const QString &value) +{ + if (list.size() != 1) + return false; + return list.first() == value; +} + +void Loader::setSearchPaths(const QStringList &searchPaths) +{ + m_searchPaths = searchPaths; +} + +ProjectFile::Ptr Loader::loadProject(const QString &fileName) +{ + m_settings->loadProjectSettings(fileName); + m_project = parseFile(fileName); + return m_project; +} + +static void setPathAndFilePath(const Scope::Ptr &scope, const QString &filePath, const QString &prefix = QString()) +{ + QString filePathPropertyName("filePath"); + QString pathPropertyName("path"); + if (!prefix.isEmpty()) { + filePathPropertyName = prefix + QLatin1String("FilePath"); + pathPropertyName = prefix + QLatin1String("Path"); + } + scope->properties.insert(filePathPropertyName, Property(QScriptValue(filePath))); + scope->properties.insert(pathPropertyName, Property(QScriptValue(FileInfo::path(filePath)))); +} + +Scope::Ptr Loader::buildFileContext(ProjectFile *file) +{ + Scope::Ptr context = Scope::create(&m_engine, QLatin1String("global file context"), file); + setPathAndFilePath(context, file->fileName, QLatin1String("local")); + evaluateImports(context, file->jsImports); + + return context; +} + +void Loader::resolveInheritance(LanguageObject *object, EvaluationObject *evaluationObject, + ScopeChain::Ptr moduleScope, const QVariantMap &userProperties) +{ + if (object->prototypeFileName.isEmpty()) { + if (object->prototype.size() != 1) + throw Error("prototype with dots does not resolve to a file", object->prototypeLocation); + evaluationObject->prototype = object->prototype.first(); + + setupInternalPrototype(evaluationObject); + + // once we know something is a project/product, add a property to + // the correct scope + if (evaluationObject->prototype == name_Project) { + if (Scope::Ptr projectPropertyScope = moduleScope->find(name_projectPropertyScope)) + projectPropertyScope->properties.insert("project", Property(evaluationObject)); + } + else if (evaluationObject->prototype == name_Product) { + if (Scope::Ptr productPropertyScope = moduleScope->find(name_productPropertyScope)) + productPropertyScope->properties.insert("product", Property(evaluationObject)); + } + + return; + } + + // load prototype (cache result) + ProjectFile::Ptr file = parseFile(object->prototypeFileName); + + // recurse to prototype's prototype + if (evaluationObject->objects.contains(file->root)) { + QString msg("circular prototypes in instantiation of '%1', '%2' recurred"); + throw Error(msg.arg(evaluationObject->instantiatingObject()->prototype.join("."), + object->prototype.join("."))); + } + evaluationObject->objects.append(file->root); + resolveInheritance(file->root, evaluationObject, moduleScope, userProperties); + + // ### expensive, and could be shared among all builds of this prototype instance + Scope::Ptr context = buildFileContext(file.data()); + + // project and product scopes are always available + ScopeChain::Ptr scopeChain(new ScopeChain(&m_engine, context)); + if (Scope::Ptr projectPropertyScope = moduleScope->findNonEmpty(name_projectPropertyScope)) + scopeChain->prepend(projectPropertyScope); + if (Scope::Ptr productPropertyScope = moduleScope->findNonEmpty(name_productPropertyScope)) + scopeChain->prepend(productPropertyScope); + + scopeChain->prepend(evaluationObject->scope); + + // having a module scope enables resolving of Depends blocks + if (moduleScope) + evaluateDependencies(file->root, evaluationObject, scopeChain, moduleScope, userProperties); + + fillEvaluationObject(scopeChain, file->root, evaluationObject->scope, evaluationObject, userProperties); + +// QByteArray indent; +// evaluationObject->dump(indent); +} + +static bool checkFileCondition(QScriptEngine *engine, const ScopeChain::Ptr &scope, const ProjectFile *file) +{ + static const bool debugCondition = false; + if (debugCondition) + qbsTrace() << "Checking condition"; + + const Binding &condition = file->root->bindings.value(QStringList("condition")); + if (!condition.isValid()) + return true; + + QScriptContext *context = engine->currentContext(); + const QScriptValue oldActivation = context->activationObject(); + context->setActivationObject(scope->value()); + + if (debugCondition) + qbsTrace() << " code is: " << condition.valueSource.sourceCode(); + const QScriptValue value = evaluate(engine, condition.valueSource); + bool result = false; + if (value.isBool()) + result = value.toBool(); + else + throw Error(QString("Condition return type must be boolean."), CodeLocation(condition.valueSource.fileName(), condition.valueSource.firstLineNumber())); + if (debugCondition) + qbsTrace() << " result: " << value.toString(); + + context->setActivationObject(oldActivation); + return result; +} + +static void applyFunctions(QScriptEngine *engine, LanguageObject *object, EvaluationObject *evaluationObject, ScopeChain::Ptr scope) +{ + if (object->functions.isEmpty()) + return; + + // set the activation object to the correct scope + QScriptValue oldActivation = engine->currentContext()->activationObject(); + engine->currentContext()->setActivationObject(scope->value()); + + foreach (const Function &func, object->functions) { + Property property; + property.value = evaluate(engine, func.source); + evaluationObject->scope->properties.insert(func.name, property); + } + + engine->currentContext()->setActivationObject(oldActivation); +} + +static void testIfValueIsAllowed(const QVariant &value, const QVariant &allowedValues, + const QString &propertyName, const CodeLocation &location) +{ + bool valueIsAllowed = false; + + if (value.type() == QVariant::String && allowedValues.type() == QVariant::List) + valueIsAllowed = allowedValues.toStringList().contains(value.toString()); + else if (value.type() == QVariant::Int && allowedValues.type() == QVariant::List) + valueIsAllowed = allowedValues.toList().contains(value); + else { + const QString msg = "The combination of the type of Property '%1' and the type of its allowedValues is not supported"; + throw Error(msg.arg(propertyName), location); + } + + if (!valueIsAllowed) { + const QString msg = "Value '%1' is not allowed for Property '%2'"; + throw Error(msg.arg(value.toString()).arg(propertyName), location); // TODO: print out allowed values? + } +} + +static void applyBinding(LanguageObject *object, const Binding &binding, const ScopeChain::Ptr &scopeChain) +{ + CodeLocation bindingLocation(binding.valueSource.fileName(), + binding.valueSource.firstLineNumber()); + Scope *target; + if (binding.name.size() == 1) { + target = scopeChain->first().data(); // assume the top scope is the 'current' one + } else { + if (compare(object->prototype, name_Artifact)) + return; + QScriptValue targetValue = scopeChain->value().property(binding.name.first()); + if (!targetValue.isValid() || targetValue.isError()) { + QString msg = "Binding '%1' failed, no property '%2' in the scope of %3"; + throw Error(msg.arg(binding.name.join("."), + binding.name.first(), + scopeChain->first()->name()), + bindingLocation); + } + target = dynamic_cast<Scope *>(targetValue.scriptClass()); + if (!target) { + QString msg = "Binding '%1' failed, property '%2' in the scope of %3 has no properties"; + throw Error(msg.arg(binding.name.join("."), + binding.name.first(), + scopeChain->first()->name()), + bindingLocation); + } + } + + for (int i = 1; i < binding.name.size() - 1; ++i) { + Scope *oldTarget = target; + const QString &bindingName = binding.name.at(i); + const QScriptValue &value = target->property(bindingName); + if (!value.isValid()) { + QString msg = "Binding '%1' failed, no property '%2' in %3"; + throw Error(msg.arg(binding.name.join("."), + binding.name.at(i), + target->name()), + bindingLocation); + } + target = dynamic_cast<Scope *>(value.scriptClass()); + if (!target) { + QString msg = "Binding '%1' failed, property '%2' in %3 has no properties"; + throw Error(msg.arg(binding.name.join("."), + bindingName, + oldTarget->name()), + bindingLocation); + } + } + + const QString name = binding.name.last(); + + if (!target->declarations.contains(name)) { + QString msg = "Binding '%1' failed, no property '%2' in %3"; + throw Error(msg.arg(binding.name.join("."), + name, + target->name()), + bindingLocation); + } + + Property newProperty; + newProperty.valueSource = binding.valueSource; + newProperty.scopeChain = scopeChain; + + Property &property = target->properties[name]; + if (!property.valueSource.isNull()) { + newProperty.baseProperties += property.baseProperties; + property.baseProperties.clear(); + newProperty.baseProperties += property; + } + property = newProperty; + + const PropertyDeclaration &decl = object->propertyDeclarations.value(name); + // ### testIfValueIsAllowed is wrong here... + if (!decl.allowedValues.isNull()) + testIfValueIsAllowed(target->property(name).toVariant(), decl.allowedValues, name, bindingLocation); +} + +static void applyBindings(LanguageObject *object, ScopeChain::Ptr scopeChain) +{ + foreach (const Binding &binding, object->bindings) + applyBinding(object, binding, scopeChain); +} + +void Loader::fillEvaluationObjectForProperties(const ScopeChain::Ptr &scope, LanguageObject *object, Scope::Ptr ids, EvaluationObject *evaluationObject, const QVariantMap &userProperties) +{ + if (!object->children.isEmpty()) + throw Error("Properties block may not have children", object->children.first()->prototypeLocation); + + const QStringList conditionName("condition"); + Binding condition = object->bindings.value(conditionName); + if (!condition.isValid()) + throw Error("Properties block must have a condition property", object->prototypeLocation); + + LanguageObject *ifCopy = new LanguageObject(*object); + + // adjust bindings to be if (condition) { original-source } + QMutableHashIterator<QStringList, Binding> it(ifCopy->bindings); + while (it.hasNext()) { + it.next(); + if (it.key() == conditionName) { + it.remove(); + continue; + } + Binding &binding = it.value(); + binding.valueSource = QScriptProgram( + QString("if (%1) { %2 }").arg( + condition.valueSource.sourceCode(), + binding.valueSource.sourceCode()), + binding.valueSource.fileName(), + binding.valueSource.firstLineNumber()); + } + + fillEvaluationObject(scope, ifCopy, ids, evaluationObject, userProperties); +} + +void Loader::setupInternalPrototype(EvaluationObject *evaluationObject) +{ + // special builtins + static QHash<QString, QList<PropertyDeclaration> > builtinDeclarations; + if (builtinDeclarations.isEmpty()) { + builtinDeclarations.insert(name_Depends, m_dependsPropertyDeclarations.values()); + PropertyDeclaration conditionProperty("condition", PropertyDeclaration::Boolean); + + QList<PropertyDeclaration> project; + project += PropertyDeclaration("references", PropertyDeclaration::Variant); + project += PropertyDeclaration(name_moduleSearchPaths, PropertyDeclaration::Variant); + builtinDeclarations.insert(name_Project, project); + + QList<PropertyDeclaration> product; + product += PropertyDeclaration("type", PropertyDeclaration::String); + product += PropertyDeclaration("name", PropertyDeclaration::String); + product += PropertyDeclaration("destination", PropertyDeclaration::String); + product += PropertyDeclaration("files", PropertyDeclaration::Variant, PropertyDeclaration::PropertyNotAvailableInConfig); + product += PropertyDeclaration("module", PropertyDeclaration::Variant); + product += PropertyDeclaration("modules", PropertyDeclaration::Variant); + product += PropertyDeclaration(name_moduleSearchPaths, PropertyDeclaration::Variant); + builtinDeclarations.insert(name_Product, product); + + QList<PropertyDeclaration> fileTagger; + fileTagger += PropertyDeclaration("pattern", PropertyDeclaration::String); + fileTagger += PropertyDeclaration("fileTags", PropertyDeclaration::Variant); + builtinDeclarations.insert(name_FileTagger, fileTagger); + + QList<PropertyDeclaration> group; + group += conditionProperty; + group += PropertyDeclaration("files", PropertyDeclaration::Variant, PropertyDeclaration::PropertyNotAvailableInConfig); + group += PropertyDeclaration("fileTags", PropertyDeclaration::Variant, PropertyDeclaration::PropertyNotAvailableInConfig); + group += PropertyDeclaration("prefix", PropertyDeclaration::Variant, PropertyDeclaration::PropertyNotAvailableInConfig); + builtinDeclarations.insert(name_Group, group); + + QList<PropertyDeclaration> artifact; + artifact += conditionProperty; + artifact += PropertyDeclaration("fileName", PropertyDeclaration::Verbatim); + artifact += PropertyDeclaration("fileTags", PropertyDeclaration::Variant); + builtinDeclarations.insert(name_Artifact, artifact); + + QList<PropertyDeclaration> rule; + rule += PropertyDeclaration("multiplex", PropertyDeclaration::Boolean); + rule += PropertyDeclaration("inputs", PropertyDeclaration::Variant); + rule += PropertyDeclaration("usings", PropertyDeclaration::Variant); + rule += PropertyDeclaration("explicitlyDependsOn", PropertyDeclaration::Variant); + rule += PropertyDeclaration("prepare", PropertyDeclaration::Verbatim); + builtinDeclarations.insert(name_Rule, rule); + + QList<PropertyDeclaration> transformer; + transformer += PropertyDeclaration("inputs", PropertyDeclaration::Variant); + transformer += PropertyDeclaration("prepare", PropertyDeclaration::Verbatim); + transformer += conditionProperty; + builtinDeclarations.insert(name_Transformer, transformer); + + QList<PropertyDeclaration> transformProperties; + builtinDeclarations.insert(name_TransformProperties, transformProperties); + + QList<PropertyDeclaration> productModule; + builtinDeclarations.insert(name_ProductModule, productModule); + + QList<PropertyDeclaration> module; + module += PropertyDeclaration("name", PropertyDeclaration::String); + module += PropertyDeclaration("setupBuildEnvironment", PropertyDeclaration::Verbatim); + module += PropertyDeclaration("setupRunEnvironment", PropertyDeclaration::Verbatim); + module += PropertyDeclaration("additionalProductFileTags", PropertyDeclaration::Variant); + module += conditionProperty; + builtinDeclarations.insert(name_Module, module); + + QList<PropertyDeclaration> propertyOptions; + propertyOptions += PropertyDeclaration("name", PropertyDeclaration::String); + propertyOptions += PropertyDeclaration("allowedValues", PropertyDeclaration::Variant); + propertyOptions += PropertyDeclaration("description", PropertyDeclaration::String); + builtinDeclarations.insert(name_PropertyOptions, propertyOptions); + } + + if (!builtinDeclarations.contains(evaluationObject->prototype)) + throw Error(QString("Type name '%1' is unknown.").arg(evaluationObject->prototype), + evaluationObject->instantiatingObject()->prototypeLocation); + + foreach (const PropertyDeclaration &pd, builtinDeclarations.value(evaluationObject->prototype)) { + evaluationObject->scope->declarations.insert(pd.name, pd); + evaluationObject->scope->properties.insert(pd.name, Property(m_engine.undefinedValue())); + } +} + +void Loader::fillEvaluationObject(const ScopeChain::Ptr &scope, LanguageObject *object, Scope::Ptr ids, EvaluationObject *evaluationObject, const QVariantMap &userProperties) +{ + // fill subobjects recursively + foreach (LanguageObject *child, object->children) { + // 'Properties' objects are treated specially, they don't introduce a scope + // and don't get added as a child object + if (compare(child->prototype, name_Properties)) { + fillEvaluationObjectForProperties(scope, child, ids, evaluationObject, userProperties); + continue; + } + + // 'Depends' blocks are already handled before this function is called + // and should not appear in the children list + if (compare(child->prototype, name_Depends)) + continue; + + EvaluationObject *childEvObject = new EvaluationObject(child); + const QString propertiesName = child->prototype.join(QLatin1String(".")); + childEvObject->scope = Scope::create(&m_engine, propertiesName, object->file); + + resolveInheritance(child, childEvObject); // ### need to pass 'moduleScope' for product/project property scopes + const uint childPrototypeHash = qHash(childEvObject->prototype); + + ScopeChain::Ptr childScope(scope->clone()->prepend(childEvObject->scope)); + + if (!child->id.isEmpty()) { + ids->properties.insert(child->id, Property(childEvObject)); + } + + // for Group and ProjectModule, add new module instances + const bool isProductModule = (childPrototypeHash == hashName_ProductModule); + const bool isArtifact = (childPrototypeHash == hashName_Artifact); + if (isProductModule || isArtifact || childPrototypeHash == hashName_Group) { + QHashIterator<QString, Module::Ptr> moduleIt(evaluationObject->modules); + while (moduleIt.hasNext()) { + moduleIt.next(); + Module::Ptr module = moduleIt.value(); + if (module->id.isEmpty()) + continue; + Scope::Ptr moduleInstance = Scope::create(&m_engine, module->object->scope->name(), module->file()); + if (isProductModule) { + // A ProductModule does not inherit module values set in the product + // but has its own module instance. + ScopeChain::Ptr moduleScope(new ScopeChain(&m_engine)); + moduleScope->prepend(scope->findNonEmpty(name_productPropertyScope)); + moduleScope->prepend(scope->findNonEmpty(name_projectPropertyScope)); + moduleScope->prepend(childEvObject->scope); + module = loadModule(module->file(), module->id, module->name, moduleScope, userProperties, module->dependsLocation); + childEvObject->modules.insert(module->name, module); + } + if (!isArtifact) + moduleInstance->fallbackScope = module->object->scope; + moduleInstance->declarations = module->object->scope->declarations; + Property property(moduleInstance); + childEvObject->scope->properties.insert(module->id, property); + } + } + + // for TransformProperties, add declarations to parent + if (childPrototypeHash == hashName_TransformProperties) { + for (QHash<QString, PropertyDeclaration>::const_iterator it = child->propertyDeclarations.begin(); + it != child->propertyDeclarations.end(); ++it) { + evaluationObject->scope->declarations.insert(it.key(), it.value()); + } + } + + fillEvaluationObject(childScope, child, ids, childEvObject, userProperties); + evaluationObject->children.append(childEvObject); + } + + fillEvaluationObjectBasics(scope, object, evaluationObject); +} + +void Loader::fillEvaluationObjectBasics(const ScopeChain::Ptr &scopeChain, LanguageObject *object, EvaluationObject *evaluationObject) +{ + // append the property declarations + foreach (const PropertyDeclaration &pd, object->propertyDeclarations) + if (!evaluationObject->scope->declarations.contains(pd.name)) + evaluationObject->scope->declarations.insert(pd.name, pd); + + applyFunctions(&m_engine, object, evaluationObject, scopeChain); + applyBindings(object, scopeChain); +} + +void Loader::evaluateImports(Scope::Ptr target, const JsImports &jsImports) +{ + for (JsImports::const_iterator importIt = jsImports.begin(); + importIt != jsImports.end(); ++importIt) { + + QScriptValue targetObject = m_engine.newObject(); + foreach (const QString &fileName, importIt.value()) { + QScriptValue importResult = m_jsImports.value(fileName); + if (importResult.isValid()) { + targetObject = importResult; + } else { + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + QString msg("Couldn't open js import '%1'."); + // ### location + throw Error(msg.arg(fileName)); + continue; + } + const QString source = QTextStream(&file).readAll(); + file.close(); + const QScriptProgram program(source, fileName); + importResult = addJSImport(&m_engine, program, targetObject); + if (importResult.isError()) + throw Error(QLatin1String("error while evaluating import: ") + importResult.toString()); + + m_jsImports.insert(fileName, targetObject); + } + } + + target->properties.insert(importIt.key(), Property(targetObject)); + } +} + +void Loader::evaluatePropertyOptions(LanguageObject *object) +{ + foreach (LanguageObject *child, object->children) { + if (child->prototype.last() != name_PropertyOptions) + continue; + + const Binding nameBinding = child->bindings.value(QStringList("name")); + + if (!nameBinding.isValid()) + throw Error(name_PropertyOptions + " needs to define a 'name'"); + + const QScriptValue nameScriptValue = evaluate(&m_engine, nameBinding.valueSource); + const QString name = nameScriptValue.toString(); + + if (!object->propertyDeclarations.contains(name)) + throw Error(QString("no propery with name '%1' found").arg(name)); + + PropertyDeclaration &decl = object->propertyDeclarations[name]; + + const Binding allowedValuesBinding = child->bindings.value(QStringList("allowedValues")); + if (allowedValuesBinding.isValid()) { + const QScriptValue allowedValuesScriptValue = evaluate(&m_engine, allowedValuesBinding.valueSource); + decl.allowedValues = allowedValuesScriptValue.toVariant(); + } + + const Binding descriptionBinding = child->bindings.value(QStringList("description")); + if (descriptionBinding.isValid()) { + const QScriptValue description = evaluate(&m_engine, descriptionBinding.valueSource); + decl.description = description.toString(); + } + } +} + +Module::Ptr Loader::loadModule(ProjectFile *file, const QString &moduleId, const QString &moduleName, + ScopeChain::Ptr moduleBaseScope, const QVariantMap &userProperties, + const CodeLocation &dependsLocation) +{ + const bool isBaseModule = (moduleName == "qbs"); + + Module::Ptr module = Module::Ptr(new Module); + module->id = moduleId; + module->name = moduleName; + module->dependsLocation = dependsLocation; + module->object = new EvaluationObject(file->root); + const QString propertiesName = QString("module %1").arg(moduleName); + module->object->scope = Scope::create(&m_engine, propertiesName, file); + + resolveInheritance(file->root, module->object, moduleBaseScope, userProperties); + if (module->object->prototype != name_Module) + return Module::Ptr(); + + module->object->scope->properties.insert("name", Property(m_engine.toScriptValue(moduleName))); + module->context = buildFileContext(file); + + ScopeChain::Ptr moduleScope(moduleBaseScope->clone()); + moduleScope->prepend(module->context); + moduleScope->prepend(module->object->scope); + if (isBaseModule) { + // setup helper properties of the base module + Property p; + p.value = m_jsFunction_getHostOS; + module->object->scope->properties.insert("getHostOS", p); + p.value = m_jsFunction_getHostDefaultArchitecture; + module->object->scope->properties.insert("getHostDefaultArchitecture", p); + p.value = m_jsFunction_configurationValue; + module->object->scope->properties.insert("configurationValue", p); + } + + evaluatePropertyOptions(file->root); + evaluateDependencies(file->root, module->object, moduleScope, moduleBaseScope, userProperties, !isBaseModule); + if (!module->object->unknownModules.isEmpty()) { + QString msg; + foreach (const UnknownModule &missingModule, module->object->unknownModules) { + msg.append(Error(QString("Module '%1' cannot be loaded.").arg(missingModule.name), + missingModule.dependsLocation).toString()); + msg.append("\n"); + } + throw Error(msg); + } + buildModulesProperty(module->object); + + if (checkFileCondition(&m_engine, moduleScope, file)) { + qbsTrace() << "loading module '" << moduleName << "' from " << file->fileName; + if (!file->root->id.isEmpty()) + module->context->properties.insert(file->root->id, Property(module->object)); + fillEvaluationObject(moduleScope, file->root, module->object->scope, module->object, userProperties); + + // override properties given on the command line + const QVariantMap userModuleProperties = userProperties.value(moduleName).toMap(); + for (QVariantMap::const_iterator vmit = userModuleProperties.begin(); vmit != userModuleProperties.end(); ++vmit) { + if (!module->object->scope->properties.contains(vmit.key())) + throw Error("Unknown property: " + module->id + '.' + vmit.key()); + module->object->scope->properties.insert(vmit.key(), Property(m_engine.toScriptValue(vmit.value()))); + + const PropertyDeclaration &decl = module->object->scope->declarations.value(vmit.key()); + if (!decl.allowedValues.isNull()) + testIfValueIsAllowed(vmit.value(), decl.allowedValues, vmit.key(), dependsLocation); + } + + return module; + } + + return Module::Ptr(); +} + +/// load all module.qbs files, checking their conditions +Module::Ptr Loader::loadModule(const QString &moduleId, const QString &moduleName, ScopeChain::Ptr moduleBaseScope, + const QVariantMap &userProperties, const CodeLocation &dependsLocation, + const QStringList &extraSearchPaths) +{ + Q_ASSERT(!moduleName.isEmpty()); + + Module::Ptr module; + QStringList searchPaths = extraSearchPaths; + + const QString searchSubDir("modules"); + foreach (const QString &path, m_searchPaths) + searchPaths += FileInfo::resolvePath(path, searchSubDir); + + foreach (const QString &path, searchPaths) { + QString dirPath = FileInfo::resolvePath(path, moduleName); + QFileInfo dirInfo(dirPath); + if (!dirInfo.isDir()) { + bool found = false; +#ifndef Q_OS_WIN + // On case sensitive file systems try to find the path. + QStringList subPaths = moduleName.split("/", QString::SkipEmptyParts); + QDir dir(path); + if (!dir.cd(searchSubDir)) + continue; + do { + QStringList lst = dir.entryList(QStringList(subPaths.takeFirst()), QDir::Dirs); + if (lst.count() != 1) + break; + if (!dir.cd(lst.first())) + break; + if (subPaths.isEmpty()) { + found = true; + dirPath = dir.absolutePath(); + } + } while (!found); +#endif + if (!found) + continue; + } + + QDirIterator dirIter(dirPath, QStringList("*.qbs")); + while (dirIter.hasNext()) { + QString fileName = dirIter.next(); + ProjectFile::Ptr file = parseFile(fileName); + if (!file) + throw Error("Error while parsing file: " + fileName, dependsLocation); + + module = loadModule(file.data(), moduleId, moduleName, moduleBaseScope, userProperties, dependsLocation); + if (module) + break; + } + if (module) + break; + } + + return module; +} + +void Loader::evaluateDependencies(LanguageObject *object, EvaluationObject *evaluationObject, const ScopeChain::Ptr &localScope, + ScopeChain::Ptr moduleScope, const QVariantMap &userProperties, bool loadBaseModule) +{ + // check for additional module search paths in the product + Binding searchPathsBinding = object->bindings.value(QStringList(name_moduleSearchPaths)); + if (searchPathsBinding.isValid()) + applyBinding(object, searchPathsBinding, localScope); + + // if none found, check for additional module search paths in the project + QStringList extraSearchPaths; + Property projectProperty = localScope->lookupProperty("project"); + if (projectProperty.isValid() && projectProperty.scope) { + extraSearchPaths = projectProperty.scope->stringListValue(name_moduleSearchPaths); + // ### depends on the project.path property + extraSearchPaths = resolvePaths(extraSearchPaths, projectProperty.scope->stringValue("path")); + } + + if (loadBaseModule) { + Module::Ptr baseModule = loadModule("qbs", "qbs", moduleScope, userProperties, CodeLocation(object->file->fileName)); + if (!baseModule) + throw Error("Cannot load the qbs base module."); + evaluationObject->modules.insert(baseModule->name, baseModule); + evaluationObject->scope->properties.insert(baseModule->id, Property(baseModule->object)); + } + + foreach (LanguageObject *child, object->children) { + if (compare(child->prototype, name_Depends)) { + QList<UnknownModule> unknownModules; + foreach (const Module::Ptr &m, evaluateDependency(evaluationObject, child, moduleScope, extraSearchPaths, &unknownModules, userProperties)) { + evaluationObject->modules.insert(m->name, m); + evaluationObject->scope->properties.insert(m->id, Property(m->object)); + } + evaluationObject->unknownModules.append(unknownModules); + } + } +} + +void Loader::buildModulesProperty(EvaluationObject *evaluationObject) +{ + // set up a XXX.modules property + Scope::Ptr modules = Scope::create(&m_engine, QLatin1String("modules property"), evaluationObject->instantiatingObject()->file); + for (QHash<QString, Module::Ptr>::const_iterator it = evaluationObject->modules.begin(); + it != evaluationObject->modules.end(); ++it) + { + modules->properties.insert(it.key(), Property(it.value()->object)); + modules->declarations.insert(it.key(), PropertyDeclaration(it.key(), PropertyDeclaration::Variant)); + } + evaluationObject->scope->properties.insert("modules", Property(modules)); + evaluationObject->scope->declarations.insert("modules", PropertyDeclaration("modules", PropertyDeclaration::Variant)); +} + +QList<Module::Ptr> Loader::evaluateDependency(EvaluationObject *parentEObj, LanguageObject *depends, ScopeChain::Ptr moduleScope, + const QStringList &extraSearchPaths, + QList<UnknownModule> *unknownModules, const QVariantMap &userProperties) +{ + const CodeLocation dependsLocation = depends->prototypeLocation; + + // check for the use of undeclared properties + foreach (const Binding &binding, depends->bindings) { + if (binding.name.count() > 1) + throw Error("Bindings with dots are forbidden in Depends.", dependsLocation); + if (!m_dependsPropertyDeclarations.contains(binding.name.first())) + throw Error(QString("There's no property '%1' in Depends.").arg(binding.name.first()), + CodeLocation(depends->file->fileName, binding.valueSource.firstLineNumber())); + } + + // check condition + Binding binding = depends->bindings.value(QStringList("condition")); + if (binding.isValid()) { + QScriptValue v = evaluate(&m_engine, binding.valueSource); + if (!v.toBool()) + return QList<Module::Ptr>(); + } + + bool isRequired = true; + binding = depends->bindings.value(QStringList("required")); + if (!binding.valueSource.isNull()) + isRequired = evaluate(&m_engine, binding.valueSource).toBool(); + + QString failureMessage; + binding = depends->bindings.value(QStringList("failureMessage")); + if (!binding.valueSource.isNull()) + failureMessage = evaluate(&m_engine, binding.valueSource).toString(); + + QString moduleName; + binding = depends->bindings.value(QStringList("name")); + if (!binding.valueSource.isNull()) { + moduleName = evaluate(&m_engine, binding.valueSource).toString(); + } else { + moduleName = depends->id; + } + + if (parentEObj->modules.contains(moduleName)) { + // If the module is already in the target object then don't add it a second time. + // This is for the case where you have the same module in the instantiating object + // and in a base object. + // + // ---Foo.qbs--- + // Product { + // cpp.defines: ["BEAGLE"] + // Depends { name: "cpp" } + // } + // + // ---bar.qbp--- + // Foo { + // Depends { name: "cpp" } + // } + // + return QList<Module::Ptr>(); + } + + QString moduleId = depends->id; + if (moduleId.isEmpty()) + moduleId = moduleName; + + QStringList subModules; + Binding subModulesBinding = depends->bindings.value(QStringList("submodules")); + if (!subModulesBinding.valueSource.isNull()) + subModules = evaluate(&m_engine, subModulesBinding.valueSource).toVariant().toStringList(); + + QStringList fullModuleIds; + QStringList fullModuleNames; + if (subModules.isEmpty()) { + fullModuleIds.append(moduleId); + fullModuleNames.append(moduleName.toLower().replace('.', "/")); + } else { + foreach (const QString &subModuleName, subModules) { + fullModuleIds.append(moduleId + "." + subModuleName); + fullModuleNames.append(moduleName.toLower().replace('.', "/") + "/" + subModuleName.toLower().replace('.', "/")); + } + } + + QList<Module::Ptr> modules; + unknownModules->clear(); + for (int i=0; i < fullModuleNames.count(); ++i) { + const QString &fullModuleName = fullModuleNames.at(i); + Module::Ptr module = loadModule(fullModuleIds.at(i), fullModuleName, moduleScope, userProperties, dependsLocation, extraSearchPaths); + if (module) { + modules.append(module); + } else { + UnknownModule unknownModule; + unknownModule.name = fullModuleName; + unknownModule.required = isRequired; + unknownModule.failureMessage = failureMessage; + unknownModule.dependsLocation = dependsLocation; + unknownModules->append(unknownModule); + } + } + return modules; +} + +static void findModuleDependencies_impl(const Module::Ptr &module, QHash<QString, ProjectFile *> &result) +{ + QString moduleName = module->name; + ProjectFile *file = module->file(); + ProjectFile *otherFile = result.value(moduleName); + if (otherFile && otherFile != file) { + throw Error(QString("two different versions of '%1' were included: '%2' and '%3'").arg( + moduleName, file->fileName, otherFile->fileName)); + } else if (otherFile) { + return; + } + + result.insert(moduleName, file); + foreach (const Module::Ptr &depModule, module->object->modules) { + findModuleDependencies_impl(depModule, result); + } +} + +static QHash<QString, ProjectFile *> findModuleDependencies(EvaluationObject *root) +{ + QHash<QString, ProjectFile *> result; + foreach (const Module::Ptr &module, root->modules) { + foreach (const Module::Ptr &depModule, module->object->modules) { + findModuleDependencies_impl(depModule, result); + } + } + return result; +} + +static QVariantMap evaluateAll(const ResolvedProduct::Ptr &rproduct, const Scope::Ptr &properties) +{ + QVariantMap result; + + if (properties->fallbackScope) + result = evaluateAll(rproduct, properties->fallbackScope); + + typedef QHash<QString, PropertyDeclaration>::const_iterator iter; + iter end = properties->declarations.end(); + for (iter it = properties->declarations.begin(); it != end; ++it) { + const PropertyDeclaration &decl = it.value(); + if (decl.type == PropertyDeclaration::Verbatim || decl.flags.testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) + continue; + + Property property = properties->properties.value(it.key()); + if (!property.isValid()) + continue; + + QVariant value; + if (property.scope) { + value = evaluateAll(rproduct, property.scope); + } else { + value = properties->property(it.key()).toVariant(); + } + + if (decl.type == PropertyDeclaration::Paths) { + QStringList lst = value.toStringList(); + value = resolvePaths(lst, rproduct->sourceDirectory); + } + + result.insert(it.key(), value); + } + return result; +} + +static void clearCachedValues() +{ + QScriptValue nullScriptValue; + const std::set<Scope *>::const_iterator scopeEnd = Scope::scopesWithEvaluatedProperties.end(); + for (std::set<Scope *>::const_iterator it = Scope::scopesWithEvaluatedProperties.begin(); it != scopeEnd; ++it) { + const QHash<QString, Property>::iterator propertiesEnd = (*it)->properties.end(); + for (QHash<QString, Property>::iterator pit = (*it)->properties.begin(); pit != propertiesEnd; ++pit) { + Property &property = pit.value(); + if (!property.valueSource.isNull()) + property.value = nullScriptValue; + } + } + Scope::scopesWithEvaluatedProperties.clear(); +} + +int Loader::productCount(Configuration::Ptr userProperties) +{ + Q_ASSERT(hasLoaded()); + + LanguageObject *object = m_project->root; + EvaluationObject *evaluationObject = new EvaluationObject(object); + + const QString propertiesName = object->prototype.join("."); + evaluationObject->scope = Scope::create(&m_engine, propertiesName, m_project->root->file); + + Scope::Ptr productProperty = Scope::create(&m_engine, name_productPropertyScope, m_project->root->file); + Scope::Ptr projectProperty = Scope::create(&m_engine, name_projectPropertyScope, m_project->root->file); + + // for the 'product' and 'project' property available to the modules + ScopeChain::Ptr moduleScope(new ScopeChain(&m_engine)); + moduleScope->prepend(productProperty); + moduleScope->prepend(projectProperty); + + ScopeChain::Ptr localScope(new ScopeChain(&m_engine)); + localScope->prepend(productProperty); + localScope->prepend(projectProperty); + localScope->prepend(evaluationObject->scope); + + resolveInheritance(object, evaluationObject, moduleScope, userProperties->value()); + + if (evaluationObject->prototype != name_Project) + return 0; + + fillEvaluationObjectBasics(localScope, object, evaluationObject); + QStringList referencedProducts = evaluationObject->scope->stringListValue("references"); + + setPathAndFilePath(evaluationObject->scope, object->file->fileName); + int productChildrenCount = 0; + foreach (LanguageObject *child, object->children) { + EvaluationObject *eoChild = new EvaluationObject(child); + eoChild->scope = Scope::Ptr(evaluationObject->scope); + resolveInheritance(child, eoChild, moduleScope, userProperties->value()); + if (eoChild->prototype == name_Product) + ++productChildrenCount; + } + + return referencedProducts.count() + productChildrenCount; +} + +ResolvedProject::Ptr Loader::resolveProject(const QString &buildDirectoryRoot, + Configuration::Ptr userProperties, + QFutureInterface<bool> &futureInterface, + bool resolveProductDependencies) +{ + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[LDR] resolving " << m_project->fileName; + ResolvedProject::Ptr rproject(new ResolvedProject); + rproject->qbsFile = m_project->fileName; + + Scope::Ptr context = buildFileContext(m_project.data()); + ScopeChain::Ptr scope(new ScopeChain(&m_engine, context)); + + ResolvedModule::Ptr dummyModule(new ResolvedModule); + dummyModule->jsImports = m_project->jsImports; + QList<Rule::Ptr> globalRules; + + ProjectData products; + resolveTopLevel(rproject, + m_project->root, + m_project->fileName, + &products, + &globalRules, + userProperties, + scope, + dummyModule, + futureInterface); + + QSet<QString> uniqueStrings; + QMultiMap<QString, ResolvedProduct::Ptr> resolvedProducts; + QHash<ResolvedProduct::Ptr, ProductData>::iterator it = products.begin(); + for (; it != products.end(); ++it) { + futureInterface.setProgressValue(futureInterface.progressValue() + 1); + ResolvedProduct::Ptr rproduct = it.key(); + ProductData &data = it.value(); + Scope *productProps = data.product->scope.data(); + + rproduct->name = productProps->stringValue("name"); + QString buildDirectory = FileInfo::resolvePath(buildDirectoryRoot, rproject->id); + + // insert property "buildDirectory" + { + Property p(m_engine.toScriptValue(buildDirectory)); + productProps->properties.insert("buildDirectory", p); + } + + rproduct->fileTags = productProps->stringListValue("type"); + rproduct->destinationDirectory = productProps->stringValue("destination"); + rproduct->buildDirectory = buildDirectory; + foreach (const Rule::Ptr &rule, globalRules) + rproduct->rules.insert(rule); + const QString lowerProductName = rproduct->name.toLower(); + uniqueStrings.insert(lowerProductName); + resolvedProducts.insert(lowerProductName, rproduct); + + // resolve the modules for this product + for (QHash<QString, Module::Ptr>::const_iterator modIt = data.product->modules.begin(); + modIt != data.product->modules.end(); ++modIt) + { + resolveModule(rproduct, modIt.key(), modIt.value()->object); + } + + QList<EvaluationObject *> unresolvedChildren = resolveCommonItems(data.product->children, rproduct, dummyModule); + + // build the product's configuration + rproduct->configuration = Configuration::Ptr(new Configuration); + QVariantMap productCfg = evaluateAll(rproduct, data.product->scope); + rproduct->configuration->setValue(productCfg); + + // handle the 'Product.files' property + { + QScriptValue files = data.product->scope->property("files"); + if (files.isValid()) { + resolveGroup(rproduct, data.product, data.product); + } + } + + foreach (EvaluationObject *child, unresolvedChildren) { + const uint prototypeNameHash = qHash(child->prototype); + if (prototypeNameHash == hashName_Group) { + resolveGroup(rproduct, data.product, child); + } else if (prototypeNameHash == hashName_ProductModule) { + child->scope->properties.insert("product", Property(data.product)); + resolveProductModule(rproduct, data.product, child); + data.usedProductsFromProductModule += child->unknownModules; + } + } + + // Apply file taggers and merge duplicate artifacts. + QHash<QString, SourceArtifact::Ptr> uniqueArtifacts; + foreach (const SourceArtifact::Ptr &artifact, rproduct->sources) { + if (artifact->fileTags.isEmpty()) { + artifact->fileTags = rproduct->fileTagsForFileName(artifact->absoluteFilePath); + if (!artifact->fileTags.isEmpty() && qbsLogLevel(LoggerTrace)) + qbsTrace() << "[LDR] adding file tags " << artifact->fileTags << " to " << FileInfo::fileName(artifact->absoluteFilePath); + } + SourceArtifact::Ptr existing = uniqueArtifacts.value(artifact->absoluteFilePath); + if (existing) { + + // if an artifact is in the product and in a group, prefer the group configuration + if (existing->configuration == rproduct->configuration) { + existing->configuration = artifact->configuration; + } else if (artifact->configuration != rproduct->configuration) { + throw Error(QString("Artifact '%1' is in more than one group.").arg(artifact->absoluteFilePath), + CodeLocation(m_project->fileName)); + } + + existing->fileTags.unite(artifact->fileTags); + } + else + uniqueArtifacts.insert(artifact->absoluteFilePath, artifact); + } + rproduct->sources = uniqueArtifacts.values().toSet(); + + // fix up source artifact configurations + foreach (SourceArtifact::Ptr artifact, rproduct->sources) + if (!artifact->configuration) + artifact->configuration = rproduct->configuration; + + rproject->products.insert(rproduct); + } + + // Change build directory for products with the same name. + foreach (const QString &name, uniqueStrings) { + QList<ResolvedProduct::Ptr> products = resolvedProducts.values(name); + if (products.count() < 2) + continue; + foreach (ResolvedProduct::Ptr product, products) { + product->buildDirectory.append('/'); + product->buildDirectory.append(product->fileTags.join("-")); + } + } + + // Resolve inter-product dependencies. + if (resolveProductDependencies) { + + // Collect product dependencies from ProductModules. + bool productDependenciesAdded; + do { + productDependenciesAdded = false; + foreach (ResolvedProduct::Ptr rproduct, rproject->products) { + ProductData &productData = products[rproduct]; + foreach (const UnknownModule &unknownModule, productData.usedProducts) { + const QString &usedProductName = unknownModule.name; + QList<ResolvedProduct::Ptr> usedProductCandidates = resolvedProducts.values(usedProductName); + if (usedProductCandidates.count() < 1) { + if (!unknownModule.required) { + if (!unknownModule.failureMessage.isEmpty()) + qbsWarning() << unknownModule.failureMessage; + continue; + } + throw Error(QString("Product dependency '%1' not found.").arg(usedProductName), + CodeLocation(m_project->fileName)); + } + if (usedProductCandidates.count() > 1) + throw Error(QString("Product dependency '%1' is ambiguous.").arg(usedProductName), + CodeLocation(m_project->fileName)); + ResolvedProduct::Ptr usedProduct = usedProductCandidates.first(); + const ProductData &usedProductData = products.value(usedProduct); + bool added; + productData.addUsedProducts(usedProductData.usedProductsFromProductModule, &added); + if (added) + productDependenciesAdded = true; + } + } + } while (productDependenciesAdded); + + // Resolve all inter-product dependencies. + foreach (ResolvedProduct::Ptr rproduct, rproject->products) { + foreach (const UnknownModule &unknownModule, products.value(rproduct).usedProducts) { + const QString &usedProductName = unknownModule.name; + QList<ResolvedProduct::Ptr> usedProductCandidates = resolvedProducts.values(usedProductName); + if (usedProductCandidates.count() < 1) { + if (!unknownModule.required) { + if (!unknownModule.failureMessage.isEmpty()) + qbsWarning() << unknownModule.failureMessage; + continue; + } + throw Error(QString("Product dependency '%1' not found.").arg(usedProductName), + CodeLocation(m_project->fileName)); + } + if (usedProductCandidates.count() > 1) + throw Error(QString("Product dependency '%1' is ambiguous.").arg(usedProductName), + CodeLocation(m_project->fileName)); + ResolvedProduct::Ptr usedProduct = usedProductCandidates.first(); + rproduct->uses.insert(usedProduct); + + // insert the configuration of the ProductModule into the product's configuration + const QVariantMap productModuleConfig = m_productModules.value(usedProductName); + if (productModuleConfig.isEmpty()) + continue; + + QVariantMap productConfigValue = rproduct->configuration->value(); + QVariantMap modules = productConfigValue.value("modules").toMap(); + modules.insert(usedProductName, productModuleConfig); + productConfigValue.insert("modules", modules); + rproduct->configuration->setValue(productConfigValue); + + // insert the configuration of the ProductModule into the artifact configurations + foreach (SourceArtifact::Ptr artifact, rproduct->sources) { + QVariantMap sourceArtifactConfigValue = artifact->configuration->value(); + QVariantMap modules = sourceArtifactConfigValue.value("modules").toMap(); + modules.insert(usedProductName, productModuleConfig); + sourceArtifactConfigValue.insert("modules", modules); + artifact->configuration->setValue(sourceArtifactConfigValue); + } + } + } + } + + // Create the unaltered configuration for this project from all used modules. + { + rproject->configuration = Configuration::Ptr(new Configuration); + QSet<QString> seenModules; + ResolvedProduct::Ptr dummyProduct(new ResolvedProduct); + foreach (const ProductData &pd, products) { + foreach (Module::Ptr module, pd.product->modules) { + if (seenModules.contains(module->name)) + continue; + seenModules.insert(module->name); + QVariantMap projectConfigValue = rproject->configuration->value(); + projectConfigValue.insert(module->name, evaluateAll(dummyProduct, module->object->scope)); + rproject->configuration->setValue(projectConfigValue); + } + } + } + + return rproject; +} + +static bool checkCondition(EvaluationObject *object) +{ + QScriptValue scriptValue = object->scope->property("condition"); + if (scriptValue.isBool()) { + return scriptValue.toBool(); + } else if (scriptValue.isValid() && !scriptValue.isUndefined()) { + const QScriptProgram &scriptProgram = object->objects.first()->bindings.value(QStringList("condition")).valueSource; + throw Error(QString("Invalid condition."), CodeLocation(scriptProgram.fileName(), scriptProgram.firstLineNumber())); + } + // no 'condition' property means 'the condition is true' + return true; +} + +void Loader::resolveModule(ResolvedProduct::Ptr rproduct, const QString &moduleName, EvaluationObject *module) +{ + ResolvedModule::Ptr rmodule(new ResolvedModule); + rmodule->name = moduleName; + rmodule->jsImports = module->instantiatingObject()->file->jsImports; + rmodule->setupBuildEnvironmentScript = module->scope->verbatimValue("setupBuildEnvironment"); + rmodule->setupRunEnvironmentScript = module->scope->verbatimValue("setupRunEnvironment"); + QStringList additionalProductFileTags = module->scope->stringListValue("additionalProductFileTags"); + if (!additionalProductFileTags.isEmpty()) { + rproduct->fileTags.append(additionalProductFileTags); + rproduct->fileTags = rproduct->fileTags.toSet().toList(); + } + foreach (Module::Ptr m, module->modules) + rmodule->moduleDependencies.append(m->name); + rproduct->modules.append(rmodule); + QList<EvaluationObject *> unhandledChildren = resolveCommonItems(module->children, rproduct, rmodule); + foreach (EvaluationObject *child, unhandledChildren) { + if (child->prototype == name_Artifact) { + if (!checkCondition(child)) + continue; + QString fileName = child->scope->stringValue("fileName"); + if (fileName.isEmpty()) + throw Error(QString("Source file %0 does not exist.").arg(fileName)); + SourceArtifact::Ptr artifact; + foreach (SourceArtifact::Ptr a, rproduct->sources) { + if (a->absoluteFilePath == fileName) { + artifact = a; + break; + } + } + if (!artifact) { + artifact = SourceArtifact::Ptr(new SourceArtifact); + artifact->absoluteFilePath = FileInfo::resolvePath(rproduct->sourceDirectory, fileName); + rproduct->sources += artifact; + } + artifact->fileTags += child->scope->stringListValue("fileTags").toSet(); + } else { + QString msg = "Items of type %0 not allowed in a Module."; + throw Error(msg.arg(child->prototype)); + } + } +} + +static QVariantMap evaluateModuleValues(ResolvedProduct::Ptr rproduct, EvaluationObject *product, Scope::Ptr objectScope) +{ + QVariantMap values; + QVariantMap modules; + for (QHash<QString, Module::Ptr>::const_iterator it = product->modules.begin(); + it != product->modules.end(); ++it) + { + Module::Ptr module = it.value(); + const QString name = module->name; + const QString id = module->id; + if (!id.isEmpty()) { + Scope::Ptr moduleScope = objectScope->properties.value(id).scope; + if (!moduleScope) + moduleScope = product->scope->properties.value(id).scope; + if (!moduleScope) + continue; + modules.insert(name, evaluateAll(rproduct, moduleScope)); + } else { + modules.insert(name, evaluateAll(rproduct, module->object->scope)); + } + } + values.insert(QLatin1String("modules"), modules); + return values; +} + +/** + * Resolve Group {} and the files part of Product {}. + */ +void Loader::resolveGroup(ResolvedProduct::Ptr rproduct, EvaluationObject *product, EvaluationObject *group) +{ + const bool isGroup = product != group; + + Configuration::Ptr configuration; + + if (isGroup) { + clearCachedValues(); + + if (!checkCondition(group)) + return; + + // build configuration for this group + configuration = Configuration::Ptr(new Configuration); + configuration->setValue(evaluateModuleValues(rproduct, product, group->scope)); + } else { + configuration = rproduct->configuration; + } + + // Products can have 'files' but not 'fileTags' + QStringList files = group->scope->stringListValue("files"); + if (isGroup) { + QString prefix = group->scope->stringValue("prefix"); + if (!prefix.isEmpty()) + for (int i=files.count(); --i >= 0;) + files[i].prepend(prefix); + } + QSet<QString> fileTags; + if (isGroup) + fileTags = group->scope->stringListValue("fileTags").toSet(); + foreach (const QString &fileName, files) { + SourceArtifact::Ptr artifact(new SourceArtifact); + artifact->configuration = configuration; + artifact->absoluteFilePath = FileInfo::resolvePath(rproduct->sourceDirectory, fileName); + artifact->fileTags = fileTags; + rproduct->sources.insert(artifact); + } +} + +void Loader::resolveProductModule(ResolvedProduct::Ptr rproduct, EvaluationObject *product, EvaluationObject *productModule) +{ + Q_ASSERT(!rproduct->name.isEmpty()); + + QVariantMap userProperties; // ### dummy + ScopeChain::Ptr localScopeChain(new ScopeChain(&m_engine, productModule->scope)); + ScopeChain::Ptr moduleScopeChain(new ScopeChain(&m_engine, productModule->scope)); + evaluateDependencies(productModule->instantiatingObject(), productModule, localScopeChain, moduleScopeChain, userProperties); + + clearCachedValues(); + QVariantMap moduleValues = evaluateModuleValues(rproduct, product, productModule->scope); + m_productModules.insert(rproduct->name.toLower(), moduleValues); +} + +void Loader::resolveTransformer(ResolvedProduct::Ptr rproduct, EvaluationObject *trafo, ResolvedModule::Ptr module) +{ + if (!checkCondition(trafo)) + return; + + ResolvedTransformer::Ptr rtrafo(new ResolvedTransformer); + rtrafo->module = module; + rtrafo->jsImports = trafo->instantiatingObject()->file->jsImports; + rtrafo->inputs = trafo->scope->stringListValue("inputs"); + for (int i=0; i < rtrafo->inputs.count(); ++i) + rtrafo->inputs[i] = FileInfo::resolvePath(rproduct->sourceDirectory, rtrafo->inputs[i]); + rtrafo->transform = RuleScript::Ptr(new RuleScript); + rtrafo->transform->script = trafo->scope->verbatimValue("prepare"); + rtrafo->transform->location.fileName = trafo->instantiatingObject()->file->fileName; + rtrafo->transform->location.column = 1; + Configuration::Ptr outputConfiguration(new Configuration); + foreach (EvaluationObject *child, trafo->children) { + if (child->prototype != name_Artifact) + throw Error(QString("Transformer: wrong child type '%0'.").arg(child->prototype)); + SourceArtifact::Ptr artifact(new SourceArtifact); + artifact->configuration = outputConfiguration; + QString fileName = child->scope->stringValue("fileName"); + if (fileName.isEmpty()) + throw Error("Artifact fileName must not be empty."); + artifact->absoluteFilePath = FileInfo::resolvePath(rproduct->buildDirectory, + fileName); + artifact->fileTags = child->scope->stringListValue("fileTags").toSet(); + rtrafo->outputs += artifact; + } + rproduct->transformers += rtrafo; +} + +static void addTransformPropertiesToRule(Rule::Ptr rule, LanguageObject *obj) +{ + foreach (const Binding &binding, obj->bindings) { + if (binding.name.length() != 1) { + throw Error("Binding with dots are prohibited in TransformProperties.", + CodeLocation(binding.valueSource.fileName(), + binding.valueSource.firstLineNumber())); + continue; + } + + rule->transformProperties.insert(binding.name.first(), binding.valueSource); + } +} + +/** + *Resolve stuff that Module and Product have in common. + */ +QList<EvaluationObject *> Loader::resolveCommonItems(const QList<EvaluationObject *> &objects, + ResolvedProduct::Ptr rproduct, ResolvedModule::Ptr module) +{ + QList<LanguageObject *> outerTransformProperties; // ### do we really want to allow these? + QList<Rule::Ptr> rules; + + QList<EvaluationObject *> unhandledObjects; + foreach (EvaluationObject *object, objects) { + const uint hashPrototypeName = qHash(object->prototype); + if (hashPrototypeName == hashName_FileTagger) { + FileTagger::Ptr fileTagger(new FileTagger); + fileTagger->artifactExpression = object->scope->stringValue("pattern"); + fileTagger->fileTags = object->scope->stringListValue("fileTags"); + rproduct->fileTaggers.insert(fileTagger); + } else if (hashPrototypeName == hashName_Rule) { + Rule::Ptr rule = resolveRule(object, module); + rproduct->rules.insert(rule); + rules.append(rule); + } else if (hashPrototypeName == hashName_Transformer) { + resolveTransformer(rproduct, object, module); + } else if (hashPrototypeName == hashName_TransformProperties) { + outerTransformProperties.append(object->instantiatingObject()); + } else if (hashPrototypeName == hashName_PropertyOptions) { + // Just ignore this type to allow it. It is handled elsewhere. + } else { + unhandledObjects.append(object); + } + } + + // attach the outer TransformProperties to the rules + foreach (Rule::Ptr rule, rules) { + foreach (LanguageObject *tp, outerTransformProperties) + addTransformPropertiesToRule(rule, tp); + } + + return unhandledObjects; +} + +Rule::Ptr Loader::resolveRule(EvaluationObject *object, ResolvedModule::Ptr module) +{ + Rule::Ptr rule(new Rule); + + LanguageObject *origObj = object->instantiatingObject(); + Q_CHECK_PTR(origObj); + + // read artifacts and TransformProperties + QList<RuleArtifact::Ptr> artifacts; + foreach (EvaluationObject *child, object->children) { + const uint hashChildPrototypeName = qHash(child->prototype); + if (hashChildPrototypeName == hashName_Artifact) { + RuleArtifact::Ptr artifact(new RuleArtifact); + artifacts.append(artifact); + artifact->fileScript = child->scope->verbatimValue("fileName"); + artifact->fileTags = child->scope->stringListValue("fileTags"); + LanguageObject *origArtifactObj = child->instantiatingObject(); + foreach (const Binding &binding, origArtifactObj->bindings) { + if (binding.name.length() <= 1) + continue; + artifact->bindings.append(qMakePair(binding.name, binding.valueSource.sourceCode())); + } + } else if (hashChildPrototypeName == hashName_TransformProperties) { + addTransformPropertiesToRule(rule, child->instantiatingObject()); + } else { + throw Error("'Rule' can only have children of type 'Artifact' or 'TransformProperties'.", + child->instantiatingObject()->prototypeLocation); + } + } + + RuleScript::Ptr ruleScript(new RuleScript); + ruleScript->script = object->scope->verbatimValue("prepare"); + ruleScript->location.fileName = object->instantiatingObject()->file->fileName; + ruleScript->location.column = 1; + { + Binding binding = object->instantiatingObject()->bindings.value(QStringList("prepare")); + ruleScript->location.line = binding.valueSource.firstLineNumber(); + } + + rule->objectId = origObj->id; + rule->jsImports = object->instantiatingObject()->file->jsImports; + rule->script = ruleScript; + rule->artifacts = artifacts; + rule->multiplex = object->scope->boolValue("multiplex", false); + rule->inputs = object->scope->stringListValue("inputs"); + rule->usings = object->scope->stringListValue("usings"); + rule->explicitlyDependsOn = object->scope->stringListValue("explicitlyDependsOn"); + rule->module = module; + + m_ruleMap.insert(rule, object); + return rule; +} + +/// -------------------------------------------------------------------------- + + +template <typename T> +static QString textOf(const QString &source, T *node) +{ + if (!node) + return QString(); + return source.mid(node->firstSourceLocation().begin(), node->lastSourceLocation().end() - node->firstSourceLocation().begin()); +} + +static void bindFunction(LanguageObject *result, const QString &source, FunctionDeclaration *ast) +{ + Function f; + if (!ast->name) + throw Error("function decl without name"); + f.name = ast->name->asString(); + + // remove the name + QString funcNoName = textOf(source, ast); + funcNoName.replace(QRegExp("^(\\s*function\\s*)\\w*"), "(\\1"); + funcNoName.append(")"); + f.source = QScriptProgram(funcNoName, result->file->fileName, ast->firstSourceLocation().startLine); + result->functions.insert(f.name, f); +} + +static QStringList toStringList(UiQualifiedId *qid) +{ + QStringList result; + for (; qid; qid = qid->next) { + if (qid->name) + result.append(qid->name->asString()); + else + result.append(QString()); // throw error instead? + } + return result; +} + +static CodeLocation location(const QString &fileName, SourceLocation location) +{ + return CodeLocation(fileName, location.startLine, location.startColumn); +} + +static QScriptProgram bindingProgram(const QString &fileName, const QString &source, Statement *statement) +{ + QString sourceCode = textOf(source, statement); + if (cast<Block *>(statement)) { + // rewrite blocks to be able to use return statements in property assignments + sourceCode.prepend("(function()"); + sourceCode.append(")()"); + } + return QScriptProgram(sourceCode, fileName, + statement->firstSourceLocation().startLine); +} + +static void checkDuplicateBinding(LanguageObject *object, const QStringList &bindingName, const SourceLocation &sourceLocation) +{ + if (object->bindings.contains(bindingName)) { + QString msg("Duplicate binding for '%1'"); + throw Error(msg.arg(bindingName.join(".")), + location(object->file->fileName, sourceLocation)); + } +} + +static void bindBinding(LanguageObject *result, const QString &source, UiScriptBinding *ast) +{ + Binding p; + if (!ast->qualifiedId || !ast->qualifiedId->name) + throw Error("script binding without name"); + p.name = toStringList(ast->qualifiedId); + checkDuplicateBinding(result, p.name, ast->qualifiedId->identifierToken); + + if (p.name == QStringList("id")) { + ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement); + if (!expStmt) + throw Error("id: must be followed by identifier"); + IdentifierExpression *idExp = cast<IdentifierExpression *>(expStmt->expression); + if (!idExp || !idExp->name) + throw Error("id: must be followed by identifier"); + result->id = idExp->name->asString(); + return; + } + + p.valueSource = bindingProgram(result->file->fileName, source, ast->statement); + + result->bindings.insert(p.name, p); +} + +static void bindBinding(LanguageObject *result, const QString &source, UiPublicMember *ast) +{ + Binding p; + if (!ast->name) + throw Error("public member without name"); + p.name = QStringList(ast->name->asString()); + checkDuplicateBinding(result, p.name, ast->identifierToken); + + if (ast->statement) + p.valueSource = bindingProgram(result->file->fileName, source, ast->statement); + else + p.valueSource = QScriptProgram("undefined"); + + result->bindings.insert(p.name, p); +} + +static PropertyDeclaration::Type propertyTypeFromString(const QString &typeName) +{ + if (typeName == "bool") + return PropertyDeclaration::Boolean; + if (typeName == "paths") + return PropertyDeclaration::Paths; + if (typeName == "string") + return PropertyDeclaration::String; + if (typeName == "var" || typeName == "variant") + return PropertyDeclaration::Variant; + return PropertyDeclaration::UnknownType; +} + +static void bindPropertyDeclaration(LanguageObject *result, UiPublicMember *ast) +{ + PropertyDeclaration p; + if (!ast->name) + throw Error("public member without name"); + if (!ast->memberType) + throw Error("public member without type"); + if (ast->typeModifier && ast->typeModifier->asString() != QLatin1String("list")) + throw Error("public member with type modifier that is not 'list'"); + if (ast->type == UiPublicMember::Signal) + throw Error("public member with signal type not supported"); + p.name = ast->name->asString(); + p.type = propertyTypeFromString(ast->memberType->asString()); + if (ast->typeModifier && ast->typeModifier->asString() == QLatin1String("list")) + p.flags |= PropertyDeclaration::ListProperty; + + result->propertyDeclarations.insert(p.name, p); +} + +static LanguageObject *bindObject(ProjectFile::Ptr file, const QString &source, UiObjectDefinition *ast, + const QHash<QStringList, QString> prototypeToFile) +{ + LanguageObject *result = new LanguageObject(file.data()); + result->file = file.data(); + +// result->location = CodeLocation( +// currentSourceFileName, +// ast->firstSourceLocation().startLine, +// ast->firstSourceLocation().startColumn +// ); + + if (!ast->qualifiedTypeNameId || !ast->qualifiedTypeNameId->name) + throw Error("no prototype"); + result->prototype = toStringList(ast->qualifiedTypeNameId); + result->prototypeLocation = location(file->fileName, ast->qualifiedTypeNameId->identifierToken); + + // resolve prototype if possible + result->prototypeFileName = prototypeToFile.value(result->prototype); + + if (!ast->initializer) + return result; + + for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { + if (UiPublicMember *publicMember = cast<UiPublicMember *>(it->member)) { + bindPropertyDeclaration(result, publicMember); + bindBinding(result, source, publicMember); + } else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding *>(it->member)) { + bindBinding(result, source, scriptBinding); + } else if (UiSourceElement *sourceElement = cast<UiSourceElement *>(it->member)) { + FunctionDeclaration *fdecl = cast<FunctionDeclaration *>(sourceElement->sourceElement); + if (!fdecl) + continue; + bindFunction(result, source, fdecl); + } else if (UiObjectDefinition *objDef = cast<UiObjectDefinition *>(it->member)) { + result->children += bindObject(file, source, objDef, prototypeToFile); + } + } + + return result; +} + +static void collectPrototypes(const QString &path, const QString &as, + QHash<QStringList, QString> *prototypeToFile) +{ + QDirIterator dirIter(path, QStringList("*.qbs")); + while (dirIter.hasNext()) { + const QString filePath = dirIter.next(); + const QString fileName = dirIter.fileName(); + + if (fileName.size() <= 4) + continue; + + const QString componentName = fileName.left(fileName.size() - 4); + // ### validate componentName + + if (!componentName.at(0).isUpper()) + continue; + + QStringList prototypeName; + if (!as.isEmpty()) + prototypeName.append(as); + prototypeName.append(componentName); + + prototypeToFile->insert(prototypeName, filePath); + } +} + +static ProjectFile::Ptr bindFile(const QString &source, const QString &fileName, UiProgram *ast, QStringList searchPaths) +{ + ProjectFile::Ptr file(new ProjectFile); + file->fileName = fileName; + const QString path = FileInfo::path(fileName); + + // maps names of known prototypes to absolute file names + QHash<QStringList, QString> prototypeToFile; + + // files in the same directory are available as prototypes + collectPrototypes(path, QString(), &prototypeToFile); + + QSet<QString> importAsNames; + + for (QmlJS::AST::UiImportList *it = ast->imports; it; it = it->next) { + QmlJS::AST::UiImport *import = it->import; + QmlJS::NameId *iFileName = import->fileName; + QmlJS::NameId *importId = import->importId; + + QStringList importUri; + bool isBase = false; + if (import->importUri) { + importUri = toStringList(import->importUri); + if (importUri.size() == 2 + && importUri.first() == "qbs" + && importUri.last() == "base") + isBase = true; // ### check version! + } + + QString as; + if (isBase) { + if (importId) { + // ### location + throw Error("Import of qbs.base must have no 'as <Name>'"); + } + } else { + if (!importId) { + // ### location + throw Error("Imports require 'as <Name>'"); + } + + as = importId->asString(); + if (importAsNames.contains(as)) { + // ### location + throw Error("Can't import into the same name more than once"); + } + importAsNames.insert(as); + } + + if (iFileName) { + QString name = FileInfo::resolvePath(path, iFileName->asString()); + + QFileInfo fi(name); + if (!fi.exists()) + throw Error(QString("Can't find imported file %0.").arg(name), + CodeLocation(fileName, import->fileNameToken.startLine, import->fileNameToken.startColumn)); + name = fi.canonicalFilePath(); + if (fi.isDir()) { + collectPrototypes(name, as, &prototypeToFile); + } else { + if (name.endsWith(".js", Qt::CaseInsensitive)) { + file->jsImports[as].append(name); + } else if (name.endsWith(".qbs", Qt::CaseInsensitive)) { + prototypeToFile.insert(QStringList(as), name); + } else { + throw Error("Can only import .qbs and .js files", + CodeLocation(fileName, import->fileNameToken.startLine, import->fileNameToken.startColumn)); + } + } + } else if (!importUri.isEmpty()) { + const QString importPath = importUri.join(QDir::separator()); + bool found = false; + foreach (const QString &searchPath, searchPaths) { + const QFileInfo fi(FileInfo::resolvePath(FileInfo::resolvePath(searchPath, "imports"), importPath)); + if (fi.isDir()) { + // ### versioning, qbsdir file, etc. + const QString &resultPath = fi.absoluteFilePath(); + collectPrototypes(resultPath, as, &prototypeToFile); + + QDirIterator dirIter(resultPath, QStringList("*.js")); + while (dirIter.hasNext()) { + dirIter.next(); + file->jsImports[as].append(dirIter.filePath()); + } + found = true; + break; + } + } + if (!found) { + // ### location + throw Error(QString("import %1 not found").arg(importUri.join("."))); + } + } + } + + UiObjectDefinition *rootDef = cast<UiObjectDefinition *>(ast->members->member); + if (rootDef) + file->root = bindObject(file, source, rootDef, prototypeToFile); + + return file; +} + +ProjectFile::Ptr Loader::parseFile(const QString &fileName) +{ + ProjectFile::Ptr result = m_parsedFiles.value(fileName); + if (result) + return result; + + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) + throw Error(QString("Couldn't open '%1'.").arg(fileName)); + + const QString code = QTextStream(&file).readAll(); + QScopedPointer<QmlJS::Engine> engine(new QmlJS::Engine); + QScopedPointer<QmlJS::NodePool> nodePool(new QmlJS::NodePool(fileName, engine.data())); + QmlJS::Lexer lexer(engine.data()); + lexer.setCode(code, 1); + QmlJS::Parser parser(engine.data()); + parser.parse(); + + QList<QmlJS::DiagnosticMessage> parserMessages = parser.diagnosticMessages(); + if (!parserMessages.isEmpty()) { + QString msgstr = "Parsing errors:\n"; + foreach (const QmlJS::DiagnosticMessage &msg, parserMessages) + msgstr += Error(msg.message, fileName, msg.loc.startLine, msg.loc.startColumn).toString() + + QLatin1String("\n"); + throw Error(msgstr); + } + + result = bindFile(code, fileName, parser.ast(), m_searchPaths); + if (result) { + Q_ASSERT(!m_parsedFiles.contains(result->fileName)); + m_parsedFiles.insert(result->fileName, result); + } + return result; +} + +Loader *Loader::get(QScriptEngine *engine) +{ + QVariant v = engine->property(szLoaderPropertyName); + return static_cast<Loader*>(v.value<void*>()); +} + +QScriptValue Loader::js_getHostOS(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(context); + QString hostSystem; +#if defined(Q_OS_WIN) + hostSystem = "windows"; +#elif defined(Q_OS_MAC) + hostSystem = "mac"; +#elif defined(Q_OS_LINUX) + hostSystem = "linux"; +#else +# error unknown host platform +#endif + return engine->toScriptValue(hostSystem); +} + +QScriptValue Loader::js_getHostDefaultArchitecture(QScriptContext *context, QScriptEngine *engine) +{ + // ### TODO implement properly, do not hard-code + Q_UNUSED(context); + QString architecture; +#if defined(Q_OS_WIN) + architecture = "x86"; +#elif defined(Q_OS_MAC) + architecture = "x86_64"; +#elif defined(Q_OS_LINUX) + architecture = "x86_64"; +#else +# error unknown host platform +#endif + return engine->toScriptValue(architecture); +} + +QScriptValue Loader::js_configurationValue(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() < 1 || context->argumentCount() > 2) { + return context->throwError(QScriptContext::SyntaxError, + QString("configurationValue expects 1 or 2 arguments")); + } + + Settings::Ptr settings = Loader::get(engine)->m_settings; + const bool defaultValueProvided = context->argumentCount() > 1; + const QString key = context->argument(0).toString(); + const QString defaultValue = (defaultValueProvided ? QString() : (context->argument(1).isUndefined() ? QString() : context->argument(1).toString())); + QVariant v = settings->value(key, defaultValue); + if (!defaultValueProvided && v.isNull()) + return context->throwError(QScriptContext::SyntaxError, + QString("configuration value '%1' does not exist").arg(context->argument(0).toString())); + return engine->toScriptValue(v); +} + +EvaluationObject *Loader::resolveTopLevel(const ResolvedProject::Ptr &rproject, + LanguageObject *object, + const QString &projectFileName, + ProjectData *projectData, + QList<Rule::Ptr> *globalRules, + const Configuration::Ptr &userProperties, + const ScopeChain::Ptr &scope, + const ResolvedModule::Ptr &dummyModule, + QFutureInterface<bool> &futureInterface) +{ + if (qbsLogLevel(LoggerTrace)) + qbsTrace() << "[LDR] resolve top-level " << object->file->fileName; + EvaluationObject *evaluationObject = new EvaluationObject(object); + + const QString propertiesName = object->prototype.join("."); + evaluationObject->scope = Scope::create(&m_engine, propertiesName, object->file); + + Scope::Ptr productProperty = Scope::create(&m_engine, name_productPropertyScope, object->file); + Scope::Ptr projectProperty = Scope::create(&m_engine, name_projectPropertyScope, object->file); + Scope::Ptr oldProductProperty(scope->findNonEmpty(name_productPropertyScope)); + Scope::Ptr oldProjectProperty(scope->findNonEmpty(name_projectPropertyScope)); + + // for the 'product' and 'project' property available to the modules + ScopeChain::Ptr moduleScope(new ScopeChain(&m_engine)); + moduleScope->prepend(oldProductProperty); + moduleScope->prepend(oldProjectProperty); + moduleScope->prepend(productProperty); + moduleScope->prepend(projectProperty); + + ScopeChain::Ptr localScope(scope->clone()); + localScope->prepend(productProperty); + localScope->prepend(projectProperty); + localScope->prepend(evaluationObject->scope); + + evaluationObject->scope->properties.insert("configurationValue", Property(m_jsFunction_configurationValue)); + + resolveInheritance(object, evaluationObject, moduleScope, userProperties->value()); + + if (evaluationObject->prototype == name_Project) { + // if this is a nested project, set a fallback scope + Property outerProperty = scope->lookupProperty("project"); + if (outerProperty.isValid() && outerProperty.scope) { + evaluationObject->scope->fallbackScope = outerProperty.scope; + } else { + // set the 'path' and 'filePath' properties + setPathAndFilePath(evaluationObject->scope, object->file->fileName); + } + + fillEvaluationObjectBasics(localScope, object, evaluationObject); + + // load referenced files + foreach (const QString &reference, evaluationObject->scope->stringListValue("references")) { + + QString projectFileDir = FileInfo::path(projectFileName); + QString absReferencePath = FileInfo::resolvePath(projectFileDir, reference); + ProjectFile::Ptr referencedFile = parseFile(absReferencePath); + ScopeChain::Ptr referencedFileScope(new ScopeChain(&m_engine, buildFileContext(referencedFile.data()))); + referencedFileScope->prepend(projectProperty); + resolveTopLevel(rproject, + referencedFile->root, + referencedFile->fileName, + projectData, + globalRules, + userProperties, + referencedFileScope, + dummyModule, futureInterface); + } + + // load children + ScopeChain::Ptr childScope(scope->clone()->prepend(projectProperty)); + foreach (LanguageObject *child, object->children) + resolveTopLevel(rproject, + child, + projectFileName, + projectData, + globalRules, + userProperties, + childScope, + dummyModule, + futureInterface); + + return evaluationObject; + } else if (evaluationObject->prototype == name_Rule) { + fillEvaluationObject(localScope, object, evaluationObject->scope, evaluationObject, userProperties->value()); + Rule::Ptr rule = resolveRule(evaluationObject, dummyModule); + globalRules->append(rule); + + return evaluationObject; + } else if (evaluationObject->prototype != name_Product) { + QString msg("unknown prototype '%1' - expected Product"); + // ### location + throw Error(msg.arg(evaluationObject->prototype)); + } + + // set the 'path' and 'filePath' properties + setPathAndFilePath(evaluationObject->scope, object->file->fileName); + + ResolvedProduct::Ptr rproduct(new ResolvedProduct); + rproduct->qbsFile = projectFileName; + rproduct->sourceDirectory = QFileInfo(projectFileName).absolutePath(); + rproduct->project = rproject.data(); + + ProductData productData; + productData.product = evaluationObject; + + evaluateDependencies(object, evaluationObject, localScope, moduleScope, userProperties->value()); + productData.usedProducts = evaluationObject->unknownModules; + fillEvaluationObject(localScope, object, evaluationObject->scope, evaluationObject, userProperties->value()); + + // check if product's name is empty and set a default value + Property &nameProperty = evaluationObject->scope->properties["name"]; + if (nameProperty.value.isUndefined()) { + QString projectName = FileInfo::fileName(projectFileName); + projectName.chop(4); + nameProperty.value = m_engine.toScriptValue(projectName); + } + + if (!object->id.isEmpty()) { + Scope::Ptr context = scope->last(); + context->properties.insert(object->id, Property(evaluationObject)); + } + + // collect all dependent modules and put them into the product + QHash<QString, ProjectFile *> moduleDependencies = findModuleDependencies(evaluationObject); + QHashIterator<QString, ProjectFile *> it(moduleDependencies); + while (it.hasNext()) { + it.next(); + if (productData.product->modules.contains(it.key())) + continue; // ### check if files equal + Module::Ptr module = loadModule(it.value(), QString(), it.key(), moduleScope, userProperties->value(), + CodeLocation(object->file->fileName)); + if (!module) { + throw Error(QString("could not load module '%1' from file '%2' into product even though it was loaded into a submodule").arg( + it.key(), it.value()->fileName)); + } + evaluationObject->modules.insert(module->name, module); + } + buildModulesProperty(evaluationObject); + + // get the build variant / determine the project id + QString buildVariant = userProperties->value().value("qbs").toMap().value("buildVariant").toString(); + if (rproject->id.isEmpty()) { + EvaluationObject *baseModule = evaluationObject->modules.value("qbs")->object; + if (!baseModule) + throw Error("base module not loaded"); + const QString hostName = baseModule->scope->stringValue("hostOS"); + const QString targetOS = baseModule->scope->stringValue("targetOS"); + const QString targetName = baseModule->scope->stringValue("targetName"); + rproject->id = buildVariant; + if (!targetName.isEmpty()) + rproject->id.prepend(targetName + "-"); + if (hostName != targetOS) { + QString platformName = targetOS; + const QString hostArchitecture = baseModule->scope->stringValue("hostArchitecture"); + const QString targetArchitecture = baseModule->scope->stringValue("architecture"); + if (hostArchitecture != targetArchitecture) { + platformName += "-"; + platformName += targetArchitecture; + } + rproject->id.prepend(platformName + "-"); + } + } + projectData->insert(rproduct, productData); + return evaluationObject; +} + +ProjectFile::ProjectFile() + : m_destructing(false) +{ +} + +ProjectFile::~ProjectFile() +{ + m_destructing = true; + qDeleteAll(m_evaluationObjects); + qDeleteAll(m_languageObjects); +} + +void Loader::ProductData::addUsedProducts(const QList<UnknownModule> &additionalUsedProducts, bool *productsAdded) +{ + int oldCount = usedProducts.count(); + QSet<QString> usedProductNames; + foreach (const UnknownModule &usedProduct, usedProducts) + usedProductNames += usedProduct.name; + foreach (const UnknownModule &usedProduct, additionalUsedProducts) + if (!usedProductNames.contains(usedProduct.name)) + usedProducts += usedProduct; + *productsAdded = (oldCount != usedProducts.count()); +} + +} // namespace qbs diff --git a/src/lib/language/loader.h b/src/lib/language/loader.h new file mode 100644 index 000000000..d4f7a48a9 --- /dev/null +++ b/src/lib/language/loader.h @@ -0,0 +1,408 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef L2_TARGETLOADER_H +#define L2_TARGETLOADER_H + +#include "language.h" +#include <tools/settings.h> +#include <tools/codelocation.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtCore/QFutureInterface> + +#include <QtScript/QScriptClass> +#include <QtScript/QScriptEngine> + +#include <set> + + + +namespace qbs { + +class Function +{ +public: + QString name; + QScriptProgram source; +}; + +class Binding +{ +public: + QStringList name; + QScriptProgram valueSource; + + bool isValid() const { return !name.isEmpty(); } +}; + +class PropertyDeclaration +{ +public: + enum Type + { + UnknownType, + Boolean, + Paths, + String, + Variant, + Verbatim + }; + + enum Flag + { + DefaultFlags = 0, + ListProperty = 0x1, + PropertyNotAvailableInConfig = 0x2 // Is this property part of a project, product or file configuration? + }; + Q_DECLARE_FLAGS(Flags, Flag) + + PropertyDeclaration(); + PropertyDeclaration(const QString &name, Type type, Flags flags = DefaultFlags); + ~PropertyDeclaration(); + + QString name; + Type type; + Flags flags; + QVariant allowedValues; + QString description; + // the initial value will be in the bindings list +}; + +class ProjectFile; + +class LanguageObject +{ +public: + LanguageObject(ProjectFile *owner); + LanguageObject(const LanguageObject &other); + ~LanguageObject(); + + QString id; + + QStringList prototype; + QString prototypeFileName; + qbs::CodeLocation prototypeLocation; + ProjectFile *file; + + QList<LanguageObject *> children; + QHash<QStringList, Binding> bindings; + QHash<QString, Function> functions; + QHash<QString, PropertyDeclaration> propertyDeclarations; +}; + +class EvaluationObject; +class Scope; + +/** + * Represents a qbs project file. + * Owns all the language objects. + */ +class ProjectFile +{ +public: + typedef QSharedPointer<ProjectFile> Ptr; + + ProjectFile(); + ~ProjectFile(); + + JsImports jsImports; + LanguageObject *root; + QString fileName; + + bool isValid() const { return !root->prototype.isEmpty(); } + bool isDestructing() const { return m_destructing; } + + void registerScope(QSharedPointer<Scope> scope) { m_scopes += scope; } + void registerEvaluationObject(EvaluationObject *eo) { m_evaluationObjects += eo; } + void unregisterEvaluationObject(EvaluationObject *eo) { m_evaluationObjects.removeOne(eo); } + void registerLanguageObject(LanguageObject *eo) { m_languageObjects += eo; } + void unregisterLanguageObject(LanguageObject *eo) { m_languageObjects.removeOne(eo); } + +private: + bool m_destructing; + QList<QSharedPointer<Scope> > m_scopes; + QList<EvaluationObject *> m_evaluationObjects; + QList<LanguageObject *> m_languageObjects; +}; + + +// evaluation objects + +class ScopeChain; +class Scope; + +class Property +{ +public: + Property() {} + explicit Property(QSharedPointer<Scope> scope) + : scope(scope) + {} + explicit Property(EvaluationObject *object); + explicit Property(const QScriptValue &scriptValue); + + // ids, project. etc, JS imports and + // where properties get resolved to + QSharedPointer<ScopeChain> scopeChain; + QScriptProgram valueSource; + QList<Property> baseProperties; + + // value once it's been evaluated + QScriptValue value; + + // if value is a scope + QSharedPointer<Scope> scope; + + bool isValid() const { return scopeChain || scope || value.isValid(); } +}; + +class ScopeChain : public QScriptClass +{ + Q_DISABLE_COPY(ScopeChain) +public: + typedef QSharedPointer<ScopeChain> Ptr; + + ScopeChain(QScriptEngine *engine, const QSharedPointer<Scope> &root = QSharedPointer<Scope>()); + ~ScopeChain(); + + ScopeChain *clone() const; + QScriptValue value(); + QSharedPointer<Scope> first() const; + QSharedPointer<Scope> last() const; + // returns this + ScopeChain *prepend(const QSharedPointer<Scope> &newTop); + + QSharedPointer<Scope> findNonEmpty(const QString &name) const; + QSharedPointer<Scope> find(const QString &name) const; + + Property lookupProperty(const QString &name) const; + +protected: + // QScriptClass interface + QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, + QueryFlags flags, uint *id); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &m_value); + +private: + QList<QWeakPointer<Scope> > m_scopes; + QScriptValue m_value; + QScriptValue m_globalObject; +}; + +class Scope : public QScriptClass +{ + Q_DISABLE_COPY(Scope) + Scope(QScriptEngine *engine, const QString &name); +public: + typedef QSharedPointer<Scope> Ptr; + static std::set<Scope *> scopesWithEvaluatedProperties; + + static Ptr create(QScriptEngine *engine, const QString &name, ProjectFile *owner); + ~Scope(); + + QString name() const; + +protected: + // QScriptClass interface + QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, + QueryFlags flags, uint *id); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + +public: + QScriptValue property(const QString &name) const; + bool boolValue(const QString &name, bool defaultValue = false) const; + QString stringValue(const QString &name) const; + QStringList stringListValue(const QString &name) const; + QString verbatimValue(const QString &name) const; + void dump(const QByteArray &indent) const; + + QHash<QString, Property> properties; + QHash<QString, PropertyDeclaration> declarations; + + QString m_name; + QWeakPointer<Scope> fallbackScope; + QScriptValue value; +}; + +class Module; + +class UnknownModule +{ +public: + QString name; + bool required; + QString failureMessage; + qbs::CodeLocation dependsLocation; +}; + +class EvaluationObject +{ + Q_DISABLE_COPY(EvaluationObject) +public: + EvaluationObject(LanguageObject *instantiatingObject); + ~EvaluationObject(); + + LanguageObject *instantiatingObject() const; + void dump(QByteArray &indent); + + QString prototype; + + Scope::Ptr scope; + QList<EvaluationObject *> children; + QHash<QString, QSharedPointer<Module> > modules; + QList<UnknownModule> unknownModules; + + // the source objects that generated this object + // the object that triggered the instantiation is first, followed by prototypes + QList<LanguageObject *> objects; +}; + +class Module +{ + Q_DISABLE_COPY(Module) +public: + typedef QSharedPointer<Module> Ptr; + + Module(); + ~Module(); + + ProjectFile *file() const; + void dump(QByteArray &indent); + + QString id; + QString name; + qbs::CodeLocation dependsLocation; + + Scope::Ptr context; + EvaluationObject *object; +}; + +class Loader +{ + Q_DECLARE_TR_FUNCTIONS(Loader) +public: + Loader(); + ~Loader(); + void setSearchPaths(const QStringList &searchPaths); + ProjectFile::Ptr loadProject(const QString &fileName); + bool hasLoaded() const { return m_project; } + int productCount(Configuration::Ptr userProperties); + ResolvedProject::Ptr resolveProject(const QString &buildDirectoryRoot, + Configuration::Ptr userProperties, + QFutureInterface<bool> &futureInterface, + bool resolveProductDependencies = true); + +protected: + ProjectFile::Ptr parseFile(const QString &fileName); + + Scope::Ptr buildFileContext(ProjectFile *file); + Module::Ptr loadModule(const QString &moduleId, const QString &moduleName, ScopeChain::Ptr moduleScope, + const QVariantMap &userProperties, const qbs::CodeLocation &dependsLocation, + const QStringList &extraSearchPaths = QStringList()); + Module::Ptr loadModule(ProjectFile *file, const QString &moduleId, const QString &moduleName, ScopeChain::Ptr moduleBaseScope, + const QVariantMap &userProperties, const qbs::CodeLocation &dependsLocation); + QList<Module::Ptr> evaluateDependency(EvaluationObject *parentEObj, LanguageObject *depends, + ScopeChain::Ptr moduleScope, + const QStringList &extraSearchPaths, QList<UnknownModule> *unknownModules, const QVariantMap &userProperties); + void evaluateDependencies(LanguageObject *object, EvaluationObject *evaluationObject, const ScopeChain::Ptr &localScope, + ScopeChain::Ptr moduleScope, const QVariantMap &userProperties, bool loadBaseModule = true); + void evaluateImports(Scope::Ptr target, const JsImports &jsImports); + void evaluatePropertyOptions(LanguageObject *object); + void resolveInheritance(LanguageObject *object, EvaluationObject *evaluationObject, + ScopeChain::Ptr moduleScope = ScopeChain::Ptr(), const QVariantMap &userProperties = QVariantMap()); + void fillEvaluationObject(const ScopeChain::Ptr &scope, LanguageObject *object, Scope::Ptr ids, EvaluationObject *evaluationObject, const QVariantMap &userProperties); + void fillEvaluationObjectBasics(const ScopeChain::Ptr &scope, LanguageObject *object, EvaluationObject *evaluationObject); + void fillEvaluationObjectForProperties(const ScopeChain::Ptr &scope, LanguageObject *object, Scope::Ptr ids, EvaluationObject *evaluationObject, const QVariantMap &userProperties); + void setupInternalPrototype(EvaluationObject *evaluationObject); + void resolveModule(ResolvedProduct::Ptr rproduct, const QString &moduleName, EvaluationObject *module); + void resolveGroup(ResolvedProduct::Ptr rproduct, EvaluationObject *product, EvaluationObject *group); + void resolveProductModule(ResolvedProduct::Ptr rproduct, EvaluationObject *product, EvaluationObject *group); + void resolveTransformer(ResolvedProduct::Ptr rproduct, EvaluationObject *trafo, ResolvedModule::Ptr module); + QList<EvaluationObject *> resolveCommonItems(const QList<EvaluationObject *> &objects, + ResolvedProduct::Ptr rproduct, ResolvedModule::Ptr module); + Rule::Ptr resolveRule(EvaluationObject *object, ResolvedModule::Ptr module); + void buildModulesProperty(EvaluationObject *evaluationObject); + + class ProductData + { + public: + EvaluationObject *product; + QList<UnknownModule> usedProducts; + QList<UnknownModule> usedProductsFromProductModule; + + void addUsedProducts(const QList<UnknownModule> &additionalUsedProducts, bool *productsAdded); + }; + typedef QHash<ResolvedProduct::Ptr, ProductData> ProjectData; + + EvaluationObject *resolveTopLevel(const ResolvedProject::Ptr &rproject, + LanguageObject *object, + const QString &projectFileName, + ProjectData *projectData, + QList<Rule::Ptr> *globalRules, + const Configuration::Ptr &userProperties, + const ScopeChain::Ptr &scope, + const ResolvedModule::Ptr &dummyModule, + QFutureInterface<bool> &futureInterface); + +private: + static Loader *get(QScriptEngine *engine); + static QScriptValue js_getHostOS(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_getHostDefaultArchitecture(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_configurationValue(QScriptContext *context, QScriptEngine *engine); + + static QHash<QString, PropertyDeclaration> m_dependsPropertyDeclarations; + + QStringList m_searchPaths; + QScriptEngine m_engine; + QScriptValue m_jsFunction_getHostOS; + QScriptValue m_jsFunction_getHostDefaultArchitecture; + QScriptValue m_jsFunction_configurationValue; + Settings::Ptr m_settings; + ProjectFile::Ptr m_project; + QHash<QString, ProjectFile::Ptr> m_parsedFiles; + QHash<QString, QScriptValue> m_jsImports; + QHash<Rule::Ptr, EvaluationObject *> m_ruleMap; + QHash<QString, QVariantMap> m_productModules; +}; + +} // namespace qbs + +#endif diff --git a/src/lib/lib.pro b/src/lib/lib.pro new file mode 100644 index 000000000..28d41e097 --- /dev/null +++ b/src/lib/lib.pro @@ -0,0 +1,16 @@ +QT = core script +TEMPLATE = lib +DESTDIR = ../../lib +TARGET = qbscore + +CONFIG += staticlib depend_includepath +DEFINES += QT_CREATOR QML_BUILD_STATIC_LIB # needed for QmlJS + +win32:CONFIG(debug, debug|release):TARGET = $${TARGET}d + +include(tools/tools.pri) +include(parser/parser.pri) +include(buildgraph/buildgraph.pri) +include(Qbs/Qbs.pri) +include(language/language.pri) +include(qtconcurrent/qtconcurrent.pri) diff --git a/src/lib/parser/cmd.sed b/src/lib/parser/cmd.sed new file mode 100644 index 000000000..d76372675 --- /dev/null +++ b/src/lib/parser/cmd.sed @@ -0,0 +1,13 @@ +s/private\/qdeclarative/qml/g +s/qdeclarative/qml/g +s/QDECLARATIVE/QML/g +s/QDeclarative/Qml/g +s/Q_DECLARATIVE_EXPORT //g + +# adjust pri file +s/ \$\$PWD\/qmljsglobal_p.h/ $$PWD\/qmljsglobal_p.h \\\ + $$PWD\/qmldirparser_p.h \\\ + $$PWD\/qmlerror.h/ +s/ \$\$PWD\/qmljsparser.cpp/ $$PWD\/qmljsparser.cpp \\\ + $$PWD\/qmldirparser.cpp \\\ + $$PWD\/qmlerror.cpp/ diff --git a/src/lib/parser/gen-parser.sh b/src/lib/parser/gen-parser.sh new file mode 100644 index 000000000..acd6a19ce --- /dev/null +++ b/src/lib/parser/gen-parser.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +me=$(dirname $0) + +for i in $QTDIR/src/declarative/qml/parser/*.{g,h,cpp,pri}; do + sed -f $me/cmd.sed $i > $me/$(echo $(basename $i) | sed s/qdeclarativejs/qmljs/) +done + +for i in $QTDIR/src/declarative/qml/qdeclarative{error.{h,cpp},dirparser{_p.h,.cpp}}; do + sed -f $me/cmd.sed $i > $me/$(echo $(basename $i) | sed s/qdeclarative/qml/) +done + +# export QmlDirParser +perl -p -0777 -i -e 's/QT_BEGIN_NAMESPACE\n\nclass QmlError;\nclass QmlDirParser/#include "qmljsglobal_p.h"\n\nQT_BEGIN_NAMESPACE\n\nclass QmlError;\nclass QML_PARSER_EXPORT QmlDirParser/' qmldirparser_p.h diff --git a/src/lib/parser/parser.pri b/src/lib/parser/parser.pri new file mode 100644 index 000000000..8ce19ae18 --- /dev/null +++ b/src/lib/parser/parser.pri @@ -0,0 +1,23 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qmljsast_p.h \ + $$PWD/qmljsastfwd_p.h \ + $$PWD/qmljsastvisitor_p.h \ + $$PWD/qmljsengine_p.h \ + $$PWD/qmljsgrammar_p.h \ + $$PWD/qmljslexer_p.h \ + $$PWD/qmljsmemorypool_p.h \ + $$PWD/qmljsnodepool_p.h \ + $$PWD/qmljsparser_p.h \ + $$PWD/qmljsglobal_p.h \ + $$PWD/qmlerror.h + +SOURCES += \ + $$PWD/qmljsast.cpp \ + $$PWD/qmljsastvisitor.cpp \ + $$PWD/qmljsengine_p.cpp \ + $$PWD/qmljsgrammar.cpp \ + $$PWD/qmljslexer.cpp \ + $$PWD/qmljsparser.cpp \ + $$PWD/qmlerror.cpp diff --git a/src/lib/parser/qmlerror.cpp b/src/lib/parser/qmlerror.cpp new file mode 100644 index 000000000..7c1ed5707 --- /dev/null +++ b/src/lib/parser/qmlerror.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlerror.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QmlError + \since 4.7 + \brief The QmlError class encapsulates a QML error. + + QmlError includes a textual description of the error, as well + as location information (the file, line, and column). The toString() + method creates a single-line, human-readable string containing all of + this information, for example: + \code + file:///home/user/test.qml:7:8: Invalid property assignment: double expected + \endcode + + You can use qDebug() or qWarning() to output errors to the console. This method + will attempt to open the file indicated by the error + and include additional contextual information. + \code + file:///home/user/test.qml:7:8: Invalid property assignment: double expected + y: "hello" + ^ + \endcode + + \sa QmlView::errors(), QmlComponent::errors() +*/ +class QmlErrorPrivate +{ +public: + QmlErrorPrivate(); + + QUrl url; + QString description; + int line; + int column; +}; + +QmlErrorPrivate::QmlErrorPrivate() +: line(-1), column(-1) +{ +} + +/*! + Creates an empty error object. +*/ +QmlError::QmlError() +: d(0) +{ +} + +/*! + Creates a copy of \a other. +*/ +QmlError::QmlError(const QmlError &other) +: d(0) +{ + *this = other; +} + +/*! + Assigns \a other to this error object. +*/ +QmlError &QmlError::operator=(const QmlError &other) +{ + if (!other.d) { + delete d; + d = 0; + } else { + if (!d) d = new QmlErrorPrivate; + d->url = other.d->url; + d->description = other.d->description; + d->line = other.d->line; + d->column = other.d->column; + } + return *this; +} + +/*! + \internal +*/ +QmlError::~QmlError() +{ + delete d; d = 0; +} + +/*! + Returns true if this error is valid, otherwise false. +*/ +bool QmlError::isValid() const +{ + return d != 0; +} + +/*! + Returns the url for the file that caused this error. +*/ +QUrl QmlError::url() const +{ + if (d) return d->url; + else return QUrl(); +} + +/*! + Sets the \a url for the file that caused this error. +*/ +void QmlError::setUrl(const QUrl &url) +{ + if (!d) d = new QmlErrorPrivate; + d->url = url; +} + +/*! + Returns the error description. +*/ +QString QmlError::description() const +{ + if (d) return d->description; + else return QString(); +} + +/*! + Sets the error \a description. +*/ +void QmlError::setDescription(const QString &description) +{ + if (!d) d = new QmlErrorPrivate; + d->description = description; +} + +/*! + Returns the error line number. +*/ +int QmlError::line() const +{ + if (d) return d->line; + else return -1; +} + +/*! + Sets the error \a line number. +*/ +void QmlError::setLine(int line) +{ + if (!d) d = new QmlErrorPrivate; + d->line = line; +} + +/*! + Returns the error column number. +*/ +int QmlError::column() const +{ + if (d) return d->column; + else return -1; +} + +/*! + Sets the error \a column number. +*/ +void QmlError::setColumn(int column) +{ + if (!d) d = new QmlErrorPrivate; + d->column = column; +} + +/*! + Returns the error as a human readable string. +*/ +QString QmlError::toString() const +{ + QString rv; + if (url().isEmpty()) { + rv = QLatin1String("<Unknown File>"); + } else if (line() != -1) { + rv = url().toString() + QLatin1Char(':') + QString::number(line()); + if(column() != -1) + rv += QLatin1Char(':') + QString::number(column()); + } else { + rv = url().toString(); + } + + rv += QLatin1String(": ") + description(); + + return rv; +} + +/*! + \relates QmlError + \fn QDebug operator<<(QDebug debug, const QmlError &error) + + Outputs a human readable version of \a error to \a debug. +*/ + +QDebug operator<<(QDebug debug, const QmlError &error) +{ + debug << qPrintable(error.toString()); + + QUrl url = error.url(); + + if (error.line() > 0 && url.scheme() == QLatin1String("file")) { + QString file = url.toLocalFile(); + QFile f(file); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QTextStream stream(data, QIODevice::ReadOnly); +#ifndef QT_NO_TEXTCODEC + stream.setCodec("UTF-8"); +#endif + const QString code = stream.readAll(); + const QStringList lines = code.split(QLatin1Char('\n')); + + if (lines.count() >= error.line()) { + const QString &line = lines.at(error.line() - 1); + debug << "\n " << qPrintable(line); + + if(error.column() > 0) { + int column = qMax(0, error.column() - 1); + column = qMin(column, line.length()); + + QByteArray ind; + ind.reserve(column); + for (int i = 0; i < column; ++i) { + const QChar ch = line.at(i); + if (ch.isSpace()) + ind.append(ch.unicode()); + else + ind.append(' '); + } + ind.append('^'); + debug << "\n " << ind.constData(); + } + } + } + } + return debug; +} + +QT_END_NAMESPACE diff --git a/src/lib/parser/qmlerror.h b/src/lib/parser/qmlerror.h new file mode 100644 index 000000000..3df3edc90 --- /dev/null +++ b/src/lib/parser/qmlerror.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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 info@qt.nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLERROR_H +#define QMLERROR_H + +#include <QtCore/qurl.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDebug; +class QmlErrorPrivate; +class QmlError +{ +public: + QmlError(); + QmlError(const QmlError &); + QmlError &operator=(const QmlError &); + ~QmlError(); + + bool isValid() const; + + QUrl url() const; + void setUrl(const QUrl &); + QString description() const; + void setDescription(const QString &); + int line() const; + void setLine(int); + int column() const; + void setColumn(int); + + QString toString() const; +private: + QmlErrorPrivate *d; +}; + +QDebug operator<<(QDebug debug, const QmlError &error); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLERROR_H diff --git a/src/lib/parser/qmljs.g b/src/lib/parser/qmljs.g new file mode 100644 index 000000000..b5439a9bc --- /dev/null +++ b/src/lib/parser/qmljs.g @@ -0,0 +1,3138 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +-- All rights reserved. +-- Contact: Nokia Corporation (qt-info@nokia.com) +-- +-- This file is part of the QtDeclarative module of the Qt Toolkit. +-- +-- $QT_BEGIN_LICENSE:LGPL-ONLY$ +-- 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. +-- +-- If you have questions regarding the use of this file, please contact +-- Nokia at qt-info@nokia.com. +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser QmlJSGrammar +%decl qmljsparser_p.h +%impl qmljsparser.cpp +%expect 2 +%expect-rr 2 + +%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" +%token T_BREAK "break" T_CASE "case" T_CATCH "catch" +%token T_COLON ":" T_COMMA ";" T_CONTINUE "continue" +%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/" +%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "." +%token T_ELSE "else" T_EQ "=" T_EQ_EQ "==" +%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for" +%token T_FUNCTION "function" T_GE ">=" T_GT ">" +%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>" +%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if" +%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{" +%token T_LBRACKET "[" T_LE "<=" T_LPAREN "(" +%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<=" +%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--" +%token T_NEW "new" T_NOT "!" T_NOT_EQ "!=" +%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|" +%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+" +%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?" +%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" +%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")" +%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*" +%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal" +%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly" +%token T_SWITCH "switch" T_THIS "this" T_THROW "throw" +%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof" +%token T_VAR "var" T_VOID "void" T_WHILE "while" +%token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" +%token T_NULL "null" T_TRUE "true" T_FALSE "false" +%token T_CONST "const" +%token T_DEBUGGER "debugger" +%token T_RESERVED_WORD "reserved word" +%token T_MULTILINE_STRING_LITERAL "multiline string literal" +%token T_COMMENT "comment" + +--- context keywords. +%token T_PUBLIC "public" +%token T_IMPORT "import" +%token T_AS "as" +%token T_ON "on" + +--- feed tokens +%token T_FEED_UI_PROGRAM +%token T_FEED_UI_OBJECT_MEMBER +%token T_FEED_JS_STATEMENT +%token T_FEED_JS_EXPRESSION +%token T_FEED_JS_SOURCE_ELEMENT +%token T_FEED_JS_PROGRAM + +%nonassoc SHIFT_THERE +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY +%nonassoc REDUCE_HERE + +%start TopLevel + +/./**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QtDebug> +#include <QtGui/QApplication> + +#include <string.h> + +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsast_p.h" +#include "qmljsnodepool_p.h" + +./ + +/:/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QMLJSPARSER_P_H +#define QMLJSPARSER_P_H + +#include "qmljsglobal_p.h" +#include "qmljsgrammar_p.h" +#include "qmljsast_p.h" +#include "qmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class Engine; +class NameId; + +class QML_PARSER_EXPORT Parser: protected $table +{ +public: + union Value { + int ival; + double dval; + NameId *sval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + AST::UiSignature *UiSignature; + AST::UiFormalList *UiFormalList; + AST::UiFormal *UiFormal; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + AST::Node *rootNode() const + { return program; } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + }; + + double yylval; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QmlJS + + +:/ + + +/. + +#include "qmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (qRealloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (qRealloc(location_stack, stack_size * sizeof(AST::SourceLocation))); +} + +inline static bool automatic(Engine *driver, int token) +{ + return token == $table::T_RBRACE + || token == 0 + || driver->lexer()->prevTerminator(); +} + + +Parser::Parser(Engine *engine): + driver(engine), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + qFree(sym_stack); + qFree(state_stack); + qFree(location_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->startLineNo(); + loc.startColumn = lexer->startColumnNo(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<NameId *, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->dval(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { +./ + +-------------------------------------------------------------------------------------------------------- +-- Declarative UI +-------------------------------------------------------------------------------------------------------- + +TopLevel: T_FEED_UI_PROGRAM UiProgram ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_STATEMENT Statement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_EXPRESSION Expression ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_PROGRAM Program ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +UiProgram: UiImportListOpt UiRootMember ; +/. +case $rule_number: { + sym(1).UiProgram = makeAstNode<AST::UiProgram> (driver->nodePool(), sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; +./ + +UiImportListOpt: Empty ; +UiImportListOpt: UiImportList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; +./ + +UiImportList: UiImport ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), sym(1).UiImport); +} break; +./ + +UiImportList: UiImportList UiImport ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), + sym(1).UiImportList, sym(2).UiImport); +} break; +./ + +ImportId: MemberExpression ; + +UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->semicolonToken = loc(2); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = sym(4).sval; + sym(1).UiImport->semicolonToken = loc(5); +} break; +./ + +UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = sym(3).sval; + sym(1).UiImport->semicolonToken = loc(4); +} break; +./ + + +UiImportHead: T_IMPORT ImportId ; +/. +case $rule_number: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = makeAstNode<AST::UiImport>(driver->nodePool(), importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = makeAstNode<AST::UiImport>(driver->nodePool(), qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; +./ + +Empty: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiRootMember: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMemberList UiObjectMember ; +/. +case $rule_number: { + AST::UiObjectMemberList *node = makeAstNode<AST:: UiObjectMemberList> (driver->nodePool(), + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; +./ + +UiArrayMemberList: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ; +/. +case $rule_number: { + AST::UiArrayMemberList *node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), (AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectDefinition: UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectDefinition *node = makeAstNode<AST::UiObjectDefinition> (driver->nodePool(), sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiObjectDefinition ; + +UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiArrayBinding *node = makeAstNode<AST::UiArrayBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; +./ + +UiScriptStatement: Block ; +UiScriptStatement: EmptyStatement ; +UiScriptStatement: ExpressionStatement ; +UiScriptStatement: IfStatement ; --- ### do we really want if statement in a binding? + +UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ; +/. +case $rule_number: +{ + AST::UiScriptBinding *node = makeAstNode<AST::UiScriptBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiPropertyType: T_VAR ; +/. +case $rule_number: +./ +UiPropertyType: T_RESERVED_WORD ; +/. +case $rule_number: { + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); + break; +} +./ + +UiPropertyType: T_IDENTIFIER ; + +UiParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiParameterListOpt: UiParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; +./ + +UiParameterList: UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).sval, sym(2).sval); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).UiParameterList, sym(3).sval, sym(4).sval); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(4).sval, sym(6).sval); + node->typeModifier = sym(2).sval; + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval, + sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(4).sval, sym(6).sval); + node->typeModifier = sym(2).sval; + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), sym(6).sval); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = makeAstNode<AST::UiArrayBinding> (driver->nodePool(), + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), sym(3).sval); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; +./ + +UiObjectMember: VariableStatement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; +./ + +JsIdentifier: T_IDENTIFIER; + +JsIdentifier: T_PROPERTY ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_PROPERTY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_SIGNAL ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_SIGNAL]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_READONLY ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_READONLY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_ON ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_ON]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +-------------------------------------------------------------------------------------------------------- +-- Expressions +-------------------------------------------------------------------------------------------------------- + +PrimaryExpression: T_THIS ; +/. +case $rule_number: { + AST::ThisExpression *node = makeAstNode<AST::ThisExpression> (driver->nodePool()); + node->thisToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: JsIdentifier ; +/. +case $rule_number: { + AST::IdentifierExpression *node = makeAstNode<AST::IdentifierExpression> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NULL ; +/. +case $rule_number: { + AST::NullExpression *node = makeAstNode<AST::NullExpression> (driver->nodePool()); + node->nullToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_TRUE ; +/. +case $rule_number: { + AST::TrueLiteral *node = makeAstNode<AST::TrueLiteral> (driver->nodePool()); + node->trueToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_FALSE ; +/. +case $rule_number: { + AST::FalseLiteral *node = makeAstNode<AST::FalseLiteral> (driver->nodePool()); + node->falseToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_MULTILINE_STRING_LITERAL ; +/.case $rule_number:./ + +PrimaryExpression: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteral *node = makeAstNode<AST::StringLiteral> (driver->nodePool(), sym(1).sval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_EQ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +-- PrimaryExpression: T_LBRACE T_RBRACE ; +-- /. +-- case $rule_number: { +-- sym(1).Node = makeAstNode<AST::ObjectLiteral> (driver->nodePool()); +-- } break; +-- ./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + else + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LPAREN Expression T_RPAREN ; +/. +case $rule_number: { + AST::NestedExpression *node = makeAstNode<AST::NestedExpression>(driver->nodePool(), sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiQualifiedId: MemberExpression ; +/. +case $rule_number: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; +./ + +ElementList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), (AST::Elision *) 0, sym(1).Expression); +} break; +./ + +ElementList: Elision AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).Elision->finish(), sym(2).Expression); +} break; +./ + +ElementList: ElementList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ElementList: ElementList T_COMMA Elision AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +Elision: T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool()); + node->commaToken = loc(1); + sym(1).Node = node; +} break; +./ + +Elision: Elision T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool(), sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +PropertyName: T_IDENTIFIER %prec SHIFT_THERE ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_SIGNAL ; +/.case $rule_number:./ + +PropertyName: T_PROPERTY ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount())); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteralPropertyName *node = makeAstNode<AST::StringLiteralPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteralPropertyName *node = makeAstNode<AST::NumericLiteralPropertyName> (driver->nodePool(), sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: ReservedIdentifier ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +ReservedIdentifier: T_BREAK ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CASE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CATCH ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CONTINUE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DEFAULT ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DELETE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DO ; +/. +case $rule_number: +./ +ReservedIdentifier: T_ELSE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FALSE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FINALLY ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FOR ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FUNCTION ; +/. +case $rule_number: +./ +ReservedIdentifier: T_IF ; +/. +case $rule_number: +./ +ReservedIdentifier: T_IN ; +/. +case $rule_number: +./ +ReservedIdentifier: T_INSTANCEOF ; +/. +case $rule_number: +./ +ReservedIdentifier: T_NEW ; +/. +case $rule_number: +./ +ReservedIdentifier: T_NULL ; +/. +case $rule_number: +./ +ReservedIdentifier: T_RETURN ; +/. +case $rule_number: +./ +ReservedIdentifier: T_SWITCH ; +/. +case $rule_number: +./ +ReservedIdentifier: T_THIS ; +/. +case $rule_number: +./ +ReservedIdentifier: T_THROW ; +/. +case $rule_number: +./ +ReservedIdentifier: T_TRUE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_TRY ; +/. +case $rule_number: +./ +ReservedIdentifier: T_TYPEOF ; +/. +case $rule_number: +./ +ReservedIdentifier: T_VAR ; +/. +case $rule_number: +./ +ReservedIdentifier: T_VOID ; +/. +case $rule_number: +./ +ReservedIdentifier: T_WHILE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CONST ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DEBUGGER ; +/. +case $rule_number: +./ +ReservedIdentifier: T_RESERVED_WORD ; +/. +case $rule_number: +./ +ReservedIdentifier: T_WITH ; +/. +case $rule_number: +{ + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); +} break; +./ + +PropertyIdentifier: JsIdentifier ; +PropertyIdentifier: ReservedIdentifier ; + +MemberExpression: PrimaryExpression ; +MemberExpression: FunctionExpression ; + +MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +MemberExpression: MemberExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::NewMemberExpression *node = makeAstNode<AST::NewMemberExpression> (driver->nodePool(), sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; +./ + +NewExpression: MemberExpression ; + +NewExpression: T_NEW NewExpression ; +/. +case $rule_number: { + AST::NewExpression *node = makeAstNode<AST::NewExpression> (driver->nodePool(), sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; +./ + +CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +ArgumentListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ArgumentListOpt: ArgumentList ; +/. +case $rule_number: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; +./ + +ArgumentList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).Expression); +} break; +./ + +ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ArgumentList *node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +LeftHandSideExpression: NewExpression ; +LeftHandSideExpression: CallExpression ; +PostfixExpression: LeftHandSideExpression ; + +PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ; +/. +case $rule_number: { + AST::PostIncrementExpression *node = makeAstNode<AST::PostIncrementExpression> (driver->nodePool(), sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ; +/. +case $rule_number: { + AST::PostDecrementExpression *node = makeAstNode<AST::PostDecrementExpression> (driver->nodePool(), sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +UnaryExpression: PostfixExpression ; + +UnaryExpression: T_DELETE UnaryExpression ; +/. +case $rule_number: { + AST::DeleteExpression *node = makeAstNode<AST::DeleteExpression> (driver->nodePool(), sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_VOID UnaryExpression ; +/. +case $rule_number: { + AST::VoidExpression *node = makeAstNode<AST::VoidExpression> (driver->nodePool(), sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TYPEOF UnaryExpression ; +/. +case $rule_number: { + AST::TypeOfExpression *node = makeAstNode<AST::TypeOfExpression> (driver->nodePool(), sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::PreIncrementExpression *node = makeAstNode<AST::PreIncrementExpression> (driver->nodePool(), sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::PreDecrementExpression *node = makeAstNode<AST::PreDecrementExpression> (driver->nodePool(), sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryPlusExpression *node = makeAstNode<AST::UnaryPlusExpression> (driver->nodePool(), sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryMinusExpression *node = makeAstNode<AST::UnaryMinusExpression> (driver->nodePool(), sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TILDE UnaryExpression ; +/. +case $rule_number: { + AST::TildeExpression *node = makeAstNode<AST::TildeExpression> (driver->nodePool(), sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_NOT UnaryExpression ; +/. +case $rule_number: { + AST::NotExpression *node = makeAstNode<AST::NotExpression> (driver->nodePool(), sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: UnaryExpression ; + +MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: MultiplicativeExpression ; + +AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: AdditiveExpression ; + +ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: ShiftExpression ; + +RelationalExpression: RelationalExpression T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_IN ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: ShiftExpression ; + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: RelationalExpression ; + +EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: RelationalExpressionNotIn ; + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpression: EqualityExpression ; + +BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpressionNotIn: EqualityExpressionNotIn ; + +BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpression: BitwiseANDExpression ; + +BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ; + +BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpression: BitwiseXORExpression ; + +BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ; + +BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpression: BitwiseORExpression ; + +LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ; + +LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpression: LogicalANDExpression ; + +LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpressionNotIn: LogicalANDExpressionNotIn ; + +LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ConditionalExpression: LogicalORExpression ; + +ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +ConditionalExpressionNotIn: LogicalORExpressionNotIn ; + +ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +AssignmentExpression: ConditionalExpression ; + +AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentExpressionNotIn: ConditionalExpressionNotIn ; + +AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentOperator: T_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::Assign; +} break; +./ + +AssignmentOperator: T_STAR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMul; +} break; +./ + +AssignmentOperator: T_DIVIDE_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceDiv; +} break; +./ + +AssignmentOperator: T_REMAINDER_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMod; +} break; +./ + +AssignmentOperator: T_PLUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAdd; +} break; +./ + +AssignmentOperator: T_MINUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceSub; +} break; +./ + +AssignmentOperator: T_LT_LT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; +./ + +AssignmentOperator: T_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; +./ + +AssignmentOperator: T_GT_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; +./ + +AssignmentOperator: T_AND_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAnd; +} break; +./ + +AssignmentOperator: T_XOR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceXor; +} break; +./ + +AssignmentOperator: T_OR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceOr; +} break; +./ + +Expression: AssignmentExpression ; + +Expression: Expression T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionOpt: Expression ; + +ExpressionNotIn: AssignmentExpressionNotIn ; + +ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionNotInOpt: ExpressionNotIn ; + +Statement: Block ; +Statement: VariableStatement ; +Statement: EmptyStatement ; +Statement: ExpressionStatement ; +Statement: IfStatement ; +Statement: IterationStatement ; +Statement: ContinueStatement ; +Statement: BreakStatement ; +Statement: ReturnStatement ; +Statement: WithStatement ; +Statement: LabelledStatement ; +Statement: SwitchStatement ; +Statement: ThrowStatement ; +Statement: TryStatement ; +Statement: DebuggerStatement ; + + +Block: T_LBRACE StatementListOpt T_RBRACE ; +/. +case $rule_number: { + AST::Block *node = makeAstNode<AST::Block> (driver->nodePool(), sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +StatementList: Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).Statement); +} break; +./ + +StatementList: StatementList Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).StatementList, sym(2).Statement); +} break; +./ + +StatementListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +StatementListOpt: StatementList ; +/. +case $rule_number: { + sym(1).Node = sym(1).StatementList->finish (); +} break; +./ + +VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; +/. +case $rule_number: { + AST::VariableStatement *node = makeAstNode<AST::VariableStatement> (driver->nodePool(), + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +VariableDeclarationKind: T_CONST ; +/. +case $rule_number: { + sym(1).ival = T_CONST; +} break; +./ + +VariableDeclarationKind: T_VAR ; +/. +case $rule_number: { + sym(1).ival = T_VAR; +} break; +./ + +VariableDeclarationList: VariableDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +/. +case $rule_number: { + AST::VariableDeclarationList *node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; +./ + +VariableDeclaration: JsIdentifier InitialiserOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +Initialiser: T_EQ AssignmentExpression ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserOpt: Initialiser ; + +InitialiserNotIn: T_EQ AssignmentExpressionNotIn ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserNotInOpt: InitialiserNotIn ; + +EmptyStatement: T_SEMICOLON ; +/. +case $rule_number: { + AST::EmptyStatement *node = makeAstNode<AST::EmptyStatement> (driver->nodePool()); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; +./ + +ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ExpressionStatement: Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ExpressionStatement *node = makeAstNode<AST::ExpressionStatement> (driver->nodePool(), sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ; +/. +case $rule_number: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(5); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + + +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::DoWhileStatement *node = makeAstNode<AST::DoWhileStatement> (driver->nodePool(), sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WhileStatement *node = makeAstNode<AST::WhileStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::ForStatement *node = makeAstNode<AST::ForStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForStatement *node = makeAstNode<AST::LocalForStatement> (driver->nodePool(), + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST:: ForEachStatement *node = makeAstNode<AST::ForEachStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForEachStatement *node = makeAstNode<AST::LocalForEachStatement> (driver->nodePool(), + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool()); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool(), sym(2).sval); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool(), sym(2).sval); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ; +/. +case $rule_number: { + AST::ReturnStatement *node = makeAstNode<AST::ReturnStatement> (driver->nodePool(), sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WithStatement *node = makeAstNode<AST::WithStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ; +/. +case $rule_number: { + AST::SwitchStatement *node = makeAstNode<AST::SwitchStatement> (driver->nodePool(), sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; +./ + +CaseClauses: CaseClause ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClause); +} break; +./ + +CaseClauses: CaseClauses CaseClause ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClauses, sym(2).CaseClause); +} break; +./ + +CaseClausesOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +CaseClausesOpt: CaseClauses ; +/. +case $rule_number: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; +./ + +CaseClause: T_CASE Expression T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::CaseClause *node = makeAstNode<AST::CaseClause> (driver->nodePool(), sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; +./ + +DefaultClause: T_DEFAULT T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::DefaultClause *node = makeAstNode<AST::DefaultClause> (driver->nodePool(), sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_SIGNAL T_COLON Statement ; +/.case $rule_number:./ + +LabelledStatement: T_PROPERTY T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount()), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_IDENTIFIER T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), sym(1).sval, sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ThrowStatement: T_THROW Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ThrowStatement *node = makeAstNode<AST::ThrowStatement> (driver->nodePool(), sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch ; +/. +case $rule_number: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Finally ; +/. +case $rule_number: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch Finally ; +/. +case $rule_number: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ; +/. +case $rule_number: { + AST::Catch *node = makeAstNode<AST::Catch> (driver->nodePool(), sym(3).sval, sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +Finally: T_FINALLY Block ; +/. +case $rule_number: { + AST::Finally *node = makeAstNode<AST::Finally> (driver->nodePool(), sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; +./ + +DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +DebuggerStatement: T_DEBUGGER T_SEMICOLON ; +/. +case $rule_number: { + AST::DebuggerStatement *node = makeAstNode<AST::DebuggerStatement> (driver->nodePool()); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +FunctionDeclaration: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionDeclaration *node = makeAstNode<AST::FunctionDeclaration> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionExpression *node = makeAstNode<AST::FunctionExpression> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (sym(2).sval) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FormalParameterList: JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +FormalParameterList: FormalParameterList T_COMMA JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).FormalParameterList, sym(3).sval); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +FormalParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FormalParameterListOpt: FormalParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; +./ + +FunctionBodyOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FunctionBodyOpt: FunctionBody ; + +FunctionBody: SourceElements ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::FunctionBody> (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; +./ + +Program: SourceElements ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::Program> (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; +./ + +SourceElements: SourceElement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElement); +} break; +./ + +SourceElements: SourceElements SourceElement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElements, sym(2).SourceElement); +} break; +./ + +SourceElement: Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::StatementSourceElement> (driver->nodePool(), sym(1).Statement); +} break; +./ + +SourceElement: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::FunctionSourceElement> (driver->nodePool(), sym(1).FunctionDeclaration); +} break; +./ + +IdentifierOpt: ; +/. +case $rule_number: { + sym(1).sval = 0; +} break; +./ + +IdentifierOpt: JsIdentifier ; + +PropertyNameAndValueListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +PropertyNameAndValueListOpt: PropertyNameAndValueList ; + +/. + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && automatic(driver, yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = qApp->translate("QmlParser", "Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->dval(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QmlParser", "Syntax error"); + else + msg = qApp->translate("QmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = qApp->translate("QmlParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + +./ +/: +QT_QML_END_NAMESPACE + + + +#endif // QMLJSPARSER_P_H +:/ diff --git a/src/lib/parser/qmljsast.cpp b/src/lib/parser/qmljsast.cpp new file mode 100644 index 000000000..02177ef5a --- /dev/null +++ b/src/lib/parser/qmljsast.cpp @@ -0,0 +1,947 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "qmljsast_p.h" + +#include "qmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +void Node::accept(Visitor *visitor) +{ + if (visitor->preVisit(this)) { + accept0(visitor); + } + visitor->postVisit(this); +} + +void Node::accept(Node *node, Visitor *visitor) +{ + if (node) + node->accept(visitor); +} + +ExpressionNode *Node::expressionCast() +{ + return 0; +} + +BinaryExpression *Node::binaryExpressionCast() +{ + return 0; +} + +Statement *Node::statementCast() +{ + return 0; +} + +UiObjectMember *Node::uiObjectMemberCast() +{ + return 0; +} + +ExpressionNode *ExpressionNode::expressionCast() +{ + return this; +} + +BinaryExpression *BinaryExpression::binaryExpressionCast() +{ + return this; +} + +Statement *Statement::statementCast() +{ + return this; +} + +UiObjectMember *UiObjectMember::uiObjectMemberCast() +{ + return this; +} + +void NestedExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + visitor->endVisit(this); +} + +void ThisExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void IdentifierExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NullExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void TrueLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void FalseLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void RegExpLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + accept(elision, visitor); + } + + visitor->endVisit(this); +} + +void ObjectLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(properties, visitor); + } + + visitor->endVisit(this); +} + +void ElementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ElementList *it = this; it; it = it->next) { + accept(it->elision, visitor); + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void Elision::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void PropertyNameAndValueList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (PropertyNameAndValueList *it = this; it; it = it->next) { + accept(it->name, visitor); + accept(it->value, visitor); + } + } + + visitor->endVisit(this); +} + +void IdentifierPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void FieldMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void NewMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void NewExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void CallExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void ArgumentList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ArgumentList *it = this; it; it = it->next) { + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void PostIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void PostDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void DeleteExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void VoidExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TypeOfExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryPlusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryMinusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TildeExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void NotExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void BinaryExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void ConditionalExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void Expression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void Block::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void StatementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (StatementList *it = this; it; it = it->next) { + accept(it->statement, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + } + + visitor->endVisit(this); +} + +void VariableDeclarationList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (VariableDeclarationList *it = this; it; it = it->next) { + accept(it->declaration, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void EmptyStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ExpressionStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void IfStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void DoWhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ContinueStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void BreakStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ReturnStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WithStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void SwitchStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(block, visitor); + } + + visitor->endVisit(this); +} + +void CaseBlock::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(clauses, visitor); + accept(defaultClause, visitor); + accept(moreClauses, visitor); + } + + visitor->endVisit(this); +} + +void CaseClauses::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (CaseClauses *it = this; it; it = it->next) { + accept(it->clause, visitor); + } + } + + visitor->endVisit(this); +} + +void CaseClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void DefaultClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void LabelledStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ThrowStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TryStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(catchExpression, visitor); + accept(finallyExpression, visitor); + } + + visitor->endVisit(this); +} + +void Catch::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void Finally::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void FunctionDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FunctionExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FormalParameterList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void FunctionBody::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void Program::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void SourceElements::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (SourceElements *it = this; it; it = it->next) { + accept(it->element, visitor); + } + } + + visitor->endVisit(this); +} + +void FunctionSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + } + + visitor->endVisit(this); +} + +void StatementSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void DebuggerStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiProgram::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(imports, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiSignature::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + } + visitor->endVisit(this); +} + +void UiFormalList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiFormalList *it = this; it; it = it->next) { + accept(it->formal, visitor); + } + } + visitor->endVisit(this); +} + +void UiFormal::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + visitor->endVisit(this); +} + +void UiPublicMember::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(binding, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectDefinition::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectInitializer::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiScriptBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiObjectMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiArrayMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiQualifiedId::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiImport::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(importUri, visitor); + } + + visitor->endVisit(this); +} + +void UiImportList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(import, visitor); + accept(next, visitor); + } + + visitor->endVisit(this); +} + +void UiSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(sourceElement, visitor); + } + + visitor->endVisit(this); +} + +} } // namespace QmlJS::AST + +QT_QML_END_NAMESPACE + + diff --git a/src/lib/parser/qmljsast_p.h b/src/lib/parser/qmljsast_p.h new file mode 100644 index 000000000..15a547df3 --- /dev/null +++ b/src/lib/parser/qmljsast_p.h @@ -0,0 +1,2537 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSAST_P_H +#define QMLJSAST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsastvisitor_p.h" +#include "qmljsglobal_p.h" + +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +#define QMLJS_DECLARE_AST_NODE(name) \ + enum { K = Kind_##name }; + +namespace QSOperator // ### rename +{ + +enum Op { + Add, + And, + InplaceAnd, + Assign, + BitAnd, + BitOr, + BitXor, + InplaceSub, + Div, + InplaceDiv, + Equal, + Ge, + Gt, + In, + InplaceAdd, + InstanceOf, + Le, + LShift, + InplaceLeftShift, + Lt, + Mod, + InplaceMod, + Mul, + InplaceMul, + NotEqual, + Or, + InplaceOr, + RShift, + InplaceRightShift, + StrictEqual, + StrictNotEqual, + Sub, + URShift, + InplaceURightShift, + InplaceXor +}; + +} // namespace QSOperator + +namespace QmlJS { +class NameId; +namespace AST { + +template <typename _T1, typename _T2> +_T1 cast(_T2 *ast) +{ + if (ast && ast->kind == static_cast<_T1>(0)->K) + return static_cast<_T1>(ast); + + return 0; +} + +class QML_PARSER_EXPORT Node +{ +public: + enum Kind { + Kind_Undefined, + + Kind_ArgumentList, + Kind_ArrayLiteral, + Kind_ArrayMemberExpression, + Kind_BinaryExpression, + Kind_Block, + Kind_BreakStatement, + Kind_CallExpression, + Kind_CaseBlock, + Kind_CaseClause, + Kind_CaseClauses, + Kind_Catch, + Kind_ConditionalExpression, + Kind_ContinueStatement, + Kind_DebuggerStatement, + Kind_DefaultClause, + Kind_DeleteExpression, + Kind_DoWhileStatement, + Kind_ElementList, + Kind_Elision, + Kind_EmptyStatement, + Kind_Expression, + Kind_ExpressionStatement, + Kind_FalseLiteral, + Kind_FieldMemberExpression, + Kind_Finally, + Kind_ForEachStatement, + Kind_ForStatement, + Kind_FormalParameterList, + Kind_FunctionBody, + Kind_FunctionDeclaration, + Kind_FunctionExpression, + Kind_FunctionSourceElement, + Kind_IdentifierExpression, + Kind_IdentifierPropertyName, + Kind_IfStatement, + Kind_LabelledStatement, + Kind_LocalForEachStatement, + Kind_LocalForStatement, + Kind_NewExpression, + Kind_NewMemberExpression, + Kind_NotExpression, + Kind_NullExpression, + Kind_NumericLiteral, + Kind_NumericLiteralPropertyName, + Kind_ObjectLiteral, + Kind_PostDecrementExpression, + Kind_PostIncrementExpression, + Kind_PreDecrementExpression, + Kind_PreIncrementExpression, + Kind_Program, + Kind_PropertyName, + Kind_PropertyNameAndValueList, + Kind_RegExpLiteral, + Kind_ReturnStatement, + Kind_SourceElement, + Kind_SourceElements, + Kind_StatementList, + Kind_StatementSourceElement, + Kind_StringLiteral, + Kind_StringLiteralPropertyName, + Kind_SwitchStatement, + Kind_ThisExpression, + Kind_ThrowStatement, + Kind_TildeExpression, + Kind_TrueLiteral, + Kind_TryStatement, + Kind_TypeOfExpression, + Kind_UnaryMinusExpression, + Kind_UnaryPlusExpression, + Kind_VariableDeclaration, + Kind_VariableDeclarationList, + Kind_VariableStatement, + Kind_VoidExpression, + Kind_WhileStatement, + Kind_WithStatement, + Kind_NestedExpression, + + Kind_UiArrayBinding, + Kind_UiImport, + Kind_UiImportList, + Kind_UiObjectBinding, + Kind_UiObjectDefinition, + Kind_UiObjectInitializer, + Kind_UiObjectMemberList, + Kind_UiArrayMemberList, + Kind_UiProgram, + Kind_UiParameterList, + Kind_UiPublicMember, + Kind_UiQualifiedId, + Kind_UiScriptBinding, + Kind_UiSourceElement, + Kind_UiFormal, + Kind_UiFormalList, + Kind_UiSignature + }; + + inline Node() + : kind(Kind_Undefined) {} + + // NOTE: node destructors are never called, + // instead we block free the memory + // (see the NodePool class) + virtual ~Node() {} + + virtual ExpressionNode *expressionCast(); + virtual BinaryExpression *binaryExpressionCast(); + virtual Statement *statementCast(); + virtual UiObjectMember *uiObjectMemberCast(); + + void accept(Visitor *visitor); + static void accept(Node *node, Visitor *visitor); + + inline static void acceptChild(Node *node, Visitor *visitor) + { return accept(node, visitor); } // ### remove + + virtual void accept0(Visitor *visitor) = 0; + +// attributes + int kind; +}; + +class QML_PARSER_EXPORT ExpressionNode: public Node +{ +public: + ExpressionNode() {} + + virtual ExpressionNode *expressionCast(); + + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; +}; + +class QML_PARSER_EXPORT Statement: public Node +{ +public: + Statement() {} + + virtual Statement *statementCast(); + + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; +}; + +class QML_PARSER_EXPORT UiFormal: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiFormal) + + UiFormal(NameId *name, NameId *alias = 0) + : name(name), alias(alias) + { } + + virtual SourceLocation firstSourceLocation() const + { return SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return SourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + NameId *alias; + SourceLocation identifierToken; + SourceLocation asToken; + SourceLocation aliasToken; +}; + +class QML_PARSER_EXPORT UiFormalList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiFormalList) + + UiFormalList(UiFormal *formal) + : formal(formal), next(this) {} + + UiFormalList(UiFormalList *previous, UiFormal *formal) + : formal(formal) + { + next = previous->next; + previous->next = this; + } + + UiFormalList *finish() + { + UiFormalList *head = next; + next = 0; + return head; + } + + virtual SourceLocation firstSourceLocation() const + { return SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return SourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + UiFormal *formal; + UiFormalList *next; +}; + +class QML_PARSER_EXPORT UiSignature: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiSignature) + + UiSignature(UiFormalList *formals = 0) + : formals(formals) + { } + + virtual SourceLocation firstSourceLocation() const + { return SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return SourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceLocation lparenToken; + UiFormalList *formals; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT NestedExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NestedExpression) + + NestedExpression(ExpressionNode *expression) + : expression(expression) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lparenToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *expression; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ThisExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ThisExpression) + + ThisExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return thisToken; } + + virtual SourceLocation lastSourceLocation() const + { return thisToken; } + +// attributes + SourceLocation thisToken; +}; + +class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(IdentifierExpression) + + IdentifierExpression(NameId *n): + name (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + +// attributes + NameId *name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NullExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NullExpression) + + NullExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return nullToken; } + + virtual SourceLocation lastSourceLocation() const + { return nullToken; } + +// attributes + SourceLocation nullToken; +}; + +class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(TrueLiteral) + + TrueLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return trueToken; } + + virtual SourceLocation lastSourceLocation() const + { return trueToken; } + +// attributes + SourceLocation trueToken; +}; + +class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(FalseLiteral) + + FalseLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return falseToken; } + + virtual SourceLocation lastSourceLocation() const + { return falseToken; } + +// attributes + SourceLocation falseToken; +}; + +class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NumericLiteral) + + NumericLiteral(double v): + value(v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + double value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT StringLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(StringLiteral) + + StringLiteral(NameId *v): + value (v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + NameId *value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(RegExpLiteral) + + RegExpLiteral(NameId *p, int f): + pattern (p), flags (f) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + NameId *pattern; + int flags; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ArrayLiteral) + + ArrayLiteral(Elision *e): + elements (0), elision (e) + { kind = K; } + + ArrayLiteral(ElementList *elts): + elements (elts), elision (0) + { kind = K; } + + ArrayLiteral(ElementList *elts, Elision *e): + elements (elts), elision (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbracketToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ElementList *elements; + Elision *elision; + SourceLocation lbracketToken; + SourceLocation commaToken; + SourceLocation rbracketToken; +}; + +class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ObjectLiteral) + + ObjectLiteral(): + properties (0) { kind = K; } + + ObjectLiteral(PropertyNameAndValueList *plist): + properties (plist) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + PropertyNameAndValueList *properties; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT ElementList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(ElementList) + + ElementList(Elision *e, ExpressionNode *expr): + elision (e), expression (expr), next (this) + { kind = K; } + + ElementList(ElementList *previous, Elision *e, ExpressionNode *expr): + elision (e), expression (expr) + { + kind = K; + next = previous->next; + previous->next = this; + } + + inline ElementList *finish () + { + ElementList *front = next; + next = 0; + return front; + } + + virtual void accept0(Visitor *visitor); + +// attributes + Elision *elision; + ExpressionNode *expression; + ElementList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT Elision: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Elision) + + Elision(): + next (this) { kind = K; } + + Elision(Elision *previous) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline Elision *finish () + { + Elision *front = next; + next = 0; + return front; + } + +// attributes + Elision *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PropertyNameAndValueList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(PropertyNameAndValueList) + + PropertyNameAndValueList(PropertyName *n, ExpressionNode *v): + name (n), value (v), next (this) + { kind = K; } + + PropertyNameAndValueList(PropertyNameAndValueList *previous, PropertyName *n, ExpressionNode *v): + name (n), value (v) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline PropertyNameAndValueList *finish () + { + PropertyNameAndValueList *front = next; + next = 0; + return front; + } + +// attributes + PropertyName *name; + ExpressionNode *value; + PropertyNameAndValueList *next; + SourceLocation colonToken; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PropertyName: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(PropertyName) + + PropertyName() { kind = K; } + +// attributes + SourceLocation propertyNameToken; +}; + +class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName +{ +public: + QMLJS_DECLARE_AST_NODE(IdentifierPropertyName) + + IdentifierPropertyName(NameId *n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *id; +}; + +class QML_PARSER_EXPORT StringLiteralPropertyName: public PropertyName +{ +public: + QMLJS_DECLARE_AST_NODE(StringLiteralPropertyName) + + StringLiteralPropertyName(NameId *n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *id; +}; + +class QML_PARSER_EXPORT NumericLiteralPropertyName: public PropertyName +{ +public: + QMLJS_DECLARE_AST_NODE(NumericLiteralPropertyName) + + NumericLiteralPropertyName(double n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + double id; +}; + +class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ArrayMemberExpression) + + ArrayMemberExpression(ExpressionNode *b, ExpressionNode *e): + base (b), expression (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ExpressionNode *base; + ExpressionNode *expression; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(FieldMemberExpression) + + FieldMemberExpression(ExpressionNode *b, NameId *n): + base (b), name (n) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + + // attributes + ExpressionNode *base; + NameId *name; + SourceLocation dotToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NewMemberExpression) + + NewMemberExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + + // attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation newToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT NewExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NewExpression) + + NewExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation newToken; +}; + +class QML_PARSER_EXPORT CallExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(CallExpression) + + CallExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ArgumentList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(ArgumentList) + + ArgumentList(ExpressionNode *e): + expression (e), next (this) + { kind = K; } + + ArgumentList(ArgumentList *previous, ExpressionNode *e): + expression (e) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline ArgumentList *finish () + { + ArgumentList *front = next; + next = 0; + return front; + } + +// attributes + ExpressionNode *expression; + ArgumentList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PostIncrementExpression) + + PostIncrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return incrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PostDecrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PostDecrementExpression) + + PostDecrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return decrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT DeleteExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(DeleteExpression) + + DeleteExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return deleteToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation deleteToken; +}; + +class QML_PARSER_EXPORT VoidExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(VoidExpression) + + VoidExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return voidToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation voidToken; +}; + +class QML_PARSER_EXPORT TypeOfExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(TypeOfExpression) + + TypeOfExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return typeofToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation typeofToken; +}; + +class QML_PARSER_EXPORT PreIncrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PreIncrementExpression) + + PreIncrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return incrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PreDecrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PreDecrementExpression) + + PreDecrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return decrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT UnaryPlusExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(UnaryPlusExpression) + + UnaryPlusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return plusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation plusToken; +}; + +class QML_PARSER_EXPORT UnaryMinusExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(UnaryMinusExpression) + + UnaryMinusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return minusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation minusToken; +}; + +class QML_PARSER_EXPORT TildeExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(TildeExpression) + + TildeExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tildeToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation tildeToken; +}; + +class QML_PARSER_EXPORT NotExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NotExpression) + + NotExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return notToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation notToken; +}; + +class QML_PARSER_EXPORT BinaryExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(BinaryExpression) + + BinaryExpression(ExpressionNode *l, int o, ExpressionNode *r): + left (l), op (o), right (r) + { kind = K; } + + virtual BinaryExpression *binaryExpressionCast(); + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + int op; + ExpressionNode *right; + SourceLocation operatorToken; +}; + +class QML_PARSER_EXPORT ConditionalExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ConditionalExpression) + + ConditionalExpression(ExpressionNode *e, ExpressionNode *t, ExpressionNode *f): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return ko->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + ExpressionNode *ok; + ExpressionNode *ko; + SourceLocation questionToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT Expression: public ExpressionNode // ### rename +{ +public: + QMLJS_DECLARE_AST_NODE(Expression) + + Expression(ExpressionNode *l, ExpressionNode *r): + left (l), right (r) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + ExpressionNode *right; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT Block: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(Block) + + Block(StatementList *slist): + statements (slist) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + + // attributes + StatementList *statements; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT StatementList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(StatementList) + + StatementList(Statement *stmt): + statement (stmt), next (this) + { kind = K; } + + StatementList(StatementList *previous, Statement *stmt): + statement (stmt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline StatementList *finish () + { + StatementList *front = next; + next = 0; + return front; + } + +// attributes + Statement *statement; + StatementList *next; +}; + +class QML_PARSER_EXPORT VariableStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(VariableStatement) + + VariableStatement(VariableDeclarationList *vlist): + declarations (vlist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declarationKindToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + VariableDeclarationList *declarations; + SourceLocation declarationKindToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT VariableDeclaration: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(VariableDeclaration) + + VariableDeclaration(NameId *n, ExpressionNode *e): + name (n), expression (e), readOnly(false) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + ExpressionNode *expression; + bool readOnly; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT VariableDeclarationList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(VariableDeclarationList) + + VariableDeclarationList(VariableDeclaration *decl): + declaration (decl), next (this) + { kind = K; } + + VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl): + declaration (decl) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline VariableDeclarationList *finish (bool readOnly) + { + VariableDeclarationList *front = next; + next = 0; + if (readOnly) { + VariableDeclarationList *vdl; + for (vdl = front; vdl != 0; vdl = vdl->next) + vdl->declaration->readOnly = true; + } + return front; + } + +// attributes + VariableDeclaration *declaration; + VariableDeclarationList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT EmptyStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(EmptyStatement) + + EmptyStatement() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return semicolonToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ExpressionStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ExpressionStatement) + + ExpressionStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT IfStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(IfStatement) + + IfStatement(ExpressionNode *e, Statement *t, Statement *f = 0): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return ifToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (ko) + return ko->lastSourceLocation(); + + return ok->lastSourceLocation(); + } + +// attributes + ExpressionNode *expression; + Statement *ok; + Statement *ko; + SourceLocation ifToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation elseToken; +}; + +class QML_PARSER_EXPORT DoWhileStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(DoWhileStatement) + + DoWhileStatement(Statement *stmt, ExpressionNode *e): + statement (stmt), expression (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return doToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + Statement *statement; + ExpressionNode *expression; + SourceLocation doToken; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT WhileStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(WhileStatement) + + WhileStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return whileToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ForStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ForStatement) + + ForStatement(ExpressionNode *i, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + initialiser (i), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT LocalForStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(LocalForStatement) + + LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + declarations (vlist), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclarationList *declarations; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ForEachStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ForEachStatement) + + ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt): + initialiser (i), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT LocalForEachStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(LocalForEachStatement) + + LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt): + declaration (v), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclaration *declaration; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ContinueStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ContinueStatement) + + ContinueStatement(NameId *l = 0): + label (l) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return continueToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + NameId *label; + SourceLocation continueToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT BreakStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(BreakStatement) + + BreakStatement(NameId *l = 0): + label (l) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return breakToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + NameId *label; + SourceLocation breakToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ReturnStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ReturnStatement) + + ReturnStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return returnToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation returnToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT WithStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(WithStatement) + + WithStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return withToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation withToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT CaseBlock: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(CaseBlock) + + CaseBlock(CaseClauses *c, DefaultClause *d = 0, CaseClauses *r = 0): + clauses (c), defaultClause (d), moreClauses (r) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + CaseClauses *clauses; + DefaultClause *defaultClause; + CaseClauses *moreClauses; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT SwitchStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(SwitchStatement) + + SwitchStatement(ExpressionNode *e, CaseBlock *b): + expression (e), block (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return switchToken; } + + virtual SourceLocation lastSourceLocation() const + { return block->rbraceToken; } + +// attributes + ExpressionNode *expression; + CaseBlock *block; + SourceLocation switchToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT CaseClauses: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(CaseClauses) + + CaseClauses(CaseClause *c): + clause (c), next (this) + { kind = K; } + + CaseClauses(CaseClauses *previous, CaseClause *c): + clause (c) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline CaseClauses *finish () + { + CaseClauses *front = next; + next = 0; + return front; + } + +//attributes + CaseClause *clause; + CaseClauses *next; +}; + +class QML_PARSER_EXPORT CaseClause: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(CaseClause) + + CaseClause(ExpressionNode *e, StatementList *slist): + expression (e), statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + ExpressionNode *expression; + StatementList *statements; + SourceLocation caseToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT DefaultClause: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(DefaultClause) + + DefaultClause(StatementList *slist): + statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + StatementList *statements; + SourceLocation defaultToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT LabelledStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(LabelledStatement) + + LabelledStatement(NameId *l, Statement *stmt): + label (l), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + NameId *label; + Statement *statement; + SourceLocation identifierToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT ThrowStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ThrowStatement) + + ThrowStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return throwToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + ExpressionNode *expression; + SourceLocation throwToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT Catch: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Catch) + + Catch(NameId *n, Block *stmt): + name (n), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + Block *statement; + SourceLocation catchToken; + SourceLocation lparenToken; + SourceLocation identifierToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT Finally: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Finally) + + Finally(Block *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + Block *statement; + SourceLocation finallyToken; +}; + +class QML_PARSER_EXPORT TryStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(TryStatement) + + TryStatement(Statement *stmt, Catch *c, Finally *f): + statement (stmt), catchExpression (c), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Finally *f): + statement (stmt), catchExpression (0), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Catch *c): + statement (stmt), catchExpression (c), finallyExpression (0) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tryToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (finallyExpression) + return finallyExpression->statement->rbraceToken; + else if (catchExpression) + return catchExpression->statement->rbraceToken; + + return statement->lastSourceLocation(); + } + +// attributes + Statement *statement; + Catch *catchExpression; + Finally *finallyExpression; + SourceLocation tryToken; +}; + +class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionExpression) + + FunctionExpression(NameId *n, FormalParameterList *f, FunctionBody *b): + name (n), formals (f), body (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return functionToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + NameId *name; + FormalParameterList *formals; + FunctionBody *body; + SourceLocation functionToken; + SourceLocation identifierToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionDeclaration) + + FunctionDeclaration(NameId *n, FormalParameterList *f, FunctionBody *b): + FunctionExpression(n, f, b) + { kind = K; } + + virtual void accept0(Visitor *visitor); +}; + +class QML_PARSER_EXPORT FormalParameterList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(FormalParameterList) + + FormalParameterList(NameId *n): + name (n), next (this) + { kind = K; } + + FormalParameterList(FormalParameterList *previous, NameId *n): + name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline FormalParameterList *finish () + { + FormalParameterList *front = next; + next = 0; + return front; + } + +// attributes + NameId *name; + FormalParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT FunctionBody: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionBody) + + FunctionBody(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT Program: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Program) + + Program(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT SourceElements: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(SourceElements) + + SourceElements(SourceElement *elt): + element (elt), next (this) + { kind = K; } + + SourceElements(SourceElements *previous, SourceElement *elt): + element (elt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline SourceElements *finish () + { + SourceElements *front = next; + next = 0; + return front; + } + +// attributes + SourceElement *element; + SourceElements *next; +}; + +class QML_PARSER_EXPORT SourceElement: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(SourceElement) + + inline SourceElement() + { kind = K; } +}; + +class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionSourceElement) + + FunctionSourceElement(FunctionDeclaration *f): + declaration (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + FunctionDeclaration *declaration; +}; + +class QML_PARSER_EXPORT StatementSourceElement: public SourceElement +{ +public: + QMLJS_DECLARE_AST_NODE(StatementSourceElement) + + StatementSourceElement(Statement *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + Statement *statement; +}; + +class QML_PARSER_EXPORT DebuggerStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(DebuggerStatement) + + DebuggerStatement() + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return debuggerToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation debuggerToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiProgram: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiProgram) + + UiProgram(UiImportList *imports, UiObjectMemberList *members) + : imports(imports), members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiImportList *imports; + UiObjectMemberList *members; +}; + +class QML_PARSER_EXPORT UiQualifiedId: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiQualifiedId) + + UiQualifiedId(NameId *name) + : next(this), name(name) + { kind = K; } + + UiQualifiedId(UiQualifiedId *previous, NameId *name) + : name(name) + { + kind = K; + next = previous->next; + previous->next = this; + } + + UiQualifiedId *finish() + { + UiQualifiedId *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *next; + NameId *name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiImport: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiImport) + + UiImport(NameId *fileName) + : fileName(fileName), importUri(0), importId(0) + { kind = K; } + + UiImport(UiQualifiedId *uri) + : fileName(0), importUri(uri), importId(0) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return importToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *fileName; + UiQualifiedId *importUri; + NameId *importId; + SourceLocation importToken; + SourceLocation fileNameToken; + SourceLocation versionToken; + SourceLocation asToken; + SourceLocation importIdToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiImportList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiImportList) + + UiImportList(UiImport *import) + : import(import), + next(this) + { kind = K; } + + UiImportList(UiImportList *previous, UiImport *import) + : import(import) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual SourceLocation firstSourceLocation() const + { + if (import) return import->firstSourceLocation(); + else return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + for (const UiImportList *it = this; it; it = it->next) + if (!it->next && it->import) + return it->import->lastSourceLocation(); + + return SourceLocation(); + } + + UiImportList *finish() + { + UiImportList *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + +// attributes + UiImport *import; + UiImportList *next; +}; + +class QML_PARSER_EXPORT UiObjectMember: public Node +{ +public: + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + + virtual UiObjectMember *uiObjectMemberCast(); +}; + +class QML_PARSER_EXPORT UiObjectMemberList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectMemberList) + + UiObjectMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiObjectMemberList(UiObjectMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + UiObjectMemberList *finish() + { + UiObjectMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiObjectMemberList *next; + UiObjectMember *member; +}; + +class QML_PARSER_EXPORT UiArrayMemberList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiArrayMemberList) + + UiArrayMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiArrayMemberList(UiArrayMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + UiArrayMemberList *finish() + { + UiArrayMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiArrayMemberList *next; + UiObjectMember *member; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT UiObjectInitializer: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectInitializer) + + UiObjectInitializer(UiObjectMemberList *members) + : members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceLocation lbraceToken; + UiObjectMemberList *members; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT UiParameterList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiParameterList) + + UiParameterList(NameId *t, NameId *n): + type (t), name (n), next (this) + { kind = K; } + + UiParameterList(UiParameterList *previous, NameId *t, NameId *n): + type (t), name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *) {} + + inline UiParameterList *finish () + { + UiParameterList *front = next; + next = 0; + return front; + } + +// attributes + NameId *type; + NameId *name; + UiParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiPublicMember) + + UiPublicMember(NameId *memberType, + NameId *name) + : type(Property), typeModifier(0), memberType(memberType), name(name), statement(0), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + UiPublicMember(NameId *memberType, + NameId *name, + Statement *statement) + : type(Property), typeModifier(0), memberType(memberType), name(name), statement(statement), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (defaultToken.isValid()) + return defaultToken; + else if (readonlyToken.isValid()) + return readonlyToken; + + return propertyToken; + } + + virtual SourceLocation lastSourceLocation() const + { + if (binding) + return binding->lastSourceLocation(); + + return semicolonToken; + } + + virtual void accept0(Visitor *visitor); + +// attributes + enum { Signal, Property } type; + NameId *typeModifier; + NameId *memberType; + NameId *name; + Statement *statement; // initialized with a JS expression + UiObjectMember *binding; // initialized with a QML object or array. + bool isDefaultMember; + bool isReadonlyMember; + UiParameterList *parameters; + SourceLocation defaultToken; + SourceLocation readonlyToken; + SourceLocation propertyToken; + SourceLocation typeModifierToken; + SourceLocation typeToken; + SourceLocation identifierToken; + SourceLocation colonToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectDefinition) + + UiObjectDefinition(UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedTypeNameId(qualifiedTypeNameId), initializer(initializer) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedTypeNameId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; +}; + +class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiSourceElement) + + UiSourceElement(Node *sourceElement) + : sourceElement(sourceElement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->firstSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->firstSourceLocation(); + + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->lastSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->lastSourceLocation(); + + return SourceLocation(); + } + + + virtual void accept0(Visitor *visitor); + +// attributes + Node *sourceElement; +}; + +class QML_PARSER_EXPORT UiObjectBinding: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectBinding) + + UiObjectBinding(UiQualifiedId *qualifiedId, + UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedId(qualifiedId), + qualifiedTypeNameId(qualifiedTypeNameId), + initializer(initializer), + hasOnToken(false) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (hasOnToken && qualifiedTypeNameId) + return qualifiedTypeNameId->identifierToken; + + return qualifiedId->identifierToken; + } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; + SourceLocation colonToken; + bool hasOnToken; +}; + +class QML_PARSER_EXPORT UiScriptBinding: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiScriptBinding) + + UiScriptBinding(UiQualifiedId *qualifiedId, + Statement *statement) + : qualifiedId(qualifiedId), + statement(statement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + Statement *statement; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT UiArrayBinding: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiArrayBinding) + + UiArrayBinding(UiQualifiedId *qualifiedId, + UiArrayMemberList *members) + : qualifiedId(qualifiedId), + members(members) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiArrayMemberList *members; + SourceLocation colonToken; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +} } // namespace AST + + + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/lib/parser/qmljsastfwd_p.h b/src/lib/parser/qmljsastfwd_p.h new file mode 100644 index 000000000..e8fb200b3 --- /dev/null +++ b/src/lib/parser/qmljsastfwd_p.h @@ -0,0 +1,180 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSAST_FWD_P_H +#define QMLJSAST_FWD_P_H + +#include "qmljsglobal_p.h" + +#include <QtCore/qglobal.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +class SourceLocation +{ +public: + SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) + : offset(offset), length(length), + startLine(line), startColumn(column) + { } + + bool isValid() const { return length != 0; } + + quint32 begin() const { return offset; } + quint32 end() const { return offset + length; } + +// attributes + // ### encode + quint32 offset; + quint32 length; + quint32 startLine; + quint32 startColumn; +}; + +class Visitor; +class Node; +class ExpressionNode; +class Statement; +class ThisExpression; +class IdentifierExpression; +class NullExpression; +class TrueLiteral; +class FalseLiteral; +class NumericLiteral; +class StringLiteral; +class RegExpLiteral; +class ArrayLiteral; +class ObjectLiteral; +class ElementList; +class Elision; +class PropertyNameAndValueList; +class PropertyName; +class IdentifierPropertyName; +class StringLiteralPropertyName; +class NumericLiteralPropertyName; +class ArrayMemberExpression; +class FieldMemberExpression; +class NewMemberExpression; +class NewExpression; +class CallExpression; +class ArgumentList; +class PostIncrementExpression; +class PostDecrementExpression; +class DeleteExpression; +class VoidExpression; +class TypeOfExpression; +class PreIncrementExpression; +class PreDecrementExpression; +class UnaryPlusExpression; +class UnaryMinusExpression; +class TildeExpression; +class NotExpression; +class BinaryExpression; +class ConditionalExpression; +class Expression; // ### rename +class Block; +class StatementList; +class VariableStatement; +class VariableDeclarationList; +class VariableDeclaration; +class EmptyStatement; +class ExpressionStatement; +class IfStatement; +class DoWhileStatement; +class WhileStatement; +class ForStatement; +class LocalForStatement; +class ForEachStatement; +class LocalForEachStatement; +class ContinueStatement; +class BreakStatement; +class ReturnStatement; +class WithStatement; +class SwitchStatement; +class CaseBlock; +class CaseClauses; +class CaseClause; +class DefaultClause; +class LabelledStatement; +class ThrowStatement; +class TryStatement; +class Catch; +class Finally; +class FunctionDeclaration; +class FunctionExpression; +class FormalParameterList; +class FunctionBody; +class Program; +class SourceElements; +class SourceElement; +class FunctionSourceElement; +class StatementSourceElement; +class DebuggerStatement; +class NestedExpression; + +// ui elements +class UiProgram; +class UiImportList; +class UiImport; +class UiPublicMember; +class UiObjectDefinition; +class UiObjectInitializer; +class UiObjectBinding; +class UiScriptBinding; +class UiSourceElement; +class UiArrayBinding; +class UiObjectMember; +class UiObjectMemberList; +class UiArrayMemberList; +class UiQualifiedId; +class UiFormalList; +class UiFormal; +class UiSignature; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/lib/parser/qmljsastvisitor.cpp b/src/lib/parser/qmljsastvisitor.cpp new file mode 100644 index 000000000..1aea4dd2a --- /dev/null +++ b/src/lib/parser/qmljsastvisitor.cpp @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "qmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +} } // namespace QmlJS::AST + +QT_QML_END_NAMESPACE diff --git a/src/lib/parser/qmljsastvisitor_p.h b/src/lib/parser/qmljsastvisitor_p.h new file mode 100644 index 000000000..07b072ff3 --- /dev/null +++ b/src/lib/parser/qmljsastvisitor_p.h @@ -0,0 +1,326 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSASTVISITOR_P_H +#define QMLJSASTVISITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsastfwd_p.h" +#include "qmljsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +class QML_PARSER_EXPORT Visitor +{ +public: + Visitor(); + virtual ~Visitor(); + + virtual bool preVisit(Node *) { return true; } + virtual void postVisit(Node *) {} + + // Ui + virtual bool visit(UiProgram *) { return true; } + virtual bool visit(UiImportList *) { return true; } + virtual bool visit(UiImport *) { return true; } + virtual bool visit(UiPublicMember *) { return true; } + virtual bool visit(UiSourceElement *) { return true; } + virtual bool visit(UiObjectDefinition *) { return true; } + virtual bool visit(UiObjectInitializer *) { return true; } + virtual bool visit(UiObjectBinding *) { return true; } + virtual bool visit(UiScriptBinding *) { return true; } + virtual bool visit(UiArrayBinding *) { return true; } + virtual bool visit(UiObjectMemberList *) { return true; } + virtual bool visit(UiArrayMemberList *) { return true; } + virtual bool visit(UiQualifiedId *) { return true; } + virtual bool visit(UiSignature *) { return true; } + virtual bool visit(UiFormalList *) { return true; } + virtual bool visit(UiFormal *) { return true; } + + virtual void endVisit(UiProgram *) {} + virtual void endVisit(UiImportList *) {} + virtual void endVisit(UiImport *) {} + virtual void endVisit(UiPublicMember *) {} + virtual void endVisit(UiSourceElement *) {} + virtual void endVisit(UiObjectDefinition *) {} + virtual void endVisit(UiObjectInitializer *) {} + virtual void endVisit(UiObjectBinding *) {} + virtual void endVisit(UiScriptBinding *) {} + virtual void endVisit(UiArrayBinding *) {} + virtual void endVisit(UiObjectMemberList *) {} + virtual void endVisit(UiArrayMemberList *) {} + virtual void endVisit(UiQualifiedId *) {} + virtual void endVisit(UiSignature *) {} + virtual void endVisit(UiFormalList *) {} + virtual void endVisit(UiFormal *) {} + + // QmlJS + virtual bool visit(ThisExpression *) { return true; } + virtual void endVisit(ThisExpression *) {} + + virtual bool visit(IdentifierExpression *) { return true; } + virtual void endVisit(IdentifierExpression *) {} + + virtual bool visit(NullExpression *) { return true; } + virtual void endVisit(NullExpression *) {} + + virtual bool visit(TrueLiteral *) { return true; } + virtual void endVisit(TrueLiteral *) {} + + virtual bool visit(FalseLiteral *) { return true; } + virtual void endVisit(FalseLiteral *) {} + + virtual bool visit(StringLiteral *) { return true; } + virtual void endVisit(StringLiteral *) {} + + virtual bool visit(NumericLiteral *) { return true; } + virtual void endVisit(NumericLiteral *) {} + + virtual bool visit(RegExpLiteral *) { return true; } + virtual void endVisit(RegExpLiteral *) {} + + virtual bool visit(ArrayLiteral *) { return true; } + virtual void endVisit(ArrayLiteral *) {} + + virtual bool visit(ObjectLiteral *) { return true; } + virtual void endVisit(ObjectLiteral *) {} + + virtual bool visit(ElementList *) { return true; } + virtual void endVisit(ElementList *) {} + + virtual bool visit(Elision *) { return true; } + virtual void endVisit(Elision *) {} + + virtual bool visit(PropertyNameAndValueList *) { return true; } + virtual void endVisit(PropertyNameAndValueList *) {} + + virtual bool visit(NestedExpression *) { return true; } + virtual void endVisit(NestedExpression *) {} + + virtual bool visit(IdentifierPropertyName *) { return true; } + virtual void endVisit(IdentifierPropertyName *) {} + + virtual bool visit(StringLiteralPropertyName *) { return true; } + virtual void endVisit(StringLiteralPropertyName *) {} + + virtual bool visit(NumericLiteralPropertyName *) { return true; } + virtual void endVisit(NumericLiteralPropertyName *) {} + + virtual bool visit(ArrayMemberExpression *) { return true; } + virtual void endVisit(ArrayMemberExpression *) {} + + virtual bool visit(FieldMemberExpression *) { return true; } + virtual void endVisit(FieldMemberExpression *) {} + + virtual bool visit(NewMemberExpression *) { return true; } + virtual void endVisit(NewMemberExpression *) {} + + virtual bool visit(NewExpression *) { return true; } + virtual void endVisit(NewExpression *) {} + + virtual bool visit(CallExpression *) { return true; } + virtual void endVisit(CallExpression *) {} + + virtual bool visit(ArgumentList *) { return true; } + virtual void endVisit(ArgumentList *) {} + + virtual bool visit(PostIncrementExpression *) { return true; } + virtual void endVisit(PostIncrementExpression *) {} + + virtual bool visit(PostDecrementExpression *) { return true; } + virtual void endVisit(PostDecrementExpression *) {} + + virtual bool visit(DeleteExpression *) { return true; } + virtual void endVisit(DeleteExpression *) {} + + virtual bool visit(VoidExpression *) { return true; } + virtual void endVisit(VoidExpression *) {} + + virtual bool visit(TypeOfExpression *) { return true; } + virtual void endVisit(TypeOfExpression *) {} + + virtual bool visit(PreIncrementExpression *) { return true; } + virtual void endVisit(PreIncrementExpression *) {} + + virtual bool visit(PreDecrementExpression *) { return true; } + virtual void endVisit(PreDecrementExpression *) {} + + virtual bool visit(UnaryPlusExpression *) { return true; } + virtual void endVisit(UnaryPlusExpression *) {} + + virtual bool visit(UnaryMinusExpression *) { return true; } + virtual void endVisit(UnaryMinusExpression *) {} + + virtual bool visit(TildeExpression *) { return true; } + virtual void endVisit(TildeExpression *) {} + + virtual bool visit(NotExpression *) { return true; } + virtual void endVisit(NotExpression *) {} + + virtual bool visit(BinaryExpression *) { return true; } + virtual void endVisit(BinaryExpression *) {} + + virtual bool visit(ConditionalExpression *) { return true; } + virtual void endVisit(ConditionalExpression *) {} + + virtual bool visit(Expression *) { return true; } + virtual void endVisit(Expression *) {} + + virtual bool visit(Block *) { return true; } + virtual void endVisit(Block *) {} + + virtual bool visit(StatementList *) { return true; } + virtual void endVisit(StatementList *) {} + + virtual bool visit(VariableStatement *) { return true; } + virtual void endVisit(VariableStatement *) {} + + virtual bool visit(VariableDeclarationList *) { return true; } + virtual void endVisit(VariableDeclarationList *) {} + + virtual bool visit(VariableDeclaration *) { return true; } + virtual void endVisit(VariableDeclaration *) {} + + virtual bool visit(EmptyStatement *) { return true; } + virtual void endVisit(EmptyStatement *) {} + + virtual bool visit(ExpressionStatement *) { return true; } + virtual void endVisit(ExpressionStatement *) {} + + virtual bool visit(IfStatement *) { return true; } + virtual void endVisit(IfStatement *) {} + + virtual bool visit(DoWhileStatement *) { return true; } + virtual void endVisit(DoWhileStatement *) {} + + virtual bool visit(WhileStatement *) { return true; } + virtual void endVisit(WhileStatement *) {} + + virtual bool visit(ForStatement *) { return true; } + virtual void endVisit(ForStatement *) {} + + virtual bool visit(LocalForStatement *) { return true; } + virtual void endVisit(LocalForStatement *) {} + + virtual bool visit(ForEachStatement *) { return true; } + virtual void endVisit(ForEachStatement *) {} + + virtual bool visit(LocalForEachStatement *) { return true; } + virtual void endVisit(LocalForEachStatement *) {} + + virtual bool visit(ContinueStatement *) { return true; } + virtual void endVisit(ContinueStatement *) {} + + virtual bool visit(BreakStatement *) { return true; } + virtual void endVisit(BreakStatement *) {} + + virtual bool visit(ReturnStatement *) { return true; } + virtual void endVisit(ReturnStatement *) {} + + virtual bool visit(WithStatement *) { return true; } + virtual void endVisit(WithStatement *) {} + + virtual bool visit(SwitchStatement *) { return true; } + virtual void endVisit(SwitchStatement *) {} + + virtual bool visit(CaseBlock *) { return true; } + virtual void endVisit(CaseBlock *) {} + + virtual bool visit(CaseClauses *) { return true; } + virtual void endVisit(CaseClauses *) {} + + virtual bool visit(CaseClause *) { return true; } + virtual void endVisit(CaseClause *) {} + + virtual bool visit(DefaultClause *) { return true; } + virtual void endVisit(DefaultClause *) {} + + virtual bool visit(LabelledStatement *) { return true; } + virtual void endVisit(LabelledStatement *) {} + + virtual bool visit(ThrowStatement *) { return true; } + virtual void endVisit(ThrowStatement *) {} + + virtual bool visit(TryStatement *) { return true; } + virtual void endVisit(TryStatement *) {} + + virtual bool visit(Catch *) { return true; } + virtual void endVisit(Catch *) {} + + virtual bool visit(Finally *) { return true; } + virtual void endVisit(Finally *) {} + + virtual bool visit(FunctionDeclaration *) { return true; } + virtual void endVisit(FunctionDeclaration *) {} + + virtual bool visit(FunctionExpression *) { return true; } + virtual void endVisit(FunctionExpression *) {} + + virtual bool visit(FormalParameterList *) { return true; } + virtual void endVisit(FormalParameterList *) {} + + virtual bool visit(FunctionBody *) { return true; } + virtual void endVisit(FunctionBody *) {} + + virtual bool visit(Program *) { return true; } + virtual void endVisit(Program *) {} + + virtual bool visit(SourceElements *) { return true; } + virtual void endVisit(SourceElements *) {} + + virtual bool visit(FunctionSourceElement *) { return true; } + virtual void endVisit(FunctionSourceElement *) {} + + virtual bool visit(StatementSourceElement *) { return true; } + virtual void endVisit(StatementSourceElement *) {} + + virtual bool visit(DebuggerStatement *) { return true; } + virtual void endVisit(DebuggerStatement *) {} +}; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif // QMLJSASTVISITOR_P_H diff --git a/src/lib/parser/qmljsengine_p.cpp b/src/lib/parser/qmljsengine_p.cpp new file mode 100644 index 000000000..e5a35ef19 --- /dev/null +++ b/src/lib/parser/qmljsengine_p.cpp @@ -0,0 +1,203 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "qmljsengine_p.h" + +#include "qmljsglobal_p.h" +#include "qmljsnodepool_p.h" + +#include <qnumeric.h> +#include <QHash> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +uint qHash(const QmlJS::NameId &id) +{ return qHash(id.asString()); } + +QString numberToString(double value) +{ return QString::number(value); } + +int Ecma::RegExp::flagFromChar(const QChar &ch) +{ + static QHash<QChar, int> flagsHash; + if (flagsHash.isEmpty()) { + flagsHash[QLatin1Char('g')] = Global; + flagsHash[QLatin1Char('i')] = IgnoreCase; + flagsHash[QLatin1Char('m')] = Multiline; + } + QHash<QChar, int>::const_iterator it; + it = flagsHash.constFind(ch); + if (it == flagsHash.constEnd()) + return 0; + return it.value(); +} + +QString Ecma::RegExp::flagsToString(int flags) +{ + QString result; + if (flags & Global) + result += QLatin1Char('g'); + if (flags & IgnoreCase) + result += QLatin1Char('i'); + if (flags & Multiline) + result += QLatin1Char('m'); + return result; +} + +NodePool::NodePool(const QString &fileName, Engine *engine) + : m_fileName(fileName), m_engine(engine) +{ + m_engine->setNodePool(this); +} + +NodePool::~NodePool() +{ +} + +Code *NodePool::createCompiledCode(AST::Node *, CompilationUnit &) +{ + Q_ASSERT(0); + return 0; +} + +static int toDigit(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'a') && (c <= 'z')) + return 10 + c - 'a'; + else if ((c >= 'A') && (c <= 'Z')) + return 10 + c - 'A'; + return -1; +} + +double integerFromString(const char *buf, int size, int radix) +{ + if (size == 0) + return qSNaN(); + + double sign = 1.0; + int i = 0; + if (buf[0] == '+') { + ++i; + } else if (buf[0] == '-') { + sign = -1.0; + ++i; + } + + if (((size-i) >= 2) && (buf[i] == '0')) { + if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) + && (radix < 34)) { + if ((radix != 0) && (radix != 16)) + return 0; + radix = 16; + i += 2; + } else { + if (radix == 0) { + radix = 8; + ++i; + } + } + } else if (radix == 0) { + radix = 10; + } + + int j = i; + for ( ; i < size; ++i) { + int d = toDigit(buf[i]); + if ((d == -1) || (d >= radix)) + break; + } + double result; + if (j == i) { + if (!qstrcmp(buf, "Infinity")) + result = qInf(); + else + result = qSNaN(); + } else { + result = 0; + double multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +double integerFromString(const QString &str, int radix) +{ + QByteArray ba = str.trimmed().toLatin1(); + return integerFromString(ba.constData(), ba.size(), radix); +} + + +Engine::Engine() + : _lexer(0), _nodePool(0) +{ } + +Engine::~Engine() +{ } + +QSet<NameId> Engine::literals() const +{ return _literals; } + +void Engine::addComment(int pos, int len, int line, int col) +{ if (len > 0) _comments.append(QmlJS::AST::SourceLocation(pos, len, line, col)); } + +QList<QmlJS::AST::SourceLocation> Engine::comments() const +{ return _comments; } + +NameId *Engine::intern(const QChar *u, int s) +{ return const_cast<NameId *>(&*_literals.insert(NameId(u, s))); } + +QString Engine::toString(NameId *id) +{ return id->asString(); } + +Lexer *Engine::lexer() const +{ return _lexer; } + +void Engine::setLexer(Lexer *lexer) +{ _lexer = lexer; } + +NodePool *Engine::nodePool() const +{ return _nodePool; } + +void Engine::setNodePool(NodePool *nodePool) +{ _nodePool = nodePool; } + + + +} // end of namespace QmlJS + +QT_QML_END_NAMESPACE diff --git a/src/lib/parser/qmljsengine_p.h b/src/lib/parser/qmljsengine_p.h new file mode 100644 index 000000000..1f5ee8877 --- /dev/null +++ b/src/lib/parser/qmljsengine_p.h @@ -0,0 +1,158 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSENGINE_P_H +#define QMLJSENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsglobal_p.h" +#include "qmljsastfwd_p.h" + +#include <QString> +#include <QSet> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { +class QML_PARSER_EXPORT NameId +{ + QString _text; + +public: + NameId(const QChar *u, int s) + : _text(u, s) + { } + + const QString asString() const + { return _text; } + + bool operator == (const NameId &other) const + { return _text == other._text; } + + bool operator != (const NameId &other) const + { return _text != other._text; } + + bool operator < (const NameId &other) const + { return _text < other._text; } +}; + +uint qHash(const QmlJS::NameId &id); + +} // end of namespace QmlJS + +namespace QmlJS { + +class Lexer; +class NodePool; + +namespace Ecma { + +class QML_PARSER_EXPORT RegExp +{ +public: + enum RegExpFlag { + Global = 0x01, + IgnoreCase = 0x02, + Multiline = 0x04 + }; + +public: + static int flagFromChar(const QChar &); + static QString flagsToString(int flags); +}; + +} // end of namespace Ecma + +class QML_PARSER_EXPORT DiagnosticMessage +{ +public: + enum Kind { Warning, Error }; + + DiagnosticMessage() + : kind(Error) {} + + DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message) + : kind(kind), loc(loc), message(message) {} + + bool isWarning() const + { return kind == Warning; } + + bool isError() const + { return kind == Error; } + + Kind kind; + AST::SourceLocation loc; + QString message; +}; + +class QML_PARSER_EXPORT Engine +{ + Lexer *_lexer; + NodePool *_nodePool; + QSet<NameId> _literals; + QList<QmlJS::AST::SourceLocation> _comments; + +public: + Engine(); + ~Engine(); + + QSet<NameId> literals() const; + + void addComment(int pos, int len, int line, int col); + QList<QmlJS::AST::SourceLocation> comments() const; + + NameId *intern(const QChar *u, int s); + + static QString toString(NameId *id); + + Lexer *lexer() const; + void setLexer(Lexer *lexer); + + NodePool *nodePool() const; + void setNodePool(NodePool *nodePool); +}; + +} // end of namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif // QMLJSENGINE_P_H diff --git a/src/lib/parser/qmljsglobal_p.h b/src/lib/parser/qmljsglobal_p.h new file mode 100644 index 000000000..b492a1f5e --- /dev/null +++ b/src/lib/parser/qmljsglobal_p.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSGLOBAL_P_H +#define QMLJSGLOBAL_P_H + +#include <QtCore/qglobal.h> + +#ifdef QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE + +# ifdef QMLJS_BUILD_DIR +# define QML_PARSER_EXPORT Q_DECL_EXPORT +# elif QML_BUILD_STATIC_LIB +# define QML_PARSER_EXPORT +# else +# define QML_PARSER_EXPORT Q_DECL_IMPORT +# endif // QMLJS_BUILD_DIR + +#else // !QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE QT_END_NAMESPACE +# define QML_PARSER_EXPORT Q_AUTOTEST_EXPORT +#endif // QT_CREATOR + +#endif // QMLJSGLOBAL_P_H diff --git a/src/lib/parser/qmljsgrammar.cpp b/src/lib/parser/qmljsgrammar.cpp new file mode 100644 index 000000000..9a628c3c9 --- /dev/null +++ b/src/lib/parser/qmljsgrammar.cpp @@ -0,0 +1,1007 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file was generated by qlalr - DO NOT EDIT! +#include "qmljsgrammar_p.h" + +QT_BEGIN_NAMESPACE + +const char *const QmlJSGrammar::spell [] = { + "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ";", "continue", + "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", + "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", + "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", + "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", + "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", + ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", + "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", + "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", "public", + "import", "as", "on", 0, 0, 0, 0, 0, 0, 0, + 0}; + +const short QmlJSGrammar::lhs [] = { + 101, 101, 101, 101, 101, 101, 102, 108, 108, 111, + 111, 113, 112, 112, 112, 112, 112, 112, 112, 112, + 115, 110, 109, 118, 118, 119, 119, 120, 120, 117, + 106, 106, 106, 106, 122, 122, 122, 122, 106, 127, + 127, 127, 128, 128, 129, 129, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 116, 116, 116, 116, 116, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 121, 134, 134, 134, + 134, 133, 133, 136, 136, 138, 138, 138, 138, 138, + 138, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 140, 140, 114, 114, 114, 114, 114, 143, + 143, 144, 144, 144, 144, 142, 142, 145, 145, 146, + 146, 147, 147, 147, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 149, 149, 149, 149, 150, 150, + 150, 151, 151, 151, 151, 152, 152, 152, 152, 152, + 152, 152, 153, 153, 153, 153, 153, 153, 154, 154, + 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, + 157, 157, 158, 158, 159, 159, 160, 160, 161, 161, + 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, + 167, 167, 137, 137, 168, 168, 169, 169, 169, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 104, 104, + 170, 170, 171, 171, 172, 172, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 123, 184, 184, 183, 183, 131, 131, 185, 185, + 186, 186, 188, 188, 187, 189, 192, 190, 190, 193, + 191, 191, 124, 125, 125, 126, 126, 173, 173, 173, + 173, 173, 173, 173, 174, 174, 174, 174, 175, 175, + 175, 175, 176, 176, 177, 179, 194, 194, 197, 197, + 195, 195, 198, 196, 178, 178, 178, 180, 180, 181, + 181, 181, 199, 200, 182, 182, 130, 141, 204, 204, + 201, 201, 202, 202, 205, 107, 206, 206, 105, 105, + 203, 203, 135, 135, 207}; + +const short QmlJSGrammar::rhs [] = { + 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 2, 1, 2, 2, 3, 3, 5, 5, 4, 4, + 2, 0, 1, 1, 2, 1, 3, 2, 3, 2, + 1, 5, 4, 4, 1, 1, 1, 1, 3, 1, + 1, 1, 0, 1, 2, 4, 6, 6, 3, 3, + 7, 7, 4, 4, 5, 5, 5, 6, 6, 10, + 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 3, 4, 5, 3, 4, 3, 1, 1, 2, 3, + 4, 1, 2, 3, 5, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4, 3, 5, 1, + 2, 4, 4, 4, 3, 0, 1, 1, 3, 1, + 1, 1, 2, 2, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 3, 3, 3, 1, 3, + 3, 1, 3, 3, 3, 1, 3, 3, 3, 3, + 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, + 3, 3, 3, 1, 3, 3, 3, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 5, + 1, 5, 1, 3, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 0, 1, 1, 3, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 2, 0, 1, 3, 3, 1, 1, + 1, 3, 1, 3, 2, 2, 2, 0, 1, 2, + 0, 1, 1, 2, 2, 7, 5, 7, 7, 5, + 9, 10, 7, 8, 2, 2, 3, 3, 2, 2, + 3, 3, 3, 3, 5, 5, 3, 5, 1, 2, + 0, 1, 4, 3, 3, 3, 3, 3, 3, 3, + 3, 4, 5, 2, 2, 2, 8, 8, 1, 3, + 0, 1, 0, 1, 1, 1, 1, 2, 1, 1, + 0, 1, 0, 1, 2}; + +const short QmlJSGrammar::action_default [] = { + 0, 0, 0, 0, 0, 0, 22, 0, 172, 239, + 203, 211, 207, 151, 223, 199, 3, 136, 70, 152, + 215, 219, 140, 169, 150, 155, 135, 189, 176, 0, + 77, 78, 73, 341, 64, 343, 0, 0, 0, 0, + 75, 0, 0, 71, 74, 68, 0, 0, 65, 67, + 66, 76, 69, 0, 72, 0, 0, 165, 0, 0, + 152, 171, 154, 153, 0, 0, 0, 167, 168, 166, + 170, 0, 200, 0, 0, 0, 0, 190, 0, 0, + 0, 0, 0, 0, 180, 0, 0, 0, 174, 175, + 173, 178, 182, 181, 179, 177, 192, 191, 193, 0, + 208, 0, 204, 0, 0, 146, 133, 145, 134, 102, + 103, 104, 129, 105, 130, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 131, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 132, + 0, 0, 144, 240, 147, 0, 148, 0, 149, 143, + 0, 236, 229, 227, 234, 235, 233, 232, 238, 231, + 230, 228, 237, 224, 0, 212, 0, 0, 216, 0, + 0, 220, 0, 0, 146, 138, 0, 137, 0, 142, + 156, 0, 342, 331, 332, 0, 329, 0, 330, 0, + 333, 247, 254, 253, 261, 249, 0, 250, 334, 0, + 340, 251, 252, 257, 255, 337, 335, 339, 258, 0, + 269, 0, 0, 0, 0, 341, 64, 0, 343, 65, + 241, 283, 66, 0, 0, 0, 270, 0, 0, 259, + 260, 0, 248, 256, 284, 285, 328, 338, 0, 299, + 300, 301, 302, 0, 295, 296, 297, 298, 325, 326, + 0, 0, 0, 0, 0, 288, 289, 245, 243, 205, + 213, 209, 225, 201, 246, 0, 152, 217, 221, 194, + 183, 0, 0, 202, 0, 0, 0, 0, 195, 0, + 0, 0, 0, 0, 187, 185, 188, 186, 184, 197, + 196, 198, 0, 210, 0, 206, 0, 244, 152, 0, + 226, 241, 242, 0, 241, 0, 0, 291, 0, 0, + 0, 293, 0, 214, 0, 0, 218, 0, 0, 222, + 281, 0, 273, 282, 276, 0, 280, 0, 241, 274, + 0, 241, 0, 0, 292, 0, 0, 0, 294, 342, + 331, 0, 0, 333, 0, 327, 0, 317, 0, 0, + 0, 287, 0, 286, 0, 344, 0, 101, 263, 266, + 0, 102, 269, 105, 130, 107, 108, 73, 112, 113, + 64, 114, 117, 71, 74, 65, 241, 66, 76, 120, + 69, 122, 72, 124, 125, 270, 127, 128, 132, 0, + 94, 0, 0, 96, 100, 98, 85, 97, 99, 0, + 95, 84, 264, 262, 140, 141, 146, 0, 139, 0, + 316, 0, 303, 304, 0, 315, 0, 0, 0, 306, + 311, 309, 312, 0, 0, 310, 311, 0, 307, 0, + 308, 265, 314, 0, 265, 313, 0, 318, 319, 0, + 265, 320, 321, 0, 0, 322, 0, 0, 0, 323, + 324, 158, 157, 0, 0, 0, 290, 0, 0, 0, + 305, 278, 271, 0, 279, 275, 0, 277, 267, 0, + 268, 272, 88, 0, 0, 92, 79, 0, 81, 90, + 0, 82, 91, 93, 83, 89, 80, 0, 86, 162, + 160, 164, 161, 159, 163, 6, 336, 4, 2, 62, + 87, 0, 0, 65, 67, 66, 31, 5, 0, 63, + 0, 42, 41, 40, 0, 0, 55, 0, 56, 35, + 36, 37, 38, 59, 0, 42, 0, 0, 0, 0, + 0, 51, 0, 52, 0, 0, 26, 0, 0, 60, + 27, 0, 30, 28, 24, 0, 29, 25, 0, 53, + 0, 54, 140, 0, 57, 61, 0, 0, 0, 0, + 58, 0, 49, 43, 50, 44, 0, 0, 0, 0, + 46, 0, 47, 48, 45, 0, 0, 140, 265, 0, + 0, 39, 102, 269, 105, 130, 107, 108, 73, 112, + 113, 64, 114, 117, 71, 74, 65, 241, 66, 76, + 120, 69, 122, 72, 124, 125, 270, 127, 128, 132, + 0, 32, 33, 0, 34, 8, 0, 10, 0, 9, + 0, 1, 21, 12, 0, 13, 0, 14, 0, 19, + 20, 0, 15, 16, 0, 17, 18, 11, 23, 7, + 345}; + +const short QmlJSGrammar::goto_default [] = { + 7, 621, 207, 196, 205, 507, 495, 620, 639, 615, + 619, 617, 622, 22, 618, 18, 506, 545, 535, 542, + 537, 523, 191, 195, 197, 201, 526, 566, 565, 200, + 232, 26, 474, 473, 356, 355, 9, 354, 357, 107, + 17, 145, 24, 13, 144, 19, 25, 57, 23, 8, + 28, 27, 269, 15, 263, 10, 259, 12, 261, 11, + 260, 20, 267, 21, 268, 14, 262, 258, 299, 411, + 264, 265, 202, 193, 192, 204, 233, 203, 208, 229, + 230, 194, 360, 359, 231, 463, 462, 321, 322, 465, + 324, 464, 323, 419, 423, 426, 422, 421, 441, 442, + 185, 199, 181, 184, 198, 206, 0}; + +const short QmlJSGrammar::action_index [] = { + 350, 1271, 2492, 2492, 2395, 999, 52, 93, 137, -101, + 104, 72, 66, 177, -101, 285, 80, -101, -101, 641, + 71, 130, 167, 178, -101, -101, -101, 431, 321, 1271, + -101, -101, -101, 393, -101, 2201, 2007, 1271, 1271, 1271, + -101, 811, 1271, -101, -101, -101, 1271, 1271, -101, -101, + -101, -101, -101, 1271, -101, 1271, 1271, -101, 1271, 1271, + 87, 188, -101, -101, 1271, 1271, 1271, -101, -101, -101, + 179, 1271, 263, 1271, 1271, 1271, 1271, 456, 1271, 1271, + 1271, 1271, 1271, 1271, 321, 1271, 1271, 1271, 128, 114, + 120, 193, 181, 172, 321, 321, 446, 395, 405, 1271, + -8, 1271, 76, 2104, 1271, 1271, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + 110, 1271, -101, -101, 68, 38, -101, 1271, -101, -101, + 1271, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, 1271, 32, 1271, 1271, 85, 83, + 1271, -101, 2104, 1271, 1271, -101, 108, -101, 53, -101, + -101, 64, -101, 393, 89, 62, -101, 297, -101, 63, + 2492, -101, -101, -101, -101, -101, 154, -101, -101, 47, + -101, -101, -101, -101, -101, -101, 2492, -101, -101, 461, + -101, 470, 74, 2395, 59, 393, 92, 67, 2686, 152, + 1271, -101, 65, 43, 1271, 41, -101, 39, 34, -101, + -101, 393, -101, -101, -101, -101, -101, -101, 86, -101, + -101, -101, -101, 90, -101, -101, -101, -101, -101, -101, + -11, 50, 1271, 103, 82, -101, -101, 1455, -101, 84, + 44, 5, -101, 267, 70, 33, 575, 79, 69, 471, + 235, 393, 1271, 275, 1271, 1271, 1271, 1271, 305, 1271, + 1271, 1271, 1271, 1271, 229, 201, 225, 202, 321, 355, + 374, 380, 1271, 35, 1271, 81, 1271, -101, 641, 1271, + -101, 1271, 61, 1, 1271, 29, 2395, -101, 1271, 133, + 2395, -101, 1271, 73, 1271, 1271, 99, 97, 1271, -101, + 51, 153, 60, -101, -101, 1271, -101, 393, 1271, -101, + 56, 1271, -25, 2395, -101, 1271, 129, 2395, -101, -35, + 309, -56, -31, 2492, -39, -101, 2395, -101, 1271, 245, + 2395, -5, 2395, -101, 6, 0, -33, -101, -101, 2395, + -43, 543, 7, 488, 112, 1271, 2395, -1, -27, 453, + 8, -18, 630, 14, 16, -101, 1365, -101, 12, -19, + 3, 1271, 58, -30, 1271, -2, 1271, -29, -36, 1271, + -101, 2298, 18, -101, -101, -101, -101, -101, -101, 1271, + -101, -101, -101, -101, 223, -101, 1271, -10, -101, 2395, + -101, 95, -101, -101, 2395, -101, 1271, 107, 20, -101, + 40, -101, 46, 100, 1271, -101, 55, 57, -101, 28, + -101, 2395, -101, 118, 2395, -101, 161, -101, -101, 126, + 2395, 37, -101, -12, -4, -101, 393, -34, -6, -101, + -101, -101, -101, 1271, 98, 2395, -101, 1271, 116, 2395, + -101, 19, -101, 186, -101, -101, 1271, -101, -101, 303, + -101, -101, -101, 119, 1638, -101, -101, 1821, -101, -101, + 1914, -101, -101, -101, -101, -101, -101, 123, -101, -101, + -101, -101, -101, -101, -101, -101, 2492, -101, -101, -101, + 94, -26, 819, 158, -28, 4, -101, -101, 210, -101, + 203, -101, -101, -101, 393, 230, -101, 1545, -101, -101, + -101, -101, -101, -101, 234, 2, 393, 232, 17, 393, + 163, -101, 10, -101, 908, 125, -101, 13, 908, -101, + -101, 1090, -101, -101, -101, 1181, -101, -101, 214, -101, + 1545, -101, 262, 9, -101, -101, 180, 318, 30, 1545, + -101, 238, -101, 236, -101, 26, -32, 315, 183, 288, + -101, 77, -101, -101, -101, 1728, 908, 291, 2589, 2007, + -3, -101, 443, 25, 497, 88, 1271, 2395, 24, 11, + 384, 36, 27, 702, 48, 49, -101, 1365, -101, 54, + 31, 45, 1271, 58, 15, 1271, 42, 1271, 23, 22, + 122, -101, -101, 21, -101, -101, 730, -101, 254, -70, + 908, -101, -101, 138, 393, -101, 143, -101, 134, -101, + -101, 268, -101, -101, 124, -101, -101, -101, -101, -101, + -101, + + -107, 25, -75, 27, 30, 272, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -42, + -107, -107, -107, -107, -107, -107, -107, -107, -107, 95, + -107, -107, -107, 31, -107, -107, 1, 37, 91, 80, + -107, 89, 167, -107, -107, -107, 175, 181, -107, -107, + -107, -107, -107, 137, -107, 130, 129, -107, 144, 152, + -107, -107, -107, -107, 140, 133, 149, -107, -107, -107, + -107, 157, -107, 182, 179, 170, 70, -107, 66, 78, + 55, 94, 100, 114, -107, 120, 109, 104, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, 172, + -107, 128, -107, 122, 58, 34, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, 51, -107, -107, -107, -107, -107, 36, -107, -107, + 47, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, 154, -107, 158, -35, -107, -107, + 13, -107, 248, 42, 115, -107, -107, -107, -107, -107, + -107, -107, -107, 20, -107, -107, -107, 2, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, 61, -107, -107, 67, + -107, 64, -107, 76, -107, 43, -107, -107, 57, -107, + 60, -107, -107, -107, 85, 69, -107, -107, -107, -107, + -107, -5, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, 24, -107, -107, -107, -107, 148, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, 9, 186, -107, 196, 221, 220, 212, -107, 102, + 98, 96, 116, 118, -107, -107, -107, -107, -107, -107, + -107, -107, 230, -107, 199, -107, 193, -107, -107, 211, + -107, 132, -107, -107, 126, -107, 14, -107, 5, -107, + 10, -107, 208, -107, 189, 202, -107, -107, 209, -107, + -107, -107, -107, -107, -107, 200, -107, 103, 121, -107, + -107, 168, -107, 50, -107, 54, -107, 62, -107, -107, + 88, -107, -107, -21, -107, -107, 184, -107, 63, -107, + 65, -107, 74, -107, -107, -107, -107, -107, -107, 92, + -107, 35, -107, 45, -107, 171, 75, -107, -107, 56, + -107, -107, 166, -107, -107, -107, 90, -107, -107, -107, + -107, 39, -107, 32, 159, -107, 178, -107, -107, -3, + -107, -17, -107, -107, -107, -107, -107, -107, -107, 3, + -107, -107, -107, -107, -107, -107, 68, -107, -107, 79, + -107, -107, -107, -107, 16, -107, 11, -107, -107, -107, + -107, -107, -8, -107, 59, -107, -41, -107, -107, -107, + -107, 99, -107, -107, 160, -107, -107, -107, -107, -107, + 93, -7, -107, -107, 77, -107, 40, -107, 46, -107, + -107, -107, -107, 53, -107, 83, -107, 72, -107, 71, + -107, -107, -107, -107, -107, -107, 48, -107, -107, 81, + -107, -107, -107, -107, 38, -107, -107, 173, -107, -107, + 33, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, 86, -107, -107, -107, + -107, -107, 73, -107, -107, -107, -107, -107, -107, -107, + 22, -107, -107, -107, -10, -107, -107, 259, -107, -107, + -107, -107, -107, -107, -107, -107, -6, -15, -107, -2, + -107, -107, -107, -107, 101, -107, -107, -107, 106, -107, + -107, 290, -107, -107, -107, 294, -107, -107, -107, -107, + 318, -107, -107, -4, -107, -107, -19, -13, -107, 364, + -107, -107, -107, -26, -107, -107, -107, -11, -20, -12, + -107, -107, -107, -107, -107, 305, 278, -107, 17, 261, + 4, -107, 28, -107, 26, -107, 87, 19, -107, -107, + 23, -107, -107, 84, -107, -107, -107, 44, -107, -107, + -107, -107, 41, -107, 29, 125, -107, 110, -107, -107, + -107, -107, -107, 15, -107, -107, 12, -107, -107, 18, + 97, -107, -107, -107, 7, -107, -107, -107, -107, -107, + -107, 21, -107, -107, -107, -107, -107, -107, -107, -107, + -107}; + +const short QmlJSGrammar::action_info [] = { + 457, 340, 343, 440, 342, -126, -110, 453, 391, 257, + -121, 352, 403, 389, -129, 346, 345, 416, 348, -99, + 616, -118, 401, -100, 446, 399, 448, 440, 571, 440, + 541, -110, -129, 561, 568, 333, 466, 559, 556, 527, + 510, 529, 541, 346, 534, 424, 541, 257, 440, -126, + 408, 424, -121, 420, 541, -118, -100, 444, 457, 453, + 424, -99, 304, 348, 431, -123, 251, 416, 325, 141, + 457, 101, 414, 164, 440, 453, 147, 71, 296, 416, + 99, 312, 272, 430, 294, 272, 252, 164, 141, 306, + 170, 335, 292, 640, 301, 257, 190, 187, 149, 346, + 183, 312, 236, 348, 318, 71, 141, 0, 0, 172, + 427, 141, 0, 179, 294, 141, 141, 331, 141, 314, + 99, 292, 189, 315, 141, 434, 141, 477, 173, 62, + 538, 141, 443, 538, 0, 249, 248, 141, 573, 572, + 63, 141, 616, 256, 255, 101, 444, 242, 241, 249, + 248, 247, 246, 172, 58, 428, 413, 412, 455, 409, + 58, 327, 141, 254, 177, 59, 142, 418, 58, 141, + 532, 59, 173, 249, 248, 478, 459, 58, 611, 59, + 166, 539, 172, 488, 167, 636, 635, 525, 59, 337, + 64, 64, 103, 310, 469, 630, 629, 85, 0, 86, + 64, 173, 0, 174, 633, 632, 85, 0, 86, 511, + 87, 104, 511, 105, 328, 235, 234, 575, 85, 87, + 86, 550, 438, 437, 533, 531, 85, 85, 86, 86, + 0, 87, 511, 513, 631, 65, 65, 517, 172, 87, + 87, 66, 66, 541, 512, 65, 0, 470, 468, 172, + 85, 66, 86, 141, 85, 513, 86, 173, 513, 406, + 85, 511, 86, 87, 0, 511, 512, 87, 173, 512, + 406, 0, 0, 87, 563, 551, 549, 172, 513, 0, + 0, 73, 74, 0, 0, 274, 275, 0, 0, 512, + 0, 518, 516, 274, 275, -87, 173, 34, 174, 564, + 562, 626, 576, 73, 74, 350, 172, 513, 75, 76, + 0, 513, 276, 277, 0, 627, 625, 34, 512, 0, + 276, 277, 512, 0, -87, 173, 34, 174, 279, 280, + 75, 76, 34, 0, 48, 50, 49, 281, 34, 0, + 282, 0, 283, 0, 34, 624, 85, 34, 86, 0, + 0, 0, 0, 0, 48, 50, 49, 0, 0, 87, + 45, 0, 0, 48, 50, 49, 0, 0, 0, 48, + 50, 49, 0, 0, 0, 48, 50, 49, 279, 280, + 45, 48, 50, 49, 48, 50, 49, 281, 0, 45, + 282, 0, 283, 0, 0, 45, 0, 279, 280, 0, + 0, 45, 0, 279, 280, 0, 281, 45, 0, 282, + 45, 283, 281, 34, 0, 282, 0, 283, 78, 79, + -341, 0, 34, 0, 0, 0, 80, 81, 78, 79, + 82, 0, 83, 0, 0, 0, 80, 81, 0, 0, + 82, 0, 83, 6, 5, 4, 1, 3, 2, 0, + 48, 50, 49, 0, 78, 79, 0, 0, 0, 48, + 50, 49, 80, 81, 0, 0, 82, 0, 83, 78, + 79, 0, 34, 0, 0, 0, 45, 80, 81, 78, + 79, 82, 34, 83, 0, 45, 0, 80, 81, -341, + 34, 82, 0, 83, 279, 280, 0, 0, 0, 34, + 0, 0, 0, 281, 240, 239, 282, 0, 283, 48, + 50, 49, 0, 0, 0, 0, 0, 34, 0, 48, + 50, 49, 240, 239, 0, 0, 34, 48, 50, 49, + 0, 245, 244, 0, 0, 45, 48, 50, 49, 0, + 0, 0, 0, 0, 0, 45, 0, 0, 0, 245, + 244, 0, 0, 45, 48, 50, 49, 0, 245, 244, + 0, 0, 45, 48, 50, 49, 0, 0, 0, 0, + 0, 0, 34, 0, 0, 0, 0, 0, 151, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 152, 45, + 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 154, 0, 155, 240, 239, 308, 0, 0, 48, + 50, 49, 0, 0, 156, 0, 157, 62, 0, 0, + 0, 0, 0, 0, 158, 0, 0, 159, 63, 0, + 0, 0, 0, 160, 0, 45, 0, 0, 0, 161, + 0, 0, 30, 31, 151, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 152, 162, 0, 0, 153, 34, + 0, 0, 0, 35, 36, 0, 37, 154, 0, 155, + 0, 0, 0, 41, 0, 0, 0, 44, 0, 0, + 156, 0, 157, 62, 0, 0, 0, 0, 0, 0, + 158, 0, 0, 159, 63, 51, 48, 50, 49, 160, + 52, 0, 0, 0, 0, 161, 0, 0, 0, 0, + 0, 43, 54, 32, 30, 31, 0, 40, 0, 0, + 0, 162, 45, 0, 33, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 35, 36, 0, 37, 0, + 0, 0, 30, 31, 0, 41, 0, 0, 0, 44, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 51, 48, 50, + 49, 0, 52, 502, 0, 0, 0, 44, 0, 0, + 0, 0, 0, 43, 54, 32, 0, 0, 0, 40, + 0, 0, 0, 0, 45, 51, 48, 50, 49, 0, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 30, 31, 0, 0, 0, 0, 0, + 0, 30, 31, 33, 0, 0, 0, 0, 0, 0, + 34, 33, 0, 0, 35, 36, 0, 37, 34, 0, + 0, 0, 35, 36, 41, 37, 0, 0, 44, 0, + 0, 0, 502, 0, 0, 0, 44, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, + 0, 52, 0, 0, 51, 48, 50, 49, 0, 52, + 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, + 43, 54, 32, 45, 0, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 0, 0, 0, 0, 0, + 0, 502, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 501, + 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 215, 0, 0, 0, 0, 0, 0, 34, 0, + 0, 0, 35, 36, 0, 37, 0, 0, 0, 0, + 0, 0, 502, 0, 0, 0, 44, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 51, 503, 505, 504, 0, 52, + 0, 0, 0, 0, 226, 0, 0, 0, 0, 0, + 43, 54, 32, 210, 0, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 501, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 215, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 0, 0, 0, 502, 0, 0, 0, 44, 0, 0, + 0, 0, 0, 0, 0, 543, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 51, 503, 505, 504, 0, + 52, 0, 0, 0, 0, 226, 0, 0, 0, 0, + 0, 43, 54, 32, 210, 0, 0, 40, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 501, 0, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, + 0, 0, 0, 0, 502, 0, 0, 0, 44, 0, + 0, 0, 0, 0, 0, 0, 546, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 503, 505, 504, + 0, 52, 0, 0, 0, 0, 226, 0, 0, 0, + 0, 0, 43, 54, 32, 210, 0, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, + 0, 52, 0, 53, 0, 55, 0, 56, 0, 0, + 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, -119, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 271, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 217, 0, 0, 218, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 0, 0, 0, 0, 0, 221, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 483, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 0, 0, 0, 38, 0, + 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, + 0, 47, 0, 0, 486, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 48, 50, 49, 0, 52, 0, + 53, 0, 55, 0, 56, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 217, 0, + 0, 578, 579, 0, 37, 0, 0, 0, 38, 0, + 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, + 0, 47, 0, 0, 0, 0, 0, 0, 0, 221, + 0, 0, 0, 51, 48, 50, 49, 0, 52, 0, + 53, 0, 55, 0, 56, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 475, + 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 481, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, + 0, 52, 0, 53, 0, 55, 0, 56, 0, 0, + 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 483, 0, 0, 29, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 0, 0, 0, 34, 0, 0, 0, 35, 36, 0, + 37, 0, 0, 0, 38, 0, 39, 41, 42, 0, + 0, 44, 0, 0, 0, 46, 0, 47, 0, 0, + 484, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 48, 50, 49, 0, 52, 0, 53, 0, 55, 0, + 56, 0, 0, 0, 0, 43, 54, 32, 0, 0, + 0, 40, 0, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 0, 475, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, + 35, 36, 0, 37, 0, 0, 0, 38, 0, 39, + 41, 42, 0, 0, 44, 0, 0, 0, 46, 0, + 47, 0, 0, 476, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 48, 50, 49, 0, 52, 0, 53, + 0, 55, 0, 56, 0, 0, 0, 0, 43, 54, + 32, 0, 0, 0, 40, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 109, 110, + 111, 0, 0, 113, 115, 116, 0, 0, 117, 0, + 118, 0, 0, 0, 120, 121, 122, 0, 0, 0, + 0, 0, 0, 34, 123, 124, 125, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 126, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 129, 0, 0, 0, 0, 0, 0, + 48, 50, 49, 130, 131, 132, 0, 134, 135, 136, + 137, 138, 139, 0, 0, 127, 133, 119, 112, 114, + 128, 0, 0, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 0, 109, 110, 111, 0, 0, + 113, 115, 116, 0, 0, 117, 0, 118, 0, 0, + 0, 120, 121, 122, 0, 0, 0, 0, 0, 0, + 393, 123, 124, 125, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 126, 0, 0, 0, 394, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 129, 0, 0, 0, 0, 0, 398, 395, 397, 0, + 130, 131, 132, 0, 134, 135, 136, 137, 138, 139, + 0, 0, 127, 133, 119, 112, 114, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 109, 110, 111, 0, 0, 113, 115, 116, + 0, 0, 117, 0, 118, 0, 0, 0, 120, 121, + 122, 0, 0, 0, 0, 0, 0, 393, 123, 124, + 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 126, 0, 0, 0, 394, 0, 0, 0, 0, + 0, 0, 0, 396, 0, 0, 0, 129, 0, 0, + 0, 0, 0, 398, 395, 397, 0, 130, 131, 132, + 0, 134, 135, 136, 137, 138, 139, 0, 0, 127, + 133, 119, 112, 114, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, + 0, 0, 0, 0, 211, 0, 29, 30, 31, 213, + 0, 0, 0, 0, 0, 0, 214, 33, 0, 0, + 0, 0, 0, 0, 216, 217, 0, 0, 218, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 0, 0, 0, 220, 0, 221, 0, 0, 0, + 51, 219, 222, 49, 223, 52, 224, 53, 225, 55, + 226, 56, 227, 228, 0, 0, 43, 54, 32, 210, + 212, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, + 0, 211, 0, 29, 30, 31, 213, 0, 0, 0, + 0, 0, 0, 214, 215, 0, 0, 0, 0, 0, + 0, 216, 217, 0, 0, 218, 36, 0, 37, 0, + 0, 0, 38, 0, 39, 41, 42, 0, 0, 44, + 0, 0, 0, 46, 0, 47, 0, 0, 0, 0, + 0, 220, 0, 221, 0, 0, 0, 51, 219, 222, + 49, 223, 52, 224, 53, 225, 55, 226, 56, 227, + 228, 0, 0, 43, 54, 32, 210, 212, 0, 40, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 582, 110, 111, 0, 0, 584, 115, + 586, 30, 31, 587, 0, 118, 0, 0, 0, 120, + 589, 590, 0, 0, 0, 0, 0, 0, 591, 592, + 124, 125, 218, 36, 0, 37, 0, 0, 0, 38, + 0, 39, 593, 42, 0, 0, 595, 0, 0, 0, + 46, 0, 47, 0, 0, 0, 0, 0, 597, 0, + 221, 0, 0, 0, 599, 596, 598, 49, 600, 601, + 602, 53, 604, 605, 606, 607, 608, 609, 0, 0, + 594, 603, 588, 583, 585, 128, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 361, 110, 111, 0, 0, 363, 115, 365, 30, 31, + 366, 0, 118, 0, 0, 0, 120, 368, 369, 0, + 0, 0, 0, 0, 0, 370, 371, 124, 125, 218, + 36, 0, 37, 0, 0, 0, 38, 0, 39, 372, + 42, 0, 0, 374, 0, 0, 0, 46, 0, 47, + 0, -265, 0, 0, 0, 376, 0, 221, 0, 0, + 0, 378, 375, 377, 49, 379, 380, 381, 53, 383, + 384, 385, 386, 387, 388, 0, 0, 373, 382, 367, + 362, 364, 128, 40, 0, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 0, 0, + + 567, 169, 558, 570, 574, 515, 569, 557, 309, 548, + 461, 528, 311, 530, 417, 555, 307, 188, 415, 358, + 392, 250, 628, 612, 320, 623, 150, 253, 16, 637, + 496, 497, 498, 390, 614, 186, 634, 472, 182, 400, + 487, 243, 436, 238, 436, 176, 182, 302, 514, 171, + 238, 439, 334, 429, 439, 447, 454, 336, 339, 358, + 243, 140, 433, 302, 338, 237, 349, 351, 449, 482, + 146, 182, 148, 460, 485, 458, 353, 250, 250, 243, + 344, 410, 238, 163, 467, 456, 524, 143, 436, 425, + 237, 439, 445, 302, 402, 358, 461, 404, 0, 450, + 60, 358, 404, 186, 146, 92, 0, 0, 0, 407, + 500, 60, 0, 638, 500, 60, 84, 536, 320, 500, + 0, 98, 540, 60, 302, 60, 405, 490, 91, 302, + 0, 405, 60, 0, 180, 302, 60, 106, 489, 60, + 60, 60, 180, 60, 93, 60, 286, 60, 285, 60, + 94, 146, 284, 90, 60, 60, 178, 452, 89, 60, + 108, 60, 358, 60, 95, 60, 287, 471, 288, 88, + 60, 302, 451, 60, 60, 60, 452, 451, 60, 404, + 68, 432, 60, 102, 494, 60, 347, 67, 341, 60, + 330, 329, 61, 266, 60, 305, 69, 60, 270, 60, + 70, 303, 60, 60, 60, 480, 451, 0, 405, 479, + 72, 0, 60, 165, 491, 60, 60, 60, 180, 168, + 60, 97, 492, 60, 60, 452, 60, 60, 493, 100, + 96, 60, 0, 77, 60, 0, 270, 332, 298, 270, + 273, 60, 435, 270, 60, 298, 270, 298, 278, 270, + 270, 316, 270, 60, 298, 295, 298, 60, 270, 270, + 297, 270, 270, 106, 291, 60, 60, 326, 313, 317, + 270, 270, 290, 289, 552, 60, 319, 536, 300, 610, + 270, 519, 520, 521, 522, 500, 108, 175, 293, 0, + 0, 500, 508, 0, 0, 544, 0, 472, 613, 547, + 0, 499, 509, 500, 0, 0, 0, 500, 0, 0, + 508, 0, 0, 0, 508, 0, 0, 0, 577, 499, + 509, 0, 0, 499, 509, 580, 581, 519, 520, 521, + 522, 552, 0, 0, 0, 0, 0, 0, 553, 554, + 519, 520, 521, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 560, 519, 520, 521, 522, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0}; + +const short QmlJSGrammar::action_check [] = { + 36, 36, 33, 33, 60, 7, 7, 36, 8, 36, + 7, 16, 55, 7, 7, 7, 55, 36, 36, 7, + 90, 7, 55, 7, 36, 7, 60, 33, 60, 33, + 33, 7, 7, 29, 8, 60, 17, 7, 66, 37, + 66, 24, 33, 7, 34, 5, 33, 36, 33, 7, + 60, 5, 7, 33, 33, 7, 7, 20, 36, 36, + 5, 7, 61, 36, 7, 7, 77, 36, 17, 8, + 36, 79, 7, 2, 33, 36, 8, 1, 8, 36, + 48, 2, 1, 55, 79, 1, 36, 2, 8, 60, + 7, 31, 48, 0, 61, 36, 33, 8, 60, 7, + 36, 2, 55, 36, 7, 1, 8, -1, -1, 15, + 10, 8, -1, 60, 79, 8, 8, 61, 8, 50, + 48, 48, 60, 54, 8, 7, 8, 8, 34, 42, + 8, 8, 6, 8, -1, 61, 62, 8, 61, 62, + 53, 8, 90, 61, 62, 79, 20, 61, 62, 61, + 62, 61, 62, 15, 40, 55, 61, 62, 60, 7, + 40, 8, 8, 60, 56, 51, 56, 60, 40, 8, + 7, 51, 34, 61, 62, 56, 60, 40, 56, 51, + 50, 56, 15, 60, 54, 61, 62, 29, 51, 60, + 12, 12, 15, 60, 8, 61, 62, 25, -1, 27, + 12, 34, -1, 36, 61, 62, 25, -1, 27, 29, + 38, 34, 29, 36, 61, 61, 62, 7, 25, 38, + 27, 7, 61, 62, 61, 62, 25, 25, 27, 27, + -1, 38, 29, 75, 91, 57, 57, 7, 15, 38, + 38, 63, 63, 33, 86, 57, -1, 61, 62, 15, + 25, 63, 27, 8, 25, 75, 27, 34, 75, 36, + 25, 29, 27, 38, -1, 29, 86, 38, 34, 86, + 36, -1, -1, 38, 36, 61, 62, 15, 75, -1, + -1, 18, 19, -1, -1, 18, 19, -1, -1, 86, + -1, 61, 62, 18, 19, 33, 34, 29, 36, 61, + 62, 47, 92, 18, 19, 60, 15, 75, 45, 46, + -1, 75, 45, 46, -1, 61, 62, 29, 86, -1, + 45, 46, 86, -1, 33, 34, 29, 36, 23, 24, + 45, 46, 29, -1, 66, 67, 68, 32, 29, -1, + 35, -1, 37, -1, 29, 91, 25, 29, 27, -1, + -1, -1, -1, -1, 66, 67, 68, -1, -1, 38, + 92, -1, -1, 66, 67, 68, -1, -1, -1, 66, + 67, 68, -1, -1, -1, 66, 67, 68, 23, 24, + 92, 66, 67, 68, 66, 67, 68, 32, -1, 92, + 35, -1, 37, -1, -1, 92, -1, 23, 24, -1, + -1, 92, -1, 23, 24, -1, 32, 92, -1, 35, + 92, 37, 32, 29, -1, 35, -1, 37, 23, 24, + 36, -1, 29, -1, -1, -1, 31, 32, 23, 24, + 35, -1, 37, -1, -1, -1, 31, 32, -1, -1, + 35, -1, 37, 93, 94, 95, 96, 97, 98, -1, + 66, 67, 68, -1, 23, 24, -1, -1, -1, 66, + 67, 68, 31, 32, -1, -1, 35, -1, 37, 23, + 24, -1, 29, -1, -1, -1, 92, 31, 32, 23, + 24, 35, 29, 37, -1, 92, -1, 31, 32, 36, + 29, 35, -1, 37, 23, 24, -1, -1, -1, 29, + -1, -1, -1, 32, 61, 62, 35, -1, 37, 66, + 67, 68, -1, -1, -1, -1, -1, 29, -1, 66, + 67, 68, 61, 62, -1, -1, 29, 66, 67, 68, + -1, 61, 62, -1, -1, 92, 66, 67, 68, -1, + -1, -1, -1, -1, -1, 92, -1, -1, -1, 61, + 62, -1, -1, 92, 66, 67, 68, -1, 61, 62, + -1, -1, 92, 66, 67, 68, -1, -1, -1, -1, + -1, -1, 29, -1, -1, -1, -1, -1, 3, -1, + 92, -1, -1, -1, -1, -1, -1, -1, 13, 92, + -1, -1, 17, -1, -1, -1, -1, -1, -1, -1, + -1, 26, -1, 28, 61, 62, 31, -1, -1, 66, + 67, 68, -1, -1, 39, -1, 41, 42, -1, -1, + -1, -1, -1, -1, 49, -1, -1, 52, 53, -1, + -1, -1, -1, 58, -1, 92, -1, -1, -1, 64, + -1, -1, 12, 13, 3, -1, -1, -1, -1, -1, + -1, -1, 22, -1, 13, 80, -1, -1, 17, 29, + -1, -1, -1, 33, 34, -1, 36, 26, -1, 28, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + 39, -1, 41, 42, -1, -1, -1, -1, -1, -1, + 49, -1, -1, 52, 53, 65, 66, 67, 68, 58, + 70, -1, -1, -1, -1, 64, -1, -1, -1, -1, + -1, 81, 82, 83, 12, 13, -1, 87, -1, -1, + -1, 80, 92, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 12, 13, -1, 43, -1, -1, -1, 47, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, 65, 66, 67, + 68, -1, 70, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, -1, -1, -1, 92, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 12, 13, -1, -1, -1, -1, -1, + -1, 12, 13, 22, -1, -1, -1, -1, -1, -1, + 29, 22, -1, -1, 33, 34, -1, 36, 29, -1, + -1, -1, 33, 34, 43, 36, -1, -1, 47, -1, + -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, -1, 65, 66, 67, 68, -1, 70, + -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, + 81, 82, 83, 92, -1, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, + -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, 10, + -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, + -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, + 81, 82, 83, 84, -1, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, + -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, + -1, -1, 92, -1, -1, -1, -1, -1, -1, -1, + -1, 10, -1, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, + -1, -1, -1, -1, -1, -1, 55, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, -1, -1, -1, 75, -1, -1, -1, + -1, -1, 81, 82, 83, 84, -1, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, + -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, 7, -1, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + 75, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, 30, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, -1, -1, 61, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, 56, -1, -1, -1, -1, -1, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, 61, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, 8, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, 56, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, + -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, + 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, 87, -1, -1, -1, -1, 92, -1, -1, -1, + -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, 56, -1, -1, -1, -1, -1, -1, + -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, + -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, + 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, + 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, + -1, -1, -1, 29, 30, 31, 32, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 43, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 59, -1, -1, -1, -1, -1, -1, + 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, + 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, + 86, -1, -1, -1, -1, -1, 92, -1, -1, -1, + -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, + 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, + -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 59, -1, -1, -1, -1, -1, 65, 66, 67, -1, + 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, 86, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, + -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, + 22, -1, -1, -1, -1, -1, -1, 29, 30, 31, + 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, -1, -1, 55, -1, -1, -1, 59, -1, -1, + -1, -1, -1, 65, 66, 67, -1, 69, 70, 71, + -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, + 82, 83, 84, 85, 86, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + -1, -1, -1, -1, 9, -1, 11, 12, 13, 14, + -1, -1, -1, -1, -1, -1, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, 59, -1, 61, -1, -1, -1, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, + -1, 9, -1, 11, 12, 13, 14, -1, -1, -1, + -1, -1, -1, 21, 22, -1, -1, -1, -1, -1, + -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, -1, -1, 81, 82, 83, 84, 85, -1, 87, + -1, -1, -1, -1, 92, -1, -1, -1, -1, -1, + -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, + 11, 12, 13, 14, -1, 16, -1, -1, -1, 20, + 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, + 31, 32, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, 59, -1, + 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, -1, -1, + 81, 82, 83, 84, 85, 86, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + 4, 5, 6, -1, -1, 9, 10, 11, 12, 13, + 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, + -1, -1, -1, -1, -1, 29, 30, 31, 32, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, 55, -1, -1, -1, 59, -1, 61, -1, -1, + -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, + 84, 85, 86, 87, -1, -1, -1, -1, 92, -1, + -1, -1, -1, -1, -1, -1, -1, + + 26, 36, 15, 15, 15, 15, 26, 26, 3, 15, + 15, 26, 2, 15, 3, 19, 2, 15, 2, 2, + 37, 2, 15, 19, 15, 13, 68, 3, 3, 11, + 105, 4, 2, 36, 19, 15, 15, 36, 15, 36, + 3, 15, 3, 15, 3, 3, 15, 3, 26, 36, + 15, 22, 2, 94, 22, 15, 3, 3, 15, 2, + 15, 3, 3, 3, 2, 4, 3, 2, 22, 36, + 36, 15, 36, 2, 36, 3, 2, 2, 2, 15, + 101, 2, 15, 36, 36, 2, 13, 36, 3, 97, + 4, 22, 99, 3, 2, 2, 15, 13, -1, 22, + 45, 2, 13, 15, 36, 50, -1, -1, -1, 41, + 13, 45, -1, 16, 13, 45, 50, 16, 15, 13, + -1, 51, 16, 45, 3, 45, 42, 47, 50, 3, + -1, 42, 45, -1, 47, 3, 45, 15, 47, 45, + 45, 45, 47, 45, 50, 45, 50, 45, 50, 45, + 50, 36, 50, 49, 45, 45, 41, 47, 49, 45, + 38, 45, 2, 45, 50, 45, 50, 86, 50, 49, + 45, 3, 47, 45, 45, 45, 47, 47, 45, 13, + 47, 82, 45, 55, 47, 45, 2, 47, 100, 45, + 69, 88, 48, 45, 45, 69, 47, 45, 50, 45, + 48, 69, 45, 45, 45, 32, 47, -1, 42, 36, + 53, -1, 45, 59, 47, 45, 45, 45, 47, 61, + 45, 51, 47, 45, 45, 47, 45, 45, 47, 57, + 51, 45, -1, 51, 45, -1, 50, 69, 45, 50, + 54, 45, 82, 50, 45, 45, 50, 45, 52, 50, + 50, 62, 50, 45, 45, 56, 45, 45, 50, 50, + 67, 50, 50, 15, 52, 45, 45, 67, 60, 67, + 50, 50, 52, 52, 13, 45, 67, 16, 67, 18, + 50, 22, 23, 24, 25, 13, 38, 39, 58, -1, + -1, 13, 20, -1, -1, 5, -1, 36, 20, 5, + -1, 29, 30, 13, -1, -1, -1, 13, -1, -1, + 20, -1, -1, -1, 20, -1, -1, -1, 13, 29, + 30, -1, -1, 29, 30, 20, 21, 22, 23, 24, + 25, 13, -1, -1, -1, -1, -1, -1, 20, 21, + 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}; + +QT_END_NAMESPACE diff --git a/src/lib/parser/qmljsgrammar_p.h b/src/lib/parser/qmljsgrammar_p.h new file mode 100644 index 000000000..25e266fd5 --- /dev/null +++ b/src/lib/parser/qmljsgrammar_p.h @@ -0,0 +1,201 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QMLJSGRAMMAR_P_H +#define QMLJSGRAMMAR_P_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QmlJSGrammar +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + REDUCE_HERE = 100, + SHIFT_THERE = 99, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_AS = 91, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_COLON = 7, + T_COMMA = 8, + T_COMMENT = 88, + T_CONST = 84, + T_CONTINUE = 9, + T_DEBUGGER = 85, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELSE = 16, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_FALSE = 83, + T_FEED_JS_EXPRESSION = 96, + T_FEED_JS_PROGRAM = 98, + T_FEED_JS_SOURCE_ELEMENT = 97, + T_FEED_JS_STATEMENT = 95, + T_FEED_UI_OBJECT_MEMBER = 94, + T_FEED_UI_PROGRAM = 93, + T_FINALLY = 20, + T_FOR = 21, + T_FUNCTION = 22, + T_GE = 23, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IMPORT = 90, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_MULTILINE_STRING_LITERAL = 87, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NULL = 81, + T_NUMERIC_LITERAL = 47, + T_ON = 92, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_PROPERTY = 66, + T_PUBLIC = 89, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_READONLY = 68, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 86, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_SIGNAL = 67, + T_STAR = 63, + T_STAR_EQ = 64, + T_STRING_LITERAL = 65, + T_SWITCH = 69, + T_THIS = 70, + T_THROW = 71, + T_TILDE = 72, + T_TRUE = 82, + T_TRY = 73, + T_TYPEOF = 74, + T_VAR = 75, + T_VOID = 76, + T_WHILE = 77, + T_WITH = 78, + T_XOR = 79, + T_XOR_EQ = 80, + + ACCEPT_STATE = 640, + RULE_COUNT = 345, + STATE_COUNT = 641, + TERMINAL_COUNT = 101, + NON_TERMINAL_COUNT = 107, + + GOTO_INDEX_OFFSET = 641, + GOTO_INFO_OFFSET = 2787, + GOTO_CHECK_OFFSET = 2787 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +QT_END_NAMESPACE +#endif // QMLJSGRAMMAR_P_H + diff --git a/src/lib/parser/qmljslexer.cpp b/src/lib/parser/qmljslexer.cpp new file mode 100644 index 000000000..a6b17c7a4 --- /dev/null +++ b/src/lib/parser/qmljslexer.cpp @@ -0,0 +1,1249 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "qmljslexer_p.h" + +#include "qmljsglobal_p.h" +#include "qmljsengine_p.h" +#include "qmljsgrammar_p.h" + +#include <QtCore/qcoreapplication.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +QT_BEGIN_NAMESPACE +Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); +QT_END_NAMESPACE + +QT_QML_BEGIN_NAMESPACE + +#define shiftWindowsLineBreak() \ + do { \ + if (((current == '\r') && (next1 == '\n')) \ + || ((current == '\n') && (next1 == '\r'))) { \ + shift(1); \ + } \ + } \ + while (0) + +namespace QmlJS { +extern double integerFromString(const char *buf, int size, int radix); +} + +using namespace QmlJS; + +Lexer::Lexer(Engine *eng, bool tokenizeComments) + : driver(eng), + yylineno(0), + done(false), + size8(128), size16(128), + pos8(0), pos16(0), + terminator(false), + restrKeyword(false), + delimited(false), + stackToken(-1), + state(Start), + pos(0), + code(0), length(0), + yycolumn(0), + startpos(0), + startlineno(0), startcolumn(0), + bol(true), + current(0), next1(0), next2(0), next3(0), + err(NoError), + wantRx(false), + check_reserved(true), + parenthesesState(IgnoreParentheses), + parenthesesCount(0), + prohibitAutomaticSemicolon(false), + tokenizeComments(tokenizeComments) +{ + if (driver) driver->setLexer(this); + // allocate space for read buffers + buffer8 = new char[size8]; + buffer16 = new QChar[size16]; + pattern = 0; + flags = 0; + +} + +Lexer::~Lexer() +{ + delete [] buffer8; + delete [] buffer16; +} + +void Lexer::setCode(const QString &c, int lineno) +{ + errmsg.clear(); + yylineno = lineno; + yycolumn = 1; + restrKeyword = false; + delimited = false; + stackToken = -1; + pos = 0; + code = c.unicode(); + length = c.length(); + bol = true; + + // read first characters + current = (length > 0) ? code[0].unicode() : 0; + next1 = (length > 1) ? code[1].unicode() : 0; + next2 = (length > 2) ? code[2].unicode() : 0; + next3 = (length > 3) ? code[3].unicode() : 0; +} + +void Lexer::shift(uint p) +{ + while (p--) { + ++pos; + ++yycolumn; + current = next1; + next1 = next2; + next2 = next3; + next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0; + } +} + +void Lexer::setDone(State s) +{ + state = s; + done = true; +} + +int Lexer::findReservedWord(const QChar *c, int size) const +{ + switch (size) { + case 2: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')) + return QmlJSGrammar::T_DO; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f')) + return QmlJSGrammar::T_IF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')) + return QmlJSGrammar::T_IN; + else if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('s')) + return QmlJSGrammar::T_AS; + else if (c[0] == QLatin1Char('o') && c[1] == QLatin1Char('n')) + return QmlJSGrammar::T_ON; + } break; + + case 3: { + if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r')) + return QmlJSGrammar::T_FOR; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w')) + return QmlJSGrammar::T_NEW; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y')) + return QmlJSGrammar::T_TRY; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r')) + return QmlJSGrammar::T_VAR; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 4: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_CASE; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_ELSE; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s')) + return QmlJSGrammar::T_THIS; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d')) + return QmlJSGrammar::T_VOID; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h')) + return QmlJSGrammar::T_WITH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_TRUE; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l')) + return QmlJSGrammar::T_NULL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 5: { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('k')) + return QmlJSGrammar::T_BREAK; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h')) + return QmlJSGrammar::T_CATCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w')) + return QmlJSGrammar::T_THROW; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e')) + return QmlJSGrammar::T_WHILE; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('t')) + return QmlJSGrammar::T_CONST; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('e')) + return QmlJSGrammar::T_FALSE; + else if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r') + && c[4] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 6: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e')) + return QmlJSGrammar::T_DELETE; + else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n')) + return QmlJSGrammar::T_RETURN; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h')) + return QmlJSGrammar::T_SWITCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f')) + return QmlJSGrammar::T_TYPEOF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QmlJSGrammar::T_IMPORT; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('g') && c[3] == QLatin1Char('n') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('l')) + return QmlJSGrammar::T_SIGNAL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QmlJSGrammar::T_PUBLIC; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i') + && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 7: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('t')) + return QmlJSGrammar::T_DEFAULT; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('y')) + return QmlJSGrammar::T_FINALLY; + else if (check_reserved) { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('n')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d') + && c[6] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t') + && c[6] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 8: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e')) + return QmlJSGrammar::T_CONTINUE; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')) + return QmlJSGrammar::T_FUNCTION; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r')) + return QmlJSGrammar::T_DEBUGGER; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('p') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('r') + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('y')) + return QmlJSGrammar::T_PROPERTY; + else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('d') + && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('y')) + return QmlJSGrammar::T_READONLY; + else if (check_reserved) { + if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 9: { + if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f') + && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c') + && c[8] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n') + && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c') + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('d')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 10: { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f')) + return QmlJSGrammar::T_INSTANCEOF; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 12: { + if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z') + && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + } // switch + + return -1; +} + +int Lexer::lex() +{ + int token = 0; + state = Start; + ushort stringType = 0; // either single or double quotes + bool multiLineString = false; + pos8 = pos16 = 0; + done = false; + terminator = false; + + // did we push a token on the stack previously ? + // (after an automatic semicolon insertion) + if (stackToken >= 0) { + setDone(Other); + token = stackToken; + stackToken = -1; + } + + bool identifierWithEscapedUnicode = false; + + while (!done) { + switch (state) { + case Start: + if (isWhiteSpace()) { + // do nothing + } else if (current == '/' && next1 == '/') { + recordStartPos(); + shift(1); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + recordStartPos(); + shift(1); + state = InMultiLineComment; + } else if (current == 0) { + syncProhibitAutomaticSemicolon(); + if (!terminator && !delimited && !prohibitAutomaticSemicolon) { + // automatic semicolon insertion if program incomplete + token = QmlJSGrammar::T_SEMICOLON; + stackToken = 0; + setDone(Other); + } else { + setDone(Eof); + } + } else if (isLineTerminator()) { + if (restrKeyword) { + // automatic semicolon insertion + recordStartPos(); + token = QmlJSGrammar::T_SEMICOLON; + setDone(Other); + } else { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + terminator = true; + syncProhibitAutomaticSemicolon(); + } + } else if (current == '"' || current == '\'') { + recordStartPos(); + state = InString; + multiLineString = false; + stringType = current; + } else if (current == '\\' && next1 == 'u') { + identifierWithEscapedUnicode = true; + recordStartPos(); + + shift(2); // skip the unicode escape prefix `\u' + + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InIdentifier; + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QCoreApplication::translate("QmlParser", "Illegal unicode escape sequence"); + break; + } + + } else if (isIdentLetter(current)) { + identifierWithEscapedUnicode = false; + recordStartPos(); + record16(current); + state = InIdentifier; + } else if (current == '0') { + recordStartPos(); + record8(current); + state = InNum0; + } else if (isDecimalDigit(current)) { + recordStartPos(); + record8(current); + state = InNum; + } else if (current == '.' && isDecimalDigit(next1)) { + recordStartPos(); + record8(current); + state = InDecimal; + } else { + recordStartPos(); + token = matchPunctuator(current, next1, next2, next3); + if (token != -1) { + if (terminator && !delimited && !prohibitAutomaticSemicolon + && (token == QmlJSGrammar::T_PLUS_PLUS + || token == QmlJSGrammar::T_MINUS_MINUS)) { + // automatic semicolon insertion + stackToken = token; + token = QmlJSGrammar::T_SEMICOLON; + } + setDone(Other); + } + else { + setDone(Bad); + err = IllegalCharacter; + errmsg = QCoreApplication::translate("QmlParser", "Illegal character"); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (isLineTerminator()) { + multiLineString = true; + record16(current); + } else if (current == 0 || isLineTerminator()) { + setDone(Bad); + err = UnclosedStringLiteral; + errmsg = QCoreApplication::translate("QmlParser", "Unclosed string at end of line"); + } else if (current == '\\') { + state = InEscapeSequence; + } else { + record16(current); + } + break; + // Escape Sequences inside of strings + case InEscapeSequence: + if (isOctalDigit(current)) { + if (current >= '0' && current <= '3' && + isOctalDigit(next1) && isOctalDigit(next2)) { + record16(convertOctal(current, next1, next2)); + shift(2); + state = InString; + } else if (isOctalDigit(current) && + isOctalDigit(next1)) { + record16(convertOctal('0', current, next1)); + shift(1); + state = InString; + } else if (isOctalDigit(current)) { + record16(convertOctal('0', '0', current)); + state = InString; + } else { + setDone(Bad); + err = IllegalEscapeSequence; + errmsg = QCoreApplication::translate("QmlParser", "Illegal escape sequence"); + } + } else if (current == 'x') + state = InHexEscape; + else if (current == 'u') + state = InUnicodeEscape; + else { + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + } else { + record16(singleEscape(current)); + } + state = InString; + } + break; + case InHexEscape: + if (isHexDigit(current) && isHexDigit(next1)) { + state = InString; + record16(QLatin1Char(convertHex(current, next1))); + shift(1); + } else if (current == stringType) { + record16(QLatin1Char('x')); + shift(1); + setDone(String); + } else { + record16(QLatin1Char('x')); + record16(current); + state = InString; + } + break; + case InUnicodeEscape: + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InString; + } else if (current == stringType) { + record16(QLatin1Char('u')); + shift(1); + setDone(String); + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QCoreApplication::translate("QmlParser", "Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QmlJSGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + if (driver) driver->addComment(startpos+2, tokenLength()-2, startlineno, startcolumn+2); + } else if (current == 0) { + if (driver) driver->addComment(startpos+2, tokenLength()-2, startlineno, startcolumn+2); + setDone(Eof); + } + + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QCoreApplication::translate("QmlParser", "Unclosed comment at end of file"); + if (driver) driver->addComment(startpos+2, tokenLength()-2, startlineno, startcolumn+2); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + if (driver) driver->addComment(startpos+2, tokenLength()-3, startlineno, startcolumn+2); + } + + break; + case InIdentifier: + if (isIdentLetter(current) || isDecimalDigit(current)) { + record16(current); + break; + } else if (current == '\\' && next1 == 'u') { + identifierWithEscapedUnicode = true; + shift(2); // skip the unicode escape prefix `\u' + + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + break; + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QCoreApplication::translate("QmlParser", "Illegal unicode escape sequence"); + break; + } + } + setDone(Identifier); + break; + case InNum0: + if (current == 'x' || current == 'X') { + record8(current); + state = InHex; + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else if (isOctalDigit(current)) { + record8(current); + state = InOctal; + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Number); + } + break; + case InHex: + if (isHexDigit(current)) + record8(current); + else + setDone(Hex); + break; + case InOctal: + if (isOctalDigit(current)) { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Octal); + } + break; + case InNum: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InDecimal: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InExponentIndicator: + if (current == '+' || current == '-') { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InExponent; + } else { + setDone(Bad); + err = IllegalExponentIndicator; + errmsg = QCoreApplication::translate("QmlParser", "Illegal syntax for exponential number"); + } + break; + case InExponent: + if (isDecimalDigit(current)) { + record8(current); + } else { + setDone(Number); + } + break; + default: + Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement"); + } + + // move on to the next character + if (!done) + shift(1); + if (state != Start && state != InSingleLineComment) + bol = false; + } + + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad + if ((state == Number || state == Octal || state == Hex) + && isIdentLetter(current)) { + state = Bad; + err = IllegalIdentifier; + errmsg = QCoreApplication::translate("QmlParser", "Identifier cannot start with numeric literal"); + } + + // terminate string + buffer8[pos8] = '\0'; + + double dval = 0; + if (state == Number) { + dval = qstrtod(buffer8, 0, 0); + } else if (state == Hex) { // scan hex numbers + dval = integerFromString(buffer8, pos8, 16); + state = Number; + } else if (state == Octal) { // scan octal number + dval = integerFromString(buffer8, pos8, 8); + state = Number; + } + + restrKeyword = false; + delimited = false; + + switch (parenthesesState) { + case IgnoreParentheses: + break; + case CountParentheses: + if (token == QmlJSGrammar::T_RPAREN) { + --parenthesesCount; + if (parenthesesCount == 0) + parenthesesState = BalancedParentheses; + } else if (token == QmlJSGrammar::T_LPAREN) { + ++parenthesesCount; + } + break; + case BalancedParentheses: + parenthesesState = IgnoreParentheses; + break; + } + + switch (state) { + case Eof: + return 0; + case Other: + if (token == QmlJSGrammar::T_RBRACE || token == QmlJSGrammar::T_SEMICOLON) + delimited = true; + return token; + case Identifier: + token = -1; + if (! identifierWithEscapedUnicode) + token = findReservedWord(buffer16, pos16); + + if (token < 0) { + /* TODO: close leak on parse error. same holds true for String */ + if (driver) + qsyylval.ustr = driver->intern(buffer16, pos16); + else + qsyylval.ustr = 0; + return QmlJSGrammar::T_IDENTIFIER; + } + if (token == QmlJSGrammar::T_CONTINUE || token == QmlJSGrammar::T_BREAK + || token == QmlJSGrammar::T_RETURN || token == QmlJSGrammar::T_THROW) { + restrKeyword = true; + } else if (token == QmlJSGrammar::T_IF || token == QmlJSGrammar::T_FOR + || token == QmlJSGrammar::T_WHILE || token == QmlJSGrammar::T_WITH) { + parenthesesState = CountParentheses; + parenthesesCount = 0; + } else if (token == QmlJSGrammar::T_DO) { + parenthesesState = BalancedParentheses; + } + return token; + case String: + if (driver) + qsyylval.ustr = driver->intern(buffer16, pos16); + else + qsyylval.ustr = 0; + return multiLineString?QmlJSGrammar::T_MULTILINE_STRING_LITERAL:QmlJSGrammar::T_STRING_LITERAL; + case Number: + qsyylval.dval = dval; + return QmlJSGrammar::T_NUMERIC_LITERAL; + case Bad: + return -1; + default: + Q_ASSERT(!"unhandled numeration value in switch"); + return -1; + } +} + +bool Lexer::isWhiteSpace() const +{ + return (current == ' ' || current == '\t' || + current == 0x0b || current == 0x0c); +} + +bool Lexer::isLineTerminator() const +{ + return (current == '\n' || current == '\r'); +} + +bool Lexer::isIdentLetter(ushort c) +{ + // ASCII-biased, since all reserved words are ASCII, aand hence the + // bulk of content to be parsed. + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '$' + || c == '_') + return true; + if (c < 128) + return false; + return QChar(c).isLetterOrNumber(); +} + +bool Lexer::isDecimalDigit(ushort c) +{ + return (c >= '0' && c <= '9'); +} + +bool Lexer::isHexDigit(ushort c) const +{ + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +bool Lexer::isOctalDigit(ushort c) const +{ + return (c >= '0' && c <= '7'); +} + +int Lexer::matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { + shift(4); + return QmlJSGrammar::T_GT_GT_GT_EQ; + } else if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_EQ_EQ_EQ; + } else if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_NOT_EQ_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return QmlJSGrammar::T_GT_GT_GT; + } else if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_LT_LT_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_GT_GT_EQ; + } else if (c1 == '<' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_LE; + } else if (c1 == '>' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_GE; + } else if (c1 == '!' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_NOT_EQ; + } else if (c1 == '+' && c2 == '+') { + shift(2); + return QmlJSGrammar::T_PLUS_PLUS; + } else if (c1 == '-' && c2 == '-') { + shift(2); + return QmlJSGrammar::T_MINUS_MINUS; + } else if (c1 == '=' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_EQ_EQ; + } else if (c1 == '+' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_PLUS_EQ; + } else if (c1 == '-' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_MINUS_EQ; + } else if (c1 == '*' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_STAR_EQ; + } else if (c1 == '/' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_DIVIDE_EQ; + } else if (c1 == '&' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_AND_EQ; + } else if (c1 == '^' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_XOR_EQ; + } else if (c1 == '%' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_REMAINDER_EQ; + } else if (c1 == '|' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_OR_EQ; + } else if (c1 == '<' && c2 == '<') { + shift(2); + return QmlJSGrammar::T_LT_LT; + } else if (c1 == '>' && c2 == '>') { + shift(2); + return QmlJSGrammar::T_GT_GT; + } else if (c1 == '&' && c2 == '&') { + shift(2); + return QmlJSGrammar::T_AND_AND; + } else if (c1 == '|' && c2 == '|') { + shift(2); + return QmlJSGrammar::T_OR_OR; + } + + switch(c1) { + case '=': shift(1); return QmlJSGrammar::T_EQ; + case '>': shift(1); return QmlJSGrammar::T_GT; + case '<': shift(1); return QmlJSGrammar::T_LT; + case ',': shift(1); return QmlJSGrammar::T_COMMA; + case '!': shift(1); return QmlJSGrammar::T_NOT; + case '~': shift(1); return QmlJSGrammar::T_TILDE; + case '?': shift(1); return QmlJSGrammar::T_QUESTION; + case ':': shift(1); return QmlJSGrammar::T_COLON; + case '.': shift(1); return QmlJSGrammar::T_DOT; + case '+': shift(1); return QmlJSGrammar::T_PLUS; + case '-': shift(1); return QmlJSGrammar::T_MINUS; + case '*': shift(1); return QmlJSGrammar::T_STAR; + case '/': shift(1); return QmlJSGrammar::T_DIVIDE_; + case '&': shift(1); return QmlJSGrammar::T_AND; + case '|': shift(1); return QmlJSGrammar::T_OR; + case '^': shift(1); return QmlJSGrammar::T_XOR; + case '%': shift(1); return QmlJSGrammar::T_REMAINDER; + case '(': shift(1); return QmlJSGrammar::T_LPAREN; + case ')': shift(1); return QmlJSGrammar::T_RPAREN; + case '{': shift(1); return QmlJSGrammar::T_LBRACE; + case '}': shift(1); return QmlJSGrammar::T_RBRACE; + case '[': shift(1); return QmlJSGrammar::T_LBRACKET; + case ']': shift(1); return QmlJSGrammar::T_RBRACKET; + case ';': shift(1); return QmlJSGrammar::T_SEMICOLON; + + default: return -1; + } +} + +ushort Lexer::singleEscape(ushort c) const +{ + switch(c) { + case 'b': + return 0x08; + case 't': + return 0x09; + case 'n': + return 0x0A; + case 'v': + return 0x0B; + case 'f': + return 0x0C; + case 'r': + return 0x0D; + case '"': + return 0x22; + case '\'': + return 0x27; + case '\\': + return 0x5C; + default: + return c; + } +} + +ushort Lexer::convertOctal(ushort c1, ushort c2, + ushort c3) const +{ + return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0'); +} + +unsigned char Lexer::convertHex(ushort c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); +} + +unsigned char Lexer::convertHex(ushort c1, ushort c2) +{ + return ((convertHex(c1) << 4) + convertHex(c2)); +} + +QChar Lexer::convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + return QChar((convertHex(c3) << 4) + convertHex(c4), + (convertHex(c1) << 4) + convertHex(c2)); +} + +void Lexer::record8(ushort c) +{ + Q_ASSERT(c <= 0xff); + + // enlarge buffer if full + if (pos8 >= size8 - 1) { + char *tmp = new char[2 * size8]; + memcpy(tmp, buffer8, size8 * sizeof(char)); + delete [] buffer8; + buffer8 = tmp; + size8 *= 2; + } + + buffer8[pos8++] = (char) c; +} + +void Lexer::record16(QChar c) +{ + // enlarge buffer if full + if (pos16 >= size16 - 1) { + QChar *tmp = new QChar[2 * size16]; + memcpy(tmp, buffer16, size16 * sizeof(QChar)); + delete [] buffer16; + buffer16 = tmp; + size16 *= 2; + } + + buffer16[pos16++] = c; +} + +void Lexer::recordStartPos() +{ + startpos = pos; + startlineno = yylineno; + startcolumn = yycolumn; +} + +bool Lexer::scanRegExp(RegExpBodyPrefix prefix) +{ + pos16 = 0; + pattern = 0; + + if (prefix == EqualPrefix) + record16(QLatin1Char('=')); + + while (true) { + switch (current) { + + case 0: // eof + case '\n': case '\r': // line terminator + errmsg = QCoreApplication::translate("QmlParser", "Unterminated regular expression literal"); + return false; + + case '/': + shift(1); + + if (driver) // create the pattern + pattern = driver->intern(buffer16, pos16); + + // scan the flags + pos16 = 0; + flags = 0; + while (isIdentLetter(current)) { + int flag = Ecma::RegExp::flagFromChar(current); + if (flag == 0) { + errmsg = QCoreApplication::translate("QmlParser", "Invalid regular expression flag '%0'") + .arg(QChar(current)); + return false; + } + flags |= flag; + record16(current); + shift(1); + } + return true; + + case '\\': + // regular expression backslash sequence + record16(current); + shift(1); + + if (! current || isLineTerminator()) { + errmsg = QCoreApplication::translate("QmlParser", "Unterminated regular expression backslash sequence"); + return false; + } + + record16(current); + shift(1); + break; + + case '[': + // regular expression class + record16(current); + shift(1); + + while (current && ! isLineTerminator()) { + if (current == ']') + break; + else if (current == '\\') { + // regular expression backslash sequence + record16(current); + shift(1); + + if (! current || isLineTerminator()) { + errmsg = QCoreApplication::translate("QmlParser", "Unterminated regular expression backslash sequence"); + return false; + } + + record16(current); + shift(1); + } else { + record16(current); + shift(1); + } + } + + if (current != ']') { + errmsg = QCoreApplication::translate("QmlParser", "Unterminated regular expression class"); + return false; + } + + record16(current); + shift(1); // skip ] + break; + + default: + record16(current); + shift(1); + } // switch + } // while + + return false; +} + +void Lexer::syncProhibitAutomaticSemicolon() +{ + if (parenthesesState == BalancedParentheses) { + // we have seen something like "if (foo)", which means we should + // never insert an automatic semicolon at this point, since it would + // then be expanded into an empty statement (ECMA-262 7.9.1) + prohibitAutomaticSemicolon = true; + parenthesesState = IgnoreParentheses; + } else { + prohibitAutomaticSemicolon = false; + } +} + +QT_QML_END_NAMESPACE + + diff --git a/src/lib/parser/qmljslexer_p.h b/src/lib/parser/qmljslexer_p.h new file mode 100644 index 000000000..8d94c0240 --- /dev/null +++ b/src/lib/parser/qmljslexer_p.h @@ -0,0 +1,240 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSLEXER_P_H +#define QMLJSLEXER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsglobal_p.h" + +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class Engine; +class NameId; + +class QML_PARSER_EXPORT Lexer +{ +public: + Lexer(Engine *eng, bool tokenizeComments = false); + ~Lexer(); + + void setCode(const QString &c, int lineno); + int lex(); + + int currentLineNo() const { return yylineno; } + int currentColumnNo() const { return yycolumn; } + + int tokenOffset() const { return startpos; } + int tokenLength() const { return pos - startpos; } + + int startLineNo() const { return startlineno; } + int startColumnNo() const { return startcolumn; } + + int endLineNo() const { return currentLineNo(); } + int endColumnNo() const + { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; } + + bool prevTerminator() const { return terminator; } + + enum State { Start, + Identifier, + InIdentifier, + InSingleLineComment, + InMultiLineComment, + InNum, + InNum0, + InHex, + InOctal, + InDecimal, + InExponentIndicator, + InExponent, + Hex, + Octal, + Number, + String, + Eof, + InString, + InEscapeSequence, + InHexEscape, + InUnicodeEscape, + Other, + Bad }; + + enum Error { + NoError, + IllegalCharacter, + UnclosedStringLiteral, + IllegalEscapeSequence, + IllegalUnicodeEscapeSequence, + UnclosedComment, + IllegalExponentIndicator, + IllegalIdentifier + }; + + enum ParenthesesState { + IgnoreParentheses, + CountParentheses, + BalancedParentheses + }; + + enum RegExpBodyPrefix { + NoPrefix, + EqualPrefix + }; + + bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); + + NameId *pattern; + int flags; + + State lexerState() const + { return state; } + + QString errorMessage() const + { return errmsg; } + void setErrorMessage(const QString &err) + { errmsg = err; } + void setErrorMessage(const char *err) + { setErrorMessage(QString::fromLatin1(err)); } + + Error error() const + { return err; } + void clearError() + { err = NoError; } + +private: + Engine *driver; + int yylineno; + bool done; + char *buffer8; + QChar *buffer16; + uint size8, size16; + uint pos8, pos16; + bool terminator; + bool restrKeyword; + // encountered delimiter like "'" and "}" on last run + bool delimited; + int stackToken; + + State state; + void setDone(State s); + uint pos; + void shift(uint p); + int lookupKeyword(const char *); + + bool isWhiteSpace() const; + bool isLineTerminator() const; + bool isHexDigit(ushort c) const; + bool isOctalDigit(ushort c) const; + + int matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4); + ushort singleEscape(ushort c) const; + ushort convertOctal(ushort c1, ushort c2, + ushort c3) const; +public: + static unsigned char convertHex(ushort c1); + static unsigned char convertHex(ushort c1, ushort c2); + static QChar convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4); + static bool isIdentLetter(ushort c); + static bool isDecimalDigit(ushort c); + + inline int ival() const { return qsyylval.ival; } + inline double dval() const { return qsyylval.dval; } + inline NameId *ustr() const { return qsyylval.ustr; } + + const QChar *characterBuffer() const { return buffer16; } + int characterCount() const { return pos16; } + +private: + void record8(ushort c); + void record16(QChar c); + void recordStartPos(); + + int findReservedWord(const QChar *buffer, int size) const; + + void syncProhibitAutomaticSemicolon(); + + const QChar *code; + uint length; + int yycolumn; + int startpos; + int startlineno; + int startcolumn; + int bol; // begin of line + + union { + int ival; + double dval; + NameId *ustr; + } qsyylval; + + // current and following unicode characters + ushort current, next1, next2, next3; + + struct keyword { + const char *name; + int token; + }; + + QString errmsg; + Error err; + + bool wantRx; + bool check_reserved; + + ParenthesesState parenthesesState; + int parenthesesCount; + bool prohibitAutomaticSemicolon; + bool tokenizeComments; +}; + +} // namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/lib/parser/qmljsmemorypool_p.h b/src/lib/parser/qmljsmemorypool_p.h new file mode 100644 index 000000000..458069282 --- /dev/null +++ b/src/lib/parser/qmljsmemorypool_p.h @@ -0,0 +1,124 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSMEMORYPOOL_P_H +#define QMLJSMEMORYPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsglobal_p.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> + +#include <string.h> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class QML_PARSER_EXPORT MemoryPool : public QSharedData +{ +public: + enum { maxBlockCount = -1 }; + enum { defaultBlockSize = 1 << 12 }; + + MemoryPool() { + m_blockIndex = maxBlockCount; + m_currentIndex = 0; + m_storage = 0; + m_currentBlock = 0; + m_currentBlockSize = 0; + } + + virtual ~MemoryPool() { + for (int index = 0; index < m_blockIndex + 1; ++index) + qFree(m_storage[index]); + + qFree(m_storage); + } + + char *allocate(int bytes) { + bytes += (8 - bytes) & 7; // ensure multiple of 8 bytes (maintain alignment) + if (m_currentBlock == 0 || m_currentBlockSize < m_currentIndex + bytes) { + ++m_blockIndex; + m_currentBlockSize = defaultBlockSize << m_blockIndex; + + m_storage = reinterpret_cast<char**>(qRealloc(m_storage, sizeof(char*) * (1 + m_blockIndex))); + m_currentBlock = m_storage[m_blockIndex] = reinterpret_cast<char*>(qMalloc(m_currentBlockSize)); + ::memset(m_currentBlock, 0, m_currentBlockSize); + + m_currentIndex = (8 - quintptr(m_currentBlock)) & 7; // ensure first chunk is 64-bit aligned + Q_ASSERT(m_currentIndex + bytes <= m_currentBlockSize); + } + + char *p = reinterpret_cast<char *> + (m_currentBlock + m_currentIndex); + + m_currentIndex += bytes; + + return p; + } + + int bytesAllocated() const { + int bytes = 0; + for (int index = 0; index < m_blockIndex; ++index) + bytes += (defaultBlockSize << index); + bytes += m_currentIndex; + return bytes; + } + +private: + int m_blockIndex; + int m_currentIndex; + char *m_currentBlock; + int m_currentBlockSize; + char **m_storage; + +private: + Q_DISABLE_COPY(MemoryPool) +}; + +} // namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/lib/parser/qmljsnodepool_p.h b/src/lib/parser/qmljsnodepool_p.h new file mode 100644 index 000000000..83144dc32 --- /dev/null +++ b/src/lib/parser/qmljsnodepool_p.h @@ -0,0 +1,130 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJSNODEPOOL_P_H +#define QMLJSNODEPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsglobal_p.h" +#include "qmljsmemorypool_p.h" + +#include <QtCore/QHash> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +namespace AST { +class Node; +} // namespace AST + +class Code; +class CompilationUnit; +class Engine; + +template <typename NodeType> +inline NodeType *makeAstNode(MemoryPool *storage) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(); + return node; +} + +template <typename NodeType, typename Arg1> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1); + return node; +} + +template <typename NodeType, typename Arg1, typename Arg2> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2); + return node; +} + +template <typename NodeType, typename Arg1, typename Arg2, typename Arg3> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2, Arg3 arg3) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2, arg3); + return node; +} + +template <typename NodeType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2, arg3, arg4); + return node; +} + +class QML_PARSER_EXPORT NodePool : public MemoryPool +{ +public: + NodePool(const QString &fileName, Engine *engine); + virtual ~NodePool(); + + Code *createCompiledCode(AST::Node *node, CompilationUnit &compilation); + + inline QString fileName() const { return m_fileName; } + inline Engine *engine() const { return m_engine; } +#ifndef J_SCRIPT_NO_EVENT_NOTIFY + inline qint64 id() const { return m_id; } +#endif + +private: + QHash<AST::Node*, Code*> m_codeCache; + QString m_fileName; + Engine *m_engine; +#ifndef J_SCRIPT_NO_EVENT_NOTIFY + qint64 m_id; +#endif + +private: + Q_DISABLE_COPY(NodePool) +}; + +} // namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/lib/parser/qmljsparser.cpp b/src/lib/parser/qmljsparser.cpp new file mode 100644 index 000000000..ca34b527c --- /dev/null +++ b/src/lib/parser/qmljsparser.cpp @@ -0,0 +1,1893 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include <QtCore/QtDebug> +#include <QtGui/QApplication> + +#include <string.h> + +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsast_p.h" +#include "qmljsnodepool_p.h" + + + +#include "qmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (qRealloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (qRealloc(location_stack, stack_size * sizeof(AST::SourceLocation))); +} + +inline static bool automatic(Engine *driver, int token) +{ + return token == QmlJSGrammar::T_RBRACE + || token == 0 + || driver->lexer()->prevTerminator(); +} + + +Parser::Parser(Engine *engine): + driver(engine), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + qFree(sym_stack); + qFree(state_stack); + qFree(location_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->startLineNo(); + loc.startColumn = lexer->startColumnNo(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<NameId *, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->dval(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { + +case 0: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 1: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 2: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 3: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 4: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 5: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 6: { + sym(1).UiProgram = makeAstNode<AST::UiProgram> (driver->nodePool(), sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; + +case 8: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; + +case 9: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), sym(1).UiImport); +} break; + +case 10: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), + sym(1).UiImportList, sym(2).UiImport); +} break; + +case 13: { + sym(1).UiImport->semicolonToken = loc(2); +} break; + +case 15: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; + +case 17: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = sym(4).sval; + sym(1).UiImport->semicolonToken = loc(5); +} break; + +case 19: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = sym(3).sval; + sym(1).UiImport->semicolonToken = loc(4); +} break; + +case 20: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = makeAstNode<AST::UiImport>(driver->nodePool(), importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = makeAstNode<AST::UiImport>(driver->nodePool(), qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; + +case 21: { + sym(1).Node = 0; +} break; + +case 22: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 23: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 24: { + AST::UiObjectMemberList *node = makeAstNode<AST:: UiObjectMemberList> (driver->nodePool(), + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; + +case 25: { + sym(1).Node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 26: { + AST::UiArrayMemberList *node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 27: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), (AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; + +case 28: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 29: { + AST::UiObjectDefinition *node = makeAstNode<AST::UiObjectDefinition> (driver->nodePool(), sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; + +case 31: { + AST::UiArrayBinding *node = makeAstNode<AST::UiArrayBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 32: { + AST::UiObjectBinding *node = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 33: { + AST::UiObjectBinding *node = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; + +case 38: +{ + AST::UiScriptBinding *node = makeAstNode<AST::UiScriptBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 39: + +case 40: { + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); + break; +} + +case 42: { + sym(1).Node = 0; +} break; + +case 43: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; + +case 44: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).sval, sym(2).sval); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; + +case 45: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).UiParameterList, sym(3).sval, sym(4).sval); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; + +case 47: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; + +case 49: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 51: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(4).sval, sym(6).sval); + node->typeModifier = sym(2).sval; + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 53: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; + +case 55: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; + +case 56: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval, + sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 57: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; + +case 58: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; + +case 59: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(4).sval, sym(6).sval); + node->typeModifier = sym(2).sval; + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), sym(6).sval); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = makeAstNode<AST::UiArrayBinding> (driver->nodePool(), + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 60: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), sym(3).sval); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 61: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; + +case 62: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; + +case 64: { + QString s = QLatin1String(QmlJSGrammar::spell[T_PROPERTY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 65: { + QString s = QLatin1String(QmlJSGrammar::spell[T_SIGNAL]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 66: { + QString s = QLatin1String(QmlJSGrammar::spell[T_READONLY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 67: { + QString s = QLatin1String(QmlJSGrammar::spell[T_ON]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 68: { + AST::ThisExpression *node = makeAstNode<AST::ThisExpression> (driver->nodePool()); + node->thisToken = loc(1); + sym(1).Node = node; +} break; + +case 69: { + AST::IdentifierExpression *node = makeAstNode<AST::IdentifierExpression> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 70: { + AST::NullExpression *node = makeAstNode<AST::NullExpression> (driver->nodePool()); + node->nullToken = loc(1); + sym(1).Node = node; +} break; + +case 71: { + AST::TrueLiteral *node = makeAstNode<AST::TrueLiteral> (driver->nodePool()); + node->trueToken = loc(1); + sym(1).Node = node; +} break; + +case 72: { + AST::FalseLiteral *node = makeAstNode<AST::FalseLiteral> (driver->nodePool()); + node->falseToken = loc(1); + sym(1).Node = node; +} break; + +case 73: { + AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +case 74: +case 75: { + AST::StringLiteral *node = makeAstNode<AST::StringLiteral> (driver->nodePool(), sym(1).sval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 76: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 77: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 78: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; + +case 79: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 80: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 81: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 82: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 83: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + else + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 84: { + AST::ObjectLiteral *node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; + +case 85: { + AST::NestedExpression *node = makeAstNode<AST::NestedExpression>(driver->nodePool(), sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; + +case 86: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; + +case 87: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), (AST::Elision *) 0, sym(1).Expression); +} break; + +case 88: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).Elision->finish(), sym(2).Expression); +} break; + +case 89: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 90: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 91: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool()); + node->commaToken = loc(1); + sym(1).Node = node; +} break; + +case 92: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool(), sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 93: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 94: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 95: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +case 96: +case 97: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount())); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 98: { + AST::StringLiteralPropertyName *node = makeAstNode<AST::StringLiteralPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 99: { + AST::NumericLiteralPropertyName *node = makeAstNode<AST::NumericLiteralPropertyName> (driver->nodePool(), sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 100: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 101: + +case 102: + +case 103: + +case 104: + +case 105: + +case 106: + +case 107: + +case 108: + +case 109: + +case 110: + +case 111: + +case 112: + +case 113: + +case 114: + +case 115: + +case 116: + +case 117: + +case 118: + +case 119: + +case 120: + +case 121: + +case 122: + +case 123: + +case 124: + +case 125: + +case 126: + +case 127: + +case 128: + +case 129: + +case 130: + +case 131: +{ + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); +} break; + +case 136: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 137: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 138: { + AST::NewMemberExpression *node = makeAstNode<AST::NewMemberExpression> (driver->nodePool(), sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; + +case 140: { + AST::NewExpression *node = makeAstNode<AST::NewExpression> (driver->nodePool(), sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; + +case 141: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 142: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 143: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 144: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 145: { + sym(1).Node = 0; +} break; + +case 146: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; + +case 147: { + sym(1).Node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).Expression); +} break; + +case 148: { + AST::ArgumentList *node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 152: { + AST::PostIncrementExpression *node = makeAstNode<AST::PostIncrementExpression> (driver->nodePool(), sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; + +case 153: { + AST::PostDecrementExpression *node = makeAstNode<AST::PostDecrementExpression> (driver->nodePool(), sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; + +case 155: { + AST::DeleteExpression *node = makeAstNode<AST::DeleteExpression> (driver->nodePool(), sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; + +case 156: { + AST::VoidExpression *node = makeAstNode<AST::VoidExpression> (driver->nodePool(), sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; + +case 157: { + AST::TypeOfExpression *node = makeAstNode<AST::TypeOfExpression> (driver->nodePool(), sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; + +case 158: { + AST::PreIncrementExpression *node = makeAstNode<AST::PreIncrementExpression> (driver->nodePool(), sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; + +case 159: { + AST::PreDecrementExpression *node = makeAstNode<AST::PreDecrementExpression> (driver->nodePool(), sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; + +case 160: { + AST::UnaryPlusExpression *node = makeAstNode<AST::UnaryPlusExpression> (driver->nodePool(), sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; + +case 161: { + AST::UnaryMinusExpression *node = makeAstNode<AST::UnaryMinusExpression> (driver->nodePool(), sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; + +case 162: { + AST::TildeExpression *node = makeAstNode<AST::TildeExpression> (driver->nodePool(), sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; + +case 163: { + AST::NotExpression *node = makeAstNode<AST::NotExpression> (driver->nodePool(), sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; + +case 165: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 166: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 167: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 169: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 170: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 172: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 173: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 174: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 176: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 177: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 178: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 179: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 180: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 181: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 183: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 184: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 185: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 186: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 187: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 189: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 190: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 191: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 192: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 194: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 195: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 196: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 197: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 199: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 201: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 203: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 205: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 207: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 209: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 211: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 213: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 215: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 217: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 219: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 221: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 223: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 225: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 226: { + sym(1).ival = QSOperator::Assign; +} break; + +case 227: { + sym(1).ival = QSOperator::InplaceMul; +} break; + +case 228: { + sym(1).ival = QSOperator::InplaceDiv; +} break; + +case 229: { + sym(1).ival = QSOperator::InplaceMod; +} break; + +case 230: { + sym(1).ival = QSOperator::InplaceAdd; +} break; + +case 231: { + sym(1).ival = QSOperator::InplaceSub; +} break; + +case 232: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; + +case 233: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; + +case 234: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; + +case 235: { + sym(1).ival = QSOperator::InplaceAnd; +} break; + +case 236: { + sym(1).ival = QSOperator::InplaceXor; +} break; + +case 237: { + sym(1).ival = QSOperator::InplaceOr; +} break; + +case 239: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 240: { + sym(1).Node = 0; +} break; + +case 243: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 244: { + sym(1).Node = 0; +} break; + +case 261: { + AST::Block *node = makeAstNode<AST::Block> (driver->nodePool(), sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 262: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).Statement); +} break; + +case 263: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).StatementList, sym(2).Statement); +} break; + +case 264: { + sym(1).Node = 0; +} break; + +case 265: { + sym(1).Node = sym(1).StatementList->finish (); +} break; + +case 267: { + AST::VariableStatement *node = makeAstNode<AST::VariableStatement> (driver->nodePool(), + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 268: { + sym(1).ival = T_CONST; +} break; + +case 269: { + sym(1).ival = T_VAR; +} break; + +case 270: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; + +case 271: { + AST::VariableDeclarationList *node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 272: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; + +case 273: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; + +case 274: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 275: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 276: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 277: { + sym(1).Node = 0; +} break; + +case 279: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 280: { + sym(1).Node = 0; +} break; + +case 282: { + AST::EmptyStatement *node = makeAstNode<AST::EmptyStatement> (driver->nodePool()); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; + +case 284: { + AST::ExpressionStatement *node = makeAstNode<AST::ExpressionStatement> (driver->nodePool(), sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 285: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(5); + sym(1).Node = node; +} break; + +case 286: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 288: { + AST::DoWhileStatement *node = makeAstNode<AST::DoWhileStatement> (driver->nodePool(), sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 289: { + AST::WhileStatement *node = makeAstNode<AST::WhileStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 290: { + AST::ForStatement *node = makeAstNode<AST::ForStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; + +case 291: { + AST::LocalForStatement *node = makeAstNode<AST::LocalForStatement> (driver->nodePool(), + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; + +case 292: { + AST:: ForEachStatement *node = makeAstNode<AST::ForEachStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; + +case 293: { + AST::LocalForEachStatement *node = makeAstNode<AST::LocalForEachStatement> (driver->nodePool(), + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; + +case 295: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool()); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 297: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool(), sym(2).sval); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 299: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 301: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool(), sym(2).sval); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 303: { + AST::ReturnStatement *node = makeAstNode<AST::ReturnStatement> (driver->nodePool(), sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 304: { + AST::WithStatement *node = makeAstNode<AST::WithStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 305: { + AST::SwitchStatement *node = makeAstNode<AST::SwitchStatement> (driver->nodePool(), sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 306: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 307: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; + +case 308: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClause); +} break; + +case 309: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClauses, sym(2).CaseClause); +} break; + +case 310: { + sym(1).Node = 0; +} break; + +case 311: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; + +case 312: { + AST::CaseClause *node = makeAstNode<AST::CaseClause> (driver->nodePool(), sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; + +case 313: { + AST::DefaultClause *node = makeAstNode<AST::DefaultClause> (driver->nodePool(), sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +case 314: +case 315: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount()), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 316: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), sym(1).sval, sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 318: { + AST::ThrowStatement *node = makeAstNode<AST::ThrowStatement> (driver->nodePool(), sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 319: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 320: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 321: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 322: { + AST::Catch *node = makeAstNode<AST::Catch> (driver->nodePool(), sym(3).sval, sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 323: { + AST::Finally *node = makeAstNode<AST::Finally> (driver->nodePool(), sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; + +case 325: { + AST::DebuggerStatement *node = makeAstNode<AST::DebuggerStatement> (driver->nodePool()); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 326: { + AST::FunctionDeclaration *node = makeAstNode<AST::FunctionDeclaration> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 327: { + AST::FunctionExpression *node = makeAstNode<AST::FunctionExpression> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (sym(2).sval) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 328: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 329: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).FormalParameterList, sym(3).sval); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 330: { + sym(1).Node = 0; +} break; + +case 331: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; + +case 332: { + sym(1).Node = 0; +} break; + +case 334: { + sym(1).Node = makeAstNode<AST::FunctionBody> (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; + +case 335: { + sym(1).Node = makeAstNode<AST::Program> (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; + +case 336: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElement); +} break; + +case 337: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElements, sym(2).SourceElement); +} break; + +case 338: { + sym(1).Node = makeAstNode<AST::StatementSourceElement> (driver->nodePool(), sym(1).Statement); +} break; + +case 339: { + sym(1).Node = makeAstNode<AST::FunctionSourceElement> (driver->nodePool(), sym(1).FunctionDeclaration); +} break; + +case 340: { + sym(1).sval = 0; +} break; + +case 342: { + sym(1).Node = 0; +} break; + + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && automatic(driver, yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = qApp->translate("QmlParser", "Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->dval(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QmlParser", "Syntax error"); + else + msg = qApp->translate("QmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = qApp->translate("QmlParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + diff --git a/src/lib/parser/qmljsparser_p.h b/src/lib/parser/qmljsparser_p.h new file mode 100644 index 000000000..16973a2ad --- /dev/null +++ b/src/lib/parser/qmljsparser_p.h @@ -0,0 +1,237 @@ +/************************************************************************** +** +** This file is part of the Qt Build System +** +** Copyright (c) 2012 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 info@qt.nokia.com. +** +**************************************************************************/ + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QMLJSPARSER_P_H +#define QMLJSPARSER_P_H + +#include "qmljsglobal_p.h" +#include "qmljsgrammar_p.h" +#include "qmljsast_p.h" +#include "qmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class Engine; +class NameId; + +class QML_PARSER_EXPORT Parser: protected QmlJSGrammar +{ +public: + union Value { + int ival; + double dval; + NameId *sval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + AST::UiSignature *UiSignature; + AST::UiFormalList *UiFormalList; + AST::UiFormal *UiFormal; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + AST::Node *rootNode() const + { return program; } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + }; + + double yylval; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QmlJS + + + +#define J_SCRIPT_REGEXPLITERAL_RULE1 76 + +#define J_SCRIPT_REGEXPLITERAL_RULE2 77 + +QT_QML_END_NAMESPACE + + + +#endif // QMLJSPARSER_P_H diff --git a/src/lib/qtconcurrent/QtConcurrentTools b/src/lib/qtconcurrent/QtConcurrentTools new file mode 100644 index 000000000..f6084a528 --- /dev/null +++ b/src/lib/qtconcurrent/QtConcurrentTools @@ -0,0 +1,39 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "qtconcurrent/multitask.h" +#include "qtconcurrent/runextensions.h" diff --git a/src/lib/qtconcurrent/multitask.h b/src/lib/qtconcurrent/multitask.h new file mode 100644 index 000000000..1b223a596 --- /dev/null +++ b/src/lib/qtconcurrent/multitask.h @@ -0,0 +1,202 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef MULTITASK_H +#define MULTITASK_H + +#include "qtconcurrent_global.h" +#include "runextensions.h" + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QEventLoop> +#include <QtCore/QFutureWatcher> +#include <QtCore/QtConcurrentRun> +#include <QtCore/QThreadPool> + +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +namespace QtConcurrent { + +class QTCONCURRENT_EXPORT MultiTaskBase : public QObject, public QRunnable +{ + Q_OBJECT +protected slots: + virtual void cancelSelf() = 0; + virtual void setFinished() = 0; + virtual void setProgressRange(int min, int max) = 0; + virtual void setProgressValue(int value) = 0; + virtual void setProgressText(QString value) = 0; +}; + +template <typename Class, typename R> +class MultiTask : public MultiTaskBase +{ +public: + MultiTask(void (Class::*fn)(QFutureInterface<R> &), const QList<Class *> &objects) + : fn(fn), + objects(objects) + { + maxProgress = 100*objects.size(); + } + + QFuture<R> future() + { + futureInterface.reportStarted(); + return futureInterface.future(); + } + + void run() + { + QThreadPool::globalInstance()->releaseThread(); + futureInterface.setProgressRange(0, maxProgress); + foreach (Class *object, objects) { + QFutureWatcher<R> *watcher = new QFutureWatcher<R>(); + watchers.insert(object, watcher); + finished.insert(watcher, false); + connect(watcher, SIGNAL(finished()), this, SLOT(setFinished())); + connect(watcher, SIGNAL(progressRangeChanged(int,int)), this, SLOT(setProgressRange(int,int))); + connect(watcher, SIGNAL(progressValueChanged(int)), this, SLOT(setProgressValue(int))); + connect(watcher, SIGNAL(progressTextChanged(QString)), this, SLOT(setProgressText(QString))); + watcher->setFuture(QtConcurrent::run(fn, object)); + } + selfWatcher = new QFutureWatcher<R>(); + connect(selfWatcher, SIGNAL(canceled()), this, SLOT(cancelSelf())); + selfWatcher->setFuture(futureInterface.future()); + loop = new QEventLoop; + loop->exec(); + futureInterface.reportFinished(); + QThreadPool::globalInstance()->reserveThread(); + qDeleteAll(watchers); + delete selfWatcher; + delete loop; + } +protected: + void cancelSelf() + { + foreach (QFutureWatcher<R> *watcher, watchers) + watcher->future().cancel(); + } + + void setFinished() + { + updateProgress(); + QFutureWatcher<R> *watcher = static_cast<QFutureWatcher<R> *>(sender()); + if (finished.contains(watcher)) + finished[watcher] = true; + bool allFinished = true; + foreach (bool isFinished, finished) { + if (!isFinished) { + allFinished = false; + break; + } + } + if (allFinished) + loop->quit(); + } + + void setProgressRange(int min, int max) + { + Q_UNUSED(min) + Q_UNUSED(max) + updateProgress(); + } + + void setProgressValue(int value) + { + Q_UNUSED(value) + updateProgress(); + } + + void setProgressText(QString value) + { + Q_UNUSED(value) + updateProgressText(); + } +private: + void updateProgress() + { + int progressSum = 0; + foreach (QFutureWatcher<R> *watcher, watchers) { + if (watcher->progressMinimum() == watcher->progressMaximum()) { + if (watcher->future().isFinished() && !watcher->future().isCanceled()) + progressSum += 100; + } else { + progressSum += 100*(watcher->progressValue()-watcher->progressMinimum())/(watcher->progressMaximum()-watcher->progressMinimum()); + } + } + futureInterface.setProgressValue(progressSum); + } + + void updateProgressText() + { + QString text; + foreach (QFutureWatcher<R> *watcher, watchers) { + if (!watcher->progressText().isEmpty()) + text += watcher->progressText() + "\n"; + } + text = text.trimmed(); + futureInterface.setProgressValueAndText(futureInterface.progressValue(), text); + } + + QFutureInterface<R> futureInterface; + void (Class::*fn)(QFutureInterface<R> &); + QList<Class *> objects; + + QFutureWatcher<R> *selfWatcher; + QMap<Class *, QFutureWatcher<R> *> watchers; + QMap<QFutureWatcher<R> *, bool> finished; + QEventLoop *loop; + int maxProgress; +}; + +template <typename Class, typename T> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), const QList<Class *> &objects, int priority = 0) +{ + MultiTask<Class, T> *task = new MultiTask<Class, T>(fn, objects); + QFuture<T> future = task->future(); + QThreadPool::globalInstance()->start(task, priority); + return future; +} + +} // namespace QtConcurrent + +QT_END_NAMESPACE + +#endif // MULTITASK_H diff --git a/src/lib/qtconcurrent/qtconcurrent.pri b/src/lib/qtconcurrent/qtconcurrent.pri new file mode 100644 index 000000000..5aebdb6af --- /dev/null +++ b/src/lib/qtconcurrent/qtconcurrent.pri @@ -0,0 +1,6 @@ +DEFINES += BUILD_QTCONCURRENT + +HEADERS += \ + $$PWD/qtconcurrent_global.h \ + $$PWD/multitask.h \ + $$PWD/runextensions.h diff --git a/src/lib/qtconcurrent/qtconcurrent_global.h b/src/lib/qtconcurrent/qtconcurrent_global.h new file mode 100644 index 000000000..1e89819be --- /dev/null +++ b/src/lib/qtconcurrent/qtconcurrent_global.h @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QTCONCURRENT_GLOBAL_H +#define QTCONCURRENT_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(BUILD_QTCONCURRENT) +# define QTCONCURRENT_EXPORT Q_DECL_EXPORT +#else +# define QTCONCURRENT_EXPORT Q_DECL_IMPORT +#endif + +#endif // QTCONCURRENT_GLOBAL_H diff --git a/src/lib/qtconcurrent/runextensions.h b/src/lib/qtconcurrent/runextensions.h new file mode 100644 index 000000000..32bb74c13 --- /dev/null +++ b/src/lib/qtconcurrent/runextensions.h @@ -0,0 +1,431 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QTCONCURRENT_RUNEX_H +#define QTCONCURRENT_RUNEX_H + +#include <qrunnable.h> +#include <qfutureinterface.h> +#include <qthreadpool.h> + +QT_BEGIN_NAMESPACE + +namespace QtConcurrent { + +template <typename T, typename FunctionPointer> +class StoredInterfaceFunctionCall0 : public QRunnable +{ +public: + StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &)) + : fn(fn) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + +}; +template <typename T, typename FunctionPointer, typename Class> +class StoredInterfaceMemberFunctionCall0 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object) + : fn(fn), object(object) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + +}; + +template <typename T, typename FunctionPointer, typename Arg1> +class StoredInterfaceFunctionCall1 : public QRunnable +{ +public: + StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1) + : fn(fn), arg1(arg1) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1> +class StoredInterfaceMemberFunctionCall1 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1) + : fn(fn), object(object), arg1(arg1) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2> +class StoredInterfaceFunctionCall2 : public QRunnable +{ +public: + StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2) + : fn(fn), arg1(arg1), arg2(arg2) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2> +class StoredInterfaceMemberFunctionCall2 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) + : fn(fn), object(object), arg1(arg1), arg2(arg2) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3> +class StoredInterfaceFunctionCall3 : public QRunnable +{ +public: + StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3> +class StoredInterfaceMemberFunctionCall3 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +class StoredInterfaceFunctionCall4 : public QRunnable +{ +public: + StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3, arg4); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +class StoredInterfaceMemberFunctionCall4 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +class StoredInterfaceFunctionCall5 : public QRunnable +{ +public: + StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3, arg4, arg5); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +class StoredInterfaceMemberFunctionCall5 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; +}; + +template <typename T> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &)) +{ + return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start(); +} +template <typename T, typename Arg1> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1) +{ + return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start(); +} +template <typename T, typename Arg1, typename Arg2> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2) +{ + return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) +{ + return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) +{ + return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) +{ + return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start(); +} + +template <typename Class, typename T> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object) +{ + return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start(); +} + +template <typename Class, typename T, typename Arg1> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1) +{ + return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) +{ + return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) +{ + return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) +{ + return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) +{ + return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start(); +} +} // namespace QtConcurrent + +QT_END_NAMESPACE + +#endif // QTCONCURRENT_RUNEX_H diff --git a/src/lib/tools/codelocation.h b/src/lib/tools/codelocation.h new file mode 100644 index 000000000..d1ebeff86 --- /dev/null +++ b/src/lib/tools/codelocation.h @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef SOURCELOCATION_H +#define SOURCELOCATION_H + +#include <QtCore/QString> +#include <QtCore/QDataStream> + +namespace qbs { + +struct CodeLocation +{ + CodeLocation() + : line(0), column(0) + {} + + CodeLocation(const QString &aFileName, int aLine = 0, int aColumn = 0) + : fileName(aFileName), + line(aLine), + column(aColumn) + {} + + bool isValid() const + { + return !fileName.isEmpty(); + } + + bool operator != (const CodeLocation &rhs) const + { + return fileName != rhs.fileName || line != rhs.line || column != rhs.column; + } + + QString fileName; + int line; + int column; +}; + +} // namespace qbs + +inline QDataStream &operator<< (QDataStream &s, const qbs::CodeLocation &o) +{ + s << o.fileName; + s << o.line; + s << o.column; + return s; +} + +inline QDataStream &operator>> (QDataStream &s, qbs::CodeLocation &o) +{ + s >> o.fileName; + s >> o.line; + s >> o.column; + return s; +} + +#endif // SOURCELOCATION_H diff --git a/src/lib/tools/coloredoutput.cpp b/src/lib/tools/coloredoutput.cpp new file mode 100644 index 000000000..8039c21ef --- /dev/null +++ b/src/lib/tools/coloredoutput.cpp @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "coloredoutput.h" +#include <qglobal.h> +#ifdef Q_OS_WIN32 +# include <qt_windows.h> +#endif + +#include <cstdarg> + +#ifdef Q_OS_UNIX +#include <unistd.h> +#endif + +namespace qbs { + +void printfColored(TextColor color, const char *str, va_list vl) +{ + fprintfColored(color, stdout, str, vl); +} + +void printfColored(TextColor color, const char *str, ...) +{ + va_list vl; + va_start(vl, str); + printfColored(color, str, vl); + va_end(vl); +} + +void fprintfColored(TextColor color, FILE *file, const char *str, va_list vl) +{ +#if defined(Q_OS_UNIX) + if (color != TextColorDefault && isatty(fileno(stdout))) { + unsigned char bright = (color & TextColorBright) >> 3; + fprintf(file, "\033[%d;%dm", bright, 30 + (color & ~TextColorBright)); + vfprintf(file, str, vl); + fprintf(stdout, "\033[0m"); + fprintf(stderr, "\033[0m"); + } else +#elif defined(Q_OS_WIN32) + HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + if (color != TextColorDefault + && hStdout != INVALID_HANDLE_VALUE + && GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) + { + WORD bgrColor = ((color & 1) << 2) | (color & 2) | ((color & 4) >> 2); // BGR instead of RGB. + if (color & TextColorBright) + bgrColor += FOREGROUND_INTENSITY; + SetConsoleTextAttribute(hStdout, (csbiInfo.wAttributes & 0xf0) | bgrColor); + vfprintf(file, str, vl); + SetConsoleTextAttribute(hStdout, csbiInfo.wAttributes); + } else +#endif + { + vfprintf(file, str, vl); + } +} + +void fprintfColored(TextColor color, FILE *file, const char *str, ...) +{ + va_list vl; + va_start(vl, str); + fprintfColored(color, file, str, vl); + va_end(vl); +} + +} // namespace qbs diff --git a/src/lib/tools/coloredoutput.h b/src/lib/tools/coloredoutput.h new file mode 100644 index 000000000..65385aa87 --- /dev/null +++ b/src/lib/tools/coloredoutput.h @@ -0,0 +1,73 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef COLOREDOUTPUT_H +#define COLOREDOUTPUT_H + +#include <cstdio> + +namespace qbs { + +// http://en.wikipedia.org/wiki/ANSI_escape_code#Colors +enum TextColor { + TextColorDefault = -1, + TextColorBlack = 0, + TextColorDarkRed = 1, + TextColorDarkGreen = 2, + TextColorDarkBlue = 4, + TextColorDarkCyan = TextColorDarkGreen | TextColorDarkBlue, + TextColorDarkMagenta = TextColorDarkRed | TextColorDarkBlue, + TextColorDarkYellow = TextColorDarkRed | TextColorDarkGreen, + TextColorGray = 7, + TextColorBright = 8, + TextColorRed = TextColorDarkRed | TextColorBright, + TextColorGreen = TextColorDarkGreen | TextColorBright, + TextColorBlue = TextColorDarkBlue | TextColorBright, + TextColorCyan = TextColorDarkCyan | TextColorBright, + TextColorMagenta = TextColorDarkMagenta | TextColorBright, + TextColorYellow = TextColorDarkYellow | TextColorBright, + TextColorWhite = TextColorGray | TextColorBright +}; + +void printfColored(TextColor color, const char *str, va_list vl); +void printfColored(TextColor color, const char *str, ...); +void fprintfColored(TextColor color, FILE *file, const char *str, va_list vl); +void fprintfColored(TextColor color, FILE *file, const char *str, ...); + +} // namespace qbs + +#endif // COLOREDOUTPUT_H diff --git a/src/lib/tools/error.cpp b/src/lib/tools/error.cpp new file mode 100644 index 000000000..39e5113f4 --- /dev/null +++ b/src/lib/tools/error.cpp @@ -0,0 +1,70 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "error.h" + +#include <QtCore/QDir> + +namespace qbs { + +QString Error::toString() const +{ + QString str; + if (!file.isEmpty()) { + str = QDir::toNativeSeparators(file); + QString lineAndColumn; + if (line > 0 && !str.contains(QRegExp(QLatin1String(":[0-9]+$")))) + lineAndColumn += QLatin1Char(':') + QString::number(line); + if (column > 0 && !str.contains(QRegExp(QLatin1String(":[0-9]+:[0-9]+$")))) + lineAndColumn += QLatin1Char(':') + QString::number(column); + str += lineAndColumn; + str += QLatin1Char(' ') + description; + } else { + str = description; + } + return str; +} + +void Error::clear() +{ + description.clear(); + file.clear(); + line = 0; + column = 0; +} + +} diff --git a/src/lib/tools/error.h b/src/lib/tools/error.h new file mode 100644 index 000000000..0a05e1534 --- /dev/null +++ b/src/lib/tools/error.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QBS_ERR +#define QBS_ERR + +#include "codelocation.h" + +namespace qbs { + +class Error +{ +public: + Error() + : line(0), column(0) + {} + + Error(const Error &rhs) + : description(rhs.description) + , file(rhs.file) + , line(rhs.line) + , column(rhs.column) + {} + + Error(const QString &_description, + const QString &_file = QString(), + int _line = 0, int _column = 0) + : description(_description) + , file(_file) + , line(_line) + , column(_column) + { + } + + Error(const QString &_description, const CodeLocation &location) + : description(_description) + , file(location.fileName) + , line(location.line) + , column(location.column) + { + } + + QString toString() const; + void clear(); + + QString description; + QString file; + int line, column; +}; + +} // namespace qbs + +#define QBS_TESTLIB_CATCH catch (qbs::Error &e) { \ + QFAIL(qPrintable(QString(QLatin1String("\n%1\n")).arg(e.toString())));} + +#endif diff --git a/src/lib/tools/fakeconcurrent.h b/src/lib/tools/fakeconcurrent.h new file mode 100644 index 000000000..8505903aa --- /dev/null +++ b/src/lib/tools/fakeconcurrent.h @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef FAKECONCURRENT_H +#define FAKECONCURRENT_H + +#include <qtconcurrent/runextensions.h> + +namespace qbs { +namespace FakeConcurrent { + +template <typename Class, typename T> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object) +{ + QFuture<T> result; + QFutureInterface<T> futureInterface; + (object->*fn)(futureInterface); + return result; +} + +template <typename Class, typename T, typename Arg1> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1) +{ + QFuture<T> result; + QFutureInterface<T> futureInterface; + (object->*fn)(futureInterface, arg1); + return result; +} + +template <typename Class, typename T, typename Arg1, typename Arg2> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) +{ + QFuture<T> result; + QFutureInterface<T> futureInterface; + (object->*fn)(futureInterface, arg1, arg2); + return result; +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) +{ + QFuture<T> result; + QFutureInterface<T> futureInterface; + (object->*fn)(futureInterface, arg1, arg2, arg3); + return result; +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) +{ + QFuture<T> result; + QFutureInterface<T> futureInterface; + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4); + return result; +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) +{ + QFuture<T> result; + QFutureInterface<T> futureInterface; + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5); + return result; +} + +} // namespace FakeConcurrent +} // namespace qbs + +#endif // FAKECONCURRENT_H diff --git a/src/lib/tools/fileinfo.cpp b/src/lib/tools/fileinfo.cpp new file mode 100644 index 000000000..3dba942e0 --- /dev/null +++ b/src/lib/tools/fileinfo.cpp @@ -0,0 +1,247 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "fileinfo.h" +#include <QtCore/QCoreApplication> +#include <QDir> +#include <QFileInfo> +#include <QDebug> +#include <cassert> + + +#ifdef Q_OS_UNIX +#include <sys/stat.h> +#endif + +namespace qbs { + +QString FileInfo::fileName(const QString &fp) +{ + int last = fp.lastIndexOf('/'); + if (last < 0) + return fp; + return fp.mid(last + 1); +} + +QString FileInfo::baseName(const QString &fp) +{ + QString fn = fileName(fp); + int dot = fn.indexOf('.'); + if (dot < 0) + return fp; + return fn.mid(0, dot); +} + +QString FileInfo::completeBaseName(const QString &fp) +{ + QString fn = fileName(fp); + int dot = fn.lastIndexOf('.'); + if (dot < 0) + return fp; + return fn.mid(0, dot); +} + +QString FileInfo::path(const QString &fp) +{ + if (fp.isEmpty()) + return QString(); + if (fp.at(fp.size() - 1) == '/') + return fp; + int last = fp.lastIndexOf('/'); + if (last < 0) + return "."; + return QDir::cleanPath(fp.mid(0, last)); +} + +bool FileInfo::exists(const QString &fp) +{ + return FileInfo(fp).exists(); +} + +// from creator/src/shared/proparser/ioutils.cpp +bool FileInfo::isAbsolute(const QString &path) +{ + if (path.startsWith(QLatin1Char('/'))) + return true; +#ifdef Q_OS_WIN + if (path.startsWith(QLatin1Char('\\'))) + return true; + // Unlike QFileInfo, this won't accept a relative path with a drive letter. + // Such paths result in a royal mess anyway ... + if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter() + && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) + return true; +#endif + return false; +} + +QString FileInfo::resolvePath(const QString &base, const QString &rel) +{ + if (isAbsolute(rel)) + return rel; + if (rel == QLatin1String(".")) + return base; + + QString r = base; + if (!r.endsWith('/')) { + r.append('/'); + } + r.append(rel); + + return r; +} + +bool FileInfo::globMatches(const QString &pattern, const QString &subject) +{ + // ### the QRegExp wildcard matcher is slow! + //QRegExp rex(pattern, Qt::CaseSensitive, QRegExp::Wildcard); + //return rex.exactMatch(subject); + return subject.endsWith(pattern.mid(1), Qt::CaseInsensitive); +} + +static QString resolveSymlinks(const QString &fileName) +{ + QFileInfo fi(fileName); + while (fi.isSymLink()) + fi.setFile(fi.symLinkTarget()); + return fi.absoluteFilePath(); +} + +#if defined(Q_OS_WIN) + +#include <qt_windows.h> + +#define z(x) reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA*>(const_cast<FileInfo::InternalStatType*>(&x)) + +template<bool> struct CompileTimeAssert; +template<> struct CompileTimeAssert<true> {}; + +FileInfo::FileInfo(const QString &fileName) +{ + static CompileTimeAssert< + sizeof(FileInfo::InternalStatType) == sizeof(WIN32_FILE_ATTRIBUTE_DATA) + > internal_type_has_wrong_size; + if (!GetFileAttributesEx(reinterpret_cast<const WCHAR*>(fileName.utf16()), + GetFileExInfoStandard, &m_stat)) + { + z(m_stat)->dwFileAttributes = INVALID_FILE_ATTRIBUTES; + } +} + +bool FileInfo::exists() const +{ + return z(m_stat)->dwFileAttributes != INVALID_FILE_ATTRIBUTES; +} + +FileTime FileInfo::lastModified() const +{ + return FileTime(*reinterpret_cast<const FileTime::InternalType*>( + &z(m_stat)->ftLastWriteTime)); +} + +QString applicationDirPath() +{ + static const QString appDirPath = FileInfo::path(resolveSymlinks(QCoreApplication::applicationFilePath())); + return appDirPath; +} + +#elif defined(Q_OS_UNIX) + +FileInfo::FileInfo(const QString &fileName) +{ + if (stat(fileName.toLocal8Bit(), &m_stat) == -1) + m_stat.st_mtime = 0; +} + +bool FileInfo::exists() const +{ + return m_stat.st_mtime != 0; +} + +FileTime FileInfo::lastModified() const +{ + return m_stat.st_mtime; +} + +QString applicationDirPath() +{ + return QCoreApplication::applicationDirPath(); +} + +#endif + +// adapted from qtc/plugins/vcsbase/cleandialog.cpp +static bool removeFileRecursion(const QFileInfo &f, QString *errorMessage) +{ + if (!f.exists()) + return true; + if (f.isDir()) { + const QDir dir(f.absoluteFilePath()); + foreach(const QFileInfo &fi, dir.entryInfoList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Hidden)) + removeFileRecursion(fi, errorMessage); + QDir parent = f.absoluteDir(); + if (!parent.rmdir(f.fileName())) { + errorMessage->append(FileInfo::tr("The directory %1 could not be deleted."). + arg(QDir::toNativeSeparators(f.absoluteFilePath()))); + return false; + } + } else if (!QFile::remove(f.absoluteFilePath())) { + if (!errorMessage->isEmpty()) + errorMessage->append(QLatin1Char('\n')); + errorMessage->append(FileInfo::tr("The file %1 could not be deleted."). + arg(QDir::toNativeSeparators(f.absoluteFilePath()))); + return false; + } + return true; +} + +bool removeDirectoryWithContents(const QString &path, QString *errorMessage) +{ + QFileInfo f(path); + if (f.exists() && !f.isDir()) { + *errorMessage = FileInfo::tr("%1 is not a directory.").arg(QDir::toNativeSeparators(path)); + return false; + } + return removeFileRecursion(f, errorMessage); +} + +QString qbsRootPath() +{ + return QDir::cleanPath(applicationDirPath() + QLatin1String("/../")); +} + +} diff --git a/src/lib/tools/fileinfo.h b/src/lib/tools/fileinfo.h new file mode 100644 index 000000000..e345f5f16 --- /dev/null +++ b/src/lib/tools/fileinfo.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef FILEINFO_H +#define FILEINFO_H + +#include "filetime.h" + +#if defined(Q_OS_UNIX) +#include <sys/stat.h> +#endif + +#include <QtCore/QString> +#include <QtCore/QCoreApplication> // for Q_DECLARE_TR_FUNCTIONS + +namespace qbs { + +class FileInfo +{ + Q_DECLARE_TR_FUNCTIONS(qbs::FileInfo) +public: + FileInfo(const QString &fileName); + + bool exists() const; + FileTime lastModified() const; + + static QString fileName(const QString &fp); + static QString baseName(const QString &fp); + static QString completeBaseName(const QString &fp); + static QString path(const QString &fp); + static bool exists(const QString &fp); + static bool isAbsolute(const QString &fp); + static QString resolvePath(const QString &base, const QString &rel); + static bool globMatches(const QString &pattern, const QString &subject); + +private: +#if defined(Q_OS_WIN) + struct InternalStatType + { + quint8 z[36]; + }; +#elif defined(Q_OS_UNIX) + typedef struct stat InternalStatType; +#else +# error unknown platform +#endif + InternalStatType m_stat; +}; + +QString applicationDirPath(); +QString qbsRootPath(); +bool removeDirectoryWithContents(const QString &path, QString *errorMessage); + +} + +#endif diff --git a/src/lib/tools/filetime.h b/src/lib/tools/filetime.h new file mode 100644 index 000000000..0b49c2e18 --- /dev/null +++ b/src/lib/tools/filetime.h @@ -0,0 +1,128 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef FILETIME_H +#define FILETIME_H + +#include <QtCore/QDataStream> +#include <QtCore/QDebug> + +#if defined(Q_OS_UNIX) +#include <time.h> +#endif + +namespace qbs { + +class FileTime +{ +public: +#if defined(Q_OS_UNIX) + typedef time_t InternalType; +#elif defined(Q_OS_WIN) + typedef quint64 InternalType; +#else +# error unknown platform +#endif + + FileTime(); + FileTime(const InternalType &ft) + : m_fileTime(ft) + { } + + bool operator < (const FileTime &rhs) const; + bool operator > (const FileTime &rhs) const; + bool operator <= (const FileTime &rhs) const; + bool operator >= (const FileTime &rhs) const; + bool operator == (const FileTime &rhs) const; + + void clear(); + bool isValid() const; + QString toString() const; + + static FileTime currentTime(); + + friend class FileInfo; + InternalType m_fileTime; +}; + +inline bool FileTime::operator > (const FileTime &rhs) const +{ + return rhs < *this; +} + +inline bool FileTime::operator <= (const FileTime &rhs) const +{ + return operator < (rhs) || operator == (rhs); +} + +inline bool FileTime::operator >= (const FileTime &rhs) const +{ + return operator > (rhs) || operator == (rhs); +} + +inline bool FileTime::operator == (const FileTime &rhs) const +{ + return m_fileTime == rhs.m_fileTime; +} + +} // namespace qbs + +QT_BEGIN_NAMESPACE + +inline QDataStream& operator>>(QDataStream &stream, qbs::FileTime &ft) +{ + quint64 u; + stream >> u; + ft.m_fileTime = u; + return stream; +} + +inline QDataStream& operator<<(QDataStream &stream, const qbs::FileTime &ft) +{ + stream << (quint64)ft.m_fileTime; + return stream; +} + +inline QDebug operator<<(QDebug dbg, const qbs::FileTime &t) +{ + dbg.nospace() << t.toString(); + return dbg.space(); +} + +QT_END_NAMESPACE + +#endif // FILETIME_H diff --git a/src/lib/tools/filetime_unix.cpp b/src/lib/tools/filetime_unix.cpp new file mode 100644 index 000000000..2d4560d4b --- /dev/null +++ b/src/lib/tools/filetime_unix.cpp @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "filetime.h" + +#include <QtCore/QString> +#include <QtCore/QDateTime> +#include <QtCore/QDebug> +#include <time.h> + +namespace qbs { + +FileTime::FileTime() + : m_fileTime(0) +{ +} + +bool FileTime::operator < (const FileTime &rhs) const +{ + return m_fileTime < rhs.m_fileTime; +} + +void FileTime::clear() +{ + m_fileTime = 0; +} + +bool FileTime::isValid() const +{ + return m_fileTime != 0; +} + +FileTime FileTime::currentTime() +{ + return time(0); +} + +QString FileTime::toString() const +{ + QDateTime dt; + dt.setTime_t(m_fileTime); + return dt.toString(); +} +} diff --git a/src/lib/tools/filetime_win.cpp b/src/lib/tools/filetime_win.cpp new file mode 100644 index 000000000..8313dbbee --- /dev/null +++ b/src/lib/tools/filetime_win.cpp @@ -0,0 +1,104 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "filetime.h" + +#include <QtCore/QString> +#include <qt_windows.h> +#ifdef Q_CC_MSVC +#include <strsafe.h> +#endif // Q_CC_MSVC + +template<bool> struct CompileTimeAssert; +template<> struct CompileTimeAssert<true> {}; +static CompileTimeAssert<sizeof(qbs::FileTime::InternalType) == sizeof(FILETIME)> internal_type_has_wrong_size; + +namespace qbs { + +FileTime::FileTime() + : m_fileTime(0) +{ +} + +bool FileTime::operator < (const FileTime &rhs) const +{ + const FILETIME *const t1 = reinterpret_cast<const FILETIME *>(&m_fileTime); + const FILETIME *const t2 = reinterpret_cast<const FILETIME *>(&rhs.m_fileTime); + return CompareFileTime(t1, t2) < 0; +} + +void FileTime::clear() +{ + m_fileTime = 0; +} + +bool FileTime::isValid() const +{ + return m_fileTime != 0; +} + +FileTime FileTime::currentTime() +{ + FileTime result; + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME *const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime); + SystemTimeToFileTime(&st, ft); + return result; +} + +QString FileTime::toString() const +{ + const FILETIME *const ft = reinterpret_cast<const FILETIME *>(&m_fileTime); + SYSTEMTIME stUTC, stLocal; + FileTimeToSystemTime(ft, &stUTC); + SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal); +#ifdef Q_CC_MSVC + WCHAR szString[512]; + HRESULT hr = StringCchPrintf(szString, sizeof(szString) / sizeof(WCHAR), + L"%02d.%02d.%d %02d:%02d:%02d", + stLocal.wDay, stLocal.wMonth, stLocal.wYear, + stLocal.wHour, stLocal.wMinute, stLocal.wSecond); + return SUCCEEDED(hr) ? QString::fromUtf16(szString) : QString(); +#else // Q_CC_MSVC + const QString result = QString("%1.%2.%3 %4:%5:%6") + .arg(stLocal.wDay, 2, 10, QLatin1Char('0')).arg(stLocal.wMonth, 2, 10, QLatin1Char('0')).arg(stLocal.wYear) + .arg(stLocal.wHour, 2, 10, QLatin1Char('0')).arg(stLocal.wMinute, 2, 10, QLatin1Char('0')).arg(stLocal.wSecond, 2, 10, QLatin1Char('0')); + return result; +#endif // Q_CC_MSVC +} +} diff --git a/src/lib/tools/logger.cpp b/src/lib/tools/logger.cpp new file mode 100644 index 000000000..977abf113 --- /dev/null +++ b/src/lib/tools/logger.cpp @@ -0,0 +1,325 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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 defined(_MSC_VER) && _MSC_VER > 0 +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "logger.h" +#include "logsink.h" + +#include <cstdarg> +#include <stdio.h> + +#include <QtCore/QSet> +#include <QtCore/QByteArray> +#include <QtCore/QMutex> +#include <QtCore/QVariant> + + +namespace qbs { + +Logger &Logger::instance() +{ + static Logger instance; + return instance; +} + +Logger::Logger() + : m_logSink(0) + , m_level(LoggerInfo) +{ +} + +Logger::~Logger() +{ + delete m_logSink; +} + +void Logger::setLogSink(ILogSink *logSink) +{ + m_logSink = logSink; +} + +void Logger::sendProcessOutput(const ProcessOutput &processOutput) +{ + if (!m_logSink) + return; + + m_logSink->processOutput(processOutput); +} + +void Logger::setLevel(int level) +{ + m_level = static_cast<LoggerLevel>(qMin(level, int(LoggerMaxLevel))); +} + +void Logger::setLevel(LoggerLevel level) +{ + m_level = level; +} + +void Logger::print(LoggerLevel l, const LogMessage &message) +{ + if (!m_logSink) + return; + m_logSink->outputLogMessage(l, message); +} + +LogWriter::LogWriter(LoggerLevel level) + : m_level(level) +{} + +LogWriter::LogWriter(const LogWriter &other) + : m_level(other.m_level) + , m_logMessage(other.m_logMessage) +{ + other.m_logMessage.data.clear(); +} + +LogWriter::~LogWriter() +{ + if (!m_logMessage.data.isEmpty()) + Logger::instance().print(m_level, m_logMessage); +} + +const LogWriter &LogWriter::operator=(const LogWriter &other) +{ + m_level = other.m_level; + m_logMessage = other.m_logMessage; + other.m_logMessage.data.clear(); + + return *this; +} + +void LogWriter::write(const char c) +{ + if (Logger::instance().level() >= m_level) + m_logMessage.data.append(c); +} + +void LogWriter::write(const char *str) +{ + if (Logger::instance().level() >= m_level) + m_logMessage.data.append(str); +} + +} // namespace qbs + + +using namespace qbs; + +Q_GLOBAL_STATIC(QMutex, logMutex) +Q_GLOBAL_STATIC(QByteArray, logByteArray) + +static void qbsLog_impl(LoggerLevel logLevel, const char *str, va_list vl) +{ + Logger &logger = Logger::instance(); + if (logger.level() < logLevel) + return; + logMutex()->lock(); + if (logByteArray()->isEmpty()) + logByteArray()->resize(1024 * 1024); + vsnprintf(logByteArray()->data(), logByteArray()->size(), str, vl); + LogMessage msg; + msg.data = *logByteArray(); + logMutex()->unlock(); + logger.print(logLevel, msg); +} + +void qbsLog(LoggerLevel logLevel, const char *str, ...) +{ + va_list vl; + va_start(vl, str); + qbsLog_impl(logLevel, str, vl); + va_end(vl); +} + +#define DEFINE_QBS_LOG_FUNCTION(LogLevel) \ + void qbs##LogLevel(const char *str, ...) \ + { \ + const LoggerLevel level = Logger##LogLevel; \ + Logger &logger = Logger::instance(); \ + if (logger.level() >= level) { \ + va_list vl; \ + va_start(vl, str); \ + qbsLog_impl(level, str, vl); \ + va_end(vl); \ + } \ + } \ + +DEFINE_QBS_LOG_FUNCTION(Fatal) +DEFINE_QBS_LOG_FUNCTION(Error) +DEFINE_QBS_LOG_FUNCTION(Warning) +DEFINE_QBS_LOG_FUNCTION(Info) +DEFINE_QBS_LOG_FUNCTION(Debug) +DEFINE_QBS_LOG_FUNCTION(Trace) + +LogWriter qbsLog(LoggerLevel level) +{ + return LogWriter(level); +} + +LogWriter qbsFatal() +{ + return LogWriter(LoggerFatal); +} + +LogWriter qbsError() +{ + return LogWriter(LoggerError); +} + +LogWriter qbsWarning() +{ + return LogWriter(LoggerWarning); +} + +LogWriter qbsInfo() +{ + return LogWriter(LoggerInfo); +} + +LogWriter qbsDebug() +{ + return LogWriter(LoggerDebug); +} + +LogWriter qbsTrace() +{ + return LogWriter(LoggerTrace); +} + +LogWriter operator<<(LogWriter w, const char *str) +{ + w.write(str); + return w; +} + +LogWriter operator<<(LogWriter w, const QByteArray &byteArray) +{ + w.write(byteArray.data()); + return w; +} + +LogWriter operator<<(LogWriter w, const QString &str) +{ + w.write(qPrintable(str)); + return w; +} + +LogWriter operator<<(LogWriter w, const QStringList &strList) +{ + for (int i = 0; i < strList.size(); ++i) { + w << strList.at(i); + if (i != strList.size() - 1) + w.write(", "); + } + return w; +} + +LogWriter operator<<(LogWriter w, const QSet<QString> &strSet) +{ + bool firstLoop = true; + w.write('('); + foreach (const QString &str, strSet) { + if (firstLoop) + firstLoop = false; + else + w.write(", "); + w.write(qPrintable(str)); + } + w.write(')'); + return w; +} + +LogWriter operator<<(LogWriter w, const QVariant &variant) +{ + QString str = variant.typeName() + QLatin1String("("); + if (variant.type() == QVariant::List) { + bool firstLoop = true; + foreach (const QVariant &item, variant.toList()) { + str += item.toString(); + if (firstLoop) + firstLoop = false; + else + str += QLatin1String(", "); + } + } else { + str += variant.toString(); + } + str += QLatin1String(")"); + w.write(qPrintable(str)); + return w; +} + +LogWriter operator<<(LogWriter w, int n) +{ + return w << QString::number(n); +} + +LogWriter operator<<(LogWriter w, qint64 n) +{ + return w << QString::number(n); +} + +LogWriter operator<<(LogWriter w, bool b) +{ + return w << (b ? "true" : "false"); +} + +LogWriter operator<<(LogWriter w, LogOutputChannel channel) +{ + w.setOutputChannel(channel); + return w; +} + +LogWriter operator<<(LogWriter w, LogModifier modifier) +{ + switch (modifier) { + case DontPrintLogLevel: + w.setPrintLogLevel(false); + break; + } + return w; +} + +LogWriter operator<<(LogWriter w, TextColor color) +{ + w.setTextColor(color); + return w; +} diff --git a/src/lib/tools/logger.h b/src/lib/tools/logger.h new file mode 100644 index 000000000..ca482b742 --- /dev/null +++ b/src/lib/tools/logger.h @@ -0,0 +1,138 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef LOGGER_H +#define LOGGER_H + +#include <Qbs/globals.h> + +#include <Qbs/ilogsink.h> + +#include <QtCore/QByteArray> +#include <QtCore/QString> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QVariant; +QT_END_NAMESPACE + +namespace qbs { + +class Logger +{ +public: + static Logger &instance(); + + void setLevel(int level); + void setLevel(LoggerLevel level); + LoggerLevel level() const { return m_level; } + void print(LoggerLevel l, const LogMessage &message); + + ~Logger(); + + void setLogSink(ILogSink *logSink); + void sendProcessOutput(const Qbs::ProcessOutput &processOutput); + +protected: + Logger(); + +private: + ILogSink *m_logSink; + LoggerLevel m_level; +}; + +class LogWriter +{ +public: + LogWriter(LoggerLevel level); + + // log writer has move semantics and the last instance of + // a << chain prints the accumulated data + LogWriter(const LogWriter &other); + ~LogWriter(); + const LogWriter &operator=(const LogWriter &other); + + void write(const char c); + void write(const char *str); + void setOutputChannel(LogOutputChannel channel) { m_logMessage.outputChannel = channel; } + void setPrintLogLevel(bool b) { m_logMessage.printLogLevel = b; } + void setTextColor(TextColor color) { m_logMessage.textColor = color; } + const Qbs::LogMessage &logMessage() const { return m_logMessage; } + +private: + LoggerLevel m_level; + mutable Qbs::LogMessage m_logMessage; +}; + +enum LogModifier +{ + DontPrintLogLevel +}; + +} // namespace qbs + +inline bool qbsLogLevel(qbs::LoggerLevel l) { return qbs::Logger::instance().level() >= l; } +void qbsLog(qbs::LoggerLevel logLevel, const char *str, ...); +void qbsFatal(const char *str, ...); +void qbsError(const char *str, ...); +void qbsWarning(const char *str, ...); +void qbsInfo(const char *str, ...); +void qbsDebug(const char *str, ...); +void qbsTrace(const char *str, ...); + +qbs::LogWriter qbsLog(qbs::LoggerLevel level); +qbs::LogWriter qbsFatal(); +qbs::LogWriter qbsError(); +qbs::LogWriter qbsWarning(); +qbs::LogWriter qbsInfo(); +qbs::LogWriter qbsDebug(); +qbs::LogWriter qbsTrace(); + +qbs::LogWriter operator<<(qbs::LogWriter w, const char *str); +qbs::LogWriter operator<<(qbs::LogWriter w, const QByteArray &byteArray); +qbs::LogWriter operator<<(qbs::LogWriter w, const QString &str); +qbs::LogWriter operator<<(qbs::LogWriter w, const QStringList &strList); +qbs::LogWriter operator<<(qbs::LogWriter w, const QSet<QString> &strSet); +qbs::LogWriter operator<<(qbs::LogWriter w, const QVariant &variant); +qbs::LogWriter operator<<(qbs::LogWriter w, int n); +qbs::LogWriter operator<<(qbs::LogWriter w, qint64 n); +qbs::LogWriter operator<<(qbs::LogWriter w, bool b); +qbs::LogWriter operator<<(qbs::LogWriter w, qbs::LogOutputChannel); +qbs::LogWriter operator<<(qbs::LogWriter w, qbs::LogModifier); +qbs::LogWriter operator<<(qbs::LogWriter w, qbs::TextColor); + +#endif // LOGGER_H diff --git a/src/lib/tools/logsink.cpp b/src/lib/tools/logsink.cpp new file mode 100644 index 000000000..a9a4cc064 --- /dev/null +++ b/src/lib/tools/logsink.cpp @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "logsink.h" +#include "logger.h" +#include "coloredoutput.h" + +#include <qhash.h> +#include <stdio.h> + +namespace qbs { + +void ConsolePrintLogSink::outputLogMessage(LoggerLevel level, const LogMessage &message) +{ + FILE *file = stdout; + if (message.outputChannel == LogOutputStdErr) + file = stderr; + + if (message.printLogLevel) { + switch (level) { + case LoggerFatal: + fprintfColored(TextColorRed, file, "FATAL ERROR: "); + break; + case LoggerError: + fprintfColored(TextColorRed, file, "ERROR: "); + break; + case LoggerWarning: + fprintfColored(TextColorYellow, file, "WARNING: "); + break; + case LoggerInfo: + fprintf(file, "INFO: "); + break; + case LoggerDebug: + fprintf(file, "DEBUG: "); + break; + case LoggerTrace: + fprintf(file, "TRACE: "); + break; + } + } + if (message.textColor == TextColorDefault || !m_coloredOutputEnabled) + fprintf(file, "%s\n", message.data.data()); + else + fprintfColored(message.textColor, file, "%s\n", message.data.data()); +} + +} // namespace qbs diff --git a/src/lib/tools/logsink.h b/src/lib/tools/logsink.h new file mode 100644 index 000000000..f9db7cfb7 --- /dev/null +++ b/src/lib/tools/logsink.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef LOGSINK_H +#define LOGSINK_H + +#include "logger.h" +#include <Qbs/ilogsink.h> + +namespace qbs { + +class ConsolePrintLogSink : public Qbs::ILogSink +{ +public: + void setColoredOutputEnabled(bool enabled) { m_coloredOutputEnabled = enabled; } + +protected: + virtual void outputLogMessage(LoggerLevel level, const LogMessage &message); + +private: + bool m_coloredOutputEnabled; +}; + +} // namespace qbs + +#endif // LOGSINK_H diff --git a/src/lib/tools/options.cpp b/src/lib/tools/options.cpp new file mode 100644 index 000000000..02ef521bc --- /dev/null +++ b/src/lib/tools/options.cpp @@ -0,0 +1,450 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "options.h" + +#include <tools/error.h> +#include <tools/fileinfo.h> +#include <tools/logger.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QCoreApplication> +#include <QtCore/QThread> +#include <tools/platform.h> + +namespace qbs { + +CommandLineOptions::CommandLineOptions() + : m_command(BuildCommand) + , m_dumpGraph(false) + , m_gephi (false) + , m_help (false) + , m_clean (false) + , m_keepGoing(false) + , m_jobs(QThread::idealThreadCount()) +{ + m_settings = Settings::create(); +} + +void CommandLineOptions::printHelp() +{ + fputs("usage: qbs [command] [options]\n" + "\ncommands:\n" + " build [variant] [property:value]\n" + " build project (default command)\n" + " clean ........... remove all generated files\n" + " shell ........... open a shell\n" + " run target [variant] [property:value] -- <args>\n" + " run the specified target\n" + " config\n" + " set, get, or get all project/global options\n" + "\ngeneral options:\n" + " -h -? --help .... this help\n" + " -d .............. dump graph\n" + " -f <file> ....... specify .qbp project file\n" + " -v .............. verbose (add up to 5 v to increase verbosity level)\n" + "\nbuild options:\n" + " -j <n> .......... use <n> jobs (default is nr of cores)\n" + " -k .............. keep going (ignore errors)\n" + " -n .............. dry run\n" + " --changed-files file [file 2] ... [file n]\n" + " .............. specify a list of out of date files\n" + " --products ProductName [product name 2] ... [product name n]\n" + " .............. specify a list of products to build\n" + "\ngraph options:\n" + " -g .............. show generation graph\n" + , stderr); +} + +/** + * Finds automatically the project file (.qbs) in the search directory. + * If there's more than one project file found then this function returns false. + * A project file can explicitely be given by the -f option. + */ +bool CommandLineOptions::readCommandLineArguments(const QStringList &args) +{ + m_projectFileName.clear(); + m_dryRun = false; + bool firstPositionalEaten = false; + bool runArgMode = false; + int verbosity = 0; + + const int argc = args.size(); + for (int i = 0; i < argc; ++i) { + QString arg = args.at(i); + if (runArgMode) { + m_runArgs.append(arg); + continue; + } + if (arg == "--") { + runArgMode = true; + continue; + } + + // --- no dash + if (arg.at(0) != '-' || arg.length() < 2) { + if (arg == QLatin1String("build")) { + m_command = BuildCommand; + } else if (arg == QLatin1String("config")) { + m_command = ConfigCommand; + m_configureArgs = args.mid(i + 1); + break; + } else if (arg == QLatin1String("clean")) { + m_command = CleanCommand; + } else if (arg == QLatin1String("shell")) { + m_command = StartShellCommand; + } else if (arg == QLatin1String("run")) { + m_command = RunCommand; + } else if (m_command == RunCommand && !firstPositionalEaten) { + m_runTargetName = arg; + firstPositionalEaten = true; + } else { + m_positional.append(arg); + } + + // --- two dashes + } else if (arg.at(1) == '-') { + + arg = arg.remove(0, 2).toLower(); + QString *targetString = 0; + QStringList *targetStringList = 0; + + if (arg == QLatin1String("help")) { + m_help = true; + } else if (arg == "changed-files" && m_command == BuildCommand) { + QStringList changedFiles; + for (++i; i < argc && !args.at(i).startsWith('-'); ++i) + changedFiles += args.at(i); + if (changedFiles.isEmpty()) { + qbsError("--changed-files expects one or more file names."); + return false; + } + m_changedFiles = changedFiles; + --i; + continue; + } else if (arg == "products" && (m_command == BuildCommand || m_command == CleanCommand)) { + QStringList productNames; + for (++i; i < argc && !args.at(i).startsWith('-'); ++i) + productNames += args.at(i); + if (productNames.isEmpty()) { + qbsError("--products expects one or more product names."); + return false; + } + m_selectedProductNames = productNames; + --i; + continue; + } else { + QString err = QLatin1String("Parameter '%1' is unknown."); + qbsError(qPrintable(err.arg(QLatin1String("--") + arg))); + return false; + } + + QString stringValue; + if (targetString || targetStringList) { + // string value expected + if (++i >= argc) { + QString err = QLatin1String("String value expected after parameter '%1'."); + qbsError(qPrintable(err.arg(QLatin1String("--") + arg))); + return false; + } + stringValue = args.at(i); + } + + if (targetString) + *targetString = stringValue; + else if (targetStringList) + targetStringList->append(stringValue); + + // --- one dash + } else { + int k = 1; + switch (arg.at(1).toLower().unicode()) { + case '?': + case 'h': + m_help = true; + break; + case 'j': + if (arg.length() > 2) { + const QByteArray str(arg.mid(2).toAscii()); + char *endStr = 0; + m_jobs = strtoul(str.data(), &endStr, 10); + k += endStr - str.data(); + } else if (++i < argc) { + m_jobs = args.at(i).toInt(); + } + if (m_jobs < 1) + return false; + break; + case 'v': + verbosity = 1; + while (k < arg.length() - 1 && arg.at(k + 1).toLower().unicode() == 'v') { + verbosity++; + k++; + } + break; + case 'd': + m_dumpGraph = true; + break; + case 'f': + if (arg.length() > 2) { + m_projectFileName = arg.mid(2); + k += m_projectFileName.length(); + } else if (++i < argc) { + m_projectFileName = args.at(i); + } + m_projectFileName = QDir::fromNativeSeparators(m_projectFileName); + break; + case 'g': + m_gephi = true; + break; + case 'k': + m_keepGoing = true; + break; + case 'n': + m_dryRun = true; + break; + default: + qbsError(qPrintable(QString("Unknown option '%1'.").arg(arg.at(1)))); + return false; + } + if (k < arg.length() - 1) { // Trailing characters? + qbsError(qPrintable(QString("Invalid option '%1'.").arg(arg))); + return false; + } + } + } + + if (verbosity) + Logger::instance().setLevel(verbosity); + + // automatically detect the project file name + if (m_projectFileName.isEmpty()) + m_projectFileName = guessProjectFileName(); + + // make the project file name absolute + if (!m_projectFileName.isEmpty() && !FileInfo::isAbsolute(m_projectFileName)) { + m_projectFileName = FileInfo::resolvePath(QDir::currentPath(), m_projectFileName); + } + + // eventually load defaults from configs + if (m_searchPaths.isEmpty()) + m_searchPaths = configurationValue("paths/cubes", QVariant()).toStringList(); + m_pluginPaths = configurationValue("paths/plugins", QVariant()).toStringList(); + + // fixup some defaults + if (m_searchPaths.isEmpty()) + m_searchPaths.append(qbsRootPath() + "/share/qbs/"); + if (m_pluginPaths.isEmpty()) + m_pluginPaths.append(qbsRootPath() + "/plugins/"); + + return true; +} + +static void showConfigUsage() +{ + puts("usage: qbs config [options]\n" + "\n" + "Config file location:\n" + " --global choose global configuration file\n" + " --local choose local configuration file (default)\n" + "\n" + "Actions:\n" + " --list list all variables\n" + " --unset remove variable with given name\n"); +} + +void CommandLineOptions::loadLocalProjectSettings(bool throwExceptionOnFailure) +{ + if (m_settings->hasProjectSettings()) + return; + if (throwExceptionOnFailure && m_projectFileName.isEmpty()) + throw Error("Can't find a project file in the current directory. Local configuration not available."); + m_settings->loadProjectSettings(m_projectFileName); +} + +void CommandLineOptions::configure() +{ + enum ConfigCommand { CfgSet, CfgUnset, CfgList }; + ConfigCommand cmd = CfgSet; + Settings::Scope scope = Settings::Local; + bool scopeSet = false; + + QStringList args = m_configureArgs; + m_configureArgs.clear(); + if (args.isEmpty()) { + showConfigUsage(); + return; + } + + while (!args.isEmpty() && args.first().startsWith("--")) { + QString arg = args.takeFirst(); + arg.remove(0, 2); + if (arg == "list") { + cmd = CfgList; + } else if (arg == "unset") { + cmd = CfgUnset; + } else if (arg == "global") { + scope = Settings::Global; + scopeSet = true; + } else if (arg == "local") { + scope = Settings::Local; + scopeSet = true; + } else { + throw Error("Unknown option for config command."); + } + } + + switch (cmd) { + case CfgList: + if (scopeSet) { + if (scope == Settings::Local) + loadLocalProjectSettings(true); + printSettings(scope); + } else { + loadLocalProjectSettings(false); + printSettings(Settings::Local); + printSettings(Settings::Global); + } + break; + case CfgSet: + if (scope == Settings::Local) + loadLocalProjectSettings(true); + if (args.count() > 2) + throw Error("Too many arguments for setting a configuration value."); + if (args.count() == 0) { + showConfigUsage(); + } else if (args.count() < 2) { + puts(qPrintable(m_settings->value(scope, args.at(0)).toString())); + } else { + m_settings->setValue(scope, args.at(0), args.at(1)); + } + break; + case CfgUnset: + if (scope == Settings::Local) + loadLocalProjectSettings(true); + if (args.isEmpty()) + throw Error("unset what?"); + foreach (const QString &arg, args) + m_settings->remove(scope, arg); + break; + } +} + +QVariant CommandLineOptions::configurationValue(const QString &key, const QVariant &defaultValue) +{ + return m_settings->value(key, defaultValue); +} + +template <typename S, typename T> +QMap<S,T> &inhaleValues(QMap<S,T> &dst, const QMap<S,T> &src) +{ + for (typename QMap<S,T>::const_iterator it=src.constBegin(); + it != src.constEnd(); ++it) + { + dst.insert(it.key(), it.value()); + } + return dst; +} + +QList<QVariantMap> CommandLineOptions::buildConfigurations() const +{ + QList<QVariantMap> ret; + QVariantMap globalSet; + QVariantMap currentSet; + QString currentName; + foreach (const QString &arg, positional()) { + int idx = arg.indexOf(':'); + if (idx < 0) { + if (currentName.isEmpty()) { + globalSet = currentSet; + } else { + QVariantMap map = globalSet; + inhaleValues(map, currentSet); + if (!map.contains("buildVariant")) + map["buildVariant"] = currentName; + ret.append(map); + } + currentSet.clear(); + currentName = arg; + } else { + currentSet.insert(arg.left(idx), arg.mid(idx + 1)); + } + } + + if (currentName.isEmpty()) + currentName = m_settings->value("defaults/buildvariant", "debug").toString(); + QVariantMap map = globalSet; + inhaleValues(map, currentSet); + if (!map.contains("buildVariant")) + map["buildVariant"] = currentName; + ret.append(map); + return ret; +} + +void CommandLineOptions::printSettings(Settings::Scope scope) +{ + if (scope == Settings::Global) + printf("global variables:\n"); + else + printf("local variables:\n"); + foreach (const QString &key, m_settings->allKeys(scope)) + printf("%s = %s\n", qPrintable(key), + qPrintable(m_settings->value(scope, key).toString())); +} + +QString CommandLineOptions::guessProjectFileName() +{ + QDir searchDir = QDir::current(); + for (;;) { + QStringList projectFiles = searchDir.entryList(QStringList() << "*.qbp", QDir::Files); + if (projectFiles.count() == 1) { + QDir::setCurrent(searchDir.path()); + return searchDir.absoluteFilePath(projectFiles.first()); + } else if (projectFiles.count() > 1) { + QString str = QLatin1String("Multiple project files found in '%1'.\n" + "Please specify the correct project file using the -f option."); + qbsError(qPrintable(str.arg(QDir::toNativeSeparators(searchDir.absolutePath())))); + return QString(); + } + if (!searchDir.cdUp()) + break; + } + return QString(); +} + +} // namespace qbs diff --git a/src/lib/tools/options.h b/src/lib/tools/options.h new file mode 100644 index 000000000..fa78a3202 --- /dev/null +++ b/src/lib/tools/options.h @@ -0,0 +1,113 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include <tools/settings.h> +#include <QtCore/QStringList> +#include <QtCore/QPair> +#include <QtCore/QVariant> + +namespace qbs { +/** + * Command line options for qbs command line tools. + * ### TODO: move to qbs application. + */ +class CommandLineOptions +{ +public: + CommandLineOptions(); + + enum Command { + BuildCommand, + CleanCommand, + ConfigCommand, + StartShellCommand, + RunCommand + }; + + static void printHelp(); + Command command() const { return m_command;} + bool readCommandLineArguments(const QStringList &args); + void configure(); + const QString &runTargetName() const { return m_runTargetName; } + const QString &projectFileName() const { return m_projectFileName; } + const QStringList &searchPaths() const { return m_searchPaths; } + const QStringList &pluginPaths() const { return m_pluginPaths; } + const QStringList &positional() const { return m_positional; } + const QStringList &runArgs() const { return m_runArgs; } + const QStringList &changedFiles() const { return m_changedFiles; } + const QStringList &selectedProductNames() const { return m_selectedProductNames; } + bool isDumpGraphSet() const { return m_dumpGraph; } + bool isGephiSet() const { return m_gephi; } + bool isDryRunSet() const { return m_dryRun; } + bool isHelpSet() const { return m_help; } + bool isCleanSet() const { return m_clean; } + bool isKeepGoingSet() const { return m_keepGoing; } + int jobs() const { return m_jobs; } + Settings::Ptr settings() const { return m_settings; } + QVariant configurationValue(const QString &key, const QVariant &defaultValue = QVariant()); + QList<QVariantMap> buildConfigurations() const; + +private: + void loadLocalProjectSettings(bool throwExceptionOnFailure); + void printSettings(Settings::Scope scope); + QString guessProjectFileName(); + +private: + Settings::Ptr m_settings; + Command m_command; + QString m_runTargetName; + QString m_projectFileName; + QStringList m_searchPaths; + QStringList m_pluginPaths; + QStringList m_positional; + QStringList m_runArgs; + QStringList m_configureArgs; + QStringList m_changedFiles; + QStringList m_selectedProductNames; + bool m_dumpGraph; + bool m_gephi; + bool m_dryRun; + bool m_help; + bool m_clean; + bool m_keepGoing; + int m_jobs; +}; +} +#endif // OPTIONS_H diff --git a/src/lib/tools/persistence.cpp b/src/lib/tools/persistence.cpp new file mode 100644 index 000000000..52a441eeb --- /dev/null +++ b/src/lib/tools/persistence.cpp @@ -0,0 +1,227 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "persistence.h" +#include "fileinfo.h" +#include <QtCore/QDir> +#include <stdio.h> + +namespace qbs { + +static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE0_0_1__1"; + +PersistentPool::PersistentPool() +{ +} + +PersistentPool::~PersistentPool() +{ +} + +bool PersistentPool::load(const QString &filePath, const LoadMode loadMode) +{ + QFile file(filePath); + if (!file.open(QFile::ReadOnly)) + return false; + QDataStream s(&file); + + QByteArray magic; + s >> magic; + if (magic != QBS_PERSISTENCE_MAGIC) { + file.close(); + file.remove(); + return false; + } + + s >> m_headData.projectConfig; + if (loadMode == LoadHeadData) + return true; + + s >> m_stringStorage; + m_inverseStringStorage.reserve(m_stringStorage.count()); + for (int i = m_stringStorage.count(); --i >= 0;) + m_inverseStringStorage.insert(m_stringStorage.at(i), i); + + s >> m_storage; + m_loadedRaw.reserve(m_storage.count()); + m_loaded.reserve(m_storage.count()); + return true; +} + +bool PersistentPool::store(const QString &filePath) +{ + QString dirPath = FileInfo::path(filePath); + if (!FileInfo::exists(dirPath)) + if (!QDir().mkpath(dirPath)) + return false; + + if (QFile::exists(filePath) && !QFile::remove(filePath)) + return false; + Q_ASSERT(!QFile::exists(filePath)); + QFile file(filePath); + if (!file.open(QFile::WriteOnly)) + return false; + + QDataStream s(&file); + s << QByteArray(QBS_PERSISTENCE_MAGIC); + s << m_headData.projectConfig; + s << m_stringStorage; + s << m_storage; + return true; +} + +PersistentObjectId PersistentPool::store(PersistentObject *object) +{ + if (!object) + return 0; + PersistentObjectId id = m_storageIndices.value(object, -1); + if (id < 0) { + PersistentObjectData data; + id = qMax(m_storage.count(), m_maxReservedId + 1); + m_storageIndices.insert(object, id); + m_storage.resize(id + 1); + m_storage[id] = data; + object->store(*this, data); + m_storage[id] = data; + } + return id; +} + +void PersistentPool::clear() +{ + m_loaded.clear(); + m_storage.clear(); + m_storageIndices.clear(); + m_stringStorage.clear(); + m_inverseStringStorage.clear(); +} + +PersistentObjectData PersistentPool::getData(PersistentObjectId id) const +{ + return m_storage.at(id); +} + +void PersistentPool::setData(PersistentObjectId id, PersistentObjectData data) +{ + Q_ASSERT(id <= m_maxReservedId); + if (id >= m_storage.count()) + m_storage.resize(id + 1); + m_storage[id] = data; +} + +int PersistentPool::storeString(const QString &t) +{ + if (t.isEmpty()) + return 0; + int id = m_inverseStringStorage.value(t, -1); + if (id < 0) { + id = m_stringStorage.count(); + m_inverseStringStorage.insert(t, id); + m_stringStorage.append(t); + } + return id; +} + +QString PersistentPool::loadString(int id) +{ + if (id == 0) + return QString(); + + if (id < 0 || id >= m_stringStorage.count()) + throw Error(QString("storage error: no string id %1 stored.").arg(id)); + + return m_stringStorage.at(id); +} + +QString PersistentPool::idLoadString(QDataStream &s) +{ + int id; + s >> id; + return loadString(id); +} + +QList<int> PersistentPool::storeStringSet(const QSet<QString> &t) +{ + QList<int> r; + r.reserve(t.count()); + foreach (const QString &s, t) + r += storeString(s); + return r; +} + +QSet<QString> PersistentPool::loadStringSet(const QList<int> &ids) +{ + QSet<QString> r; + r.reserve(ids.count()); + foreach (int id, ids) + r.insert(loadString(id)); + return r; +} + +QSet<QString> PersistentPool::idLoadStringSet(QDataStream &s) +{ + QList<int> list; + s >> list; + return loadStringSet(list); +} + +QList<int> PersistentPool::storeStringList(const QStringList &t) +{ + QList<int> r; + r.reserve(t.count()); + foreach (const QString &s, t) + r += storeString(s); + return r; +} + +QStringList PersistentPool::loadStringList(const QList<int> &ids) +{ + QStringList result; + result.reserve(ids.count()); + foreach (const int &id, ids) + result.append(loadString(id)); + return result; +} + +QStringList PersistentPool::idLoadStringList(QDataStream &s) +{ + QList<int> list; + s >> list; + return loadStringList(list); +} + +} // namespace qbs diff --git a/src/lib/tools/persistence.h b/src/lib/tools/persistence.h new file mode 100644 index 000000000..fa9536622 --- /dev/null +++ b/src/lib/tools/persistence.h @@ -0,0 +1,237 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QBS_PERSISTENCE +#define QBS_PERSISTENCE + +#include <QtCore/QString> +#include <QtCore/QSharedPointer> +#include <QtCore/QFile> +#include <QtCore/QDataStream> +#include <QtCore/QMap> +#include <QtCore/QVariantMap> +#include <QtCore/QDebug> +#include <tools/error.h> + +namespace qbs { + +typedef int PersistentObjectId; +typedef QByteArray PersistentObjectData; +class PersistentPool; + +class PersistentObject +{ +public: + virtual ~PersistentObject() {} + virtual void load(PersistentPool &/*pool*/, PersistentObjectData &/*data*/) {} + virtual void store(PersistentPool &/*pool*/, PersistentObjectData &/*data*/) const {} +}; + +class PersistentPool +{ +public: + PersistentPool(); + ~PersistentPool(); + + struct HeadData + { + QVariantMap projectConfig; + }; + + enum LoadMode + { + LoadHeadData, LoadAll + }; + + bool load(const QString &filePath, const LoadMode loadMode = LoadAll); + bool store(const QString &filePath); + void clear(); + + template <typename T> + inline T *idLoad(QDataStream &s) + { + PersistentObjectId id; + s >> id; + return loadRaw<T>(id); + } + + template <class T> + inline T *loadRaw(PersistentObjectId id) + { + if (id == 0) + return 0; + + PersistentObject *obj = 0; + if (id < m_loadedRaw.count()) { + obj = m_loadedRaw.value(id); + } else { + int i = m_loadedRaw.count(); + m_loadedRaw.resize(id + 1); + for (; i < m_loadedRaw.count(); ++i) + m_loadedRaw[i] = 0; + } + if (!obj) { + if (id >= m_storage.count()) + throw Error(QString("storage error: no id %1 stored.").arg(id).arg(m_storage.count())); + PersistentObjectData data = m_storage.at(id); + obj = new T; + m_loadedRaw[id] = obj; + obj->load(*this, data); + } + + return static_cast<T*>(obj); + } + + template <class T> + inline typename T::Ptr idLoadS(QDataStream &s) + { + PersistentObjectId id; + s >> id; + return load<T>(id); + } + + template <class T> + inline QSharedPointer<T> load(PersistentObjectId id) + { + if (id == 0) + return QSharedPointer<T>(); + + QSharedPointer<PersistentObject> obj; + if (id < m_loaded.count()) + obj = m_loaded.value(id); + else + m_loaded.resize(id + 1); + if (!obj) { + if (id >= m_storage.count()) + throw Error(QString("storage error: no id %1 stored. I have that many: %2").arg(id).arg(m_storage.count())); + + PersistentObjectData data = m_storage.at(id); + T *t = new T; + obj = QSharedPointer<PersistentObject>(t); + m_loaded[id] = obj; + obj->load(*this, data); + } + + return obj.staticCast<T>(); + } + + PersistentObject *load(PersistentObjectId); + + template <class T> + PersistentObjectId store(QSharedPointer<T> ptr) + { + return store(ptr.data()); + } + + PersistentObjectId store(PersistentObject *object); + + int storeString(const QString &t); + QString loadString(int id); + QString idLoadString(QDataStream &s); + + QList<int> storeStringSet(const QSet<QString> &t); + QSet<QString> loadStringSet(const QList<int> &id); + QSet<QString> idLoadStringSet(QDataStream &s); + + QList<int> storeStringList(const QStringList &t); + QStringList loadStringList(const QList<int> &ids); + QStringList idLoadStringList(QDataStream &s); + + PersistentObjectData getData(PersistentObjectId) const; + void setData(PersistentObjectId, PersistentObjectData); + + const HeadData &headData() const { return m_headData; } + void setHeadData(const HeadData &hd) { m_headData = hd; } + +private: + static const int m_maxReservedId = 0; + HeadData m_headData; + QVector<PersistentObject *> m_loadedRaw; + QVector<QSharedPointer<PersistentObject> > m_loaded; + QVector<PersistentObjectData> m_storage; + QHash<PersistentObject *, int> m_storageIndices; + + QVector<QString> m_stringStorage; + QHash<QString, int> m_inverseStringStorage; +}; + +template<typename T> struct RemovePointer { typedef T Type; }; +template<typename T> struct RemovePointer<T*> { typedef T Type; }; + +template <typename T> +void loadContainerS(T &container, QDataStream &s, qbs::PersistentPool &pool) +{ + int count; + s >> count; + container.clear(); + container.reserve(count); + for (int i = count; --i >= 0;) + container += pool.idLoadS<typename T::value_type::Type>(s); +} + +template <typename T> +void loadContainer(T &container, QDataStream &s, qbs::PersistentPool &pool) +{ + int count; + s >> count; + container.clear(); + container.reserve(count); + for (int i = count; --i >= 0;) + container += pool.idLoad<typename RemovePointer<typename T::value_type>::Type>(s); +} + +template <typename T> +void storeContainer(T &container, QDataStream &s, qbs::PersistentPool &pool) +{ + s << container.count(); + typename T::const_iterator it = container.constBegin(); + const typename T::const_iterator itEnd = container.constEnd(); + for (; it != itEnd; ++it) + s << pool.store(*it); +} + +template <typename T> +void storeHashContainer(T &container, QDataStream &s, qbs::PersistentPool &pool) +{ + s << container.count(); + foreach (const typename T::mapped_type &item, container) + s << pool.store(item); +} + +} // namespace qbs + +#endif diff --git a/src/lib/tools/platform.cpp b/src/lib/tools/platform.cpp new file mode 100644 index 000000000..5ca25dc87 --- /dev/null +++ b/src/lib/tools/platform.cpp @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "platform.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QtCore/QTextStream> +#include <QtCore/QSettings> + +#ifdef Q_OS_WIN +#include <qt_windows.h> +#include <Shlobj.h> +#endif + +namespace qbs { + +Platform::Platform(const QString &_name, const QString& configpath) + : name(_name) + , settings(configpath + "/setup.ini", QSettings::IniFormat) +{ +} + +QHash<QString, Platform::Ptr> Platform::platforms() +{ +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + QString localSettingsPath = QDir::homePath() + "/.config/Nokia/qbs/platforms/"; +#elif defined(Q_OS_WIN) + QString localSettingsPath; + wchar_t wszPath[MAX_PATH]; + if (SHGetSpecialFolderPath(NULL, wszPath, CSIDL_APPDATA, TRUE)) + localSettingsPath = QString::fromUtf16(wszPath) + "/qbs/platforms"; +#else +#error port me! +#endif + QHash<QString, Platform::Ptr> targets; + QDirIterator i(localSettingsPath, QDir::Dirs | QDir::NoDotAndDotDot); + while (i.hasNext()) { + i.next(); + Platform *t = new Platform(i.fileName(), i.filePath()); + targets.insert(t->name, Platform::Ptr(t)); + } + return targets; +} + +} // namespace qbs diff --git a/src/lib/tools/platform.h b/src/lib/tools/platform.h new file mode 100644 index 000000000..fd70a59d0 --- /dev/null +++ b/src/lib/tools/platform.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef QBS_PLATFORM_H +#define QBS_PLATFORM_H + +#include <QtCore/QSharedPointer> +#include <QtCore/QTextStream> +#include <QtCore/QSettings> + +namespace qbs { + +class Platform +{ +public: + typedef QSharedPointer<Platform> Ptr; + Platform(const QString &name, const QString& configpath); + QString name; + QSettings settings; + static QHash<QString, Platform::Ptr> platforms(); +}; + +} // namespace qbs + +#endif diff --git a/src/lib/tools/runenvironment.cpp b/src/lib/tools/runenvironment.cpp new file mode 100644 index 000000000..70d5dcc83 --- /dev/null +++ b/src/lib/tools/runenvironment.cpp @@ -0,0 +1,134 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "runenvironment.h" +#include <tools/settings.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QProcess> +#include <QtScript/QScriptEngine> + +#include <stdio.h> +#include <stdlib.h> +#ifdef Q_OS_UNIX +#include <unistd.h> +#endif + +namespace qbs { + +RunEnvironment::RunEnvironment(ResolvedProduct::Ptr product) + : m_product(product) +{ +} + +int RunEnvironment::runShell() +{ + try { + QScriptEngine engine; + m_product->setupBuildEnvironment(&engine, QProcessEnvironment::systemEnvironment()); + } catch (Error &e) { + fprintf(stderr, "%s", qPrintable(e.toString())); + return 1; + } + + QString productId = m_product->project->id + '/' + m_product->name; + printf("Starting shell for target '%s'.\n", qPrintable(productId)); +#ifdef Q_OS_UNIX + Settings::Ptr settings = Settings::create(); + QByteArray shellProcess = settings->value("shell", "/bin/sh").toString().toLocal8Bit(); + QByteArray prompt = "PS1=qbs " + productId.toLocal8Bit() + " $ "; + + QStringList senv = m_product->buildEnvironment.toStringList(); + + char *argv[2]; + argv[0] = shellProcess.data(); + argv[1] = 0; + char **env = new char *[senv.count() + 1]; + int i = 0; + foreach (const QString &s, senv){ + QByteArray b = s.toLocal8Bit(); + if (b.startsWith("PS1")) { + b = prompt; + } + env[i] = (char*)malloc(b.size() + 1); + memcpy(env[i], b.data(), b.size()); + env[i][b.size()] = 0; + ++i; + } + env[i] = 0; + execve(argv[0], argv, env); + qDebug("executing the shell failed"); + return 890; +#else + foreach (const QString &s, m_product->buildEnvironment.toStringList()) { + int idx = s.indexOf(QLatin1Char('=')); + qputenv(s.left(idx).toLocal8Bit(), s.mid(idx + 1, s.length() - idx - 1).toLocal8Bit()); + } + QByteArray shellProcess = qgetenv("COMSPEC"); + if (shellProcess.isEmpty()) + shellProcess = "cmd"; + shellProcess.append(" /k prompt [qbs] " + qgetenv("PROMPT")); + return system(shellProcess); +#endif +} + +int RunEnvironment::runTarget(const QString &targetBin, const QStringList &arguments) +{ + if (!QFileInfo(targetBin).isExecutable()) { + fprintf(stderr, "File '%s' is not an executable.\n", qPrintable(targetBin)); + return 1; + } + + try { + QScriptEngine engine; + m_product->setupRunEnvironment(&engine, QProcessEnvironment::systemEnvironment()); + } catch (Error &e) { + fprintf(stderr, "%s", qPrintable(e.toString())); + return 1; + } + + printf("Starting target '%s'.\n", qPrintable(QDir::toNativeSeparators(targetBin))); + QProcess proc; + proc.setProcessEnvironment(m_product->runEnvironment); + proc.setProcessChannelMode(QProcess::ForwardedChannels); + proc.start(targetBin, arguments); + proc.waitForFinished(-1); + return proc.exitCode(); +} + +} // namespace qbs diff --git a/src/lib/tools/runenvironment.h b/src/lib/tools/runenvironment.h new file mode 100644 index 000000000..cc2567e06 --- /dev/null +++ b/src/lib/tools/runenvironment.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef RUNENVIRONMENT_H +#define RUNENVIRONMENT_H + +#include <QString> +#include <language/language.h> + +namespace qbs { + +class RunEnvironment +{ +public: + RunEnvironment(qbs::ResolvedProduct::Ptr product); + + int runShell(); + int runTarget(const QString &targetBin, const QStringList &arguments); + +private: + qbs::ResolvedProduct::Ptr m_product; +}; + +} // namespace qbs + +#endif diff --git a/src/lib/tools/scannerpluginmanager.cpp b/src/lib/tools/scannerpluginmanager.cpp new file mode 100644 index 000000000..38afb84d3 --- /dev/null +++ b/src/lib/tools/scannerpluginmanager.cpp @@ -0,0 +1,113 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "scannerpluginmanager.h" +#include "logger.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QDirIterator> +#include <QtCore/QLibrary> + +namespace qbs { +ScannerPluginManager *ScannerPluginManager::instance() +{ + static ScannerPluginManager *i = new ScannerPluginManager; + return i; +} + +ScannerPluginManager::ScannerPluginManager() +{ +} + +QList<ScannerPlugin *> ScannerPluginManager::scannersForFileTag(const QString &fileTag) +{ + return instance()->m_scannerPlugins.value(fileTag); +} + +void ScannerPluginManager::loadPlugins(const QStringList &pluginPaths) +{ + QStringList filters; +#if defined(Q_OS_WIN) + QString sharedLibSuffix = ".dll"; +#elif defined(Q_OS_DARWIN) + QString sharedLibSuffix = ".dylib"; +#else + QString sharedLibSuffix = ".so"; +#endif + filters << "*" + sharedLibSuffix; + + foreach (const QString &pluginPath, pluginPaths) { + qbsTrace("pluginmanager: loading plugins from '%s'.", qPrintable(QDir::toNativeSeparators(pluginPath))); + QDirIterator it(pluginPath, filters, QDir::Files); + while (it.hasNext()) { + const QString fileName = it.next(); + QScopedPointer<QLibrary> lib(new QLibrary(fileName)); + if (!lib->load()) { + qbsWarning("pluginmanager: couldn't load '%s'.", + qPrintable(QDir::toNativeSeparators(fileName))); + continue; + } + + getScanners_f getScanners = reinterpret_cast<getScanners_f>(lib->resolve("getScanners")); + if (!getScanners) { + qbsWarning("pluginmanager: couldn't resolve symbol in '%s'.", + qPrintable(QDir::toNativeSeparators(fileName))); + continue; + } + + ScannerPlugin **plugins = getScanners(); + if (plugins == 0) { + qbsWarning("pluginmanager: no scanners returned from '%s'.", + qPrintable(QDir::toNativeSeparators(fileName))); + continue; + } + + qbsTrace("pluginmanager: scanner plugin '%s' loaded.", + qPrintable(QDir::toNativeSeparators(fileName))); + + int i = 0; + while (plugins[i] != 0) { + m_scannerPlugins[QString::fromLocal8Bit(plugins[i]->fileTag)] += plugins[i]; + ++i; + } + m_libs.append(lib.take()); + } + } +} + +} // namespace qbs diff --git a/src/lib/tools/scannerpluginmanager.h b/src/lib/tools/scannerpluginmanager.h new file mode 100644 index 000000000..0a2b1e5de --- /dev/null +++ b/src/lib/tools/scannerpluginmanager.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef PLUGINS_H +#define PLUGINS_H + +#include <QtCore/QString> +#include <QtCore/QMap> +#include <plugins/scanner/scanner.h> + +QT_BEGIN_NAMESPACE +class QLibrary; +QT_END_NAMESPACE + +namespace qbs { +class ScannerPluginManager +{ +public: + static ScannerPluginManager *instance(); + static QList<ScannerPlugin *> scannersForFileTag(const QString &fileTag); + void loadPlugins(const QStringList &paths); + +private: + ScannerPluginManager(); + +private: + QList<QLibrary *> m_libs; + QMap<QString, QList<ScannerPlugin*> > m_scannerPlugins; +}; + +} // namespace qbs + +#endif diff --git a/src/lib/tools/scripttools.cpp b/src/lib/tools/scripttools.cpp new file mode 100644 index 000000000..e2fe36ea9 --- /dev/null +++ b/src/lib/tools/scripttools.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "scripttools.h" +#include <QtScript/QScriptEngine> +#include <QtScript/QScriptValueIterator> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QDataStream &operator<< (QDataStream &s, const QScriptProgram &script) +{ + s << script.sourceCode() + << script.fileName() + << script.firstLineNumber(); + return s; +} + +QDataStream &operator>> (QDataStream &s, QScriptProgram &script) +{ + QString fileName, sourceCode; + int lineNumber; + s >> sourceCode + >> fileName + >> lineNumber; + script = QScriptProgram(sourceCode, fileName, lineNumber); + return s; +} + +QT_END_NAMESPACE + +namespace qbs { + +static const bool debugJSImports = false; + +void addJSImport(QScriptEngine *engine, + const QScriptValue &targetObject, + const QString &id) +{ + if (debugJSImports) + qDebug() << "addJSImport (cached): " << id; + engine->globalObject().setProperty(id, targetObject); +} + +QScriptValue addJSImport(QScriptEngine *engine, + const QScriptProgram &program, + const QString &id) +{ + if (debugJSImports) + qDebug() << "addJSImport: " << id; + QScriptValue targetObject = engine->globalObject().property(id); + QScriptValue result = addJSImport(engine, program, targetObject); + engine->globalObject().setProperty(id, targetObject); + return result; +} + +QScriptValue addJSImport(QScriptEngine *engine, + const QScriptProgram &program, + QScriptValue &targetObject) +{ + engine->pushContext(); + QScriptValue result = engine->evaluate(program); + QScriptValue activationObject = engine->currentContext()->activationObject(); + engine->popContext(); + if (result.isError()) + return result; + + if (targetObject.isValid() && !targetObject.isUndefined()) { + // try to merge imports with the same target into the same object + // it is necessary for library imports that have multiple js files + QScriptValueIterator it(activationObject); + while (it.hasNext()) { + it.next(); + if (debugJSImports) + qDebug() << "mergeJSImport copying property" << it.name(); + targetObject.setProperty(it.name(), it.value()); + } + } else { + targetObject = activationObject; + } + return result; +} + +void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value) +{ + if (name.length() == 1) { + cfg.insert(name.first(), value); + } else { + QVariant &subCfg = cfg[name.first()]; + QVariantMap subCfgMap = subCfg.toMap(); + setConfigProperty(subCfgMap, name.mid(1), value); + subCfg = subCfgMap; + } +} + +QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name) +{ + if (name.length() == 1) { + return cfg.value(name.first()); + } else { + return getConfigProperty(cfg.value(name.first()).toMap(), name.mid(1)); + } +} + +} // namespace qbs diff --git a/src/lib/tools/scripttools.h b/src/lib/tools/scripttools.h new file mode 100644 index 000000000..bdb218f95 --- /dev/null +++ b/src/lib/tools/scripttools.h @@ -0,0 +1,83 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef SCRIPTTOOLS_H +#define SCRIPTTOOLS_H + +#include <QtCore/QSet> +#include <QtCore/QStringList> +#include <QtCore/QVariantMap> +#include <QtScript/QScriptProgram> +#include <QtScript/QScriptValue> +#include <QtScript/QScriptEngine> + +QT_BEGIN_NAMESPACE + +QDataStream &operator<< (QDataStream &s, const QScriptProgram &script); +QDataStream &operator>> (QDataStream &s, QScriptProgram &script); + +QT_END_NAMESPACE + + +namespace qbs { + +template <typename C> +QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container) +{ + QScriptValue v = scriptEngine->newArray(container.count()); + int i = 0; + foreach (const typename C::value_type &item, container) + v.setProperty(i++, scriptEngine->toScriptValue(item)); + return v; +} + +void addJSImport(QScriptEngine *engine, + const QScriptValue &targetObject, + const QString &id); +QScriptValue addJSImport(QScriptEngine *engine, + const QScriptProgram &program, + const QString &id); +QScriptValue addJSImport(QScriptEngine *engine, + const QScriptProgram &program, + QScriptValue &targetObject); + +void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value); +QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name); + +} // namespace qbs + +#endif // SCRIPTTOOLS_H diff --git a/src/lib/tools/settings.cpp b/src/lib/tools/settings.cpp new file mode 100644 index 000000000..290b66478 --- /dev/null +++ b/src/lib/tools/settings.cpp @@ -0,0 +1,117 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "settings.h" +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QStringList> +#include <algorithm> + +namespace qbs { + +Settings::Settings() + : m_globalSettings(0), + m_localSettings(0) +{ + m_globalSettings = new QSettings(QSettings::UserScope, + QLatin1String("Nokia"), + QLatin1String("qbs")); +} + +Settings::~Settings() +{ + delete m_globalSettings; + delete m_localSettings; +} + +void Settings::loadProjectSettings(const QString &projectFileName) +{ + delete m_localSettings; + m_localSettings = new QSettings(QFileInfo(projectFileName).path() + + "/.qbs/config", QSettings::IniFormat); +} + +QVariant Settings::value(const QString &key, const QVariant &defaultValue) const +{ + if (m_localSettings && m_localSettings->contains(key)) + return m_localSettings->value(key, defaultValue); + return m_globalSettings->value(key, defaultValue); +} + +QVariant Settings::value(Scope scope, const QString &key, const QVariant &defaultValue) const +{ + QSettings *s = (scope == Global ? m_globalSettings : m_localSettings); + return s ? s->value(key, defaultValue) : defaultValue; +} + +QStringList Settings::allKeys() const +{ + QStringList keys; + if (m_localSettings) + keys = m_localSettings->allKeys(); + keys.append(m_globalSettings->allKeys()); + keys.sort(); + std::unique(keys.begin(), keys.end()); + return keys; +} + +QStringList Settings::allKeys(Scope scope) const +{ + QSettings *s = (scope == Global ? m_globalSettings : m_localSettings); + return s ? s->allKeys() : QStringList(); +} + +void Settings::setValue(const QString &key, const QVariant &value) +{ + setValue(Global, key, value); +} + +void Settings::setValue(Scope scope, const QString &key, const QVariant &value) +{ + QSettings *s = (scope == Global ? m_globalSettings : m_localSettings); + Q_CHECK_PTR(s); + s->setValue(key, value); +} + +void Settings::remove(Settings::Scope scope, const QString &key) +{ + QSettings *s = (scope == Global ? m_globalSettings : m_localSettings); + if (s) + s->remove(key); +} + +} // namespace qbs diff --git a/src/lib/tools/settings.h b/src/lib/tools/settings.h new file mode 100644 index 000000000..c2d33d534 --- /dev/null +++ b/src/lib/tools/settings.h @@ -0,0 +1,83 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include <QtCore/QSharedPointer> +#include <QtCore/QVariant> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace qbs { + +class Settings +{ +public: + ~Settings(); + + typedef QSharedPointer<Settings> Ptr; + static Ptr create() { return Ptr(new Settings); } + + enum Scope + { + Local, Global + }; + + void loadProjectSettings(const QString &projectFileName); + bool hasProjectSettings() const { return m_localSettings; } + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + QVariant value(Scope scope, const QString &key, const QVariant &defaultValue = QVariant()) const; + QStringList allKeys() const; + QStringList allKeys(Scope scope) const; + void setValue(const QString &key, const QVariant &value); + void setValue(Scope scope, const QString &key, const QVariant &value); + void remove(Scope scope, const QString &key); + +protected: + Settings(); + +protected: + QSettings *m_globalSettings; + QSettings *m_localSettings; +}; + +} + +#endif // SETTINGS_H diff --git a/src/lib/tools/tools.pri b/src/lib/tools/tools.pri new file mode 100644 index 000000000..71268117c --- /dev/null +++ b/src/lib/tools/tools.pri @@ -0,0 +1,40 @@ +INCLUDEPATH += $$PWD $$PWD/.. $$PWD/../.. $$PWD/../../.. + +HEADERS += \ + $$PWD/codelocation.h \ + $$PWD/error.h \ + $$PWD/fileinfo.h \ + $$PWD/filetime.h \ + $$PWD/logger.h \ + $$PWD/logsink.h \ + $$PWD/options.h \ + $$PWD/persistence.h \ + $$PWD/platform.h \ + $$PWD/scannerpluginmanager.h \ + $$PWD/runenvironment.h \ + $$PWD/scripttools.h \ + $$PWD/settings.h \ + $$PWD/coloredoutput.h \ + $$PWD/fakeconcurrent.h + +SOURCES += \ + $$PWD/error.cpp \ + $$PWD/fileinfo.cpp \ + $$PWD/logger.cpp \ + $$PWD/logsink.cpp \ + $$PWD/options.cpp \ + $$PWD/persistence.cpp \ + $$PWD/platform.cpp \ + $$PWD/scannerpluginmanager.cpp \ + $$PWD/runenvironment.cpp \ + $$PWD/scripttools.cpp \ + $$PWD/settings.cpp \ + $$PWD/coloredoutput.cpp + +win32 { + SOURCES += $$PWD/filetime_win.cpp +} + +unix { + SOURCES += $$PWD/filetime_unix.cpp +} diff --git a/src/lib/use.pri b/src/lib/use.pri new file mode 100644 index 000000000..d69ea6a7f --- /dev/null +++ b/src/lib/use.pri @@ -0,0 +1,43 @@ +isEmpty(QBSLIBDIR) { + QBSLIBDIR = $$OUT_PWD/../../../lib +} + +QT += script + +unix { + LIBS += -L$$QBSLIBDIR -lqbscore + POST_TARGETDEPS += $$QBSLIBDIR/libqbscore.a +} + +win32 { + CONFIG(debug, debug|release) { + QBSCORELIB = qbscored + } + CONFIG(release, debug|release) { + QBSCORELIB = qbscore + } + win32-msvc* { + LIBS += /LIBPATH:$$QBSLIBDIR + QBSCORELIB = $${QBSCORELIB}.lib + } else { + LIBS += -L$${QBSLIBDIR} + QBSCORELIB = lib$${QBSCORELIB} + } + QBSLIBS += $$QBSCORELIB + LIBS += Shell32.lib $$QBSLIBS + for(x, QBSLIBS): POST_TARGETDEPS+=$$QBSLIBDIR/$$x +} + +INCLUDEPATH += \ + $$PWD \ + $$PWD/.. + + + +DEPENDPATH += \ + $$PWD/buildgraph\ + $$PWD/Qbs\ + $$PWD/l2\ + $$PWD/parser\ + $$PWD/tools\ + $$INCLUDEPATH diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 000000000..d6a7e6d5b --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = scanner script diff --git a/src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h b/src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h new file mode 100644 index 000000000..4b8b1f1d1 --- /dev/null +++ b/src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h @@ -0,0 +1,151 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CPLUSPLUS_CPLUSPLUSFORWARDDECLARATIONS_H +#define CPLUSPLUS_CPLUSPLUSFORWARDDECLARATIONS_H + +#include <cstdlib> +#include <cstddef> + +#ifndef CPLUSPLUS_WITHOUT_QT +# include <QtCore/qglobal.h> + +//# if defined(CPLUSPLUS_BUILD_LIB) +//# define CPLUSPLUS_EXPORT Q_DECL_EXPORT +//# elif defined(CPLUSPLUS_BUILD_STATIC_LIB) +//# define CPLUSPLUS_EXPORT +//# else +//# define CPLUSPLUS_EXPORT Q_DECL_IMPORT +//# endif +//#else +# define CPLUSPLUS_EXPORT +#endif + +namespace CPlusPlus { + +class TranslationUnit; +class Control; +class MemoryPool; +class DiagnosticClient; + +class Identifier; +class Literal; +class StringLiteral; +class NumericLiteral; + +class SymbolTable; + +// names +class NameVisitor; +class Name; +class Identifier; +class TemplateNameId; +class DestructorNameId; +class OperatorNameId; +class ConversionNameId; +class QualifiedNameId; +class SelectorNameId; + +// types +class TypeMatcher; +class FullySpecifiedType; +class TypeVisitor; +class Type; +class UndefinedType; +class VoidType; +class IntegerType; +class FloatType; +class PointerToMemberType; +class PointerType; +class ReferenceType; +class ArrayType; +class NamedType; + +// symbols +class SymbolVisitor; +class Symbol; +class Scope; +class UsingNamespaceDirective; +class UsingDeclaration; +class Declaration; +class Argument; +class TypenameArgument; +class Function; +class Namespace; +class NamespaceAlias; +class Template; +class BaseClass; +class Block; +class Class; +class Enum; +class ForwardClassDeclaration; + +class Token; + +// Objective-C symbols +class ObjCBaseClass; +class ObjCBaseProtocol; +class ObjCClass; +class ObjCForwardClassDeclaration; +class ObjCProtocol; +class ObjCForwardProtocolDeclaration; +class ObjCMethod; +class ObjCPropertyDeclaration; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_CPLUSPLUSFORWARDDECLARATIONS_H diff --git a/src/plugins/scanner/cpp/Lexer.cpp b/src/plugins/scanner/cpp/Lexer.cpp new file mode 100644 index 000000000..6d6f22e18 --- /dev/null +++ b/src/plugins/scanner/cpp/Lexer.cpp @@ -0,0 +1,669 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "Lexer.h" +#include <cctype> + +using namespace CPlusPlus; + +Lexer::Lexer(const char *firstChar, const char *lastChar) + : _state(State_Default), + _flags(0), + _currentLine(1) +{ + setSource(firstChar, lastChar); +} + +Lexer::~Lexer() +{ } + +void Lexer::setSource(const char *firstChar, const char *lastChar) +{ + _firstChar = firstChar; + _lastChar = lastChar; + _currentChar = _firstChar - 1; + _tokenStart = _currentChar; + _yychar = '\n'; +} + +void Lexer::setStartWithNewline(bool enabled) +{ + if (enabled) + _yychar = '\n'; + else + _yychar = ' '; +} + +int Lexer::state() const +{ return _state; } + +void Lexer::setState(int state) +{ _state = state; } + +bool Lexer::qtMocRunEnabled() const +{ return f._qtMocRunEnabled; } + +void Lexer::setQtMocRunEnabled(bool onoff) +{ f._qtMocRunEnabled = onoff; } + +bool Lexer::cxx0xEnabled() const +{ return f._cxx0xEnabled; } + +void Lexer::setCxxOxEnabled(bool onoff) +{ f._cxx0xEnabled = onoff; } + +bool Lexer::objCEnabled() const +{ return f._objCEnabled; } + +void Lexer::setObjCEnabled(bool onoff) +{ f._objCEnabled = onoff; } + +bool Lexer::isIncremental() const +{ return f._isIncremental; } + +void Lexer::setIncremental(bool isIncremental) +{ f._isIncremental = isIncremental; } + +bool Lexer::scanCommentTokens() const +{ return f._scanCommentTokens; } + +void Lexer::setScanCommentTokens(bool onoff) +{ f._scanCommentTokens = onoff; } + +void Lexer::setScanAngleStringLiteralTokens(bool onoff) +{ f._scanAngleStringLiteralTokens = onoff; } + +void Lexer::pushLineStartOffset() +{ + ++_currentLine; +} + +unsigned Lexer::tokenOffset() const +{ return _tokenStart - _firstChar; } + +unsigned Lexer::tokenLength() const +{ return _currentChar - _tokenStart; } + +const char *Lexer::tokenBegin() const +{ return _tokenStart; } + +const char *Lexer::tokenEnd() const +{ return _currentChar; } + +unsigned Lexer::currentLine() const +{ return _currentLine; } + +void Lexer::scan(Token *tok) +{ + tok->reset(); + scan_helper(tok); + tok->f.length = _currentChar - _tokenStart; +} + +void Lexer::scan_helper(Token *tok) +{ + _Lagain: + while (_yychar && std::isspace(_yychar)) { + if (_yychar == '\n') { + tok->f.joined = false; + tok->f.newline = true; + } else { + tok->f.whitespace = true; + } + yyinp(); + } + + tok->lineno = _currentLine; + _tokenStart = _currentChar; + tok->offset = _currentChar - _firstChar; + + if (_state == State_MultiLineComment || _state == State_MultiLineDoxyComment) { + const int originalState = _state; + + if (! _yychar) { + tok->f.kind = T_EOF_SYMBOL; + return; + } + + while (_yychar) { + if (_yychar != '*') + yyinp(); + else { + yyinp(); + if (_yychar == '/') { + yyinp(); + _state = State_Default; + break; + } + } + } + + if (! f._scanCommentTokens) + goto _Lagain; + + else if (originalState == State_MultiLineComment) + tok->f.kind = T_COMMENT; + else + tok->f.kind = T_DOXY_COMMENT; + return; // done + } + + if (! _yychar) { + tok->f.kind = T_EOF_SYMBOL; + return; + } + + unsigned char ch = _yychar; + yyinp(); + + switch (ch) { + case '\\': + while (_yychar != '\n' && std::isspace(_yychar)) + yyinp(); + // ### assert(! _yychar || _yychar == '\n'); + if (_yychar == '\n') { + tok->f.joined = true; + tok->f.newline = false; + yyinp(); + } + goto _Lagain; + + case '"': case '\'': { + const char quote = ch; + + tok->f.kind = quote == '"' + ? T_STRING_LITERAL + : T_CHAR_LITERAL; + + while (_yychar && _yychar != quote) { + if (_yychar == '\n') + break; + else if (_yychar != '\\') + yyinp(); + else { + yyinp(); // skip `\\' + + if (_yychar) + yyinp(); + } + } + // assert(_yychar == quote); + + if (_yychar == quote) + yyinp(); + } break; + + case '{': + tok->f.kind = T_LBRACE; + break; + + case '}': + tok->f.kind = T_RBRACE; + break; + + case '[': + tok->f.kind = T_LBRACKET; + break; + + case ']': + tok->f.kind = T_RBRACKET; + break; + + case '#': + if (_yychar == '#') { + tok->f.kind = T_POUND_POUND; + yyinp(); + } else { + tok->f.kind = T_POUND; + } + break; + + case '(': + tok->f.kind = T_LPAREN; + break; + + case ')': + tok->f.kind = T_RPAREN; + break; + + case ';': + tok->f.kind = T_SEMICOLON; + break; + + case ':': + if (_yychar == ':') { + yyinp(); + tok->f.kind = T_COLON_COLON; + } else { + tok->f.kind = T_COLON; + } + break; + + case '.': + if (_yychar == '*') { + yyinp(); + tok->f.kind = T_DOT_STAR; + } else if (_yychar == '.') { + yyinp(); + // ### assert(_yychar); + if (_yychar == '.') { + yyinp(); + tok->f.kind = T_DOT_DOT_DOT; + } else { + tok->f.kind = T_ERROR; + } + } else if (std::isdigit(_yychar)) { + do { + if (_yychar == 'e' || _yychar == 'E') { + yyinp(); + if (_yychar == '-' || _yychar == '+') { + yyinp(); + // ### assert(std::isdigit(_yychar)); + } + } else if (std::isalnum(_yychar) || _yychar == '.') { + yyinp(); + } else { + break; + } + } while (_yychar); + tok->f.kind = T_NUMERIC_LITERAL; + } else { + tok->f.kind = T_DOT; + } + break; + + case '?': + tok->f.kind = T_QUESTION; + break; + + case '+': + if (_yychar == '+') { + yyinp(); + tok->f.kind = T_PLUS_PLUS; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_PLUS_EQUAL; + } else { + tok->f.kind = T_PLUS; + } + break; + + case '-': + if (_yychar == '-') { + yyinp(); + tok->f.kind = T_MINUS_MINUS; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_MINUS_EQUAL; + } else if (_yychar == '>') { + yyinp(); + if (_yychar == '*') { + yyinp(); + tok->f.kind = T_ARROW_STAR; + } else { + tok->f.kind = T_ARROW; + } + } else { + tok->f.kind = T_MINUS; + } + break; + + case '*': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_STAR_EQUAL; + } else { + tok->f.kind = T_STAR; + } + break; + + case '/': + if (_yychar == '/') { + yyinp(); + + bool doxy = false; + + if (_yychar == '/' || _yychar == '!') { + yyinp(); + + if (_yychar == '<') + yyinp(); + + if (_yychar != '\n' && std::isspace(_yychar)) + doxy = true; + } + + while (_yychar && _yychar != '\n') + yyinp(); + + if (! f._scanCommentTokens) + goto _Lagain; + + tok->f.kind = doxy ? T_CPP_DOXY_COMMENT : T_CPP_COMMENT; + + } else if (_yychar == '*') { + yyinp(); + + bool doxy = false; + + if (_yychar == '*' || _yychar == '!') { + const char ch = _yychar; + + yyinp(); + + if (ch == '*' && _yychar == '/') + goto _Ldone; + + if (_yychar == '<') + yyinp(); + + if (! _yychar || std::isspace(_yychar)) + doxy = true; + } + + while (_yychar) { + if (_yychar != '*') { + yyinp(); + } else { + yyinp(); + if (_yychar == '/') + break; + } + } + + _Ldone: + if (_yychar) + yyinp(); + else + _state = doxy ? State_MultiLineDoxyComment : State_MultiLineComment; + + if (! f._scanCommentTokens) + goto _Lagain; + + tok->f.kind = doxy ? T_DOXY_COMMENT : T_COMMENT; + + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_SLASH_EQUAL; + } else { + tok->f.kind = T_SLASH; + } + break; + + case '%': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_PERCENT_EQUAL; + } else { + tok->f.kind = T_PERCENT; + } + break; + + case '^': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_CARET_EQUAL; + } else { + tok->f.kind = T_CARET; + } + break; + + case '&': + if (_yychar == '&') { + yyinp(); + tok->f.kind = T_AMPER_AMPER; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_AMPER_EQUAL; + } else { + tok->f.kind = T_AMPER; + } + break; + + case '|': + if (_yychar == '|') { + yyinp(); + tok->f.kind = T_PIPE_PIPE; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_PIPE_EQUAL; + } else { + tok->f.kind = T_PIPE; + } + break; + + case '~': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_TILDE_EQUAL; + } else { + tok->f.kind = T_TILDE; + } + break; + + case '!': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_EXCLAIM_EQUAL; + } else { + tok->f.kind = T_EXCLAIM; + } + break; + + case '=': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_EQUAL_EQUAL; + } else { + tok->f.kind = T_EQUAL; + } + break; + + case '<': + if (f._scanAngleStringLiteralTokens) { + const char *yytext = _currentChar; + while (_yychar && _yychar != '>') + yyinp(); + int yylen = _currentChar - yytext; + // ### assert(_yychar == '>'); + if (_yychar == '>') + yyinp(); + tok->f.kind = T_ANGLE_STRING_LITERAL; + } else if (_yychar == '<') { + yyinp(); + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_LESS_LESS_EQUAL; + } else + tok->f.kind = T_LESS_LESS; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_LESS_EQUAL; + } else { + tok->f.kind = T_LESS; + } + break; + + case '>': + if (_yychar == '>') { + yyinp(); + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_GREATER_GREATER_EQUAL; + } else + tok->f.kind = T_LESS_LESS; + tok->f.kind = T_GREATER_GREATER; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_GREATER_EQUAL; + } else { + tok->f.kind = T_GREATER; + } + break; + + case ',': + tok->f.kind = T_COMMA; + break; + + default: { + if (f._objCEnabled) { + if (ch == '@' && _yychar >= 'a' && _yychar <= 'z') { + const char *yytext = _currentChar; + + do { + yyinp(); + if (! (isalnum(_yychar) || _yychar == '_' || _yychar == '$')) + break; + } while (_yychar); + + const int yylen = _currentChar - yytext; + //tok->f.kind = classifyObjCAtKeyword(yytext, yylen); /// ### FIXME + break; + } else if (ch == '@' && _yychar == '"') { + // objc @string literals + ch = _yychar; + yyinp(); + tok->f.kind = T_AT_STRING_LITERAL; + + const char *yytext = _currentChar; + + while (_yychar && _yychar != '"') { + if (_yychar != '\\') + yyinp(); + else { + yyinp(); // skip `\\' + + if (_yychar) + yyinp(); + } + } + // assert(_yychar == '"'); + + int yylen = _currentChar - yytext; + + if (_yychar == '"') + yyinp(); + + break; + } + } + + if (ch == 'L' && (_yychar == '"' || _yychar == '\'')) { + // wide char/string literals + ch = _yychar; + yyinp(); + + const char quote = ch; + + tok->f.kind = quote == '"' + ? T_WIDE_STRING_LITERAL + : T_WIDE_CHAR_LITERAL; + + const char *yytext = _currentChar; + + while (_yychar && _yychar != quote) { + if (_yychar != '\\') + yyinp(); + else { + yyinp(); // skip `\\' + + if (_yychar) + yyinp(); + } + } + // assert(_yychar == quote); + + int yylen = _currentChar - yytext; + + if (_yychar == quote) + yyinp(); + + } else if (std::isalpha(ch) || ch == '_' || ch == '$') { + const char *yytext = _currentChar - 1; + while (std::isalnum(_yychar) || _yychar == '_' || _yychar == '$') + yyinp(); + int yylen = _currentChar - yytext; + tok->f.kind = T_IDENTIFIER; + break; + } else if (std::isdigit(ch)) { + const char *yytext = _currentChar - 1; + while (_yychar) { + if (_yychar == 'e' || _yychar == 'E') { + yyinp(); + if (_yychar == '-' || _yychar == '+') { + yyinp(); + // ### assert(std::isdigit(_yychar)); + } + } else if (std::isalnum(_yychar) || _yychar == '.') { + yyinp(); + } else { + break; + } + } + int yylen = _currentChar - yytext; + tok->f.kind = T_NUMERIC_LITERAL; + break; + } else { + tok->f.kind = T_ERROR; + break; + } + } // default + + } // switch +} + + diff --git a/src/plugins/scanner/cpp/Lexer.h b/src/plugins/scanner/cpp/Lexer.h new file mode 100644 index 000000000..e697bad9a --- /dev/null +++ b/src/plugins/scanner/cpp/Lexer.h @@ -0,0 +1,162 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CPLUSPLUS_LEXER_H +#define CPLUSPLUS_LEXER_H + +#include "CPlusPlusForwardDeclarations.h" +#include "Token.h" + + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT Lexer +{ + Lexer(const Lexer &other); + void operator =(const Lexer &other); + +public: + enum State { + State_Default, + State_MultiLineComment, + State_MultiLineDoxyComment + }; + + Lexer(const char *firstChar, const char *lastChar); + ~Lexer(); + + bool qtMocRunEnabled() const; + void setQtMocRunEnabled(bool onoff); + + bool cxx0xEnabled() const; + void setCxxOxEnabled(bool onoff); + + bool objCEnabled() const; + void setObjCEnabled(bool onoff); + + void scan(Token *tok); + + inline void operator()(Token *tok) + { scan(tok); } + + unsigned tokenOffset() const; + unsigned tokenLength() const; + const char *tokenBegin() const; + const char *tokenEnd() const; + unsigned currentLine() const; + + bool scanCommentTokens() const; + void setScanCommentTokens(bool onoff); + + bool scanAngleStringLiteralTokens() const; + void setScanAngleStringLiteralTokens(bool onoff); + + void setStartWithNewline(bool enabled); + + int state() const; + void setState(int state); + + bool isIncremental() const; + void setIncremental(bool isIncremental); + +private: + void scan_helper(Token *tok); + void setSource(const char *firstChar, const char *lastChar); + static int classify(const char *string, int length, bool q, bool cxx0x); + static int classifyObjCAtKeyword(const char *s, int n); + static int classifyOperator(const char *string, int length); + + inline void yyinp() + { + if (++_currentChar == _lastChar) + _yychar = 0; + else { + _yychar = *_currentChar; + if (_yychar == '\n') + pushLineStartOffset(); + } + } + + void pushLineStartOffset(); + +private: + struct Flags { + unsigned _isIncremental: 1; + unsigned _scanCommentTokens: 1; + unsigned _scanAngleStringLiteralTokens: 1; + unsigned _qtMocRunEnabled: 1; + unsigned _cxx0xEnabled: 1; + unsigned _objCEnabled: 1; + }; + + const char *_firstChar; + const char *_currentChar; + const char *_lastChar; + const char *_tokenStart; + unsigned char _yychar; + int _state; + union { + unsigned _flags; + Flags f; + }; + unsigned _currentLine; +}; + +} // end of namespace CPlusPlus + + +#endif // CPLUSPLUS_LEXER_H diff --git a/src/plugins/scanner/cpp/Token.cpp b/src/plugins/scanner/cpp/Token.cpp new file mode 100644 index 000000000..066e3c469 --- /dev/null +++ b/src/plugins/scanner/cpp/Token.cpp @@ -0,0 +1,154 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "Token.h" +#ifndef CPLUSPLUS_NO_PARSER +# include "Literals.h" +#endif + +using namespace CPlusPlus; + +static const char *token_names[] = { + (""), ("<error>"), + + ("<C++ comment>"), ("<C++ doxy comment>"), + ("<comment>"), ("<doxy comment>"), + + ("<identifier>"), ("<numeric literal>"), ("<char literal>"), + ("<wide char literal>"), ("<string literal>"), ("<wide char literal>"), + ("<@string literal>"), ("<angle string literal>"), + + ("&"), ("&&"), ("&="), ("->"), ("->*"), ("^"), ("^="), (":"), ("::"), + (","), ("/"), ("/="), ("."), ("..."), (".*"), ("="), ("=="), ("!"), + ("!="), (">"), (">="), (">>"), (">>="), ("{"), ("["), ("<"), ("<="), + ("<<"), ("<<="), ("("), ("-"), ("-="), ("--"), ("%"), ("%="), ("|"), + ("|="), ("||"), ("+"), ("+="), ("++"), ("#"), ("##"), ("?"), ("}"), + ("]"), (")"), (";"), ("*"), ("*="), ("~"), ("~="), + + ("asm"), ("auto"), ("bool"), ("break"), ("case"), ("catch"), ("char"), + ("class"), ("const"), ("const_cast"), ("continue"), ("default"), + ("delete"), ("do"), ("double"), ("dynamic_cast"), ("else"), ("enum"), + ("explicit"), ("export"), ("extern"), ("false"), ("float"), ("for"), + ("friend"), ("goto"), ("if"), ("inline"), ("int"), ("long"), + ("mutable"), ("namespace"), ("new"), ("operator"), ("private"), + ("protected"), ("public"), ("register"), ("reinterpret_cast"), + ("return"), ("short"), ("signed"), ("sizeof"), ("static"), + ("static_cast"), ("struct"), ("switch"), ("template"), ("this"), + ("throw"), ("true"), ("try"), ("typedef"), ("typeid"), ("typename"), + ("union"), ("unsigned"), ("using"), ("virtual"), ("void"), + ("volatile"), ("wchar_t"), ("while"), + + // gnu + ("__attribute__"), ("__typeof__"), + + // objc @keywords + ("@catch"), ("@class"), ("@compatibility_alias"), ("@defs"), ("@dynamic"), + ("@encode"), ("@end"), ("@finally"), ("@implementation"), ("@interface"), + ("@not_keyword"), ("@optional"), ("@package"), ("@private"), ("@property"), + ("@protected"), ("@protocol"), ("@public"), ("@required"), ("@selector"), + ("@synchronized"), ("@synthesize"), ("@throw"), ("@try"), + + // Qt keywords + ("SIGNAL"), ("SLOT"), ("Q_SIGNAL"), ("Q_SLOT"), ("signals"), ("slots"), + ("Q_FOREACH"), ("Q_D"), ("Q_Q"), + ("Q_INVOKABLE"), ("Q_PROPERTY"), ("Q_INTERFACES"), ("Q_ENUMS"), ("Q_FLAGS"), + ("Q_PRIVATE_SLOT"), ("Q_DECLARE_INTERFACE"), ("Q_OBJECT"), ("Q_GADGET"), + +}; + +Token::Token() : + flags(0), offset(0), ptr(0) +{ +} + +Token::~Token() +{ +} + +void Token::reset() +{ + flags = 0; + offset = 0; + ptr = 0; +} + +const char *Token::name(int kind) +{ return token_names[kind]; } + +#ifndef CPLUSPLUS_NO_PARSER +const char *Token::spell() const +{ + switch (f.kind) { + case T_IDENTIFIER: + return identifier->chars(); + + case T_NUMERIC_LITERAL: + case T_CHAR_LITERAL: + case T_STRING_LITERAL: + case T_AT_STRING_LITERAL: + case T_ANGLE_STRING_LITERAL: + case T_WIDE_CHAR_LITERAL: + case T_WIDE_STRING_LITERAL: + return literal->chars(); + + default: + return token_names[f.kind]; + } // switch +} +#endif + + diff --git a/src/plugins/scanner/cpp/Token.h b/src/plugins/scanner/cpp/Token.h new file mode 100644 index 000000000..140af2f42 --- /dev/null +++ b/src/plugins/scanner/cpp/Token.h @@ -0,0 +1,371 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CPLUSPLUS_TOKEN_H +#define CPLUSPLUS_TOKEN_H + +#include "CPlusPlusForwardDeclarations.h" + +namespace CPlusPlus { + +enum Kind { + T_EOF_SYMBOL = 0, + T_ERROR, + + T_CPP_COMMENT, + T_CPP_DOXY_COMMENT, + T_COMMENT, + T_DOXY_COMMENT, + T_IDENTIFIER, + + T_FIRST_LITERAL, + T_NUMERIC_LITERAL = T_FIRST_LITERAL, + T_CHAR_LITERAL, + T_WIDE_CHAR_LITERAL, + T_STRING_LITERAL, + T_WIDE_STRING_LITERAL, + T_AT_STRING_LITERAL, + T_ANGLE_STRING_LITERAL, + T_LAST_LITERAL = T_ANGLE_STRING_LITERAL, + + T_FIRST_OPERATOR, + T_AMPER = T_FIRST_OPERATOR, + T_AMPER_AMPER, + T_AMPER_EQUAL, + T_ARROW, + T_ARROW_STAR, + T_CARET, + T_CARET_EQUAL, + T_COLON, + T_COLON_COLON, + T_COMMA, + T_SLASH, + T_SLASH_EQUAL, + T_DOT, + T_DOT_DOT_DOT, + T_DOT_STAR, + T_EQUAL, + T_EQUAL_EQUAL, + T_EXCLAIM, + T_EXCLAIM_EQUAL, + T_GREATER, + T_GREATER_EQUAL, + T_GREATER_GREATER, + T_GREATER_GREATER_EQUAL, + T_LBRACE, + T_LBRACKET, + T_LESS, + T_LESS_EQUAL, + T_LESS_LESS, + T_LESS_LESS_EQUAL, + T_LPAREN, + T_MINUS, + T_MINUS_EQUAL, + T_MINUS_MINUS, + T_PERCENT, + T_PERCENT_EQUAL, + T_PIPE, + T_PIPE_EQUAL, + T_PIPE_PIPE, + T_PLUS, + T_PLUS_EQUAL, + T_PLUS_PLUS, + T_POUND, + T_POUND_POUND, + T_QUESTION, + T_RBRACE, + T_RBRACKET, + T_RPAREN, + T_SEMICOLON, + T_STAR, + T_STAR_EQUAL, + T_TILDE, + T_TILDE_EQUAL, + T_LAST_OPERATOR = T_TILDE_EQUAL, + + T_FIRST_KEYWORD, + T_ASM = T_FIRST_KEYWORD, + T_AUTO, + T_BOOL, + T_BREAK, + T_CASE, + T_CATCH, + T_CHAR, + T_CLASS, + T_CONST, + T_CONST_CAST, + T_CONTINUE, + T_DEFAULT, + T_DELETE, + T_DO, + T_DOUBLE, + T_DYNAMIC_CAST, + T_ELSE, + T_ENUM, + T_EXPLICIT, + T_EXPORT, + T_EXTERN, + T_FALSE, + T_FLOAT, + T_FOR, + T_FRIEND, + T_GOTO, + T_IF, + T_INLINE, + T_INT, + T_LONG, + T_MUTABLE, + T_NAMESPACE, + T_NEW, + T_OPERATOR, + T_PRIVATE, + T_PROTECTED, + T_PUBLIC, + T_REGISTER, + T_REINTERPRET_CAST, + T_RETURN, + T_SHORT, + T_SIGNED, + T_SIZEOF, + T_STATIC, + T_STATIC_CAST, + T_STRUCT, + T_SWITCH, + T_TEMPLATE, + T_THIS, + T_THROW, + T_TRUE, + T_TRY, + T_TYPEDEF, + T_TYPEID, + T_TYPENAME, + T_UNION, + T_UNSIGNED, + T_USING, + T_VIRTUAL, + T_VOID, + T_VOLATILE, + T_WCHAR_T, + T_WHILE, + + T___ATTRIBUTE__, + T___TYPEOF__, + + // obj c++ @ keywords + T_FIRST_OBJC_AT_KEYWORD, + + T_AT_CATCH = T_FIRST_OBJC_AT_KEYWORD, + T_AT_CLASS, + T_AT_COMPATIBILITY_ALIAS, + T_AT_DEFS, + T_AT_DYNAMIC, + T_AT_ENCODE, + T_AT_END, + T_AT_FINALLY, + T_AT_IMPLEMENTATION, + T_AT_INTERFACE, + T_AT_NOT_KEYWORD, + T_AT_OPTIONAL, + T_AT_PACKAGE, + T_AT_PRIVATE, + T_AT_PROPERTY, + T_AT_PROTECTED, + T_AT_PROTOCOL, + T_AT_PUBLIC, + T_AT_REQUIRED, + T_AT_SELECTOR, + T_AT_SYNCHRONIZED, + T_AT_SYNTHESIZE, + T_AT_THROW, + T_AT_TRY, + + T_LAST_OBJC_AT_KEYWORD = T_AT_TRY, + + T_FIRST_QT_KEYWORD, + + // Qt keywords + T_SIGNAL = T_FIRST_QT_KEYWORD, + T_SLOT, + T_Q_SIGNAL, + T_Q_SLOT, + T_Q_SIGNALS, + T_Q_SLOTS, + T_Q_FOREACH, + T_Q_D, + T_Q_Q, + T_Q_INVOKABLE, + T_Q_PROPERTY, + T_Q_INTERFACES, + T_Q_ENUMS, + T_Q_FLAGS, + T_Q_PRIVATE_SLOT, + T_Q_DECLARE_INTERFACE, + T_Q_OBJECT, + T_Q_GADGET, + T_LAST_KEYWORD = T_Q_GADGET, + + // aliases + T_OR = T_PIPE_PIPE, + T_AND = T_AMPER_AMPER, + T_NOT = T_EXCLAIM, + T_XOR = T_CARET, + T_BITOR = T_PIPE, + T_COMPL = T_TILDE, + T_OR_EQ = T_PIPE_EQUAL, + T_AND_EQ = T_AMPER_EQUAL, + T_BITAND = T_AMPER, + T_NOT_EQ = T_EXCLAIM_EQUAL, + T_XOR_EQ = T_CARET_EQUAL, + + T___ASM = T_ASM, + T___ASM__ = T_ASM, + + T_TYPEOF = T___TYPEOF__, + T___TYPEOF = T___TYPEOF__, + + T___INLINE = T_INLINE, + T___INLINE__ = T_INLINE, + + T___CONST = T_CONST, + T___CONST__ = T_CONST, + + T___VOLATILE = T_VOLATILE, + T___VOLATILE__ = T_VOLATILE, + + T___ATTRIBUTE = T___ATTRIBUTE__ +}; + +class CPLUSPLUS_EXPORT Token +{ +public: + Token(); + ~Token(); + + inline bool is(unsigned k) const { return f.kind == k; } + inline bool isNot(unsigned k) const { return f.kind != k; } +#ifndef CPLUSPLUS_NO_PARSER + const char *spell() const; +#endif + void reset(); + + inline unsigned kind() const { return f.kind; } + inline bool newline() const { return f.newline; } + inline bool whitespace() const { return f.whitespace; } + inline bool joined() const { return f.joined; } + inline bool expanded() const { return f.expanded; } + inline bool generated() const { return f.generated; } + inline unsigned length() const { return f.length; } + + inline unsigned begin() const + { return offset; } + + inline unsigned end() const + { return offset + f.length; } + + inline bool isLiteral() const + { return f.kind >= T_FIRST_LITERAL && f.kind <= T_LAST_LITERAL; } + + inline bool isOperator() const + { return f.kind >= T_FIRST_OPERATOR && f.kind <= T_LAST_OPERATOR; } + + inline bool isKeyword() const + { return f.kind >= T_FIRST_KEYWORD && f.kind < T_FIRST_QT_KEYWORD; } + + inline bool isComment() const + { return f.kind == T_COMMENT || f.kind == T_DOXY_COMMENT || + f.kind == T_CPP_COMMENT || f.kind == T_CPP_DOXY_COMMENT; } + + inline bool isObjCAtKeyword() const + { return f.kind >= T_FIRST_OBJC_AT_KEYWORD && f.kind <= T_LAST_OBJC_AT_KEYWORD; } + + static const char *name(int kind); + +public: + struct Flags { + unsigned kind : 8; + unsigned newline : 1; + unsigned whitespace : 1; + unsigned joined : 1; + unsigned expanded : 1; + unsigned generated : 1; + unsigned pad : 3; + unsigned length : 16; + }; + union { + unsigned flags; + Flags f; + }; + + unsigned offset; + + union { + void *ptr; +#ifndef CPLUSPLUS_NO_PARSER + const Literal *literal; + const NumericLiteral *number; + const StringLiteral *string; + const Identifier *identifier; +#endif + unsigned close_brace; + unsigned lineno; + }; +}; + +} // end of namespace CPlusPlus + + +#endif // CPLUSPLUS_TOKEN_H diff --git a/src/plugins/scanner/cpp/cpp.cpp b/src/plugins/scanner/cpp/cpp.cpp new file mode 100644 index 000000000..3fde5b6cc --- /dev/null +++ b/src/plugins/scanner/cpp/cpp.cpp @@ -0,0 +1,281 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "../scanner.h" +#include "cpp_global.h" +#include <Lexer.h> + +using namespace CPlusPlus; + +#ifdef Q_OS_UNIX +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#else +#include <QtCore/QFile> +#endif + +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QScopedPointer> +#include <QtCore/QString> + +struct ScanResult +{ + char *fileName; + unsigned int size; + int flags; +}; + +struct Opaq +{ + enum FileType + { + FT_UNKNOWN, FT_HPP, FT_CPP + }; + + Opaq() + : +#ifdef Q_OS_UNIX + fd(0), + mapl(0), +#endif + fileContent(0), + fileType(FT_UNKNOWN), + hasQObjectMacro(false), + currentResultIndex(0) + {} + + ~Opaq() + { +#ifdef Q_OS_UNIX + if (fileContent) + munmap(fileContent, mapl); + if (fd) + close(fd); +#endif + } + +#ifdef Q_OS_WIN + QFile file; +#endif +#ifdef Q_OS_UNIX + int fd; + size_t mapl; +#endif + + QString fileName; + char *fileContent; + FileType fileType; + QList<ScanResult> includedFiles; + bool hasQObjectMacro; + int currentResultIndex; +}; + +static void scanCppFile(void *opaq, Lexer &yylex, bool scanForFileTags) +{ + static const size_t lengthOfIncludeLiteral = strlen("include"); + Opaq *opaque = static_cast<Opaq *>(opaq); + Token tk; + ScanResult scanResult; + + yylex(&tk); + + while (tk.isNot(T_EOF_SYMBOL)) { + if (tk.newline() && tk.is(T_POUND)) { + yylex(&tk); + + if (!scanForFileTags && !tk.newline() && tk.is(T_IDENTIFIER)) { + if (tk.length() >= lengthOfIncludeLiteral + && (strncmp(opaque->fileContent + tk.begin(), "include", lengthOfIncludeLiteral) == 0)) + { + yylex.setScanAngleStringLiteralTokens(true); + yylex(&tk); + yylex.setScanAngleStringLiteralTokens(false); + + if (!tk.newline() && (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL))) { + scanResult.size = tk.length() - 2; + if (tk.is(T_STRING_LITERAL)) + scanResult.flags = SC_LOCAL_INCLUDE_FLAG; + else + scanResult.flags = SC_GLOBAL_INCLUDE_FLAG; + scanResult.fileName = opaque->fileContent + tk.begin() + 1; + opaque->includedFiles.append(scanResult); + } + } + } + } else if (tk.is(T_IDENTIFIER) && !opaque->hasQObjectMacro) { + if (scanForFileTags + && tk.length() == 8 + && opaque->fileContent[tk.begin()] == 'Q' + && opaque->fileContent[tk.begin() + 1] == '_' + && (strncmp(opaque->fileContent + tk.begin() + 2, "OBJECT", 6) == 0 + || strncmp(opaque->fileContent + tk.begin() + 2, "GADGET", 6) == 0)) + { + opaque->hasQObjectMacro = true; + break; + } + } + yylex(&tk); + } +} + +static void *openScanner(const unsigned short *filePath, char **fileTags, int numFileTags) +{ + QScopedPointer<Opaq> opaque(new Opaq); + opaque->fileName = QString::fromUtf16(filePath); + + size_t mapl = 0; +#ifdef Q_OS_UNIX + QString filePathS = opaque->fileName; + + opaque->fd = open(qPrintable(filePathS), O_RDONLY); + if (opaque->fd == -1) { + opaque->fd = 0; + return 0; + } + + struct stat s; + int r = fstat(opaque->fd, &s); + if (r != 0) + return 0; + mapl = s.st_size; + opaque->mapl = mapl; + + void *vmap = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0); + if (vmap == MAP_FAILED) + return 0; +#else + opaque->file.setFileName(opaque->fileName); + if (!opaque->file.open(QFile::ReadOnly)) + return 0; + + uchar *vmap = opaque->file.map(0, opaque->file.size()); + mapl = opaque->file.size(); +#endif + if (!vmap) + return 0; + + for (int i=0; i < numFileTags; ++i) { + const char *fileTag = fileTags[i]; + if (strncmp("cpp", fileTag, 3) == 0) + opaque->fileType = Opaq::FT_CPP; + else if (strncmp("hpp", fileTag, 3) == 0) + opaque->fileType = Opaq::FT_HPP; + } + + opaque->fileContent = reinterpret_cast<char *>(vmap); + Lexer lex(opaque->fileContent, opaque->fileContent + mapl); + const bool scanForFileTags = fileTags && numFileTags; + scanCppFile(opaque.data(), lex, scanForFileTags); + return static_cast<void *>(opaque.take()); +} + +static void closeScanner(void *ptr) +{ + Opaq *opaque = static_cast<Opaq *>(ptr); + delete opaque; +} + +static const char *next(void *opaq, int *size, int *flags) +{ + Opaq *opaque = static_cast<Opaq*>(opaq); + if (opaque->currentResultIndex < opaque->includedFiles.count()) { + const ScanResult &result = opaque->includedFiles.at(opaque->currentResultIndex); + ++opaque->currentResultIndex; + *size = result.size; + *flags = result.flags; + return result.fileName; + } + *size = 0; + *flags = 0; + return 0; +} + +static const char **additionalFileTags(void *opaq, int *size) +{ + static const char *thMocCpp[] = { "moc_cpp" }; + static const char *thMocHpp[] = { "moc_hpp" }; + + Opaq *opaque = static_cast<Opaq*>(opaq); + if (opaque->hasQObjectMacro) { + *size = 1; + switch (opaque->fileType) { + case Opaq::FT_CPP: + return thMocCpp; + case Opaq::FT_HPP: + return thMocHpp; + default: + break; + } + } + *size = 0; + return 0; +} + +extern "C" { + +ScannerPlugin hppScanner = +{ + "include_scanner", + "hpp", + openScanner, + closeScanner, + next, + additionalFileTags +}; + +ScannerPlugin cppScanner = +{ + "include_scanner", + "cpp", + openScanner, + closeScanner, + next, + additionalFileTags +}; + +ScannerPlugin *theScanners[3] = {&hppScanner, &cppScanner, NULL}; + +CPPSCANNER_EXPORT ScannerPlugin **getScanners() +{ + return theScanners; +} + +} // extern "C" diff --git a/src/plugins/scanner/cpp/cpp.pro b/src/plugins/scanner/cpp/cpp.pro new file mode 100644 index 000000000..e5d4d8381 --- /dev/null +++ b/src/plugins/scanner/cpp/cpp.pro @@ -0,0 +1,15 @@ +DEFINES += CPLUSPLUS_NO_PARSER + +DESTDIR= ../../../../plugins/ +TEMPLATE = lib +TARGET = qbs_cpp_scanner +DEPENDPATH += . +INCLUDEPATH += . + +QT = core + +unix: CONFIG += plugin + +HEADERS += CPlusPlusForwardDeclarations.h Lexer.h Token.h ../scanner.h \ + cpp_global.h +SOURCES += cpp.cpp Lexer.cpp Token.cpp diff --git a/src/plugins/scanner/cpp/cpp_global.h b/src/plugins/scanner/cpp/cpp_global.h new file mode 100644 index 000000000..1e57911dd --- /dev/null +++ b/src/plugins/scanner/cpp/cpp_global.h @@ -0,0 +1,47 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef CPP_GLOBAL_H +#define CPP_GLOBAL_H + +#if defined(WIN32) || defined(_WIN32) +#define CPPSCANNER_EXPORT __declspec(dllexport) +#else +#define CPPSCANNER_EXPORT +#endif + +#endif // CPP_GLOBAL_H diff --git a/src/plugins/scanner/qt/qt.cpp b/src/plugins/scanner/qt/qt.cpp new file mode 100644 index 000000000..66d59e59a --- /dev/null +++ b/src/plugins/scanner/qt/qt.cpp @@ -0,0 +1,227 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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 defined(WIN32) || defined(_WIN32) +#define SCANNER_EXPORT __declspec(dllexport) +#else +#define SCANNER_EXPORT +#endif + +#include "../scanner.h" + +#include <QtCore/qglobal.h> + +#ifdef Q_OS_UNIX +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#else +#include <QtCore/QFile> +#endif + +#include <QtCore/QString> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QScopedPointer> + + +#include <QDebug> + +struct Opaq +{ +#ifdef Q_OS_UNIX + int fd; + int mapl; +#else + QFile *file; +#endif + + char *map; + QXmlStreamReader *xml; + QByteArray current; + Opaq() +#ifdef Q_OS_UNIX + : fd (0), +#else + : file(0), +#endif + map(0), + xml(0) + {} + + ~Opaq() + { +#ifdef Q_OS_UNIX + if (map) + munmap (map, mapl); + if (fd) + close (fd); +#else + delete file; +#endif + delete xml; + } +}; + +static void *openScanner(const unsigned short *filePath, char **fileTags, int numFileTags) +{ + Q_UNUSED(fileTags); + Q_UNUSED(numFileTags); + QScopedPointer<Opaq> opaque(new Opaq); + +#ifdef Q_OS_UNIX + QString filePathS = QString::fromUtf16(filePath); + opaque->fd = open(qPrintable(filePathS), O_RDONLY); + if (opaque->fd == -1) { + opaque->fd = 0; + return 0; + } + + struct stat s; + int r = fstat(opaque->fd, &s); + if (r != 0) + return 0; + opaque->mapl = s.st_size; + + void *map = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0); + if (map == 0) + return 0; +#else + opaque->file = new QFile(QString::fromUtf16(filePath)); + if (!opaque->file->open(QFile::ReadOnly)) + return 0; + + uchar *map = opaque->file->map(0, opaque->file->size()); + if (!map) + return 0; +#endif + + opaque->map = reinterpret_cast<char *>(map); + opaque->xml = new QXmlStreamReader(opaque->map); + + return static_cast<void *>(opaque.take()); +} + +static void closeScanner(void *ptr) +{ + Opaq *opaque = static_cast<Opaq *>(ptr); + delete opaque; +} + +static const char *nextUi(void *opaq, int *size, int *flags) +{ + Opaq *o= static_cast<Opaq *>(opaq); + while (!o->xml->atEnd()) { + o->xml->readNext(); + switch (o->xml->tokenType()) { + case QXmlStreamReader::StartElement: + if ( o->xml->name() == "include") { + o->current = o->xml->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).toUtf8(); + *flags = SC_GLOBAL_INCLUDE_FLAG; + *size = o->current.size(); + return o->current.data(); + } + break; + case QXmlStreamReader::EndDocument: + return 0; + default: + break; + } + } + return 0; +} + +static const char *nextQrc(void *opaq, int *size, int *flags) +{ + Opaq *o= static_cast<Opaq *>(opaq); + while (!o->xml->atEnd()) { + o->xml->readNext(); + switch (o->xml->tokenType()) { + case QXmlStreamReader::StartElement: + if ( o->xml->name() == "file") { + o->current = o->xml->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).toUtf8(); + *flags = SC_LOCAL_INCLUDE_FLAG; + *size = o->current.size(); + return o->current.data(); + } + break; + case QXmlStreamReader::EndDocument: + return 0; + default: + break; + } + } + return 0; +} + +static const char **additionalFileTags(void *, int *size) +{ + *size = 0; + return 0; +} + +extern "C" { + +ScannerPlugin uiScanner = +{ + "qt_ui_scanner", + "ui", + openScanner, + closeScanner, + nextUi, + additionalFileTags +}; + +ScannerPlugin qrcScanner = +{ + "qt_qrc_scanner", + "qrc", + openScanner, + closeScanner, + nextQrc, + additionalFileTags +}; + +ScannerPlugin *theScanners[3] = {&uiScanner, &qrcScanner, NULL}; + +SCANNER_EXPORT ScannerPlugin **getScanners() +{ + return theScanners; +} + +} // extern "C" diff --git a/src/plugins/scanner/qt/qt.pro b/src/plugins/scanner/qt/qt.pro new file mode 100644 index 000000000..c825e9d15 --- /dev/null +++ b/src/plugins/scanner/qt/qt.pro @@ -0,0 +1,12 @@ +DESTDIR= ../../../../plugins +TEMPLATE = lib +TARGET = qbs_qt_scanner +DEPENDPATH += . +INCLUDEPATH += . + +Qt = core xml + +unix: CONFIG += plugin + +HEADERS += ../scanner.h +SOURCES += qt.cpp diff --git a/src/plugins/scanner/scanner.h b/src/plugins/scanner/scanner.h new file mode 100644 index 000000000..402112d8e --- /dev/null +++ b/src/plugins/scanner/scanner.h @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef SCANNER_H +#define SCANNER_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define SC_LOCAL_INCLUDE_FLAG 0x1 +#define SC_GLOBAL_INCLUDE_FLAG 0x2 + +/** + * Open a file that's going to be scanned. + * The file path encoding is UTF-16 on all platforms. + * + * If the scanner is used for more than one type hint (e.g. C++ header / source) + * the scanner can read the parameter fileTag which file type it is going to scan. + * + * Returns a scanner handle. + */ +typedef void *(*scanOpen_f) (const unsigned short *filePath, char **fileTags, int numFileTags); + +/** + * Closes the given scanner handle. + */ +typedef void (*scanClose_f) (void *opaq); + +/** + * Return the next result (filename) of the scan. + */ +typedef const char *(*scanNext_f) (void *opaq, int *size, int *flags); + +/** + * Returns a list of type hints for the scanned file. + * May return null. + * + * Example: if a C++ header file contains Q_OBJECT, + * the type hint 'moc_hpp' is returned. + */ +typedef const char** (*scanAdditionalFileTags_f) (void *opaq, int *size); + +struct ScannerPlugin +{ + const char *name; + const char *fileTag; + scanOpen_f open; + scanClose_f close; + scanNext_f next; + scanAdditionalFileTags_f additionalFileTags; +}; + +typedef ScannerPlugin **(*getScanners_f)(); + +#ifdef __cplusplus +} +#endif +#endif // SCANNER_H diff --git a/src/plugins/scanner/scanner.pro b/src/plugins/scanner/scanner.pro new file mode 100644 index 000000000..68acae7da --- /dev/null +++ b/src/plugins/scanner/scanner.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = cpp qt + diff --git a/src/plugins/script/file/file.cpp b/src/plugins/script/file/file.cpp new file mode 100644 index 000000000..247d46056 --- /dev/null +++ b/src/plugins/script/file/file.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "file.h" + +#include <tools/fileinfo.h> + +#include <QtCore/QDebug> +#include <QtScript/QScriptEngine> + +#ifdef Q_OS_UNIX +#include <unistd.h> +#endif + +void File::init(QScriptValue &extensionObject, QScriptEngine *engine) +{ + QScriptValue fileObj = engine->newQObject(new File, QScriptEngine::ScriptOwnership); + fileObj.setProperty("copy", engine->newFunction(File::js_copy)); + fileObj.setProperty("exists", engine->newFunction(File::js_exists)); + fileObj.setProperty("remove", engine->newFunction(File::js_remove)); + extensionObject.setProperty("File", fileObj); +} + +QScriptValue File::js_copy(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (context->argumentCount() < 2) { + return context->throwError(QScriptContext::SyntaxError, + tr("copy expects 2 arguments")); + } + return QFile::copy(context->argument(0).toString(), context->argument(1).toString()); +} + +QScriptValue File::js_exists(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (context->argumentCount() < 1) { + return context->throwError(QScriptContext::SyntaxError, + tr("exist expects 1 argument")); + } + return qbs::FileInfo::exists(context->argument(0).toString()); +} + +QScriptValue File::js_remove(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (context->argumentCount() < 1) { + return context->throwError(QScriptContext::SyntaxError, + tr("remove expects 1 argument")); + } + QString fileName = context->argument(0).toString(); +#ifdef Q_OS_UNIX + return unlink(fileName.toLocal8Bit().data()) == 0; +#else + return QFile::remove(fileName); +#endif +} diff --git a/src/plugins/script/file/file.h b/src/plugins/script/file/file.h new file mode 100644 index 000000000..0e4126718 --- /dev/null +++ b/src/plugins/script/file/file.h @@ -0,0 +1,58 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ +#ifndef FILE_H + +#include <QtCore/QMetaType> +#include <QtCore/QObject> +#include <QtCore/QFile> +#include <QtCore/QTextStream> +#include <QtScript/QScriptable> +#include <QtScript/QScriptValue> + +class File : public QObject, public QScriptable +{ +Q_OBJECT +public: + static void init(QScriptValue &extensionObject, QScriptEngine *engine); + +private: + static QScriptValue js_copy(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_exists(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_remove(QScriptContext *context, QScriptEngine *engine); +}; + +#endif // FILE_H diff --git a/src/plugins/script/file/file.pro b/src/plugins/script/file/file.pro new file mode 100644 index 000000000..eded5fdbc --- /dev/null +++ b/src/plugins/script/file/file.pro @@ -0,0 +1,20 @@ +DESTDIR= ../../../../plugins/script/ +TEMPLATE = lib +TARGET = qtscript_fileapi +DEPENDPATH += . +INCLUDEPATH += . ../../../lib/ + +QT = core script + +unix: CONFIG += plugin + +HEADERS += \ + file.h \ + ../../../lib/tools/fileinfo.h \ + textfile.h + +SOURCES += \ + plugin.cpp \ + file.cpp \ + ../../../lib/tools/fileinfo.cpp \ + textfile.cpp diff --git a/src/plugins/script/file/plugin.cpp b/src/plugins/script/file/plugin.cpp new file mode 100644 index 000000000..c493145a3 --- /dev/null +++ b/src/plugins/script/file/plugin.cpp @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "file.h" +#include "textfile.h" + +#include <QtScript/QScriptExtensionPlugin> +#include <QtScript/QScriptValue> +#include <QtScript/QScriptEngine> + +void qtscript_initialize_com_nokia_qbs_fileapi_bindings(QScriptValue &); + +class com_nokia_qbs_fileapi_ScriptPlugin : public QScriptExtensionPlugin +{ +public: + QStringList keys() const; + void initialize(const QString &key, QScriptEngine *engine); +}; + +QStringList com_nokia_qbs_fileapi_ScriptPlugin::keys() const +{ + QStringList list; + list << QLatin1String("qbs"); + list << QLatin1String("qbs.fileapi"); + return list; +} + +void com_nokia_qbs_fileapi_ScriptPlugin::initialize(const QString &key, QScriptEngine *engine) +{ + if (key == QLatin1String("qbs")) { + } else if (key == QLatin1String("qbs.fileapi")) { + QScriptValue extensionObject = engine->globalObject(); + File::init(extensionObject, engine); + TextFile::init(extensionObject, engine); + } else { + Q_ASSERT_X(false, "qbs.fileapi::initialize", qPrintable(key)); + } +} + +Q_EXPORT_STATIC_PLUGIN(com_nokia_qbs_fileapi_ScriptPlugin) +Q_EXPORT_PLUGIN2(qtscript_com_nokia_qbs_fileapi, com_nokia_qbs_fileapi_ScriptPlugin) diff --git a/src/plugins/script/file/textfile.cpp b/src/plugins/script/file/textfile.cpp new file mode 100644 index 000000000..91817799e --- /dev/null +++ b/src/plugins/script/file/textfile.cpp @@ -0,0 +1,186 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#include "textfile.h" +#include <QtCore/QFile> +#include <QtCore/QTextStream> +#include <QtScript/QScriptEngine> +#include <QtScript/QScriptValue> + +void TextFile::init(QScriptValue &extensionObject, QScriptEngine *engine) +{ + QScriptValue obj = engine->newQMetaObject(&TextFile::staticMetaObject, engine->newFunction(&TextFile::ctor)); + extensionObject.setProperty("TextFile", obj); +} + +QScriptValue TextFile::ctor(QScriptContext *context, QScriptEngine *engine) +{ + TextFile *t; + switch (context->argumentCount()) { + case 1: + t = new TextFile(context, + context->argument(0).toString()); + break; + case 2: + t = new TextFile(context, + context->argument(0).toString(), + static_cast<OpenMode>(context->argument(1).toInt32()) + ); + break; + case 3: + t = new TextFile(context, + context->argument(0).toString(), + static_cast<OpenMode>(context->argument(1).toInt32()), + context->argument(2).toString() + ); + break; + default: + return context->throwError("TextFile(QString file, OpenMode mode = ReadOnly, QString codec = QLatin1String(\"UTF8\"))"); + } + + QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership); +// obj.setProperty("d", engine->newQObject(new FileImplementation(t), +// QScriptEngine::QScriptEngine::QtOwnership)); + return obj; +} + +TextFile::~TextFile() +{ + delete qstream; + delete qfile; +} + +TextFile::TextFile(QScriptContext *context, QString file, OpenMode mode, QString codec) +{ + Q_UNUSED(codec) + Q_ASSERT(thisObject().engine() == engine()); + TextFile *t = this; + + t->qfile = new QFile(file); + QIODevice::OpenMode m = QIODevice::ReadOnly; + if (mode == ReadWrite) { + m = QIODevice::ReadWrite; + } else if (mode == ReadOnly) { + m = QIODevice::ReadOnly; + } else if (mode == WriteOnly) { + m = QIODevice::WriteOnly; + } + if (!t->qfile->open(m)) { + delete t->qfile; + t->qfile = 0; + context->throwError(QString::fromLatin1("unable to open '%1'") + .arg(file) + ); + } + + t->qstream = new QTextStream(t->qfile); +} + +void TextFile::close() +{ + Q_ASSERT(thisObject().engine() == engine()); + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (t->qfile) + t->qfile->close(); + delete t->qfile; + t->qfile = 0; + delete t->qstream; + t->qstream = 0; +} + +void TextFile::setCodec(QString codec) +{ + Q_ASSERT(thisObject().engine() == engine()); + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qstream) + return; + t->qstream->setCodec(qPrintable(codec)); +} + +QString TextFile::readLine() +{ + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qfile) + return QString(); + return t->qstream->readLine(); +} + +QString TextFile::readAll() +{ + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qfile) + return QString(); + return t->qstream->readAll(); +} + +bool TextFile::eof() +{ + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qstream) + return true; + return t->qstream->atEnd(); +} + +void TextFile::truncate() +{ + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qstream) + return; + t->qfile->resize(0); + t->qstream->reset(); +} + +void TextFile::write(QString str) +{ + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qstream) + return; + (*t->qstream) << str; +} + +void TextFile::writeLine(QString str) +{ + TextFile *t = qscriptvalue_cast<TextFile*>(thisObject()); + if (!t->qstream) + return; + (*t->qstream) << str; +#ifdef Q_OS_WINDOWS + (*t->qstream) <<"\r\n"; +#else + (*t->qstream) <<"\n"; +#endif +} diff --git a/src/plugins/script/file/textfile.h b/src/plugins/script/file/textfile.h new file mode 100644 index 000000000..3ac722311 --- /dev/null +++ b/src/plugins/script/file/textfile.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of the Qt Build Suite +** +** Copyright (c) 2012 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. +** Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** 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. +** +**************************************************************************/ + +#ifndef TEXTFILE_H +#define TEXTFILE_H + +#include <QtCore/QObject> +#include <QtCore/QVariant> +#include <QtScript/QScriptable> + +QT_BEGIN_NAMESPACE +class QFile; +class QTextStream; +QT_END_NAMESPACE + +class TextFile : public QObject, public QScriptable +{ + Q_OBJECT + Q_ENUMS(OpenMode) +public: + static void init(QScriptValue &extensionObject, QScriptEngine *engine); + + enum OpenMode { ReadOnly, WriteOnly, ReadWrite }; + static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine); + TextFile(QScriptContext *context, QString file, OpenMode mode = ReadOnly, QString codec = QLatin1String("UTF8")); + ~TextFile(); + Q_INVOKABLE void close(); + Q_INVOKABLE void setCodec(QString codec); + Q_INVOKABLE QString readLine(); + Q_INVOKABLE QString readAll(); + Q_INVOKABLE bool eof(); + Q_INVOKABLE void truncate(); + Q_INVOKABLE void write(QString); + Q_INVOKABLE void writeLine(QString); +private: + QFile *qfile; + QTextStream *qstream; +}; + +Q_DECLARE_METATYPE(TextFile *); + +#endif // TEXTFILE_H diff --git a/src/plugins/script/script.pro b/src/plugins/script/script.pro new file mode 100644 index 000000000..702ca17ef --- /dev/null +++ b/src/plugins/script/script.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = file |