aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@nokia.com>2012-02-15 15:56:22 +0100
committerOswald Buddenhagen <oswald.buddenhagen@nokia.com>2012-02-15 21:57:41 +0100
commit5672fa8fd3bd6b09125d9e143a7bb277cea2e87f (patch)
treec945b6ad76bd8c8f6af8b520cf3ce72ef298d4b5 /src
Long live qbs!
Diffstat (limited to 'src')
-rw-r--r--src/app/app.pro6
-rw-r--r--src/app/graph/graph.cpp465
-rw-r--r--src/app/graph/graph.pro12
-rw-r--r--src/app/platforms/main.cpp299
-rw-r--r--src/app/platforms/platforms.pro12
-rw-r--r--src/app/platforms/probe.cpp393
-rw-r--r--src/app/qbs/application.cpp61
-rw-r--r--src/app/qbs/application.h63
-rw-r--r--src/app/qbs/ctrlchandler.cpp74
-rw-r--r--src/app/qbs/ctrlchandler.h43
-rw-r--r--src/app/qbs/main.cpp304
-rw-r--r--src/app/qbs/qbs.pro16
-rw-r--r--src/lib/Qbs/Qbs.pri37
-rw-r--r--src/lib/Qbs/buildexecutor.cpp156
-rw-r--r--src/lib/Qbs/buildexecutor.h94
-rw-r--r--src/lib/Qbs/buildproduct.cpp121
-rw-r--r--src/lib/Qbs/buildproduct.h86
-rw-r--r--src/lib/Qbs/buildproject.cpp139
-rw-r--r--src/lib/Qbs/buildproject.h88
-rw-r--r--src/lib/Qbs/error.h74
-rw-r--r--src/lib/Qbs/globals.h63
-rw-r--r--src/lib/Qbs/ilogsink.cpp52
-rw-r--r--src/lib/Qbs/ilogsink.h90
-rw-r--r--src/lib/Qbs/logmessageevent.cpp72
-rw-r--r--src/lib/Qbs/logmessageevent.h67
-rw-r--r--src/lib/Qbs/mainthreadcommunication.cpp123
-rw-r--r--src/lib/Qbs/mainthreadcommunication.h78
-rw-r--r--src/lib/Qbs/oldsourceproject.cpp193
-rw-r--r--src/lib/Qbs/oldsourceproject.h74
-rw-r--r--src/lib/Qbs/processoutput.cpp124
-rw-r--r--src/lib/Qbs/processoutput.h80
-rw-r--r--src/lib/Qbs/processoutputevent.cpp65
-rw-r--r--src/lib/Qbs/processoutputevent.h62
-rw-r--r--src/lib/Qbs/qbserror.cpp76
-rw-r--r--src/lib/Qbs/sourceproject.cpp255
-rw-r--r--src/lib/Qbs/sourceproject.h95
-rw-r--r--src/lib/buildgraph/artifact.cpp97
-rw-r--r--src/lib/buildgraph/artifact.h112
-rw-r--r--src/lib/buildgraph/artifactlist.cpp57
-rw-r--r--src/lib/buildgraph/artifactlist.h109
-rw-r--r--src/lib/buildgraph/automoc.cpp273
-rw-r--r--src/lib/buildgraph/automoc.h86
-rw-r--r--src/lib/buildgraph/buildgraph.cpp1384
-rw-r--r--src/lib/buildgraph/buildgraph.h216
-rw-r--r--src/lib/buildgraph/buildgraph.pri26
-rw-r--r--src/lib/buildgraph/command.cpp231
-rw-r--r--src/lib/buildgraph/command.h154
-rw-r--r--src/lib/buildgraph/commandexecutor.cpp376
-rw-r--r--src/lib/buildgraph/commandexecutor.h106
-rw-r--r--src/lib/buildgraph/executor.cpp885
-rw-r--r--src/lib/buildgraph/executor.h147
-rw-r--r--src/lib/buildgraph/executorjob.cpp128
-rw-r--r--src/lib/buildgraph/executorjob.h86
-rw-r--r--src/lib/buildgraph/rulegraph.cpp201
-rw-r--r--src/lib/buildgraph/rulegraph.h77
-rw-r--r--src/lib/buildgraph/scanresultcache.cpp56
-rw-r--r--src/lib/buildgraph/scanresultcache.h71
-rw-r--r--src/lib/buildgraph/transformer.cpp119
-rw-r--r--src/lib/buildgraph/transformer.h85
-rw-r--r--src/lib/language/language.cpp453
-rw-r--r--src/lib/language/language.h264
-rw-r--r--src/lib/language/language.pri9
-rw-r--r--src/lib/language/loader.cpp2647
-rw-r--r--src/lib/language/loader.h408
-rw-r--r--src/lib/lib.pro16
-rw-r--r--src/lib/parser/cmd.sed13
-rw-r--r--src/lib/parser/gen-parser.sh14
-rw-r--r--src/lib/parser/parser.pri23
-rw-r--r--src/lib/parser/qmlerror.cpp285
-rw-r--r--src/lib/parser/qmlerror.h82
-rw-r--r--src/lib/parser/qmljs.g3138
-rw-r--r--src/lib/parser/qmljsast.cpp947
-rw-r--r--src/lib/parser/qmljsast_p.h2537
-rw-r--r--src/lib/parser/qmljsastfwd_p.h180
-rw-r--r--src/lib/parser/qmljsastvisitor.cpp49
-rw-r--r--src/lib/parser/qmljsastvisitor_p.h326
-rw-r--r--src/lib/parser/qmljsengine_p.cpp203
-rw-r--r--src/lib/parser/qmljsengine_p.h158
-rw-r--r--src/lib/parser/qmljsglobal_p.h56
-rw-r--r--src/lib/parser/qmljsgrammar.cpp1007
-rw-r--r--src/lib/parser/qmljsgrammar_p.h201
-rw-r--r--src/lib/parser/qmljslexer.cpp1249
-rw-r--r--src/lib/parser/qmljslexer_p.h240
-rw-r--r--src/lib/parser/qmljsmemorypool_p.h124
-rw-r--r--src/lib/parser/qmljsnodepool_p.h130
-rw-r--r--src/lib/parser/qmljsparser.cpp1893
-rw-r--r--src/lib/parser/qmljsparser_p.h237
-rw-r--r--src/lib/qtconcurrent/QtConcurrentTools39
-rw-r--r--src/lib/qtconcurrent/multitask.h202
-rw-r--r--src/lib/qtconcurrent/qtconcurrent.pri6
-rw-r--r--src/lib/qtconcurrent/qtconcurrent_global.h49
-rw-r--r--src/lib/qtconcurrent/runextensions.h431
-rw-r--r--src/lib/tools/codelocation.h91
-rw-r--r--src/lib/tools/coloredoutput.cpp103
-rw-r--r--src/lib/tools/coloredoutput.h73
-rw-r--r--src/lib/tools/error.cpp70
-rw-r--r--src/lib/tools/error.h90
-rw-r--r--src/lib/tools/fakeconcurrent.h103
-rw-r--r--src/lib/tools/fileinfo.cpp247
-rw-r--r--src/lib/tools/fileinfo.h90
-rw-r--r--src/lib/tools/filetime.h128
-rw-r--r--src/lib/tools/filetime_unix.cpp78
-rw-r--r--src/lib/tools/filetime_win.cpp104
-rw-r--r--src/lib/tools/logger.cpp325
-rw-r--r--src/lib/tools/logger.h138
-rw-r--r--src/lib/tools/logsink.cpp81
-rw-r--r--src/lib/tools/logsink.h60
-rw-r--r--src/lib/tools/options.cpp450
-rw-r--r--src/lib/tools/options.h113
-rw-r--r--src/lib/tools/persistence.cpp227
-rw-r--r--src/lib/tools/persistence.h237
-rw-r--r--src/lib/tools/platform.cpp82
-rw-r--r--src/lib/tools/platform.h59
-rw-r--r--src/lib/tools/runenvironment.cpp134
-rw-r--r--src/lib/tools/runenvironment.h60
-rw-r--r--src/lib/tools/scannerpluginmanager.cpp113
-rw-r--r--src/lib/tools/scannerpluginmanager.h67
-rw-r--r--src/lib/tools/scripttools.cpp139
-rw-r--r--src/lib/tools/scripttools.h83
-rw-r--r--src/lib/tools/settings.cpp117
-rw-r--r--src/lib/tools/settings.h83
-rw-r--r--src/lib/tools/tools.pri40
-rw-r--r--src/lib/use.pri43
-rw-r--r--src/plugins/plugins.pro2
-rw-r--r--src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h151
-rw-r--r--src/plugins/scanner/cpp/Lexer.cpp669
-rw-r--r--src/plugins/scanner/cpp/Lexer.h162
-rw-r--r--src/plugins/scanner/cpp/Token.cpp154
-rw-r--r--src/plugins/scanner/cpp/Token.h371
-rw-r--r--src/plugins/scanner/cpp/cpp.cpp281
-rw-r--r--src/plugins/scanner/cpp/cpp.pro15
-rw-r--r--src/plugins/scanner/cpp/cpp_global.h47
-rw-r--r--src/plugins/scanner/qt/qt.cpp227
-rw-r--r--src/plugins/scanner/qt/qt.pro12
-rw-r--r--src/plugins/scanner/scanner.h93
-rw-r--r--src/plugins/scanner/scanner.pro3
-rw-r--r--src/plugins/script/file/file.cpp91
-rw-r--r--src/plugins/script/file/file.h58
-rw-r--r--src/plugins/script/file/file.pro20
-rw-r--r--src/plugins/script/file/plugin.cpp75
-rw-r--r--src/plugins/script/file/textfile.cpp186
-rw-r--r--src/plugins/script/file/textfile.h76
-rw-r--r--src/plugins/script/script.pro2
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 *> &currentBranch)
+{
+ 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 *> &currentBranch);
+
+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