update qmake evaluator to newest version from qt creator
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>
Thu, 6 Sep 2012 19:50:09 +0000 (21:50 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 20 Sep 2012 12:34:56 +0000 (14:34 +0200)
Change-Id: I66ec46dd87f922094f6937b50cc40e02ef08cc86
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
18 files changed:
src/linguist/lrelease/main.cpp
src/linguist/lupdate/main.cpp
src/linguist/shared/ioutils.cpp
src/linguist/shared/ioutils.h
src/linguist/shared/profileevaluator.cpp
src/linguist/shared/profileevaluator.h
src/linguist/shared/proitems.cpp
src/linguist/shared/proitems.h
src/linguist/shared/proparser.pri
src/linguist/shared/qmake_global.h [moved from src/linguist/shared/proparser_global.h with 74% similarity]
src/linguist/shared/qmakebuiltins.cpp [new file with mode: 0644]
src/linguist/shared/qmakeevaluator.cpp [new file with mode: 0644]
src/linguist/shared/qmakeevaluator.h [new file with mode: 0644]
src/linguist/shared/qmakeevaluator_p.h [new file with mode: 0644]
src/linguist/shared/qmakeglobals.cpp [new file with mode: 0644]
src/linguist/shared/qmakeglobals.h [new file with mode: 0644]
src/linguist/shared/qmakeparser.cpp [moved from src/linguist/shared/profileparser.cpp with 76% similarity]
src/linguist/shared/qmakeparser.h [moved from src/linguist/shared/profileparser.h with 70% similarity]

index c282f66..1a0fd99 100644 (file)
@@ -41,7 +41,7 @@
 
 #include "translator.h"
 
-#include <profileparser.h>
+#include <qmakeparser.h>
 #include <profileevaluator.h>
 
 #ifndef QT_BOOTSTRAPPED
@@ -190,30 +190,25 @@ static bool releaseTsFile(const QString& tsFileName,
     return releaseTranslator(tor, qmFileName, cd, removeIdentical);
 }
 
-static void print(const QString &fileName, int lineNo, const QString &msg)
+static void print(const QString &fileName, int lineNo, int type, const QString &msg)
 {
-    if (lineNo)
-        printErr(QString::fromLatin1("%2(%1): %3").arg(lineNo).arg(fileName, msg));
+    QString pfx = ((type & QMakeHandler::CategoryMask) == QMakeHandler::WarningMessage)
+                  ? QString::fromLatin1("WARNING: ") : QString();
+    if (lineNo > 0)
+        printErr(QString::fromLatin1("%1%2:%3: %4\n").arg(pfx, fileName, QString::number(lineNo), msg));
+    else if (lineNo)
+        printErr(QString::fromLatin1("%1%2: %3\n").arg(pfx, fileName, msg));
     else
-        printErr(msg);
+        printErr(QString::fromLatin1("%1%2\n").arg(pfx, msg));
 }
 
-class ParseHandler : public ProFileParserHandler {
+class EvalHandler : public QMakeHandler {
 public:
-    virtual void parseError(const QString &fileName, int lineNo, const QString &msg)
-        { if (verbose) print(fileName, lineNo, msg); }
+    virtual void message(int type, const QString &msg, const QString &fileName, int lineNo)
+        { if (verbose) print(fileName, lineNo, type, msg); }
 
-    bool verbose;
-};
-
-class EvalHandler : public ProFileEvaluatorHandler {
-public:
-    virtual void configError(const QString &msg)
-        { printErr(msg); }
-    virtual void evalError(const QString &fileName, int lineNo, const QString &msg)
-        { if (verbose) print(fileName, lineNo, msg); }
     virtual void fileMessage(const QString &msg)
-        { printErr(msg); }
+        { printErr(msg + QLatin1Char('\n')); }
 
     virtual void aboutToEval(ProFile *, ProFile *, EvalFileType) {}
     virtual void doneWithEval(ProFile *) {}
@@ -221,7 +216,6 @@ public:
     bool verbose;
 };
 
-static ParseHandler parseHandler;
 static EvalHandler evalHandler;
 
 int main(int argc, char **argv)
@@ -312,15 +306,17 @@ int main(int argc, char **argv)
             || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
             QFileInfo fi(inputFile);
 
-            parseHandler.verbose = evalHandler.verbose = cd.isVerbose();
-            ProFileOption option;
+            evalHandler.verbose = cd.isVerbose();
+            ProFileGlobals option;
 #ifdef QT_BOOTSTRAPPED
-            option.initProperties(binDir + QLatin1String("/qmake"));
+            option.qmake_abslocation = binDir + QLatin1String("/qmake");
 #else
-            option.initProperties(app.applicationDirPath() + QLatin1String("/qmake"));
+            option.qmake_abslocation = app.applicationDirPath() + QLatin1String("/qmake");
 #endif
-            ProFileParser parser(0, &parseHandler);
+            option.initProperties();
+            QMakeParser parser(0, &evalHandler);
             ProFileEvaluator visitor(&option, &parser, &evalHandler);
+            visitor.setCumulative(true);
 
             ProFile *pro;
             if (!(pro = parser.parsedProFile(QDir::cleanPath(fi.absoluteFilePath())))) {
index a90e112..eb225d9 100644 (file)
@@ -42,7 +42,7 @@
 #include "lupdate.h"
 
 #include <translator.h>
-#include <profileparser.h>
+#include <qmakeparser.h>
 #include <profileevaluator.h>
 
 #include <QtCore/QCoreApplication>
@@ -229,30 +229,25 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
     }
 }
 
-static void print(const QString &fileName, int lineNo, const QString &msg)
+static void print(const QString &fileName, int lineNo, int type, const QString &msg)
 {
-    if (lineNo)
-        printErr(QString::fromLatin1("%2(%1): %3").arg(lineNo).arg(fileName, msg));
+    QString pfx = ((type & QMakeHandler::CategoryMask) == QMakeHandler::WarningMessage)
+                  ? QString::fromLatin1("WARNING: ") : QString();
+    if (lineNo > 0)
+        printErr(QString::fromLatin1("%1%2:%3: %4\n").arg(pfx, fileName, QString::number(lineNo), msg));
+    else if (lineNo)
+        printErr(QString::fromLatin1("%1%2: %3\n").arg(pfx, fileName, msg));
     else
-        printErr(msg);
+        printErr(QString::fromLatin1("%1%2\n").arg(pfx, msg));
 }
 
-class ParseHandler : public ProFileParserHandler {
+class EvalHandler : public QMakeHandler {
 public:
-    virtual void parseError(const QString &fileName, int lineNo, const QString &msg)
-        { if (verbose) print(fileName, lineNo, msg); }
+    virtual void message(int type, const QString &msg, const QString &fileName, int lineNo)
+        { if (verbose) print(fileName, lineNo, type, msg); }
 
-    bool verbose;
-};
-
-class EvalHandler : public ProFileEvaluatorHandler {
-public:
-    virtual void configError(const QString &msg)
-        { printErr(msg); }
-    virtual void evalError(const QString &fileName, int lineNo, const QString &msg)
-        { if (verbose) print(fileName, lineNo, msg); }
     virtual void fileMessage(const QString &msg)
-        { printErr(msg); }
+        { printErr(msg + QLatin1Char('\n')); }
 
     virtual void aboutToEval(ProFile *, ProFile *, EvalFileType) {}
     virtual void doneWithEval(ProFile *) {}
@@ -260,7 +255,6 @@ public:
     bool verbose;
 };
 
-static ParseHandler parseHandler;
 static EvalHandler evalHandler;
 
 static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
@@ -338,16 +332,15 @@ static void processSources(Translator &fetchedTor,
         printErr(cd.error());
 }
 
-static void processProjects(
-        bool topLevel, bool nestComplain, const QStringList &proFiles,
-        ProFileOption *option, ProFileParser *parser,
+static void processProjects(bool topLevel, bool nestComplain, const QStringList &proFiles,
+        ProFileGlobals *option, QMakeParser *parser,
         UpdateOptions options, const QByteArray &codecForSource,
         const QString &targetLanguage, const QString &sourceLanguage,
         Translator *parentTor, bool *fail);
 
 static void processProject(
         bool nestComplain, const QFileInfo &pfi,
-        ProFileOption *option, ProFileParser *parser, ProFileEvaluator &visitor,
+        ProFileGlobals *option, QMakeParser *parser, ProFileEvaluator &visitor,
         UpdateOptions options, const QByteArray &_codecForSource,
         const QString &targetLanguage, const QString &sourceLanguage,
         Translator *fetchedTor, bool *fail)
@@ -399,9 +392,8 @@ static void processProject(
     }
 }
 
-static void processProjects(
-        bool topLevel, bool nestComplain, const QStringList &proFiles,
-        ProFileOption *option, ProFileParser *parser,
+static void processProjects(bool topLevel, bool nestComplain, const QStringList &proFiles,
+        ProFileGlobals *option, QMakeParser *parser,
         UpdateOptions options, const QByteArray &codecForSource,
         const QString &targetLanguage, const QString &sourceLanguage,
         Translator *parentTor, bool *fail)
@@ -410,6 +402,7 @@ static void processProjects(
         QFileInfo pfi(proFile);
 
         ProFileEvaluator visitor(option, parser, &evalHandler);
+        visitor.setCumulative(true);
         ProFile *pro;
         if (!(pro = parser->parsedProFile(QDir::cleanPath(pfi.absoluteFilePath())))) {
             if (topLevel)
@@ -789,11 +782,13 @@ int main(int argc, char **argv)
             return 1;
         }
 
-        parseHandler.verbose = evalHandler.verbose = !!(options & Verbose);
-        ProFileOption option;
-        option.initProperties(app.applicationDirPath() + QLatin1String("/qmake"));
-        option.setCommandLineArguments(QStringList() << QLatin1String("CONFIG+=lupdate_run"));
-        ProFileParser parser(0, &parseHandler);
+        evalHandler.verbose = !!(options & Verbose);
+        ProFileGlobals option;
+        option.qmake_abslocation = app.applicationDirPath() + QLatin1String("/qmake");
+        option.initProperties();
+        option.setCommandLineArguments(QDir::currentPath(),
+                                       QStringList() << QLatin1String("CONFIG+=lupdate_run"));
+        QMakeParser parser(0, &evalHandler);
 
         if (!tsFileNames.isEmpty()) {
             Translator fetchedTor;
index 7f7a694..4038b85 100644 (file)
@@ -41,8 +41,8 @@
 
 #include "ioutils.h"
 
-#include <QtCore/QDir>
-#include <QtCore/QFile>
+#include <qdir.h>
+#include <qfile.h>
 
 #ifdef Q_OS_WIN
 #  include <windows.h>
@@ -52,7 +52,9 @@
 #  include <unistd.h>
 #endif
 
-using namespace ProFileEvaluatorInternal;
+QT_BEGIN_NAMESPACE
+
+using namespace QMakeInternal;
 
 IoUtils::FileType IoUtils::fileType(const QString &fileName)
 {
@@ -100,44 +102,53 @@ QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
     return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName);
 }
 
-#ifdef QT_BOOTSTRAPPED
-inline static bool isSpecialChar(ushort c)
+inline static
+bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
+{
+    for (int x = arg.length() - 1; x >= 0; --x) {
+        ushort c = arg.unicode()[x].unicode();
+        if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
+            return true;
+    }
+    return false;
+}
+
+QString IoUtils::shellQuoteUnix(const QString &arg)
 {
     // Chars that should be quoted (TM). This includes:
-#ifdef Q_OS_WIN
-    // - control chars & space
-    // - the shell meta chars "&()<>^|
-    // - the potential separators ,;=
-    static const uchar iqm[] = {
-        0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
-        0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
-    };
-#else
     static const uchar iqm[] = {
         0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
         0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
     }; // 0-32 \'"$`<>|;&(){}*?#!~[]
-#endif
 
-    return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
-}
+    if (!arg.length())
+        return QString::fromLatin1("\"\"");
 
-inline static bool hasSpecialChars(const QString &arg)
-{
-    for (int x = arg.length() - 1; x >= 0; --x)
-        if (isSpecialChar(arg.unicode()[x].unicode()))
-            return true;
-    return false;
+    QString ret(arg);
+    if (hasSpecialChars(ret, iqm)) {
+        ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
+        ret.prepend(QLatin1Char('\''));
+        ret.append(QLatin1Char('\''));
+    }
+    return ret;
 }
 
-QString IoUtils::shellQuote(const QString &arg)
+QString IoUtils::shellQuoteWin(const QString &arg)
 {
+    // Chars that should be quoted (TM). This includes:
+    // - control chars & space
+    // - the shell meta chars "&()<>^|
+    // - the potential separators ,;=
+    static const uchar iqm[] = {
+        0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
+        0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
+    };
+
     if (!arg.length())
         return QString::fromLatin1("\"\"");
 
     QString ret(arg);
-    if (hasSpecialChars(ret)) {
-#ifdef Q_OS_WIN
+    if (hasSpecialChars(ret, iqm)) {
         // Quotes are escaped and their preceding backslashes are doubled.
         // It's impossible to escape anything inside a quoted string on cmd
         // level, so the outer quoting must be "suspended".
@@ -150,12 +161,8 @@ QString IoUtils::shellQuote(const QString &arg)
             --i;
         ret.insert(i, QLatin1Char('"'));
         ret.prepend(QLatin1Char('"'));
-#else // Q_OS_WIN
-        ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
-        ret.prepend(QLatin1Char('\''));
-        ret.append(QLatin1Char('\''));
-#endif // Q_OS_WIN
     }
     return ret;
 }
-#endif
+
+QT_END_NAMESPACE
index 84b0fd4..86e098f 100644 (file)
 #ifndef IOUTILS_H
 #define IOUTILS_H
 
-#include <QtCore/QString>
+#include <qstring.h>
 
-namespace ProFileEvaluatorInternal {
+QT_BEGIN_NAMESPACE
+
+namespace QMakeInternal {
 
 /*!
   This class provides replacement functionality for QFileInfo, QFile & QDir,
@@ -64,11 +66,18 @@ public:
     static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); }
     static QStringRef fileName(const QString &fileName); // Requires normalized path
     static QString resolvePath(const QString &baseDir, const QString &fileName);
-#ifdef QT_BOOTSTRAPPED
-    static QString shellQuote(const QString &arg);
+    static QString shellQuoteUnix(const QString &arg);
+    static QString shellQuoteWin(const QString &arg);
+    static QString shellQuote(const QString &arg)
+#ifdef Q_OS_UNIX
+        { return shellQuoteUnix(arg); }
+#else
+        { return shellQuoteWin(arg); }
 #endif
 };
 
-}
+} // namespace ProFileEvaluatorInternal
+
+QT_END_NAMESPACE
 
 #endif // IOUTILS_H
index 266bfa4..0ad9e50 100644 (file)
 
 #include "profileevaluator.h"
 
-#include "profileparser.h"
+#include "qmakeglobals.h"
 #include "ioutils.h"
 
-#include <QtCore/QByteArray>
-#include <QtCore/QDateTime>
-#include <QtCore/QDebug>
-#include <QtCore/QDir>
-#include <QtCore/QFile>
-#include <QtCore/QFileInfo>
-#include <QtCore/QList>
-#include <QtCore/QRegExp>
-#include <QtCore/QSet>
-#include <QtCore/QStack>
-#include <QtCore/QString>
-#include <QtCore/QStringList>
-#include <QtCore/QTextStream>
-#ifdef PROEVALUATOR_THREAD_SAFE
-# include <QtCore/QThreadPool>
-#endif
-
-#ifdef Q_OS_UNIX
-#include <unistd.h>
-#include <sys/utsname.h>
-#else
-#include <windows.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef Q_OS_WIN32
-#define QT_POPEN _popen
-#define QT_PCLOSE _pclose
-#else
-#define QT_POPEN popen
-#define QT_PCLOSE pclose
-#endif
-
-using namespace ProFileEvaluatorInternal;
-
-QT_BEGIN_NAMESPACE
-
-using namespace ProStringConstants;
-
-
-#define fL1S(s) QString::fromLatin1(s)
-
-///////////////////////////////////////////////////////////////////////
-//
-// ProFileOption
-//
-///////////////////////////////////////////////////////////////////////
-
-ProFileOption::ProFileOption()
-{
-#ifdef Q_OS_WIN
-    dirlist_sep = QLatin1Char(';');
-    dir_sep = QLatin1Char('\\');
-#else
-    dirlist_sep = QLatin1Char(':');
-    dir_sep = QLatin1Char('/');
-#endif
-    qmakespec = getEnv(QLatin1String("QMAKESPEC"));
-
-    host_mode = HOST_UNKNOWN_MODE;
-    target_mode = TARG_UNKNOWN_MODE;
-
-#ifdef PROEVALUATOR_THREAD_SAFE
-    base_inProgress = false;
-#endif
-}
-
-ProFileOption::~ProFileOption()
-{
-}
-
-void ProFileOption::setCommandLineArguments(const QStringList &args)
-{
-    QStringList _precmds, _preconfigs, _postcmds, _postconfigs;
-    bool after = false;
-
-    bool isConf = false;
-    foreach (const QString &arg, args) {
-        if (isConf) {
-            isConf = false;
-            if (after)
-                _postconfigs << arg;
-            else
-                _preconfigs << arg;
-        } else if (arg.startsWith(QLatin1Char('-'))) {
-            if (arg == QLatin1String("-after")) {
-                after = true;
-            } else if (arg == QLatin1String("-config")) {
-                isConf = true;
-            } else if (arg == QLatin1String("-win32")) {
-                host_mode = HOST_WIN_MODE;
-                target_mode = TARG_WIN_MODE;
-            } else if (arg == QLatin1String("-unix")) {
-                host_mode = HOST_UNIX_MODE;
-                target_mode = TARG_UNIX_MODE;
-            } else if (arg == QLatin1String("-macx")) {
-                host_mode = HOST_MACX_MODE;
-                target_mode = TARG_MACX_MODE;
-            }
-        } else if (arg.contains(QLatin1Char('='))) {
-            if (after)
-                _postcmds << arg;
-            else
-                _precmds << arg;
-        }
-    }
-
-    if (!_preconfigs.isEmpty())
-        _precmds << (fL1S("CONFIG += ") + _preconfigs.join(fL1S(" ")));
-    precmds = _precmds.join(fL1S("\n"));
-    if (!_postconfigs.isEmpty())
-        _postcmds << (fL1S("CONFIG += ") + _postconfigs.join(fL1S(" ")));
-    postcmds = _postcmds.join(fL1S("\n"));
-
-    if (host_mode != HOST_UNKNOWN_MODE)
-        applyHostMode();
-}
-
-void ProFileOption::applyHostMode()
-{
-   if (host_mode == HOST_WIN_MODE) {
-       dir_sep = fL1S("\\");
-   } else {
-       dir_sep = fL1S("/");
-   }
-}
-
-QString ProFileOption::getEnv(const QString &var) const
-{
-#ifndef QT_BOOTSTRAPPED
-    if (!environment.isEmpty())
-        return environment.value(var);
-#endif
-    return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
-}
-
-#ifdef PROEVALUATOR_INIT_PROPS
-bool ProFileOption::initProperties(const QString &qmake)
-{
-    QByteArray data;
-#ifndef QT_BOOTSTRAPPED
-    QProcess proc;
-    proc.start(qmake, QStringList() << QLatin1String("-query"));
-    if (!proc.waitForFinished())
-        return false;
-    data = proc.readAll();
-#else
-    if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake) + QLatin1String(" -query"))
-                              .toLocal8Bit(), "r")) {
-        char buff[1024];
-        while (!feof(proc))
-            data.append(buff, int(fread(buff, 1, 1023, proc)));
-        QT_PCLOSE(proc);
-    }
-#endif
-    foreach (QByteArray line, data.split('\n'))
-        if (!line.startsWith("QMAKE_")) {
-            int off = line.indexOf(':');
-            if (off < 0) // huh?
-                continue;
-            if (line.endsWith('\r'))
-                line.chop(1);
-            properties.insert(QString::fromLatin1(line.left(off)),
-                              QString::fromLocal8Bit(line.mid(off + 1)));
-        }
-    return true;
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////
-//
-// ProFileEvaluator::Private
-//
-///////////////////////////////////////////////////////////////////////
-
-class ProFileEvaluator::Private
-{
-public:
-    static void initStatics();
-    Private(ProFileEvaluator *q_, ProFileOption *option, ProFileParser *parser,
-            ProFileEvaluatorHandler *handler);
-    ~Private();
-
-    ProFileEvaluator *q;
-
-    enum VisitReturn {
-        ReturnFalse,
-        ReturnTrue,
-        ReturnBreak,
-        ReturnNext,
-        ReturnReturn
-    };
-
-    static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
-    ProString getStr(const ushort *&tokPtr);
-    ProString getHashStr(const ushort *&tokPtr);
-    void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
-    static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
-    static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
-    void skipExpression(const ushort *&tokPtr);
-
-    void visitCmdLine(const QString &cmds);
-    VisitReturn visitProFile(ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
-                             ProFileEvaluator::LoadFlags flags);
-    VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
-    VisitReturn visitProBlock(const ushort *tokPtr);
-    VisitReturn visitProLoop(const ProString &variable, const ushort *exprPtr,
-                             const ushort *tokPtr);
-    void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr);
-    void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
-
-    static inline const ProString &map(const ProString &var);
-    QHash<ProString, ProStringList> *findValues(const ProString &variableName,
-                                                QHash<ProString, ProStringList>::Iterator *it);
-    ProStringList &valuesRef(const ProString &variableName);
-    ProStringList valuesDirect(const ProString &variableName) const;
-    ProStringList values(const ProString &variableName) const;
-    QString propertyValue(const QString &val, bool complain) const;
-
-    ProStringList split_value_list(const QString &vals, const ProFile *source = 0);
-    bool isActiveConfig(const QString &config, bool regex = false);
-    ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
-    ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
-    ProStringList evaluateExpandFunction(const ProString &function, const ProString &arguments);
-    ProStringList evaluateExpandFunction(const ProString &function, const ushort *&tokPtr);
-    ProStringList evaluateExpandFunction(const ProString &function, const ProStringList &args);
-    void evalError(const QString &msg) const;
-
-    QString currentFileName() const;
-    QString currentDirectory() const;
-    ProFile *currentProFile() const;
-    QString resolvePath(const QString &fileName) const
-        { return IoUtils::resolvePath(currentDirectory(), fileName); }
-
-    VisitReturn evaluateConditionalFunction(const ProString &function, const ProString &arguments);
-    VisitReturn evaluateConditionalFunction(const ProString &function, const ushort *&tokPtr);
-    VisitReturn evaluateConditionalFunction(const ProString &function, const ProStringList &args);
-    bool evaluateFileDirect(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
-                            ProFileEvaluator::LoadFlags flags);
-    bool evaluateFile(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
-                      ProFileEvaluator::LoadFlags flags);
-    bool evaluateFeatureFile(const QString &fileName);
-    enum EvalIntoMode { EvalProOnly, EvalWithDefaults, EvalWithSetup };
-    bool evaluateFileInto(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
-                          QHash<ProString, ProStringList> *values, FunctionDefs *defs,
-                          EvalIntoMode mode); // values are output-only, defs are input-only
-
-    static ALWAYS_INLINE VisitReturn returnBool(bool b)
-        { return b ? ReturnTrue : ReturnFalse; }
-
-    QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
-    QList<ProStringList> prepareFunctionArgs(const ProString &arguments);
-    ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok);
-    VisitReturn evaluateBoolFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList,
-                                     const ProString &function);
-
-    bool modesForGenerator(const QString &gen,
-            ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const;
-    void validateModes() const;
-    QStringList qmakeMkspecPaths() const;
-    QStringList qmakeFeaturePaths() const;
-
-    void populateDeps(
-            const ProStringList &deps, const ProString &prefix,
-            QHash<ProString, QSet<ProString> > &dependencies,
-            QHash<ProString, ProStringList> &dependees,
-            ProStringList &rootSet) const;
-
-    QString expandEnvVars(const QString &str) const;
-    QString fixPathToLocalOS(const QString &str) const;
-    QString sysrootify(const QString &path, const QString &baseDir) const;
-
-#ifndef QT_BOOTSTRAPPED
-    void runProcess(QProcess *proc, const QString &command, QProcess::ProcessChannel chan) const;
-#endif
-
-    int m_skipLevel;
-    int m_loopLevel; // To report unexpected break() and next()s
-#ifdef PROEVALUATOR_CUMULATIVE
-    bool m_cumulative;
-#else
-    enum { m_cumulative = 0 };
-#endif
-
-    struct Location {
-        Location() : pro(0), line(0) {}
-        Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {}
-        ProFile *pro;
-        int line;
-    };
-
-    Location m_current; // Currently evaluated location
-    QStack<Location> m_locationStack; // All execution location changes
-    QStack<ProFile *> m_profileStack; // Includes only
-
-    QString m_outputDir;
-
-    int m_listCount;
-    FunctionDefs m_functionDefs;
-    ProStringList m_returnValue;
-    QStack<QHash<ProString, ProStringList> > m_valuemapStack;         // VariableName must be us-ascii, the content however can be non-us-ascii.
-    QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
-
-    ProFileOption *m_option;
-    ProFileParser *m_parser;
-    ProFileEvaluatorHandler *m_handler;
-
-    enum ExpandFunc {
-        E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
-        E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
-        E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
-        E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
-        E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS
-    };
-
-    enum TestFunc {
-        T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
-        T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
-        T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
-        T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF
-    };
-
-    enum VarName {
-        V_LITERAL_DOLLAR, V_LITERAL_HASH, V_LITERAL_WHITESPACE,
-        V_DIRLIST_SEPARATOR, V_DIR_SEPARATOR,
-        V_OUT_PWD, V_PWD, V_IN_PWD,
-        V__FILE_, V__LINE_, V__PRO_FILE_, V__PRO_FILE_PWD_,
-        V_QMAKE_HOST_arch, V_QMAKE_HOST_name, V_QMAKE_HOST_os,
-        V_QMAKE_HOST_version, V_QMAKE_HOST_version_string,
-        V__DATE_, V__QMAKE_CACHE_
-    };
-};
-
-static struct {
-    QString field_sep;
-    QString strtrue;
-    QString strfalse;
-    QString strunix;
-    QString strmacx;
-    QString strmac;
-    QString strwin32;
-    QString strsymbian;
-    ProString strCONFIG;
-    ProString strARGS;
-    QString strDot;
-    QString strDotDot;
-    QString strever;
-    QString strforever;
-    ProString strTEMPLATE;
-    ProString strQMAKE_DIR_SEP;
-    QHash<ProString, int> expands;
-    QHash<ProString, int> functions;
-    QHash<ProString, int> varList;
-    QHash<ProString, ProString> varMap;
-    QRegExp reg_variableName;
-    ProStringList fakeValue;
-} statics;
-
-void ProFileEvaluator::Private::initStatics()
-{
-    if (!statics.field_sep.isNull())
-        return;
-
-    statics.field_sep = QLatin1String(" ");
-    statics.strtrue = QLatin1String("true");
-    statics.strfalse = QLatin1String("false");
-    statics.strunix = QLatin1String("unix");
-    statics.strmacx = QLatin1String("macx");
-    statics.strmac = QLatin1String("mac");
-    statics.strwin32 = QLatin1String("win32");
-    statics.strsymbian = QLatin1String("symbian");
-    statics.strCONFIG = ProString("CONFIG");
-    statics.strARGS = ProString("ARGS");
-    statics.strDot = QLatin1String(".");
-    statics.strDotDot = QLatin1String("..");
-    statics.strever = QLatin1String("ever");
-    statics.strforever = QLatin1String("forever");
-    statics.strTEMPLATE = ProString("TEMPLATE");
-    statics.strQMAKE_DIR_SEP = ProString("QMAKE_DIR_SEP");
-
-    statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
-    statics.reg_variableName.setMinimal(true);
-
-    statics.fakeValue.detach(); // It has to have a unique begin() value
-
-    static const struct {
-        const char * const name;
-        const ExpandFunc func;
-    } expandInits[] = {
-        { "member", E_MEMBER },
-        { "first", E_FIRST },
-        { "last", E_LAST },
-        { "size", E_SIZE },
-        { "cat", E_CAT },
-        { "fromfile", E_FROMFILE },
-        { "eval", E_EVAL },
-        { "list", E_LIST },
-        { "sprintf", E_SPRINTF },
-        { "join", E_JOIN },
-        { "split", E_SPLIT },
-        { "basename", E_BASENAME },
-        { "dirname", E_DIRNAME },
-        { "section", E_SECTION },
-        { "find", E_FIND },
-        { "system", E_SYSTEM },
-        { "unique", E_UNIQUE },
-        { "quote", E_QUOTE },
-        { "escape_expand", E_ESCAPE_EXPAND },
-        { "upper", E_UPPER },
-        { "lower", E_LOWER },
-        { "re_escape", E_RE_ESCAPE },
-        { "files", E_FILES },
-        { "prompt", E_PROMPT }, // interactive, so cannot be implemented
-        { "replace", E_REPLACE },
-        { "sort_depends", E_SORT_DEPENDS },
-        { "resolve_depends", E_RESOLVE_DEPENDS }
-    };
-    for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
-        statics.expands.insert(ProString(expandInits[i].name), expandInits[i].func);
-
-    static const struct {
-        const char * const name;
-        const TestFunc func;
-    } testInits[] = {
-        { "requires", T_REQUIRES },
-        { "greaterThan", T_GREATERTHAN },
-        { "lessThan", T_LESSTHAN },
-        { "equals", T_EQUALS },
-        { "isEqual", T_EQUALS },
-        { "exists", T_EXISTS },
-        { "export", T_EXPORT },
-        { "clear", T_CLEAR },
-        { "unset", T_UNSET },
-        { "eval", T_EVAL },
-        { "CONFIG", T_CONFIG },
-        { "if", T_IF },
-        { "isActiveConfig", T_CONFIG },
-        { "system", T_SYSTEM },
-        { "return", T_RETURN },
-        { "break", T_BREAK },
-        { "next", T_NEXT },
-        { "defined", T_DEFINED },
-        { "contains", T_CONTAINS },
-        { "infile", T_INFILE },
-        { "count", T_COUNT },
-        { "isEmpty", T_ISEMPTY },
-        { "load", T_LOAD },
-        { "include", T_INCLUDE },
-        { "debug", T_DEBUG },
-        { "message", T_MESSAGE },
-        { "warning", T_MESSAGE },
-        { "error", T_MESSAGE },
-    };
-    for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
-        statics.functions.insert(ProString(testInits[i].name), testInits[i].func);
-
-    static const char * const names[] = {
-        "LITERAL_DOLLAR", "LITERAL_HASH", "LITERAL_WHITESPACE",
-        "DIRLIST_SEPARATOR", "DIR_SEPARATOR",
-        "OUT_PWD", "PWD", "IN_PWD",
-        "_FILE_", "_LINE_", "_PRO_FILE_", "_PRO_FILE_PWD_",
-        "QMAKE_HOST.arch", "QMAKE_HOST.name", "QMAKE_HOST.os",
-        "QMAKE_HOST.version", "QMAKE_HOST.version_string",
-        "_DATE_", "_QMAKE_CACHE_"
-    };
-    for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
-        statics.varList.insert(ProString(names[i]), i);
-
-    static const struct {
-        const char * const oldname, * const newname;
-    } mapInits[] = {
-        { "INTERFACES", "FORMS" },
-        { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
-        { "TARGETDEPS", "POST_TARGETDEPS" },
-        { "LIBPATH", "QMAKE_LIBDIR" },
-        { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
-        { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
-        { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
-        { "PRECOMPH", "PRECOMPILED_HEADER" },
-        { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
-        { "INCPATH", "INCLUDEPATH" },
-        { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
-        { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
-        { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
-        { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
-        { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
-        { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
-        { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
-        { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
-        { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }
-    };
-    for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
-        statics.varMap.insert(ProString(mapInits[i].oldname),
-                              ProString(mapInits[i].newname));
-}
-
-const ProString &ProFileEvaluator::Private::map(const ProString &var)
-{
-    QHash<ProString, ProString>::ConstIterator it = statics.varMap.constFind(var);
-    return (it != statics.varMap.constEnd()) ? it.value() : var;
-}
-
-
-ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option,
-                                   ProFileParser *parser, ProFileEvaluatorHandler *handler)
-  : q(q_), m_option(option), m_parser(parser), m_handler(handler)
-{
-    // So that single-threaded apps don't have to call initialize() for now.
-    initStatics();
-
-    // Configuration, more or less
-#ifdef PROEVALUATOR_CUMULATIVE
-    m_cumulative = true;
-#endif
-
-    // Evaluator state
-    m_skipLevel = 0;
-    m_loopLevel = 0;
-    m_listCount = 0;
-    m_valuemapStack.push(QHash<ProString, ProStringList>());
-}
-
-ProFileEvaluator::Private::~Private()
-{
-}
-
-//////// Evaluator tools /////////
-
-uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr)
-{
-    uint len = *tokPtr++;
-    len |= (uint)*tokPtr++ << 16;
-    return len;
-}
-
-ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr)
-{
-    uint len = *tokPtr++;
-    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, NoHash);
-    ret.setSource(m_current.pro);
-    tokPtr += len;
-    return ret;
-}
-
-ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr)
-{
-    uint hash = getBlockLen(tokPtr);
-    uint len = *tokPtr++;
-    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
-    tokPtr += len;
-    return ret;
-}
-
-void ProFileEvaluator::Private::skipStr(const ushort *&tokPtr)
-{
-    uint len = *tokPtr++;
-    tokPtr += len;
-}
-
-void ProFileEvaluator::Private::skipHashStr(const ushort *&tokPtr)
-{
-    tokPtr += 2;
-    uint len = *tokPtr++;
-    tokPtr += len;
-}
-
-// FIXME: this should not build new strings for direct sections.
-// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
-ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals, const ProFile *source)
-{
-    QString build;
-    ProStringList ret;
-    QStack<char> quote;
-
-    const ushort SPACE = ' ';
-    const ushort LPAREN = '(';
-    const ushort RPAREN = ')';
-    const ushort SINGLEQUOTE = '\'';
-    const ushort DOUBLEQUOTE = '"';
-    const ushort BACKSLASH = '\\';
-
-    if (!source)
-        source = currentProFile();
-
-    ushort unicode;
-    const QChar *vals_data = vals.data();
-    const int vals_len = vals.length();
-    for (int x = 0, parens = 0; x < vals_len; x++) {
-        unicode = vals_data[x].unicode();
-        if (x != (int)vals_len-1 && unicode == BACKSLASH &&
-            (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
-            build += vals_data[x++]; //get that 'escape'
-        } else if (!quote.isEmpty() && unicode == quote.top()) {
-            quote.pop();
-        } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
-            quote.push(unicode);
-        } else if (unicode == RPAREN) {
-            --parens;
-        } else if (unicode == LPAREN) {
-            ++parens;
-        }
-
-        if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
-            ret << ProString(build, NoHash).setSource(source);
-            build.clear();
-        } else {
-            build += vals_data[x];
-        }
-    }
-    if (!build.isEmpty())
-        ret << ProString(build, NoHash).setSource(source);
-    return ret;
-}
-
-static void zipEmpty(ProStringList *value)
-{
-    for (int i = value->size(); --i >= 0;)
-        if (value->at(i).isEmpty())
-            value->remove(i);
-}
-
-static void insertUnique(ProStringList *varlist, const ProStringList &value)
-{
-    foreach (const ProString &str, value)
-        if (!str.isEmpty() && !varlist->contains(str))
-            varlist->append(str);
-}
-
-static void removeAll(ProStringList *varlist, const ProString &value)
-{
-    for (int i = varlist->size(); --i >= 0; )
-        if (varlist->at(i) == value)
-            varlist->remove(i);
-}
-
-static void removeEach(ProStringList *varlist, const ProStringList &value)
-{
-    foreach (const ProString &str, value)
-        if (!str.isEmpty())
-            removeAll(varlist, str);
-}
-
-static void replaceInList(ProStringList *varlist,
-        const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
-{
-    for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
-        QString val = varit->toQString(tmp);
-        QString copy = val; // Force detach and have a reference value
-        val.replace(regexp, replace);
-        if (!val.isSharedWith(copy)) {
-            if (val.isEmpty()) {
-                varit = varlist->erase(varit);
-            } else {
-                (*varit).setValue(val, NoHash);
-                ++varit;
-            }
-            if (!global)
-                break;
-        } else {
-            ++varit;
-        }
-    }
-}
-
-QString ProFileEvaluator::Private::expandEnvVars(const QString &str) const
-{
-    QString string = str;
-    int rep;
-    QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
-    while ((rep = reg_variableName.indexIn(string)) != -1)
-        string.replace(rep, reg_variableName.matchedLength(),
-                       m_option->getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3)));
-    return string;
-}
-
-// This is braindead, but we want qmake compat
-QString ProFileEvaluator::Private::fixPathToLocalOS(const QString &str) const
-{
-    QString string = expandEnvVars(str);
-
-    if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
-        string[0] = string[0].toLower();
-
-#if defined(Q_OS_WIN32)
-    string.replace(QLatin1Char('/'), QLatin1Char('\\'));
-#else
-    string.replace(QLatin1Char('\\'), QLatin1Char('/'));
-#endif
-    return string;
-}
-
-static bool isTrue(const ProString &_str, QString &tmp)
-{
-    const QString &str = _str.toQString(tmp);
-    return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
-}
-
-//////// Evaluator /////////
-
-static ALWAYS_INLINE void addStr(
-        const ProString &str, ProStringList *ret, bool &pending, bool joined)
-{
-    if (joined) {
-        ret->last().append(str, &pending);
-    } else {
-        if (!pending) {
-            pending = true;
-            *ret << str;
-        } else {
-            ret->last().append(str);
-        }
-    }
-}
-
-static ALWAYS_INLINE void addStrList(
-        const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
-{
-    if (!list.isEmpty()) {
-        if (joined) {
-            ret->last().append(list, &pending, !(tok & TokQuoted));
-        } else {
-            if (tok & TokQuoted) {
-                if (!pending) {
-                    pending = true;
-                    *ret << ProString();
-                }
-                ret->last().append(list);
-            } else {
-                if (!pending) {
-                    // Another qmake bizzarity: if nothing is pending and the
-                    // first element is empty, it will be eaten
-                    if (!list.at(0).isEmpty()) {
-                        // The common case
-                        pending = true;
-                        *ret += list;
-                        return;
-                    }
-                } else {
-                    ret->last().append(list.at(0));
-                }
-                // This is somewhat slow, but a corner case
-                for (int j = 1; j < list.size(); ++j) {
-                    pending = true;
-                    *ret << list.at(j);
-                }
-            }
-        }
-    }
-}
-
-void ProFileEvaluator::Private::evaluateExpression(
-        const ushort *&tokPtr, ProStringList *ret, bool joined)
-{
-    if (joined)
-        *ret << ProString();
-    bool pending = false;
-    forever {
-        ushort tok = *tokPtr++;
-        if (tok & TokNewStr)
-            pending = false;
-        ushort maskedTok = tok & TokMask;
-        switch (maskedTok) {
-        case TokLine:
-            m_current.line = *tokPtr++;
-            break;
-        case TokLiteral:
-            addStr(getStr(tokPtr), ret, pending, joined);
-            break;
-        case TokHashLiteral:
-            addStr(getHashStr(tokPtr), ret, pending, joined);
-            break;
-        case TokVariable:
-            addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined);
-            break;
-        case TokProperty:
-            addStr(ProString(propertyValue(
-                      getStr(tokPtr).toQString(m_tmp1), true), NoHash).setSource(currentProFile()),
-                   ret, pending, joined);
-            break;
-        case TokEnvVar:
-            addStrList(split_value_list(m_option->getEnv(getStr(tokPtr).toQString(m_tmp1))),
-                       tok, ret, pending, joined);
-            break;
-        case TokFuncName: {
-            ProString func = getHashStr(tokPtr);
-            addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
-            break; }
-        default:
-            tokPtr--;
-            return;
-        }
-    }
-}
-
-void ProFileEvaluator::Private::skipExpression(const ushort *&pTokPtr)
-{
-    const ushort *tokPtr = pTokPtr;
-    forever {
-        ushort tok = *tokPtr++;
-        switch (tok) {
-        case TokLine:
-            m_current.line = *tokPtr++;
-            break;
-        case TokValueTerminator:
-        case TokFuncTerminator:
-            pTokPtr = tokPtr;
-            return;
-        case TokArgSeparator:
-            break;
-        default:
-            switch (tok & TokMask) {
-            case TokLiteral:
-            case TokProperty:
-            case TokEnvVar:
-                skipStr(tokPtr);
-                break;
-            case TokHashLiteral:
-            case TokVariable:
-                skipHashStr(tokPtr);
-                break;
-            case TokFuncName:
-                skipHashStr(tokPtr);
-                pTokPtr = tokPtr;
-                skipExpression(pTokPtr);
-                tokPtr = pTokPtr;
-                break;
-            default:
-                Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
-                break;
-            }
-        }
-    }
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
-        ProFile *pro, const ushort *tokPtr)
-{
-    m_current.pro = pro;
-    m_current.line = 0;
-    return visitProBlock(tokPtr);
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
-        const ushort *tokPtr)
-{
-    ProStringList curr;
-    bool okey = true, or_op = false, invert = false;
-    uint blockLen;
-    VisitReturn ret = ReturnTrue;
-    while (ushort tok = *tokPtr++) {
-        switch (tok) {
-        case TokLine:
-            m_current.line = *tokPtr++;
-            continue;
-        case TokAssign:
-        case TokAppend:
-        case TokAppendUnique:
-        case TokRemove:
-        case TokReplace:
-            visitProVariable(tok, curr, tokPtr);
-            curr.clear();
-            continue;
-        case TokBranch:
-            blockLen = getBlockLen(tokPtr);
-            if (m_cumulative) {
-                if (!okey)
-                    m_skipLevel++;
-                ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
-                tokPtr += blockLen;
-                blockLen = getBlockLen(tokPtr);
-                if (!okey)
-                    m_skipLevel--;
-                else
-                    m_skipLevel++;
-                if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
-                    ret = visitProBlock(tokPtr);
-                if (okey)
-                    m_skipLevel--;
-            } else {
-                if (okey)
-                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
-                tokPtr += blockLen;
-                blockLen = getBlockLen(tokPtr);
-                if (!okey)
-                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
-            }
-            tokPtr += blockLen;
-            okey = true, or_op = false; // force next evaluation
-            break;
-        case TokForLoop:
-            if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
-                skipHashStr(tokPtr);
-                uint exprLen = getBlockLen(tokPtr);
-                tokPtr += exprLen;
-                blockLen = getBlockLen(tokPtr);
-                ret = visitProBlock(tokPtr);
-            } else if (okey != or_op) {
-                const ProString &variable = getHashStr(tokPtr);
-                uint exprLen = getBlockLen(tokPtr);
-                const ushort *exprPtr = tokPtr;
-                tokPtr += exprLen;
-                blockLen = getBlockLen(tokPtr);
-                ret = visitProLoop(variable, exprPtr, tokPtr);
-            } else {
-                skipHashStr(tokPtr);
-                uint exprLen = getBlockLen(tokPtr);
-                tokPtr += exprLen;
-                blockLen = getBlockLen(tokPtr);
-                ret = ReturnTrue;
-            }
-            tokPtr += blockLen;
-            okey = true, or_op = false; // force next evaluation
-            break;
-        case TokTestDef:
-        case TokReplaceDef:
-            if (m_cumulative || okey != or_op) {
-                const ProString &name = getHashStr(tokPtr);
-                blockLen = getBlockLen(tokPtr);
-                visitProFunctionDef(tok, name, tokPtr);
-            } else {
-                skipHashStr(tokPtr);
-                blockLen = getBlockLen(tokPtr);
-            }
-            tokPtr += blockLen;
-            okey = true, or_op = false; // force next evaluation
-            continue;
-        case TokNot:
-            invert ^= true;
-            continue;
-        case TokAnd:
-            or_op = false;
-            continue;
-        case TokOr:
-            or_op = true;
-            continue;
-        case TokCondition:
-            if (!m_skipLevel && okey != or_op) {
-                if (curr.size() != 1) {
-                    if (!m_cumulative || !curr.isEmpty())
-                        evalError(fL1S("Conditional must expand to exactly one word."));
-                    okey = false;
-                } else {
-                    okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert;
-                }
-            }
-            or_op = !okey; // tentatively force next evaluation
-            invert = false;
-            curr.clear();
-            continue;
-        case TokTestCall:
-            if (!m_skipLevel && okey != or_op) {
-                if (curr.size() != 1) {
-                    if (!m_cumulative || !curr.isEmpty())
-                        evalError(fL1S("Test name must expand to exactly one word."));
-                    skipExpression(tokPtr);
-                    okey = false;
-                } else {
-                    ret = evaluateConditionalFunction(curr.at(0), tokPtr);
-                    switch (ret) {
-                    case ReturnTrue: okey = true; break;
-                    case ReturnFalse: okey = false; break;
-                    default: return ret;
-                    }
-                    okey ^= invert;
-                }
-            } else if (m_cumulative) {
-                m_skipLevel++;
-                if (curr.size() != 1)
-                    skipExpression(tokPtr);
-                else
-                    evaluateConditionalFunction(curr.at(0), tokPtr);
-                m_skipLevel--;
-            } else {
-                skipExpression(tokPtr);
-            }
-            or_op = !okey; // tentatively force next evaluation
-            invert = false;
-            curr.clear();
-            continue;
-        default: {
-                const ushort *oTokPtr = --tokPtr;
-                evaluateExpression(tokPtr, &curr, false);
-                if (tokPtr != oTokPtr)
-                    continue;
-            }
-            Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
-        }
-        if (ret != ReturnTrue && ret != ReturnFalse)
-            break;
-    }
-    return ret;
-}
-
-
-void ProFileEvaluator::Private::visitProFunctionDef(
-        ushort tok, const ProString &name, const ushort *tokPtr)
-{
-    QHash<ProString, FunctionDef> *hash =
-            (tok == TokTestDef
-             ? &m_functionDefs.testFunctions
-             : &m_functionDefs.replaceFunctions);
-    hash->insert(name, FunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(
-        const ProString &_variable, const ushort *exprPtr, const ushort *tokPtr)
-{
-    VisitReturn ret = ReturnTrue;
-    bool infinite = false;
-    int index = 0;
-    ProString variable;
-    ProStringList oldVarVal;
-    ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
-    if (_variable.isEmpty()) {
-        if (it_list != statics.strever) {
-            evalError(fL1S("Invalid loop expression."));
-            return ReturnFalse;
-        }
-        it_list = ProString(statics.strforever);
-    } else {
-        variable = map(_variable);
-        oldVarVal = valuesDirect(variable);
-    }
-    ProStringList list = valuesDirect(it_list);
-    if (list.isEmpty()) {
-        if (it_list == statics.strforever) {
-            infinite = true;
-        } else {
-            const QString &itl = it_list.toQString(m_tmp1);
-            int dotdot = itl.indexOf(statics.strDotDot);
-            if (dotdot != -1) {
-                bool ok;
-                int start = itl.left(dotdot).toInt(&ok);
-                if (ok) {
-                    int end = itl.mid(dotdot+2).toInt(&ok);
-                    if (ok) {
-                        if (start < end) {
-                            for (int i = start; i <= end; i++)
-                                list << ProString(QString::number(i), NoHash);
-                        } else {
-                            for (int i = start; i >= end; i--)
-                                list << ProString(QString::number(i), NoHash);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    m_loopLevel++;
-    forever {
-        if (infinite) {
-            if (!variable.isEmpty())
-                m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++), NoHash));
-            if (index > 1000) {
-                evalError(fL1S("ran into infinite loop (> 1000 iterations)."));
-                break;
-            }
-        } else {
-            ProString val;
-            do {
-                if (index >= list.count())
-                    goto do_break;
-                val = list.at(index++);
-            } while (val.isEmpty()); // stupid, but qmake is like that
-            m_valuemapStack.top()[variable] = ProStringList(val);
-        }
-
-        ret = visitProBlock(tokPtr);
-        switch (ret) {
-        case ReturnTrue:
-        case ReturnFalse:
-            break;
-        case ReturnNext:
-            ret = ReturnTrue;
-            break;
-        case ReturnBreak:
-            ret = ReturnTrue;
-            goto do_break;
-        default:
-            goto do_break;
-        }
-    }
-  do_break:
-    m_loopLevel--;
-
-    if (!variable.isEmpty())
-        m_valuemapStack.top()[variable] = oldVarVal;
-    return ret;
-}
-
-void ProFileEvaluator::Private::visitProVariable(
-        ushort tok, const ProStringList &curr, const ushort *&tokPtr)
-{
-    int sizeHint = *tokPtr++;
-
-    if (curr.size() != 1) {
-        skipExpression(tokPtr);
-        if (!m_cumulative || !curr.isEmpty())
-            evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
-        return;
-    }
-    const ProString &varName = map(curr.first());
-
-    if (tok == TokReplace) {      // ~=
-        // DEFINES ~= s/a/b/?[gqi]
-
-        const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
-        const QString &val = varVal.at(0).toQString(m_tmp1);
-        if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
-            evalError(fL1S("the ~= operator can handle only the s/// function."));
-            return;
-        }
-        QChar sep = val.at(1);
-        QStringList func = val.split(sep);
-        if (func.count() < 3 || func.count() > 4) {
-            evalError(fL1S("the s/// function expects 3 or 4 arguments."));
-            return;
-        }
-
-        bool global = false, quote = false, case_sense = false;
-        if (func.count() == 4) {
-            global = func[3].indexOf(QLatin1Char('g')) != -1;
-            case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
-            quote = func[3].indexOf(QLatin1Char('q')) != -1;
-        }
-        QString pattern = func[1];
-        QString replace = func[2];
-        if (quote)
-            pattern = QRegExp::escape(pattern);
-
-        QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
-
-        if (!m_skipLevel || m_cumulative) {
-            // We could make a union of modified and unmodified values,
-            // but this will break just as much as it fixes, so leave it as is.
-            replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
-        }
-    } else {
-        ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
-        switch (tok) {
-        default: // whatever - cannot happen
-        case TokAssign:          // =
-            if (!m_cumulative) {
-                if (!m_skipLevel) {
-                    zipEmpty(&varVal);
-                    m_valuemapStack.top()[varName] = varVal;
-                }
-            } else {
-                zipEmpty(&varVal);
-                if (!varVal.isEmpty()) {
-                    // We are greedy for values. But avoid exponential growth.
-                    ProStringList &v = valuesRef(varName);
-                    if (v.isEmpty()) {
-                        v = varVal;
-                    } else {
-                        ProStringList old = v;
-                        v = varVal;
-                        QSet<ProString> has;
-                        has.reserve(v.size());
-                        foreach (const ProString &s, v)
-                            has.insert(s);
-                        v.reserve(v.size() + old.size());
-                        foreach (const ProString &s, old)
-                            if (!has.contains(s))
-                                v << s;
-                    }
-                }
-            }
-            break;
-        case TokAppendUnique:    // *=
-            if (!m_skipLevel || m_cumulative)
-                insertUnique(&valuesRef(varName), varVal);
-            break;
-        case TokAppend:          // +=
-            if (!m_skipLevel || m_cumulative) {
-                zipEmpty(&varVal);
-                valuesRef(varName) += varVal;
-            }
-            break;
-        case TokRemove:       // -=
-            if (!m_cumulative) {
-                if (!m_skipLevel)
-                    removeEach(&valuesRef(varName), varVal);
-            } else {
-                // We are stingy with our values, too.
-            }
-            break;
-        }
-    }
-}
-
-void ProFileEvaluator::Private::visitCmdLine(const QString &cmds)
-{
-    if (!cmds.isEmpty()) {
-        if (ProFile *pro = m_parser->parsedProBlock(fL1S("(command line)"), cmds)) {
-            m_locationStack.push(m_current);
-            visitProBlock(pro, pro->tokPtr());
-            m_current = m_locationStack.pop();
-            pro->deref();
-        }
-    }
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(
-        ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
-        ProFileEvaluator::LoadFlags flags)
-{
-    if (!m_cumulative && !pro->isOk())
-        return ReturnFalse;
-
-    m_handler->aboutToEval(currentProFile(), pro, type);
-    m_profileStack.push(pro);
-    if (flags & LoadPreFiles) {
-#ifdef PROEVALUATOR_THREAD_SAFE
-        {
-            QMutexLocker locker(&m_option->mutex);
-            if (m_option->base_inProgress) {
-                QThreadPool::globalInstance()->releaseThread();
-                m_option->cond.wait(&m_option->mutex);
-                QThreadPool::globalInstance()->reserveThread();
-            } else
-#endif
-            if (m_option->base_valuemap.isEmpty()) {
-#ifdef PROEVALUATOR_THREAD_SAFE
-                m_option->base_inProgress = true;
-                locker.unlock();
-#endif
-
-#ifdef PROEVALUATOR_CUMULATIVE
-                bool cumulative = m_cumulative;
-                m_cumulative = false;
-#endif
-
-                // ### init QMAKE_QMAKE, QMAKE_SH
-                // ### init QMAKE_EXT_{C,H,CPP,OBJ}
-                // ### init TEMPLATE_PREFIX
-
-                QString qmake_cache = m_option->cachefile;
-                if (qmake_cache.isEmpty() && !m_outputDir.isEmpty())  { //find it as it has not been specified
-                    QDir dir(m_outputDir);
-                    forever {
-                        qmake_cache = dir.path() + QLatin1String("/.qmake.cache");
-                        if (IoUtils::exists(qmake_cache))
-                            break;
-                        if (!dir.cdUp() || dir.isRoot()) {
-                            qmake_cache.clear();
-                            break;
-                        }
-                    }
-                }
-                if (!qmake_cache.isEmpty()) {
-                    qmake_cache = resolvePath(qmake_cache);
-                    QHash<ProString, ProStringList> cache_valuemap;
-                    if (evaluateFileInto(qmake_cache, ProFileEvaluatorHandler::EvalConfigFile,
-                                         &cache_valuemap, 0, EvalProOnly)) {
-                        if (m_option->qmakespec.isEmpty()) {
-                            const ProStringList &vals = cache_valuemap.value(ProString("QMAKESPEC"));
-                            if (!vals.isEmpty())
-                                m_option->qmakespec = vals.first().toQString();
-                        }
-                    } else {
-                        qmake_cache.clear();
-                    }
-                }
-                m_option->cachefile = qmake_cache;
-
-                QStringList mkspec_roots = qmakeMkspecPaths();
-
-                QString qmakespec = expandEnvVars(m_option->qmakespec);
-                if (qmakespec.isEmpty()) {
-                    foreach (const QString &root, mkspec_roots) {
-                        QString mkspec = root + QLatin1String("/default");
-                        if (IoUtils::fileType(mkspec) == IoUtils::FileIsDir) {
-                            qmakespec = mkspec;
-                            break;
-                        }
-                    }
-                    if (qmakespec.isEmpty()) {
-                        m_handler->configError(fL1S("Could not find qmake configuration directory"));
-                        // Unlike in qmake, not finding the spec is not critical ...
-                    }
-                }
-
-                if (IoUtils::isRelativePath(qmakespec)) {
-                    if (IoUtils::exists(currentDirectory() + QLatin1Char('/') + qmakespec
-                                        + QLatin1String("/qmake.conf"))) {
-                        qmakespec = currentDirectory() + QLatin1Char('/') + qmakespec;
-                    } else if (!m_outputDir.isEmpty()
-                               && IoUtils::exists(m_outputDir + QLatin1Char('/') + qmakespec
-                                                  + QLatin1String("/qmake.conf"))) {
-                        qmakespec = m_outputDir + QLatin1Char('/') + qmakespec;
-                    } else {
-                        foreach (const QString &root, mkspec_roots) {
-                            QString mkspec = root + QLatin1Char('/') + qmakespec;
-                            if (IoUtils::exists(mkspec)) {
-                                qmakespec = mkspec;
-                                goto cool;
-                            }
-                        }
-                        m_handler->configError(fL1S("Could not find qmake configuration file"));
-                        // Unlike in qmake, a missing config is not critical ...
-                        qmakespec.clear();
-                      cool: ;
-                    }
-                }
-
-                if (!qmakespec.isEmpty()) {
-                    m_option->qmakespec = QDir::cleanPath(qmakespec);
-
-                    QString spec = m_option->qmakespec + QLatin1String("/qmake.conf");
-                    if (!evaluateFileDirect(spec, ProFileEvaluatorHandler::EvalConfigFile,
-                                            ProFileEvaluator::LoadProOnly)) {
-                        m_handler->configError(
-                                fL1S("Could not read qmake configuration file %1").arg(spec));
-                    } else if (!m_option->cachefile.isEmpty()) {
-                        evaluateFileDirect(m_option->cachefile,
-                                           ProFileEvaluatorHandler::EvalConfigFile,
-                                           ProFileEvaluator::LoadProOnly);
-                    }
-                    m_option->qmakespec_name = IoUtils::fileName(m_option->qmakespec).toString();
-                    if (m_option->qmakespec_name == QLatin1String("default")) {
-#ifdef Q_OS_UNIX
-                        char buffer[1024];
-                        int l = ::readlink(m_option->qmakespec.toLocal8Bit().constData(), buffer, 1024);
-                        if (l != -1)
-                            m_option->qmakespec_name =
-                                    IoUtils::fileName(QString::fromLocal8Bit(buffer, l)).toString();
-#else
-                        // We can't resolve symlinks as they do on Unix, so configure.exe puts
-                        // the source of the qmake.conf at the end of the default/qmake.conf in
-                        // the QMAKESPEC_ORG variable.
-                        const ProStringList &spec_org =
-                                m_option->base_valuemap.value(ProString("QMAKESPEC_ORIGINAL"));
-                        if (!spec_org.isEmpty())
-                            m_option->qmakespec_name =
-                                    IoUtils::fileName(spec_org.first().toQString()).toString();
-#endif
-                    }
-                }
-
-                evaluateFeatureFile(QLatin1String("default_pre.prf"));
-
-                m_option->base_valuemap = m_valuemapStack.top();
-                m_option->base_functions = m_functionDefs;
-
-#ifdef PROEVALUATOR_CUMULATIVE
-                m_cumulative = cumulative;
-#endif
-
-#ifdef PROEVALUATOR_THREAD_SAFE
-                locker.relock();
-                m_option->base_inProgress = false;
-                m_option->cond.wakeAll();
-#endif
-                goto fresh;
-            }
-#ifdef PROEVALUATOR_THREAD_SAFE
-        }
-#endif
-
-            m_valuemapStack.top() = m_option->base_valuemap;
-            m_functionDefs = m_option->base_functions;
-
-          fresh:
-            ProStringList &tgt = m_valuemapStack.top()[ProString("TARGET")];
-            if (tgt.isEmpty())
-                tgt.append(ProString(QFileInfo(pro->fileName()).baseName(), NoHash));
-
-        visitCmdLine(m_option->precmds);
-    }
-
-    visitProBlock(pro, pro->tokPtr());
-
-    if (flags & LoadPostFiles) {
-        visitCmdLine(m_option->postcmds);
-
-            evaluateFeatureFile(QLatin1String("default_post.prf"));
-
-            QSet<QString> processed;
-            forever {
-                bool finished = true;
-                ProStringList configs = valuesDirect(statics.strCONFIG);
-                for (int i = configs.size() - 1; i >= 0; --i) {
-                    QString config = configs.at(i).toQString(m_tmp1).toLower();
-                    if (!processed.contains(config)) {
-                        config.detach();
-                        processed.insert(config);
-                        if (evaluateFeatureFile(config)) {
-                            finished = false;
-                            break;
-                        }
-                    }
-                }
-                if (finished)
-                    break;
-            }
-    }
-    m_profileStack.pop();
-    m_handler->doneWithEval(currentProFile());
-
-    return ReturnTrue;
-}
-
-
-QStringList ProFileEvaluator::Private::qmakeMkspecPaths() const
-{
-    QStringList ret;
-    const QString concat = QLatin1String("/mkspecs");
-
-    QString qmakepath = m_option->getEnv(QLatin1String("QMAKEPATH"));
-    if (!qmakepath.isEmpty())
-        foreach (const QString &it, qmakepath.split(m_option->dirlist_sep))
-            ret << QDir::cleanPath(it) + concat;
-
-    QString builtIn = propertyValue(QLatin1String("QT_INSTALL_DATA"), false) + concat;
-    if (!ret.contains(builtIn))
-        ret << builtIn;
-
-    return ret;
-}
-
-QStringList ProFileEvaluator::Private::qmakeFeaturePaths() const
-{
-    QString mkspecs_concat = QLatin1String("/mkspecs");
-    QString features_concat = QLatin1String("/features");
-    QStringList concat;
-
-    validateModes();
-    switch (m_option->target_mode) {
-    case ProFileOption::TARG_MACX_MODE:
-        concat << QLatin1String("/features/mac");
-        concat << QLatin1String("/features/macx");
-        concat << QLatin1String("/features/unix");
-        break;
-    default: // Can't happen, just make the compiler shut up
-    case ProFileOption::TARG_UNIX_MODE:
-        concat << QLatin1String("/features/unix");
-        break;
-    case ProFileOption::TARG_WIN_MODE:
-        concat << QLatin1String("/features/win32");
-        break;
-    case ProFileOption::TARG_SYMBIAN_MODE:
-        concat << QLatin1String("/features/symbian");
-        break;
-    }
-    concat << features_concat;
-
-    QStringList feature_roots;
-
-    QString mkspec_path = m_option->getEnv(QLatin1String("QMAKEFEATURES"));
-    if (!mkspec_path.isEmpty())
-        foreach (const QString &f, mkspec_path.split(m_option->dirlist_sep))
-            feature_roots += resolvePath(f);
-
-    feature_roots += propertyValue(QLatin1String("QMAKEFEATURES"), false).split(
-            m_option->dirlist_sep, QString::SkipEmptyParts);
-
-    if (!m_option->cachefile.isEmpty()) {
-        QString path = m_option->cachefile.left(m_option->cachefile.lastIndexOf((ushort)'/'));
-        foreach (const QString &concat_it, concat)
-            feature_roots << (path + concat_it);
-    }
-
-    QString qmakepath = m_option->getEnv(QLatin1String("QMAKEPATH"));
-    if (!qmakepath.isNull()) {
-        const QStringList lst = qmakepath.split(m_option->dirlist_sep);
-        foreach (const QString &item, lst) {
-            QString citem = resolvePath(item);
-            foreach (const QString &concat_it, concat)
-                feature_roots << (citem + mkspecs_concat + concat_it);
-        }
-    }
-
-    if (!m_option->qmakespec.isEmpty()) {
-        QString qmakespec = resolvePath(m_option->qmakespec);
-        feature_roots << (qmakespec + features_concat);
-
-        QDir specdir(qmakespec);
-        while (!specdir.isRoot()) {
-            if (!specdir.cdUp() || specdir.isRoot())
-                break;
-            if (IoUtils::exists(specdir.path() + features_concat)) {
-                foreach (const QString &concat_it, concat)
-                    feature_roots << (specdir.path() + concat_it);
-                break;
-            }
-        }
-    }
-
-    foreach (const QString &concat_it, concat)
-        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX"), false) +
-                          mkspecs_concat + concat_it);
-    foreach (const QString &concat_it, concat)
-        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA"), false) +
-                          mkspecs_concat + concat_it);
-
-    for (int i = 0; i < feature_roots.count(); ++i)
-        if (!feature_roots.at(i).endsWith((ushort)'/'))
-            feature_roots[i].append((ushort)'/');
-
-    feature_roots.removeDuplicates();
-
-    return feature_roots;
-}
-
-QString ProFileEvaluator::Private::propertyValue(const QString &name, bool complain) const
-{
-    if (m_option->properties.contains(name))
-        return m_option->properties.value(name);
-    if (name == QLatin1String("QMAKE_MKSPECS"))
-        return qmakeMkspecPaths().join(m_option->dirlist_sep);
-    if (name == QLatin1String("QMAKE_VERSION"))
-        return QLatin1String("1.0");        //### FIXME
-    if (complain)
-        evalError(fL1S("Querying unknown property %1").arg(name));
-    return QString();
-}
-
-ProFile *ProFileEvaluator::Private::currentProFile() const
-{
-    if (m_profileStack.count() > 0)
-        return m_profileStack.top();
-    return 0;
-}
-
-QString ProFileEvaluator::Private::currentFileName() const
-{
-    ProFile *pro = currentProFile();
-    if (pro)
-        return pro->fileName();
-    return QString();
-}
-
-QString ProFileEvaluator::Private::currentDirectory() const
-{
-    ProFile *cur = m_profileStack.top();
-    return cur->directoryName();
-}
-
-QString ProFileEvaluator::Private::sysrootify(const QString &path, const QString &baseDir) const
-{
-#ifdef Q_OS_WIN
-    Qt::CaseSensitivity cs = Qt::CaseInsensitive;
-#else
-    Qt::CaseSensitivity cs = Qt::CaseSensitive;
-#endif
-    const bool isHostSystemPath = m_option->sysroot.isEmpty() || path.startsWith(m_option->sysroot, cs)
-        || path.startsWith(baseDir, cs) || path.startsWith(m_outputDir, cs);
-
-    return isHostSystemPath ? path : m_option->sysroot + path;
-}
-
-#ifndef QT_BOOTSTRAPPED
-void ProFileEvaluator::Private::runProcess(QProcess *proc, const QString &command,
-                                           QProcess::ProcessChannel chan) const
-{
-    proc->setWorkingDirectory(currentDirectory());
-    if (!m_option->environment.isEmpty())
-        proc->setProcessEnvironment(m_option->environment);
-# ifdef Q_OS_WIN
-    proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
-    proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
-# else
-    proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
-# endif
-    proc->waitForFinished(-1);
-    proc->setReadChannel(chan);
-    QByteArray errout = proc->readAll();
-    if (errout.endsWith('\n'))
-        errout.chop(1);
-    m_handler->evalError(QString(), 0, QString::fromLocal8Bit(errout));
-}
-#endif
-
-// The (QChar*)current->constData() constructs below avoid pointless detach() calls
-// FIXME: This is inefficient. Should not make new string if it is a straight subsegment
-static ALWAYS_INLINE void appendChar(ushort unicode,
-    QString *current, QChar **ptr, ProString *pending)
-{
-    if (!pending->isEmpty()) {
-        int len = pending->size();
-        current->resize(current->size() + len);
-        ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
-        pending->clear();
-        *ptr = (QChar*)current->constData() + len;
-    }
-    *(*ptr)++ = QChar(unicode);
-}
-
-static void appendString(const ProString &string,
-    QString *current, QChar **ptr, ProString *pending)
-{
-    if (string.isEmpty())
-        return;
-    QChar *uc = (QChar*)current->constData();
-    int len;
-    if (*ptr != uc) {
-        len = *ptr - uc;
-        current->resize(current->size() + string.size());
-    } else if (!pending->isEmpty()) {
-        len = pending->size();
-        current->resize(current->size() + len + string.size());
-        ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
-        pending->clear();
-    } else {
-        *pending = string;
-        return;
-    }
-    *ptr = (QChar*)current->constData() + len;
-    ::memcpy(*ptr, string.constData(), string.size() * 2);
-    *ptr += string.size();
-}
-
-static void flushCurrent(ProStringList *ret,
-    QString *current, QChar **ptr, ProString *pending, bool joined)
-{
-    QChar *uc = (QChar*)current->constData();
-    int len = *ptr - uc;
-    if (len) {
-        ret->append(ProString(QString(uc, len), NoHash));
-        *ptr = uc;
-    } else if (!pending->isEmpty()) {
-        ret->append(*pending);
-        pending->clear();
-    } else if (joined) {
-        ret->append(ProString());
-    }
-}
-
-static inline void flushFinal(ProStringList *ret,
-    const QString &current, const QChar *ptr, const ProString &pending,
-    const ProString &str, bool replaced, bool joined)
-{
-    int len = ptr - current.data();
-    if (len) {
-        if (!replaced && len == str.size())
-            ret->append(str);
-        else
-            ret->append(ProString(QString(current.data(), len), NoHash));
-    } else if (!pending.isEmpty()) {
-        ret->append(pending);
-    } else if (joined) {
-        ret->append(ProString());
-    }
-}
-
-ProStringList ProFileEvaluator::Private::expandVariableReferences(
-    const ProString &str, int *pos, bool joined)
-{
-    ProStringList ret;
-//    if (ok)
-//        *ok = true;
-    if (str.isEmpty() && !pos)
-        return ret;
-
-    const ushort LSQUARE = '[';
-    const ushort RSQUARE = ']';
-    const ushort LCURLY = '{';
-    const ushort RCURLY = '}';
-    const ushort LPAREN = '(';
-    const ushort RPAREN = ')';
-    const ushort DOLLAR = '$';
-    const ushort BACKSLASH = '\\';
-    const ushort UNDERSCORE = '_';
-    const ushort DOT = '.';
-    const ushort SPACE = ' ';
-    const ushort TAB = '\t';
-    const ushort COMMA = ',';
-    const ushort SINGLEQUOTE = '\'';
-    const ushort DOUBLEQUOTE = '"';
-
-    ushort unicode, quote = 0, parens = 0;
-    const ushort *str_data = (const ushort *)str.constData();
-    const int str_len = str.size();
-
-    ProString var, args;
-
-    bool replaced = false;
-    bool putSpace = false;
-    QString current; // Buffer for successively assembled string segments
-    current.resize(str.size());
-    QChar *ptr = current.data();
-    ProString pending; // Buffer for string segments from variables
-    // Only one of the above buffers can be filled at a given time.
-    for (int i = pos ? *pos : 0; i < str_len; ++i) {
-        unicode = str_data[i];
-        if (unicode == DOLLAR) {
-            if (str_len > i+2 && str_data[i+1] == DOLLAR) {
-                ++i;
-                ushort term = 0;
-                enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
-                unicode = str_data[++i];
-                if (unicode == LSQUARE) {
-                    unicode = str_data[++i];
-                    term = RSQUARE;
-                    var_type = PROPERTY;
-                } else if (unicode == LCURLY) {
-                    unicode = str_data[++i];
-                    var_type = VAR;
-                    term = RCURLY;
-                } else if (unicode == LPAREN) {
-                    unicode = str_data[++i];
-                    var_type = ENVIRON;
-                    term = RPAREN;
-                }
-                int name_start = i;
-                forever {
-                    if (!(unicode & (0xFF<<8)) &&
-                       unicode != DOT && unicode != UNDERSCORE &&
-                       //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
-                       (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
-                       (unicode < '0' || unicode > '9'))
-                        break;
-                    if (++i == str_len)
-                        break;
-                    unicode = str_data[i];
-                    // at this point, i points to either the 'term' or 'next' character (which is in unicode)
-                }
-                var = str.mid(name_start, i - name_start);
-                if (var_type == VAR && unicode == LPAREN) {
-                    var_type = FUNCTION;
-                    name_start = i + 1;
-                    int depth = 0;
-                    forever {
-                        if (++i == str_len)
-                            break;
-                        unicode = str_data[i];
-                        if (unicode == LPAREN) {
-                            depth++;
-                        } else if (unicode == RPAREN) {
-                            if (!depth)
-                                break;
-                            --depth;
-                        }
-                    }
-                    args = str.mid(name_start, i - name_start);
-                    if (++i < str_len)
-                        unicode = str_data[i];
-                    else
-                        unicode = 0;
-                    // at this point i is pointing to the 'next' character (which is in unicode)
-                    // this might actually be a term character since you can do $${func()}
-                }
-                if (term) {
-                    if (unicode != term) {
-                        evalError(fL1S("Missing %1 terminator [found %2]")
-                                  .arg(QChar(term))
-                                  .arg(unicode ? QString(unicode) : fL1S("end-of-line")));
-//                        if (ok)
-//                            *ok = false;
-                        if (pos)
-                            *pos = str_len;
-                        return ProStringList();
-                    }
-                } else {
-                    // move the 'cursor' back to the last char of the thing we were looking at
-                    --i;
-                }
-
-                ProStringList replacement;
-                if (var_type == ENVIRON) {
-                    replacement = split_value_list(m_option->getEnv(var.toQString(m_tmp1)));
-                } else if (var_type == PROPERTY) {
-                    replacement << ProString(propertyValue(var.toQString(m_tmp1), true), NoHash);
-                } else if (var_type == FUNCTION) {
-                    replacement += evaluateExpandFunction(var, args);
-                } else if (var_type == VAR) {
-                    replacement = values(map(var));
-                }
-                if (!replacement.isEmpty()) {
-                    if (quote || joined) {
-                        if (putSpace) {
-                            putSpace = false;
-                            if (!replacement.at(0).isEmpty()) // Bizarre, indeed
-                                appendChar(' ', &current, &ptr, &pending);
-                        }
-                        appendString(ProString(replacement.join(statics.field_sep), NoHash),
-                                     &current, &ptr, &pending);
-                    } else {
-                        appendString(replacement.at(0), &current, &ptr, &pending);
-                        if (replacement.size() > 1) {
-                            flushCurrent(&ret, &current, &ptr, &pending, false);
-                            int j = 1;
-                            if (replacement.size() > 2) {
-                                // FIXME: ret.reserve(ret.size() + replacement.size() - 2);
-                                for (; j < replacement.size() - 1; ++j)
-                                    ret << replacement.at(j);
-                            }
-                            pending = replacement.at(j);
-                        }
-                    }
-                    replaced = true;
-                }
-                continue;
-            }
-        } else if (unicode == BACKSLASH) {
-            static const char symbols[] = "[]{}()$\\'\"";
-            ushort unicode2 = str_data[i+1];
-            if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
-                unicode = unicode2;
-                ++i;
-            }
-        } else if (quote) {
-            if (unicode == quote) {
-                quote = 0;
-                continue;
-            }
-        } else {
-            if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
-                quote = unicode;
-                continue;
-            } else if (unicode == SPACE || unicode == TAB) {
-                if (!joined)
-                    flushCurrent(&ret, &current, &ptr, &pending, false);
-                else if ((ptr - (QChar*)current.constData()) || !pending.isEmpty())
-                    putSpace = true;
-                continue;
-            } else if (pos) {
-                if (unicode == LPAREN) {
-                    ++parens;
-                } else if (unicode == RPAREN) {
-                    --parens;
-                } else if (!parens && unicode == COMMA) {
-                    if (!joined) {
-                        *pos = i + 1;
-                        flushFinal(&ret, current, ptr, pending, str, replaced, false);
-                        return ret;
-                    }
-                    flushCurrent(&ret, &current, &ptr, &pending, true);
-                    putSpace = false;
-                    continue;
-                }
-            }
-        }
-        if (putSpace) {
-            putSpace = false;
-            appendChar(' ', &current, &ptr, &pending);
-        }
-        appendChar(unicode, &current, &ptr, &pending);
-    }
-    if (pos)
-        *pos = str_len;
-    flushFinal(&ret, current, ptr, pending, str, replaced, joined);
-    return ret;
-}
-
-bool ProFileEvaluator::Private::modesForGenerator(const QString &gen,
-        ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const
-{
-    if (gen == fL1S("UNIX")) {
-#ifdef Q_OS_MAC
-        *host_mode = ProFileOption::HOST_MACX_MODE;
-        *target_mode = ProFileOption::TARG_MACX_MODE;
-#else
-        *host_mode = ProFileOption::HOST_UNIX_MODE;
-        *target_mode = ProFileOption::TARG_UNIX_MODE;
-#endif
-    } else if (gen == fL1S("MSVC.NET") || gen == fL1S("BMAKE") || gen == fL1S("MSBUILD")) {
-        *host_mode = ProFileOption::HOST_WIN_MODE;
-        *target_mode = ProFileOption::TARG_WIN_MODE;
-    } else if (gen == fL1S("MINGW")) {
-#if defined(Q_OS_MAC)
-        *host_mode = ProFileOption::HOST_MACX_MODE;
-#elif defined(Q_OS_UNIX)
-        *host_mode = ProFileOption::HOST_UNIX_MODE;
-#else
-        *host_mode = ProFileOption::HOST_WIN_MODE;
-#endif
-        *target_mode = ProFileOption::TARG_WIN_MODE;
-    } else if (gen == fL1S("PROJECTBUILDER") || gen == fL1S("XCODE")) {
-        *host_mode = ProFileOption::HOST_MACX_MODE;
-        *target_mode = ProFileOption::TARG_MACX_MODE;
-    } else if (gen == fL1S("SYMBIAN_ABLD") || gen == fL1S("SYMBIAN_SBSV2")
-               || gen == fL1S("SYMBIAN_UNIX") || gen == fL1S("SYMBIAN_MINGW")) {
-#if defined(Q_OS_MAC)
-        *host_mode = ProFileOption::HOST_MACX_MODE;
-#elif defined(Q_OS_UNIX)
-        *host_mode = ProFileOption::HOST_UNIX_MODE;
-#else
-        *host_mode = ProFileOption::HOST_WIN_MODE;
-#endif
-        *target_mode = ProFileOption::TARG_SYMBIAN_MODE;
-    } else {
-        evalError(fL1S("Unknown generator specified: %1").arg(gen));
-        return false;
-    }
-    return true;
-}
-
-void ProFileEvaluator::Private::validateModes() const
-{
-    if (m_option->host_mode == ProFileOption::HOST_UNKNOWN_MODE
-        || m_option->target_mode == ProFileOption::TARG_UNKNOWN_MODE) {
-        const QHash<ProString, ProStringList> &vals =
-                m_option->base_valuemap.isEmpty() ? m_valuemapStack[0] : m_option->base_valuemap;
-        ProFileOption::HOST_MODE host_mode;
-        ProFileOption::TARG_MODE target_mode;
-        const ProStringList &gen = vals.value(ProString("MAKEFILE_GENERATOR"));
-        if (gen.isEmpty()) {
-            evalError(fL1S("Using OS scope before setting MAKEFILE_GENERATOR"));
-        } else if (modesForGenerator(gen.at(0).toQString(), &host_mode, &target_mode)) {
-            if (m_option->host_mode == ProFileOption::HOST_UNKNOWN_MODE) {
-                m_option->host_mode = host_mode;
-                m_option->applyHostMode();
-            }
-
-            if (m_option->target_mode == ProFileOption::TARG_UNKNOWN_MODE) {
-                const ProStringList &tgt = vals.value(ProString("TARGET_PLATFORM"));
-                if (!tgt.isEmpty()) {
-                    const QString &os = tgt.at(0).toQString();
-                    if (os == statics.strunix)
-                        m_option->target_mode = ProFileOption::TARG_UNIX_MODE;
-                    else if (os == statics.strmacx)
-                        m_option->target_mode = ProFileOption::TARG_MACX_MODE;
-                    else if (os == statics.strsymbian)
-                        m_option->target_mode = ProFileOption::TARG_SYMBIAN_MODE;
-                    else if (os == statics.strwin32)
-                        m_option->target_mode = ProFileOption::TARG_WIN_MODE;
-                    else
-                        evalError(fL1S("Unknown target platform specified: %1").arg(os));
-                } else {
-                    m_option->target_mode = target_mode;
-                }
-            }
-        }
-    }
-}
-
-bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
-{
-    // magic types for easy flipping
-    if (config == statics.strtrue)
-        return true;
-    if (config == statics.strfalse)
-        return false;
-
-    if (config == statics.strunix) {
-        validateModes();
-        return m_option->target_mode == ProFileOption::TARG_UNIX_MODE
-               || m_option->target_mode == ProFileOption::TARG_MACX_MODE
-               || m_option->target_mode == ProFileOption::TARG_SYMBIAN_MODE;
-    } else if (config == statics.strmacx || config == statics.strmac) {
-        validateModes();
-        return m_option->target_mode == ProFileOption::TARG_MACX_MODE;
-    } else if (config == statics.strsymbian) {
-        validateModes();
-        return m_option->target_mode == ProFileOption::TARG_SYMBIAN_MODE;
-    } else if (config == statics.strwin32) {
-        validateModes();
-        return m_option->target_mode == ProFileOption::TARG_WIN_MODE;
-    }
-
-    if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
-        QString cfg = config;
-        cfg.detach(); // Keep m_tmp out of QRegExp's cache
-        QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
+#include <QDir>
 
-        // mkspecs
-        if (re.exactMatch(m_option->qmakespec_name))
-            return true;
+using namespace QMakeInternal;
 
-        // CONFIG variable
-        int t = 0;
-        foreach (const ProString &configValue, valuesDirect(statics.strCONFIG)) {
-            if (re.exactMatch(configValue.toQString(m_tmp[t])))
-                return true;
-            t ^= 1;
-        }
-    } else {
-        // mkspecs
-        if (m_option->qmakespec_name == config)
-            return true;
-
-        // CONFIG variable
-        if (valuesDirect(statics.strCONFIG).contains(ProString(config, NoHash)))
-            return true;
-    }
-
-    return false;
-}
-
-ProStringList ProFileEvaluator::Private::expandVariableReferences(
-        const ushort *&tokPtr, int sizeHint, bool joined)
-{
-    ProStringList ret;
-    ret.reserve(sizeHint);
-    forever {
-        evaluateExpression(tokPtr, &ret, joined);
-        switch (*tokPtr) {
-        case TokValueTerminator:
-        case TokFuncTerminator:
-            tokPtr++;
-            return ret;
-        case TokArgSeparator:
-            if (joined) {
-                tokPtr++;
-                continue;
-            }
-            // fallthrough
-        default:
-            Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
-            break;
-        }
-    }
-}
-
-void ProFileEvaluator::Private::populateDeps(
-        const ProStringList &deps, const ProString &prefix,
-        QHash<ProString, QSet<ProString> > &dependencies, QHash<ProString, ProStringList> &dependees,
-        ProStringList &rootSet) const
-{
-    foreach (const ProString &item, deps)
-        if (!dependencies.contains(item)) {
-            QSet<ProString> &dset = dependencies[item]; // Always create entry
-            ProStringList depends = valuesDirect(ProString(prefix + item + QString::fromLatin1(".depends")));
-            if (depends.isEmpty()) {
-                rootSet << item;
-            } else {
-                foreach (const ProString &dep, depends) {
-                    dset.insert(dep);
-                    dependees[dep] << item;
-                }
-                populateDeps(depends, prefix, dependencies, dependees, rootSet);
-            }
-        }
-}
-
-QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ushort *&tokPtr)
-{
-    QList<ProStringList> args_list;
-    if (*tokPtr != TokFuncTerminator) {
-        for (;; tokPtr++) {
-            ProStringList arg;
-            evaluateExpression(tokPtr, &arg, false);
-            args_list << arg;
-            if (*tokPtr == TokFuncTerminator)
-                break;
-            Q_ASSERT(*tokPtr == TokArgSeparator);
-        }
-    }
-    tokPtr++;
-    return args_list;
-}
-
-QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ProString &arguments)
-{
-    QList<ProStringList> args_list;
-    for (int pos = 0; pos < arguments.size(); )
-        args_list << expandVariableReferences(arguments, &pos);
-    return args_list;
-}
-
-ProStringList ProFileEvaluator::Private::evaluateFunction(
-        const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
-{
-    bool oki;
-    ProStringList ret;
-
-    if (m_valuemapStack.count() >= 100) {
-        evalError(fL1S("ran into infinite recursion (depth > 100)."));
-        oki = false;
-    } else {
-        m_valuemapStack.push(QHash<ProString, ProStringList>());
-        m_locationStack.push(m_current);
-        int loopLevel = m_loopLevel;
-        m_loopLevel = 0;
-
-        ProStringList args;
-        for (int i = 0; i < argumentsList.count(); ++i) {
-            args += argumentsList[i];
-            m_valuemapStack.top()[ProString(QString::number(i+1))] = argumentsList[i];
-        }
-        m_valuemapStack.top()[statics.strARGS] = args;
-        oki = (visitProBlock(func.pro(), func.tokPtr()) != ReturnFalse); // True || Return
-        ret = m_returnValue;
-        m_returnValue.clear();
-
-        m_loopLevel = loopLevel;
-        m_current = m_locationStack.pop();
-        m_valuemapStack.pop();
-    }
-    if (ok)
-        *ok = oki;
-    if (oki)
-        return ret;
-    return ProStringList();
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateBoolFunction(
-        const FunctionDef &func, const QList<ProStringList> &argumentsList,
-        const ProString &function)
-{
-    bool ok;
-    ProStringList ret = evaluateFunction(func, argumentsList, &ok);
-    if (ok) {
-        if (ret.isEmpty())
-            return ReturnTrue;
-        if (ret.at(0) != statics.strfalse) {
-            if (ret.at(0) == statics.strtrue)
-                return ReturnTrue;
-            int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
-            if (ok) {
-                if (val)
-                    return ReturnTrue;
-            } else {
-                evalError(fL1S("Unexpected return value from test '%1': %2")
-                          .arg(function.toQString(m_tmp1))
-                          .arg(ret.join(QLatin1String(" :: "))));
-            }
-        }
-    }
-    return ReturnFalse;
-}
-
-ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
-        const ProString &func, const ushort *&tokPtr)
-{
-    QHash<ProString, FunctionDef>::ConstIterator it =
-            m_functionDefs.replaceFunctions.constFind(func);
-    if (it != m_functionDefs.replaceFunctions.constEnd())
-        return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0);
-
-    //why don't the builtin functions just use args_list? --Sam
-    return evaluateExpandFunction(func, expandVariableReferences(tokPtr, 5, true));
-}
-
-ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
-        const ProString &func, const ProString &arguments)
-{
-    QHash<ProString, FunctionDef>::ConstIterator it =
-            m_functionDefs.replaceFunctions.constFind(func);
-    if (it != m_functionDefs.replaceFunctions.constEnd())
-        return evaluateFunction(*it, prepareFunctionArgs(arguments), 0);
-
-    //why don't the builtin functions just use args_list? --Sam
-    int pos = 0;
-    return evaluateExpandFunction(func, expandVariableReferences(arguments, &pos, true));
-}
-
-ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
-        const ProString &func, const ProStringList &args)
-{
-    ExpandFunc func_t = ExpandFunc(statics.expands.value(func));
-    if (func_t == 0) {
-        const QString &fn = func.toQString(m_tmp1);
-        const QString &lfn = fn.toLower();
-        if (!fn.isSharedWith(lfn))
-            func_t = ExpandFunc(statics.expands.value(ProString(lfn)));
-    }
-
-    ProStringList ret;
-
-    switch (func_t) {
-        case E_BASENAME:
-        case E_DIRNAME:
-        case E_SECTION: {
-            bool regexp = false;
-            QString sep;
-            ProString var;
-            int beg = 0;
-            int end = -1;
-            if (func_t == E_SECTION) {
-                if (args.count() != 3 && args.count() != 4) {
-                    evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
-                                   " three or four arguments.").arg(func.toQString(m_tmp1)));
-                } else {
-                    var = args[0];
-                    sep = args.at(1).toQString();
-                    beg = args.at(2).toQString(m_tmp2).toInt();
-                    if (args.count() == 4)
-                        end = args.at(3).toQString(m_tmp2).toInt();
-                }
-            } else {
-                if (args.count() != 1) {
-                    evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
-                } else {
-                    var = args[0];
-                    regexp = true;
-                    sep = QLatin1String("[\\\\/]");
-                    if (func_t == E_DIRNAME)
-                        end = -2;
-                    else
-                        beg = -1;
-                }
-            }
-            if (!var.isEmpty()) {
-                if (regexp) {
-                    QRegExp sepRx(sep);
-                    foreach (const ProString &str, values(map(var))) {
-                        const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
-                        ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash).setSource(str));
-                    }
-                } else {
-                    foreach (const ProString &str, values(map(var))) {
-                        const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
-                        ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash).setSource(str));
-                    }
-                }
-            }
-            break;
-        }
-        case E_SPRINTF:
-            if(args.count() < 1) {
-                evalError(fL1S("sprintf(format, ...) requires at least one argument"));
-            } else {
-                QString tmp = args.at(0).toQString(m_tmp1);
-                for (int i = 1; i < args.count(); ++i)
-                    tmp = tmp.arg(args.at(i).toQString(m_tmp2));
-                // Note: this depends on split_value_list() making a deep copy
-                ret = split_value_list(tmp);
-            }
-            break;
-        case E_JOIN: {
-            if (args.count() < 1 || args.count() > 4) {
-                evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
-            } else {
-                QString glue;
-                ProString before, after;
-                if (args.count() >= 2)
-                    glue = args.at(1).toQString(m_tmp1);
-                if (args.count() >= 3)
-                    before = args[2];
-                if (args.count() == 4)
-                    after = args[3];
-                const ProStringList &var = values(map(args.at(0)));
-                if (!var.isEmpty()) {
-                    const ProFile *src = currentProFile();
-                    foreach (const ProString &v, var)
-                        if (const ProFile *s = v.sourceFile()) {
-                            src = s;
-                            break;
-                        }
-                    ret = split_value_list(before + var.join(glue) + after, src);
-                }
-            }
-            break;
-        }
-        case E_SPLIT:
-            if (args.count() != 2) {
-                evalError(fL1S("split(var, sep) requires one or two arguments"));
-            } else {
-                const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
-                foreach (const ProString &var, values(map(args.at(0))))
-                    foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
-                        ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt, NoHash).setSource(var));
-            }
-            break;
-        case E_MEMBER:
-            if (args.count() < 1 || args.count() > 3) {
-                evalError(fL1S("member(var, start, end) requires one to three arguments."));
-            } else {
-                bool ok = true;
-                const ProStringList &var = values(map(args.at(0)));
-                int start = 0, end = 0;
-                if (args.count() >= 2) {
-                    const QString &start_str = args.at(1).toQString(m_tmp1);
-                    start = start_str.toInt(&ok);
-                    if (!ok) {
-                        if (args.count() == 2) {
-                            int dotdot = start_str.indexOf(statics.strDotDot);
-                            if (dotdot != -1) {
-                                start = start_str.left(dotdot).toInt(&ok);
-                                if (ok)
-                                    end = start_str.mid(dotdot+2).toInt(&ok);
-                            }
-                        }
-                        if (!ok)
-                            evalError(fL1S("member() argument 2 (start) '%2' invalid.")
-                                      .arg(start_str));
-                    } else {
-                        end = start;
-                        if (args.count() == 3)
-                            end = args.at(2).toQString(m_tmp1).toInt(&ok);
-                        if (!ok)
-                            evalError(fL1S("member() argument 3 (end) '%2' invalid.\n")
-                                      .arg(args.at(2).toQString(m_tmp1)));
-                    }
-                }
-                if (ok) {
-                    if (start < 0)
-                        start += var.count();
-                    if (end < 0)
-                        end += var.count();
-                    if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
-                        //nothing
-                    } else if (start < end) {
-                        for (int i = start; i <= end && var.count() >= i; i++)
-                            ret.append(var[i]);
-                    } else {
-                        for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
-                            ret += var[i];
-                    }
-                }
-            }
-            break;
-        case E_FIRST:
-        case E_LAST:
-            if (args.count() != 1) {
-                evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
-            } else {
-                const ProStringList &var = values(map(args.at(0)));
-                if (!var.isEmpty()) {
-                    if (func_t == E_FIRST)
-                        ret.append(var[0]);
-                    else
-                        ret.append(var.last());
-                }
-            }
-            break;
-        case E_SIZE:
-            if (args.count() != 1)
-                evalError(fL1S("size(var) requires one argument."));
-            else
-                ret.append(ProString(QString::number(values(map(args.at(0))).size()), NoHash));
-            break;
-        case E_CAT:
-            if (args.count() < 1 || args.count() > 2) {
-                evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
-            } else {
-                const QString &file = args.at(0).toQString(m_tmp1);
-
-                bool singleLine = true;
-                if (args.count() > 1)
-                    singleLine = isTrue(args.at(1), m_tmp2);
-
-                QFile qfile(resolvePath(expandEnvVars(file)));
-                if (qfile.open(QIODevice::ReadOnly)) {
-                    QTextStream stream(&qfile);
-                    while (!stream.atEnd()) {
-                        ret += split_value_list(stream.readLine().trimmed());
-                        if (!singleLine)
-                            ret += ProString("\n", NoHash);
-                    }
-                    qfile.close();
-                }
-            }
-            break;
-        case E_FROMFILE:
-            if (args.count() != 2) {
-                evalError(fL1S("fromfile(file, variable) requires two arguments."));
-            } else {
-                QHash<ProString, ProStringList> vars;
-                QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
-                fn.detach();
-                if (evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
-                                     &vars, &m_functionDefs, EvalWithDefaults))
-                    ret = vars.value(map(args.at(1)));
-            }
-            break;
-        case E_EVAL:
-            if (args.count() != 1) {
-                evalError(fL1S("eval(variable) requires one argument"));
-            } else {
-                ret += values(map(args.at(0)));
-            }
-            break;
-        case E_LIST: {
-            QString tmp;
-            tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
-            ret = ProStringList(ProString(tmp, NoHash));
-            ProStringList lst;
-            foreach (const ProString &arg, args)
-                lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy
-            m_valuemapStack.top()[ret.at(0)] = lst;
-            break; }
-        case E_FIND:
-            if (args.count() != 2) {
-                evalError(fL1S("find(var, str) requires two arguments."));
-            } else {
-                QRegExp regx(args.at(1).toQString());
-                int t = 0;
-                foreach (const ProString &val, values(map(args.at(0)))) {
-                    if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
-                        ret += val;
-                    t ^= 1;
-                }
-            }
-            break;
-        case E_SYSTEM:
-            if (!m_skipLevel) {
-                if (args.count() < 1 || args.count() > 2) {
-                    evalError(fL1S("system(execute) requires one or two arguments."));
-                } else {
-                    bool singleLine = true;
-                    if (args.count() > 1)
-                        singleLine = isTrue(args.at(1), m_tmp2);
-                    QByteArray output;
-#ifndef QT_BOOTSTRAPPED
-                    QProcess proc;
-                    runProcess(&proc, args.at(0).toQString(m_tmp2), QProcess::StandardError);
-                    output = proc.readAllStandardOutput();
-                    output.replace('\t', ' ');
-                    if (singleLine)
-                        output.replace('\n', ' ');
-#else
-                    char buff[256];
-                    FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
-                                           + IoUtils::shellQuote(currentDirectory())
-                                           + QLatin1String(" && ") + args[0]).toLocal8Bit(), "r");
-                    while (proc && !feof(proc)) {
-                        int read_in = int(fread(buff, 1, 255, proc));
-                        if (!read_in)
-                            break;
-                        for (int i = 0; i < read_in; i++) {
-                            if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
-                                buff[i] = ' ';
-                        }
-                        output.append(buff, read_in);
-                    }
-                    if (proc)
-                        QT_PCLOSE(proc);
-#endif
-                    ret += split_value_list(QString::fromLocal8Bit(output));
-                }
-            }
-            break;
-        case E_UNIQUE:
-            if(args.count() != 1) {
-                evalError(fL1S("unique(var) requires one argument."));
-            } else {
-                ret = values(map(args.at(0)));
-                ret.removeDuplicates();
-            }
-            break;
-        case E_QUOTE:
-            ret += args;
-            break;
-        case E_ESCAPE_EXPAND:
-            for (int i = 0; i < args.size(); ++i) {
-                QString str = args.at(i).toQString();
-                QChar *i_data = str.data();
-                int i_len = str.length();
-                for (int x = 0; x < i_len; ++x) {
-                    if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
-                        if (*(i_data+x+1) == QLatin1Char('\\')) {
-                            ++x;
-                        } else {
-                            struct {
-                                char in, out;
-                            } mapped_quotes[] = {
-                                { 'n', '\n' },
-                                { 't', '\t' },
-                                { 'r', '\r' },
-                                { 0, 0 }
-                            };
-                            for (int i = 0; mapped_quotes[i].in; ++i) {
-                                if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
-                                    *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
-                                    if (x < i_len-2)
-                                        memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
-                                    --i_len;
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                }
-                ret.append(ProString(QString(i_data, i_len), NoHash).setSource(args.at(i)));
-            }
-            break;
-        case E_RE_ESCAPE:
-            for (int i = 0; i < args.size(); ++i) {
-                const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
-                ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash).setSource(args.at(i)));
-            }
-            break;
-        case E_UPPER:
-        case E_LOWER:
-            for (int i = 0; i < args.count(); ++i) {
-                QString rstr = args.at(i).toQString(m_tmp1);
-                rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
-                ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash).setSource(args.at(i)));
-            }
-            break;
-        case E_FILES:
-            if (args.count() != 1 && args.count() != 2) {
-                evalError(fL1S("files(pattern, recursive=false) requires one or two arguments"));
-            } else {
-                bool recursive = false;
-                if (args.count() == 2)
-                    recursive = isTrue(args.at(1), m_tmp2);
-                QStringList dirs;
-                QString r = fixPathToLocalOS(args.at(0).toQString(m_tmp1));
-                QString pfx;
-                if (IoUtils::isRelativePath(r)) {
-                    pfx = currentDirectory();
-                    if (!pfx.endsWith(QLatin1Char('/')))
-                        pfx += QLatin1Char('/');
-                }
-                int slash = r.lastIndexOf(QDir::separator());
-                if (slash != -1) {
-                    dirs.append(r.left(slash+1));
-                    r = r.mid(slash+1);
-                } else {
-                    dirs.append(QString());
-                }
-
-                r.detach(); // Keep m_tmp out of QRegExp's cache
-                QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
-                for (int d = 0; d < dirs.count(); d++) {
-                    QString dir = dirs[d];
-                    QDir qdir(pfx + dir);
-                    for (int i = 0; i < (int)qdir.count(); ++i) {
-                        if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
-                            continue;
-                        QString fname = dir + qdir[i];
-                        if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
-                            if (recursive)
-                                dirs.append(fname + QDir::separator());
-                        }
-                        if (regex.exactMatch(qdir[i]))
-                            ret += ProString(fname, NoHash).setSource(currentProFile());
-                    }
-                }
-            }
-            break;
-        case E_REPLACE:
-            if(args.count() != 3 ) {
-                evalError(fL1S("replace(var, before, after) requires three arguments"));
-            } else {
-                QRegExp before(args.at(1).toQString());
-                const QString &after(args.at(2).toQString(m_tmp2));
-                foreach (const ProString &val, values(map(args.at(0)))) {
-                    QString rstr = val.toQString(m_tmp1);
-                    QString copy = rstr; // Force a detach on modify
-                    rstr.replace(before, after);
-                    ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr, NoHash).setSource(val));
-                }
-            }
-            break;
-        case E_SORT_DEPENDS:
-        case E_RESOLVE_DEPENDS:
-            if (args.count() < 1 || args.count() > 2) {
-                evalError(fL1S("%1(var, prefix) requires one or two arguments").arg(func.toQString(m_tmp1)));
-            } else {
-                QHash<ProString, QSet<ProString> > dependencies;
-                QHash<ProString, ProStringList> dependees;
-                ProStringList rootSet;
-                ProStringList orgList = valuesDirect(args.at(0));
-                populateDeps(orgList, (args.count() < 2 ? ProString() : args.at(1)),
-                             dependencies, dependees, rootSet);
-                for (int i = 0; i < rootSet.size(); ++i) {
-                    const ProString &item = rootSet.at(i);
-                    if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item))
-                        ret.prepend(item);
-                    foreach (const ProString &dep, dependees[item]) {
-                        QSet<ProString> &dset = dependencies[dep];
-                        dset.remove(rootSet.at(i)); // *Don't* use 'item' - rootSet may have changed!
-                        if (dset.isEmpty())
-                            rootSet << dep;
-                    }
-                }
-            }
-            break;
-        case E_INVALID:
-            evalError(fL1S("'%1' is not a recognized replace function")
-                      .arg(func.toQString(m_tmp1)));
-            break;
-        default:
-            evalError(fL1S("Function '%1' is not implemented").arg(func.toQString(m_tmp1)));
-            break;
-    }
-
-    return ret;
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
-        const ProString &function, const ProString &arguments)
-{
-    QHash<ProString, FunctionDef>::ConstIterator it =
-            m_functionDefs.testFunctions.constFind(function);
-    if (it != m_functionDefs.testFunctions.constEnd())
-        return evaluateBoolFunction(*it, prepareFunctionArgs(arguments), function);
-
-    //why don't the builtin functions just use args_list? --Sam
-    int pos = 0;
-    return evaluateConditionalFunction(function, expandVariableReferences(arguments, &pos, true));
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
-        const ProString &function, const ushort *&tokPtr)
-{
-    QHash<ProString, FunctionDef>::ConstIterator it =
-            m_functionDefs.testFunctions.constFind(function);
-    if (it != m_functionDefs.testFunctions.constEnd())
-        return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function);
-
-    //why don't the builtin functions just use args_list? --Sam
-    return evaluateConditionalFunction(function, expandVariableReferences(tokPtr, 5, true));
-}
-
-ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
-        const ProString &function, const ProStringList &args)
-{
-    TestFunc func_t = (TestFunc)statics.functions.value(function);
-
-    switch (func_t) {
-        case T_DEFINED:
-            if (args.count() < 1 || args.count() > 2) {
-                evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
-                               " requires one or two arguments."));
-                return ReturnFalse;
-            }
-            if (args.count() > 1) {
-                if (args[1] == QLatin1String("test"))
-                    return returnBool(m_functionDefs.testFunctions.contains(args[0]));
-                else if (args[1] == QLatin1String("replace"))
-                    return returnBool(m_functionDefs.replaceFunctions.contains(args[0]));
-                evalError(fL1S("defined(function, type): unexpected type [%1].\n")
-                          .arg(args.at(1).toQString(m_tmp1)));
-                return ReturnFalse;
-            }
-            return returnBool(m_functionDefs.replaceFunctions.contains(args[0])
-                              || m_functionDefs.testFunctions.contains(args[0]));
-        case T_RETURN:
-            m_returnValue = args;
-            // It is "safe" to ignore returns - due to qmake brokeness
-            // they cannot be used to terminate loops anyway.
-            if (m_skipLevel || m_cumulative)
-                return ReturnTrue;
-            if (m_valuemapStack.isEmpty()) {
-                evalError(fL1S("unexpected return()."));
-                return ReturnFalse;
-            }
-            return ReturnReturn;
-        case T_EXPORT: {
-            if (m_skipLevel && !m_cumulative)
-                return ReturnTrue;
-            if (args.count() != 1) {
-                evalError(fL1S("export(variable) requires one argument."));
-                return ReturnFalse;
-            }
-            const ProString &var = map(args.at(0));
-            for (int i = m_valuemapStack.size(); --i > 0; ) {
-                QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(var);
-                if (it != m_valuemapStack.at(i).end()) {
-                    if (it->constBegin() == statics.fakeValue.constBegin()) {
-                        // This is stupid, but qmake doesn't propagate deletions
-                        m_valuemapStack[0][var] = ProStringList();
-                    } else {
-                        m_valuemapStack[0][var] = *it;
-                    }
-                    m_valuemapStack[i].erase(it);
-                    while (--i)
-                        m_valuemapStack[i].remove(var);
-                    break;
-                }
-            }
-            return ReturnTrue;
-        }
-        case T_INFILE:
-            if (args.count() < 2 || args.count() > 3) {
-                evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
-            } else {
-                QHash<ProString, ProStringList> vars;
-                QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
-                fn.detach();
-                if (!evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
-                                      &vars, &m_functionDefs, EvalWithDefaults))
-                    return ReturnFalse;
-                if (args.count() == 2)
-                    return returnBool(vars.contains(args.at(1)));
-                QRegExp regx;
-                const QString &qry = args.at(2).toQString(m_tmp1);
-                if (qry != QRegExp::escape(qry)) {
-                    QString copy = qry;
-                    copy.detach();
-                    regx.setPattern(copy);
-                }
-                int t = 0;
-                foreach (const ProString &s, vars.value(map(args.at(1)))) {
-                    if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
-                        return ReturnTrue;
-                    t ^= 1;
-                }
-            }
-            return ReturnFalse;
-#if 0
-        case T_REQUIRES:
-#endif
-        case T_EVAL: {
-                ProFile *pro = m_parser->parsedProBlock(fL1S("(eval)"),
-                                                        args.join(statics.field_sep));
-                if (!pro)
-                    return ReturnFalse;
-                m_locationStack.push(m_current);
-                VisitReturn ret = visitProBlock(pro, pro->tokPtr());
-                m_current = m_locationStack.pop();
-                pro->deref();
-                return ret;
-            }
-        case T_BREAK:
-            if (m_skipLevel)
-                return ReturnFalse;
-            if (m_loopLevel)
-                return ReturnBreak;
-            evalError(fL1S("unexpected break()."));
-            return ReturnFalse;
-        case T_NEXT:
-            if (m_skipLevel)
-                return ReturnFalse;
-            if (m_loopLevel)
-                return ReturnNext;
-            evalError(fL1S("unexpected next()."));
-            return ReturnFalse;
-        case T_IF: {
-            if (m_skipLevel && !m_cumulative)
-                return ReturnFalse;
-            if (args.count() != 1) {
-                evalError(fL1S("if(condition) requires one argument."));
-                return ReturnFalse;
-            }
-            const ProString &cond = args.at(0);
-            bool quoted = false;
-            bool ret = true;
-            bool orOp = false;
-            bool invert = false;
-            bool isFunc = false;
-            int parens = 0;
-            QString test;
-            test.reserve(20);
-            QString argsString;
-            argsString.reserve(50);
-            const QChar *d = cond.constData();
-            const QChar *ed = d + cond.size();
-            while (d < ed) {
-                ushort c = (d++)->unicode();
-                bool isOp = false;
-                if (quoted) {
-                    if (c == '"')
-                        quoted = false;
-                    else if (c == '!' && test.isEmpty())
-                        invert = true;
-                    else
-                        test += c;
-                } else if (c == '(') {
-                    isFunc = true;
-                    if (parens)
-                        argsString += c;
-                    ++parens;
-                } else if (c == ')') {
-                    --parens;
-                    if (parens)
-                        argsString += c;
-                } else if (!parens) {
-                    if (c == '"')
-                        quoted = true;
-                    else if (c == ':' || c == '|')
-                        isOp = true;
-                    else if (c == '!' && test.isEmpty())
-                        invert = true;
-                    else
-                        test += c;
-                } else {
-                    argsString += c;
-                }
-                if (!quoted && !parens && (isOp || d == ed)) {
-                    if (m_cumulative || (orOp != ret)) {
-                        test = test.trimmed();
-                        if (isFunc)
-                            ret = evaluateConditionalFunction(ProString(test), ProString(argsString, NoHash));
-                        else
-                            ret = isActiveConfig(test, true);
-                        ret ^= invert;
-                    }
-                    orOp = (c == '|');
-                    invert = false;
-                    isFunc = false;
-                    test.clear();
-                    argsString.clear();
-                }
-            }
-            return returnBool(ret);
-        }
-        case T_CONFIG: {
-            if (args.count() < 1 || args.count() > 2) {
-                evalError(fL1S("CONFIG(config) requires one or two arguments."));
-                return ReturnFalse;
-            }
-            if (args.count() == 1)
-                return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
-            const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
-            const ProStringList &configs = valuesDirect(statics.strCONFIG);
-
-            for (int i = configs.size() - 1; i >= 0; i--) {
-                for (int mut = 0; mut < mutuals.count(); mut++) {
-                    if (configs[i] == mutuals[mut].trimmed()) {
-                        return returnBool(configs[i] == args[0]);
-                    }
-                }
-            }
-            return ReturnFalse;
-        }
-        case T_CONTAINS: {
-            if (args.count() < 2 || args.count() > 3) {
-                evalError(fL1S("contains(var, val) requires two or three arguments."));
-                return ReturnFalse;
-            }
-
-            const QString &qry = args.at(1).toQString(m_tmp1);
-            QRegExp regx;
-            if (qry != QRegExp::escape(qry)) {
-                QString copy = qry;
-                copy.detach();
-                regx.setPattern(copy);
-            }
-            const ProStringList &l = values(map(args.at(0)));
-            if (args.count() == 2) {
-                int t = 0;
-                for (int i = 0; i < l.size(); ++i) {
-                    const ProString &val = l[i];
-                    if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
-                        return ReturnTrue;
-                    t ^= 1;
-                }
-            } else {
-                const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
-                for (int i = l.size() - 1; i >= 0; i--) {
-                    const ProString val = l[i];
-                    for (int mut = 0; mut < mutuals.count(); mut++) {
-                        if (val == mutuals[mut].trimmed()) {
-                            return returnBool((!regx.isEmpty()
-                                               && regx.exactMatch(val.toQString(m_tmp2)))
-                                              || val == qry);
-                        }
-                    }
-                }
-            }
-            return ReturnFalse;
-        }
-        case T_COUNT: {
-            if (args.count() != 2 && args.count() != 3) {
-                evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
-                return ReturnFalse;
-            }
-            int cnt = values(map(args.at(0))).count();
-            if (args.count() == 3) {
-                const ProString &comp = args.at(2);
-                const int val = args.at(1).toQString(m_tmp1).toInt();
-                if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
-                    return returnBool(cnt > val);
-                } else if (comp == QLatin1String(">=")) {
-                    return returnBool(cnt >= val);
-                } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
-                    return returnBool(cnt < val);
-                } else if (comp == QLatin1String("<=")) {
-                    return returnBool(cnt <= val);
-                } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
-                           || comp == QLatin1String("=") || comp == QLatin1String("==")) {
-                    return returnBool(cnt == val);
-                } else {
-                    evalError(fL1S("unexpected modifier to count(%2)").arg(comp.toQString(m_tmp1)));
-                    return ReturnFalse;
-                }
-            }
-            return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
-        }
-        case T_GREATERTHAN:
-        case T_LESSTHAN: {
-            if (args.count() != 2) {
-                evalError(fL1S("%1(variable, value) requires two arguments.")
-                          .arg(function.toQString(m_tmp1)));
-                return ReturnFalse;
-            }
-            const QString &rhs(args.at(1).toQString(m_tmp1)),
-                          &lhs(values(map(args.at(0))).join(statics.field_sep));
-            bool ok;
-            int rhs_int = rhs.toInt(&ok);
-            if (ok) { // do integer compare
-                int lhs_int = lhs.toInt(&ok);
-                if (ok) {
-                    if (func_t == T_GREATERTHAN)
-                        return returnBool(lhs_int > rhs_int);
-                    return returnBool(lhs_int < rhs_int);
-                }
-            }
-            if (func_t == T_GREATERTHAN)
-                return returnBool(lhs > rhs);
-            return returnBool(lhs < rhs);
-        }
-        case T_EQUALS:
-            if (args.count() != 2) {
-                evalError(fL1S("%1(variable, value) requires two arguments.")
-                          .arg(function.toQString(m_tmp1)));
-                return ReturnFalse;
-            }
-            return returnBool(values(map(args.at(0))).join(statics.field_sep)
-                              == args.at(1).toQString(m_tmp1));
-        case T_CLEAR: {
-            if (m_skipLevel && !m_cumulative)
-                return ReturnFalse;
-            if (args.count() != 1) {
-                evalError(fL1S("%1(variable) requires one argument.")
-                          .arg(function.toQString(m_tmp1)));
-                return ReturnFalse;
-            }
-            QHash<ProString, ProStringList> *hsh;
-            QHash<ProString, ProStringList>::Iterator it;
-            const ProString &var = map(args.at(0));
-            if (!(hsh = findValues(var, &it)))
-                return ReturnFalse;
-            if (hsh == &m_valuemapStack.top())
-                it->clear();
-            else
-                m_valuemapStack.top()[var].clear();
-            return ReturnTrue;
-        }
-        case T_UNSET: {
-            if (m_skipLevel && !m_cumulative)
-                return ReturnFalse;
-            if (args.count() != 1) {
-                evalError(fL1S("%1(variable) requires one argument.")
-                          .arg(function.toQString(m_tmp1)));
-                return ReturnFalse;
-            }
-            QHash<ProString, ProStringList> *hsh;
-            QHash<ProString, ProStringList>::Iterator it;
-            const ProString &var = map(args.at(0));
-            if (!(hsh = findValues(var, &it)))
-                return ReturnFalse;
-            if (m_valuemapStack.size() == 1)
-                hsh->erase(it);
-            else if (hsh == &m_valuemapStack.top())
-                *it = statics.fakeValue;
-            else
-                m_valuemapStack.top()[var] = statics.fakeValue;
-            return ReturnTrue;
-        }
-        case T_INCLUDE: {
-            if (m_skipLevel && !m_cumulative)
-                return ReturnFalse;
-            QString parseInto;
-            // the third optional argument to include() controls warnings
-            //      and is not used here
-            if ((args.count() == 2) || (args.count() == 3) ) {
-                parseInto = args.at(1).toQString(m_tmp2);
-            } else if (args.count() != 1) {
-                evalError(fL1S("include(file, into, silent) requires one, two or three arguments."));
-                return ReturnFalse;
-            }
-            QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
-            fn.detach();
-            bool ok;
-            if (parseInto.isEmpty()) {
-                ok = evaluateFile(fn, ProFileEvaluatorHandler::EvalIncludeFile,
-                                  ProFileEvaluator::LoadProOnly);
-            } else {
-                QHash<ProString, ProStringList> symbols;
-                if ((ok = evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
-                                           &symbols, 0, EvalWithSetup))) {
-                    QHash<ProString, ProStringList> newMap;
-                    for (QHash<ProString, ProStringList>::ConstIterator
-                            it = m_valuemapStack.top().constBegin(),
-                            end = m_valuemapStack.top().constEnd();
-                            it != end; ++it) {
-                        const QString &ky = it.key().toQString(m_tmp1);
-                        if (!(ky.startsWith(parseInto) &&
-                              (ky.length() == parseInto.length()
-                               || ky.at(parseInto.length()) == QLatin1Char('.'))))
-                            newMap[it.key()] = it.value();
-                    }
-                    for (QHash<ProString, ProStringList>::ConstIterator it = symbols.constBegin();
-                         it != symbols.constEnd(); ++it) {
-                        const QString &ky = it.key().toQString(m_tmp1);
-                        if (!ky.startsWith(QLatin1Char('.')))
-                            newMap.insert(ProString(parseInto + QLatin1Char('.') + ky), it.value());
-                    }
-                    m_valuemapStack.top() = newMap;
-                }
-            }
-            return returnBool(ok);
-        }
-        case T_LOAD: {
-            if (m_skipLevel && !m_cumulative)
-                return ReturnFalse;
-            // bool ignore_error = false;
-            if (args.count() == 2) {
-                // ignore_error = isTrue(args.at(1), m_tmp2);
-            } else if (args.count() != 1) {
-                evalError(fL1S("load(feature) requires one or two arguments."));
-                return ReturnFalse;
-            }
-            // XXX ignore_error unused
-            return returnBool(evaluateFeatureFile(expandEnvVars(args.at(0).toQString())));
-        }
-        case T_DEBUG:
-            // Yup - do nothing. Nothing is going to enable debug output anyway.
-            return ReturnFalse;
-        case T_MESSAGE: {
-            if (args.count() != 1) {
-                evalError(fL1S("%1(message) requires one argument.")
-                          .arg(function.toQString(m_tmp1)));
-                return ReturnFalse;
-            }
-            const QString &msg = expandEnvVars(args.at(0).toQString(m_tmp2));
-            if (!m_skipLevel)
-                m_handler->fileMessage(fL1S("Project %1: %2")
-                                       .arg(function.toQString(m_tmp1).toUpper(), msg));
-            // ### Consider real termination in non-cumulative mode
-            return returnBool(function != QLatin1String("error"));
-        }
-#if 0 // Way too dangerous to enable.
-        case T_SYSTEM: {
-            if (args.count() != 1) {
-                evalError(fL1S("system(exec) requires one argument."));
-                return ReturnFalse;
-            }
-#ifndef QT_BOOTSTRAPPED
-            QProcess proc;
-            proc.setProcessChannelMode(QProcess::MergedChannels);
-            runProcess(&proc, args.at(0).toQString(m_tmp2), QProcess::StandardOutput);
-            return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
-#else
-            return returnBool(system((QLatin1String("cd ")
-                                      + IoUtils::shellQuote(currentDirectory())
-                                      + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
-#endif
-        }
-#endif
-        case T_ISEMPTY: {
-            if (args.count() != 1) {
-                evalError(fL1S("isEmpty(var) requires one argument."));
-                return ReturnFalse;
-            }
-            const ProStringList &sl = values(map(args.at(0)));
-            if (sl.count() == 0) {
-                return ReturnTrue;
-            } else if (sl.count() > 0) {
-                const ProString &var = sl.first();
-                if (var.isEmpty())
-                    return ReturnTrue;
-            }
-            return ReturnFalse;
-        }
-        case T_EXISTS: {
-            if (args.count() != 1) {
-                evalError(fL1S("exists(file) requires one argument."));
-                return ReturnFalse;
-            }
-            const QString &file = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
-
-            if (IoUtils::exists(file)) {
-                return ReturnTrue;
-            }
-            int slsh = file.lastIndexOf(QLatin1Char('/'));
-            QString fn = file.mid(slsh+1);
-            if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
-                QString dirstr = file.left(slsh+1);
-                if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
-                    return ReturnTrue;
-            }
-
-            return ReturnFalse;
-        }
-        case T_INVALID:
-            evalError(fL1S("'%1' is not a recognized test function")
-                      .arg(function.toQString(m_tmp1)));
-            return ReturnFalse;
-        default:
-            evalError(fL1S("Function '%1' is not implemented").arg(function.toQString(m_tmp1)));
-            return ReturnFalse;
-    }
-}
-
-QHash<ProString, ProStringList> *ProFileEvaluator::Private::findValues(
-        const ProString &variableName, QHash<ProString, ProStringList>::Iterator *rit)
-{
-    for (int i = m_valuemapStack.size(); --i >= 0; ) {
-        QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(variableName);
-        if (it != m_valuemapStack[i].end()) {
-            if (it->constBegin() == statics.fakeValue.constBegin())
-                return 0;
-            *rit = it;
-            return &m_valuemapStack[i];
-        }
-    }
-    return 0;
-}
-
-ProStringList &ProFileEvaluator::Private::valuesRef(const ProString &variableName)
-{
-    QHash<ProString, ProStringList>::Iterator it = m_valuemapStack.top().find(variableName);
-    if (it != m_valuemapStack.top().end())
-        return *it;
-    for (int i = m_valuemapStack.size() - 1; --i >= 0; ) {
-        QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
-        if (it != m_valuemapStack.at(i).constEnd()) {
-            ProStringList &ret = m_valuemapStack.top()[variableName];
-            ret = *it;
-            return ret;
-        }
-    }
-    return m_valuemapStack.top()[variableName];
-}
-
-ProStringList ProFileEvaluator::Private::valuesDirect(const ProString &variableName) const
-{
-    for (int i = m_valuemapStack.size(); --i >= 0; ) {
-        QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
-        if (it != m_valuemapStack.at(i).constEnd()) {
-            if (it->constBegin() == statics.fakeValue.constBegin())
-                break;
-            return *it;
-        }
-    }
-    return ProStringList();
-}
-
-ProStringList ProFileEvaluator::Private::values(const ProString &variableName) const
-{
-    QHash<ProString, int>::ConstIterator vli = statics.varList.find(variableName);
-    if (vli != statics.varList.constEnd()) {
-        int vlidx = *vli;
-        QString ret;
-        switch ((VarName)vlidx) {
-        case V_LITERAL_WHITESPACE: ret = QLatin1String("\t"); break;
-        case V_LITERAL_DOLLAR: ret = QLatin1String("$"); break;
-        case V_LITERAL_HASH: ret = QLatin1String("#"); break;
-        case V_OUT_PWD: // the outgoing dir (shadow of _PRO_FILE_PWD_)
-            ret = m_outputDir;
-            break;
-        case V_PWD: // containing directory of most nested project/include file
-        case V_IN_PWD:
-            ret = currentDirectory();
-            break;
-        case V_DIR_SEPARATOR:
-            validateModes();
-            ret = m_option->dir_sep;
-            break;
-        case V_DIRLIST_SEPARATOR:
-            ret = m_option->dirlist_sep;
-            break;
-        case V__LINE_: // currently executed line number
-            ret = QString::number(m_current.line);
-            break;
-        case V__FILE_: // currently executed file
-            ret = m_current.pro->fileName();
-            break;
-        case V__DATE_: //current date/time
-            ret = QDateTime::currentDateTime().toString();
-            break;
-        case V__PRO_FILE_:
-            ret = m_profileStack.first()->fileName();
-            break;
-        case V__PRO_FILE_PWD_:
-            ret = m_profileStack.first()->directoryName();
-            break;
-        case V__QMAKE_CACHE_:
-            ret = m_option->cachefile;
-            break;
-#if defined(Q_OS_WIN32)
-        case V_QMAKE_HOST_os: ret = QLatin1String("Windows"); break;
-        case V_QMAKE_HOST_name: {
-            DWORD name_length = 1024;
-            TCHAR name[1024];
-            if (GetComputerName(name, &name_length))
-                ret = QString::fromUtf16((ushort*)name, name_length);
-            break;
-        }
-        case V_QMAKE_HOST_version:
-            ret = QString::number(QSysInfo::WindowsVersion);
-            break;
-        case V_QMAKE_HOST_version_string:
-            switch (QSysInfo::WindowsVersion) {
-            case QSysInfo::WV_Me: ret = QLatin1String("WinMe"); break;
-            case QSysInfo::WV_95: ret = QLatin1String("Win95"); break;
-            case QSysInfo::WV_98: ret = QLatin1String("Win98"); break;
-            case QSysInfo::WV_NT: ret = QLatin1String("WinNT"); break;
-            case QSysInfo::WV_2000: ret = QLatin1String("Win2000"); break;
-            case QSysInfo::WV_2003: ret = QLatin1String("Win2003"); break;
-            case QSysInfo::WV_XP: ret = QLatin1String("WinXP"); break;
-            case QSysInfo::WV_VISTA: ret = QLatin1String("WinVista"); break;
-            default: ret = QLatin1String("Unknown"); break;
-            }
-            break;
-        case V_QMAKE_HOST_arch:
-            SYSTEM_INFO info;
-            GetSystemInfo(&info);
-            switch(info.wProcessorArchitecture) {
-#ifdef PROCESSOR_ARCHITECTURE_AMD64
-            case PROCESSOR_ARCHITECTURE_AMD64:
-                ret = QLatin1String("x86_64");
-                break;
-#endif
-            case PROCESSOR_ARCHITECTURE_INTEL:
-                ret = QLatin1String("x86");
-                break;
-            case PROCESSOR_ARCHITECTURE_IA64:
-#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
-            case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
-#endif
-                ret = QLatin1String("IA64");
-                break;
-            default:
-                ret = QLatin1String("Unknown");
-                break;
-            }
-            break;
-#elif defined(Q_OS_UNIX)
-        case V_QMAKE_HOST_os:
-        case V_QMAKE_HOST_name:
-        case V_QMAKE_HOST_version:
-        case V_QMAKE_HOST_version_string:
-        case V_QMAKE_HOST_arch:
-            {
-                struct utsname name;
-                const char *what;
-                if (!uname(&name)) {
-                    switch (vlidx) {
-                    case V_QMAKE_HOST_os: what = name.sysname; break;
-                    case V_QMAKE_HOST_name: what = name.nodename; break;
-                    case V_QMAKE_HOST_version: what = name.release; break;
-                    case V_QMAKE_HOST_version_string: what = name.version; break;
-                    case V_QMAKE_HOST_arch: what = name.machine; break;
-                    }
-                    ret = QString::fromLocal8Bit(what);
-                }
-            }
-#endif
-        }
-        return ProStringList(ProString(ret, NoHash));
-    }
-
-    ProStringList result = valuesDirect(variableName);
-    if (result.isEmpty()) {
-        if (variableName == statics.strTEMPLATE) {
-            result.append(ProString("app", NoHash));
-        } else if (variableName == statics.strQMAKE_DIR_SEP) {
-            result.append(ProString(m_option->dirlist_sep, NoHash));
-        }
-    }
-    return result;
-}
-
-bool ProFileEvaluator::Private::evaluateFileDirect(
-        const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
-        ProFileEvaluator::LoadFlags flags)
-{
-    if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
-        m_locationStack.push(m_current);
-        bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
-        m_current = m_locationStack.pop();
-        pro->deref();
-        return ok;
-    } else {
-        return false;
-    }
-}
-
-bool ProFileEvaluator::Private::evaluateFile(
-        const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
-        ProFileEvaluator::LoadFlags flags)
-{
-    if (fileName.isEmpty())
-        return false;
-    foreach (const ProFile *pf, m_profileStack)
-        if (pf->fileName() == fileName) {
-            evalError(fL1S("circular inclusion of %1").arg(fileName));
-            return false;
-        }
-    return evaluateFileDirect(fileName, type, flags);
-}
-
-bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
-{
-    QString fn = fileName;
-    if (!fn.endsWith(QLatin1String(".prf")))
-        fn += QLatin1String(".prf");
-
-    if ((!fileName.contains((ushort)'/') && !fileName.contains((ushort)'\\'))
-        || !IoUtils::exists(resolvePath(fn))) {
-        if (m_option->feature_roots.isEmpty())
-            m_option->feature_roots = qmakeFeaturePaths();
-        int start_root = 0;
-        QString currFn = currentFileName();
-        if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
-            for (int root = 0; root < m_option->feature_roots.size(); ++root)
-                if (currFn == m_option->feature_roots.at(root) + fn) {
-                    start_root = root + 1;
-                    break;
-                }
-        }
-        for (int root = start_root; root < m_option->feature_roots.size(); ++root) {
-            QString fname = m_option->feature_roots.at(root) + fn;
-            if (IoUtils::exists(fname)) {
-                fn = fname;
-                goto cool;
-            }
-        }
-        return false;
-
-      cool:
-        // It's beyond me why qmake has this inside this if ...
-        ProStringList &already = valuesRef(ProString("QMAKE_INTERNAL_INCLUDED_FEATURES"));
-        ProString afn(fn, NoHash);
-        if (already.contains(afn))
-            return true;
-        already.append(afn);
-    } else {
-        fn = resolvePath(fn);
-    }
-
-#ifdef PROEVALUATOR_CUMULATIVE
-    bool cumulative = m_cumulative;
-    m_cumulative = false;
-#endif
-
-    // The path is fully normalized already.
-    bool ok = evaluateFileDirect(fn, ProFileEvaluatorHandler::EvalFeatureFile,
-                                 ProFileEvaluator::LoadProOnly);
-
-#ifdef PROEVALUATOR_CUMULATIVE
-    m_cumulative = cumulative;
-#endif
-    return ok;
-}
-
-bool ProFileEvaluator::Private::evaluateFileInto(
-        const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
-        QHash<ProString, ProStringList> *values, FunctionDefs *funcs, EvalIntoMode mode)
-{
-    ProFileEvaluator visitor(m_option, m_parser, m_handler);
-#ifdef PROEVALUATOR_CUMULATIVE
-    visitor.d->m_cumulative = false;
-#endif
-    visitor.d->m_outputDir = m_outputDir;
-//    visitor.d->m_valuemapStack.top() = *values;
-    if (funcs)
-        visitor.d->m_functionDefs = *funcs;
-    if (mode == EvalWithDefaults)
-        visitor.d->evaluateFeatureFile(QLatin1String("default_pre.prf"));
-    if (!visitor.d->evaluateFile(fileName, type,
-            (mode == EvalWithSetup) ? ProFileEvaluator::LoadAll : ProFileEvaluator::LoadProOnly))
-        return false;
-    *values = visitor.d->m_valuemapStack.top();
-//    if (funcs)
-//        *funcs = visitor.d->m_functionDefs;
-    return true;
-}
-
-void ProFileEvaluator::Private::evalError(const QString &message) const
-{
-    if (!m_skipLevel)
-        m_handler->evalError(m_current.line ? m_current.pro->fileName() : QString(),
-                             m_current.line, message);
-}
-
-
-///////////////////////////////////////////////////////////////////////
-//
-// ProFileEvaluator
-//
-///////////////////////////////////////////////////////////////////////
+QT_BEGIN_NAMESPACE
 
 void ProFileEvaluator::initialize()
 {
-    Private::initStatics();
+    QMakeEvaluator::initStatics();
 }
 
-ProFileEvaluator::ProFileEvaluator(ProFileOption *option, ProFileParser *parser,
-                                   ProFileEvaluatorHandler *handler)
-  : d(new Private(this, option, parser, handler))
+ProFileEvaluator::ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser,
+                                   QMakeHandler *handler)
+  : d(new QMakeEvaluator(option, parser, handler))
 {
 }
 
@@ -3418,7 +68,7 @@ ProFileEvaluator::~ProFileEvaluator()
 
 bool ProFileEvaluator::contains(const QString &variableName) const
 {
-    return d->m_valuemapStack.top().contains(ProString(variableName));
+    return d->m_valuemapStack.top().contains(ProKey(variableName));
 }
 
 QString ProFileEvaluator::value(const QString &variable) const
@@ -3432,33 +82,48 @@ QString ProFileEvaluator::value(const QString &variable) const
 
 QStringList ProFileEvaluator::values(const QString &variableName) const
 {
-    const ProStringList &values = d->values(ProString(variableName));
+    const ProStringList &values = d->values(ProKey(variableName));
     QStringList ret;
     ret.reserve(values.size());
     foreach (const ProString &str, values)
-        ret << d->expandEnvVars(str.toQString());
+        ret << d->m_option->expandEnvVars(str.toQString());
     return ret;
 }
 
 QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
 {
     // It makes no sense to put any kind of magic into expanding these
-    const ProStringList &values = d->m_valuemapStack.at(0).value(ProString(variableName));
+    const ProStringList &values = d->m_valuemapStack.first().value(ProKey(variableName));
     QStringList ret;
     ret.reserve(values.size());
     foreach (const ProString &str, values)
         if (str.sourceFile() == pro)
-            ret << d->expandEnvVars(str.toQString());
+            ret << d->m_option->expandEnvVars(str.toQString());
     return ret;
 }
 
+QString ProFileEvaluator::sysrootify(const QString &path, const QString &baseDir) const
+{
+    ProFileGlobals *option = static_cast<ProFileGlobals *>(d->m_option);
+#ifdef Q_OS_WIN
+    Qt::CaseSensitivity cs = Qt::CaseInsensitive;
+#else
+    Qt::CaseSensitivity cs = Qt::CaseSensitive;
+#endif
+    const bool isHostSystemPath =
+        option->sysroot.isEmpty() || path.startsWith(option->sysroot, cs)
+        || path.startsWith(baseDir, cs) || path.startsWith(d->m_outputDir, cs);
+
+    return isHostSystemPath ? path : option->sysroot + path;
+}
+
 QStringList ProFileEvaluator::absolutePathValues(
         const QString &variable, const QString &baseDirectory) const
 {
     QStringList result;
     foreach (const QString &el, values(variable)) {
         QString absEl = IoUtils::isAbsolutePath(el)
-            ? d->sysrootify(el, baseDirectory) : IoUtils::resolvePath(baseDirectory, el);
+            ? sysrootify(el, baseDirectory) : IoUtils::resolvePath(baseDirectory, el);
         if (IoUtils::fileType(absEl) == IoUtils::FileIsDir)
             result << QDir::cleanPath(absEl);
     }
@@ -3473,7 +138,7 @@ QStringList ProFileEvaluator::absoluteFileValues(
     foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
         QString absEl;
         if (IoUtils::isAbsolutePath(el)) {
-            const QString elWithSysroot = d->sysrootify(el, baseDirectory);
+            const QString elWithSysroot = sysrootify(el, baseDirectory);
             if (IoUtils::exists(elWithSysroot)) {
                 result << QDir::cleanPath(elWithSysroot);
                 goto next;
@@ -3502,7 +167,7 @@ QStringList ProFileEvaluator::absoluteFileValues(
                     wildcard.detach(); // Keep m_tmp out of QRegExp's cache
                     QDir theDir(absDir);
                     foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
-                        if (fn != statics.strDot && fn != statics.strDotDot)
+                        if (fn != QLatin1String(".") && fn != QLatin1String(".."))
                             result << absDir + QLatin1Char('/') + fn;
                 } // else if (acceptMissing)
             }
@@ -3514,7 +179,7 @@ QStringList ProFileEvaluator::absoluteFileValues(
 
 ProFileEvaluator::TemplateType ProFileEvaluator::templateType() const
 {
-    const ProStringList &templ = d->values(statics.strTEMPLATE);
+    const ProStringList &templ = d->values(ProKey("TEMPLATE"));
     if (templ.count() >= 1) {
         const QString &t = templ.at(0).toQString();
         if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
@@ -3531,14 +196,23 @@ ProFileEvaluator::TemplateType ProFileEvaluator::templateType() const
     return TT_Unknown;
 }
 
-bool ProFileEvaluator::accept(ProFile *pro, LoadFlags flags)
+bool ProFileEvaluator::loadNamedSpec(const QString &specDir, bool hostSpec)
+{
+    d->m_qmakespec = specDir;
+    d->m_hostBuild = hostSpec;
+
+    d->updateMkspecPaths();
+    return d->loadSpecInternal();
+}
+
+bool ProFileEvaluator::accept(ProFile *pro, QMakeEvaluator::LoadFlags flags)
 {
-    return d->visitProFile(pro, ProFileEvaluatorHandler::EvalProjectFile, flags) == Private::ReturnTrue;
+    return d->visitProFile(pro, QMakeHandler::EvalProjectFile, flags) == QMakeEvaluator::ReturnTrue;
 }
 
 QString ProFileEvaluator::propertyValue(const QString &name) const
 {
-    return d->propertyValue(name, false);
+    return d->m_option->propertyValue(ProKey(name)).toQString();
 }
 
 #ifdef PROEVALUATOR_CUMULATIVE
index 2b92603..2de40a7 100644 (file)
 #ifndef PROFILEEVALUATOR_H
 #define PROFILEEVALUATOR_H
 
-#include "proparser_global.h"
+#include "qmake_global.h"
+#include "qmakeglobals.h"
+#include "qmakeevaluator.h"
 #include "proitems.h"
 
-#include <QtCore/QHash>
-#include <QtCore/QStringList>
-#ifndef QT_BOOTSTRAPPED
-# include <QtCore/QProcess>
-#endif
-#ifdef PROEVALUATOR_THREAD_SAFE
-# include <QtCore/QMutex>
-# include <QtCore/QWaitCondition>
-#endif
+#include <QHash>
+#include <QStringList>
 
 QT_BEGIN_NAMESPACE
 
-struct ProFileOption;
-class ProFileParser;
+class QMakeParser;
+class QMakeEvaluator;
+class QMakeHandler;
 
-class PROPARSER_EXPORT ProFileEvaluatorHandler
+class QMAKE_EXPORT ProFileGlobals : public QMakeGlobals
 {
 public:
-    // qmake/project configuration error
-    virtual void configError(const QString &msg) = 0;
-    // Some error during evaluation
-    virtual void evalError(const QString &filename, int lineNo, const QString &msg) = 0;
-    // error() and message() from .pro file
-    virtual void fileMessage(const QString &msg) = 0;
-
-    enum EvalFileType { EvalProjectFile, EvalIncludeFile, EvalConfigFile, EvalFeatureFile, EvalAuxFile };
-    virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) = 0;
-    virtual void doneWithEval(ProFile *parent) = 0;
+    QString sysroot;
 };
 
-
-class PROPARSER_EXPORT ProFileEvaluator
+class QMAKE_EXPORT ProFileEvaluator
 {
-    class Private;
-
 public:
-    class FunctionDef {
-    public:
-        FunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); }
-        FunctionDef(const FunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
-        ~FunctionDef() { m_pro->deref(); }
-        FunctionDef &operator=(const FunctionDef &o)
-        {
-            if (this != &o) {
-                m_pro->deref();
-                m_pro = o.m_pro;
-                m_pro->ref();
-                m_offset = o.m_offset;
-            }
-            return *this;
-        }
-        ProFile *pro() const { return m_pro; }
-        const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; }
-    private:
-        ProFile *m_pro;
-        int m_offset;
-    };
-
-    struct FunctionDefs {
-        QHash<ProString, FunctionDef> testFunctions;
-        QHash<ProString, FunctionDef> replaceFunctions;
-    };
-
     enum TemplateType {
         TT_Unknown = 0,
         TT_Application,
@@ -120,23 +77,18 @@ public:
     // Call this from a concurrency-free context
     static void initialize();
 
-    ProFileEvaluator(ProFileOption *option, ProFileParser *parser, ProFileEvaluatorHandler *handler);
+    ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeHandler *handler);
     ~ProFileEvaluator();
 
     ProFileEvaluator::TemplateType templateType() const;
 #ifdef PROEVALUATOR_CUMULATIVE
-    void setCumulative(bool on); // Default is true!
+    void setCumulative(bool on); // Default is false
 #endif
     void setOutputDir(const QString &dir); // Default is empty
 
-    enum LoadFlag {
-        LoadProOnly = 0,
-        LoadPreFiles = 1,
-        LoadPostFiles = 2,
-        LoadAll = LoadPreFiles|LoadPostFiles
-    };
-    Q_DECLARE_FLAGS(LoadFlags, LoadFlag)
-    bool accept(ProFile *pro, LoadFlags flags = LoadAll);
+    bool loadNamedSpec(const QString &specDir, bool hostSpec);
+
+    bool accept(ProFile *pro, QMakeEvaluator::LoadFlags flags = QMakeEvaluator::LoadAll);
 
     bool contains(const QString &variableName) const;
     QString value(const QString &variableName) const;
@@ -149,82 +101,11 @@ public:
     QString propertyValue(const QString &val) const;
 
 private:
-    Private *d;
+    QString sysrootify(const QString &path, const QString &baseDir) const;
 
-    friend struct ProFileOption;
+    QMakeEvaluator *d;
 };
 
-Q_DECLARE_OPERATORS_FOR_FLAGS(ProFileEvaluator::LoadFlags)
-
-// This struct is from qmake, but we are not using everything.
-struct PROPARSER_EXPORT ProFileOption
-{
-    ProFileOption();
-    ~ProFileOption();
-
-    //simply global convenience
-    //QString libtool_ext;
-    //QString pkgcfg_ext;
-    //QString prf_ext;
-    //QString prl_ext;
-    //QString ui_ext;
-    //QStringList h_ext;
-    //QStringList cpp_ext;
-    //QString h_moc_ext;
-    //QString cpp_moc_ext;
-    //QString obj_ext;
-    //QString lex_ext;
-    //QString yacc_ext;
-    //QString h_moc_mod;
-    //QString cpp_moc_mod;
-    //QString lex_mod;
-    //QString yacc_mod;
-    QString dir_sep;
-    QString dirlist_sep;
-    QString qmakespec;
-    QString cachefile;
-    QHash<QString, QString> properties;
-#ifndef QT_BOOTSTRAPPED
-    QProcessEnvironment environment;
-#endif
-    QString sysroot;
-
-    //QString pro_ext;
-    //QString res_ext;
-
-    // -nocache, -cache, -spec, QMAKESPEC
-    // -set persistent value
-    void setCommandLineArguments(const QStringList &args);
-#ifdef PROEVALUATOR_INIT_PROPS
-    bool initProperties(const QString &qmake);
-#endif
-
-  private:
-    friend class ProFileEvaluator;
-    friend class ProFileEvaluator::Private;
-
-    void applyHostMode();
-    QString getEnv(const QString &) const;
-
-    QHash<ProString, ProStringList> base_valuemap; // Cached results of qmake.conf, .qmake.cache & default_pre.prf
-    ProFileEvaluator::FunctionDefs base_functions;
-    QStringList feature_roots;
-    QString qmakespec_name;
-    QString precmds, postcmds;
-    enum HOST_MODE { HOST_UNKNOWN_MODE, HOST_UNIX_MODE, HOST_WIN_MODE, HOST_MACX_MODE };
-    HOST_MODE host_mode;
-    enum TARG_MODE { TARG_UNKNOWN_MODE, TARG_UNIX_MODE, TARG_WIN_MODE, TARG_MACX_MODE,
-                     TARG_SYMBIAN_MODE };
-    TARG_MODE target_mode;
-#ifdef PROEVALUATOR_THREAD_SAFE
-    QMutex mutex;
-    QWaitCondition cond;
-    bool base_inProgress;
-#endif
-};
-
-Q_DECLARE_TYPEINFO(ProFileEvaluator::FunctionDef, Q_MOVABLE_TYPE);
-
 QT_END_NAMESPACE
 
 #endif // PROFILEEVALUATOR_H
index 43ab905..8377a9a 100644 (file)
 
 #include "proitems.h"
 
-#include <QtCore/QFileInfo>
-#include <QtCore/QSet>
+#include <qfileinfo.h>
+#include <qset.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
 
 QT_BEGIN_NAMESPACE
 
-using namespace ProStringConstants;
-
 // from qhash.cpp
 uint ProString::hash(const QChar *p, int n)
 {
@@ -76,29 +76,29 @@ ProString::ProString(const ProString &other, OmitPreHashing) :
 {
 }
 
-ProString::ProString(const QString &str) :
+ProString::ProString(const QString &str, DoPreHashing) :
     m_string(str), m_offset(0), m_length(str.length()), m_file(0)
 {
     updatedHash();
 }
 
-ProString::ProString(const QString &str, OmitPreHashing) :
+ProString::ProString(const QString &str) :
     m_string(str), m_offset(0), m_length(str.length()), m_file(0), m_hash(0x80000000)
 {
 }
 
-ProString::ProString(const char *str) :
+ProString::ProString(const char *str, DoPreHashing) :
     m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0)
 {
     updatedHash();
 }
 
-ProString::ProString(const char *str, OmitPreHashing) :
+ProString::ProString(const char *str) :
     m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0), m_hash(0x80000000)
 {
 }
 
-ProString::ProString(const QString &str, int offset, int length) :
+ProString::ProString(const QString &str, int offset, int length, DoPreHashing) :
     m_string(str), m_offset(offset), m_length(length), m_file(0)
 {
     updatedHash();
@@ -109,19 +109,13 @@ ProString::ProString(const QString &str, int offset, int length, uint hash) :
 {
 }
 
-ProString::ProString(const QString &str, int offset, int length, ProStringConstants::OmitPreHashing) :
+ProString::ProString(const QString &str, int offset, int length) :
     m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000)
 {
 }
 
 void ProString::setValue(const QString &str)
 {
-    m_string = str, m_offset = 0, m_length = str.length();
-    updatedHash();
-}
-
-void ProString::setValue(const QString &str, OmitPreHashing)
-{
     m_string = str, m_offset = 0, m_length = str.length(), m_hash = 0x80000000;
 }
 
@@ -137,57 +131,50 @@ uint qHash(const ProString &str)
     return str.updatedHash();
 }
 
-QString ProString::toQString() const
+ProKey::ProKey(const QString &str) :
+    ProString(str, DoHash)
 {
-    return m_string.mid(m_offset, m_length);
 }
 
-QString &ProString::toQString(QString &tmp) const
+ProKey::ProKey(const char *str) :
+    ProString(str, DoHash)
 {
-    return tmp.setRawData(m_string.constData() + m_offset, m_length);
 }
 
-bool ProString::operator==(const ProString &other) const
+ProKey::ProKey(const QString &str, int off, int len) :
+    ProString(str, off, len, DoHash)
 {
-    if (m_length != other.m_length)
-        return false;
-    return !memcmp(m_string.constData() + m_offset,
-                   other.m_string.constData() + other.m_offset, m_length * 2);
 }
 
-bool ProString::operator==(const QString &other) const
+ProKey::ProKey(const QString &str, int off, int len, uint hash) :
+    ProString(str, off, len, hash)
 {
-    if (m_length != other.length())
-        return false;
-    return !memcmp(m_string.constData() + m_offset, other.constData(), m_length * 2);
 }
 
-bool ProString::operator==(const QLatin1String &other) const
+void ProKey::setValue(const QString &str)
 {
-    const ushort *uc = (ushort *)m_string.constData() + m_offset;
-    const ushort *e = uc + m_length;
-    const uchar *c = (uchar *)other.latin1();
+    m_string = str, m_offset = 0, m_length = str.length();
+    updatedHash();
+}
 
-    if (!c)
-        return isEmpty();
+QString ProString::toQString() const
+{
+    return m_string.mid(m_offset, m_length);
+}
 
-    while (*c) {
-        if (uc == e || *uc != *c)
-            return false;
-        ++uc;
-        ++c;
-    }
-    return (uc == e);
+QString &ProString::toQString(QString &tmp) const
+{
+    return tmp.setRawData(m_string.constData() + m_offset, m_length);
 }
 
-QChar *ProString::prepareAppend(int extraLen)
+QChar *ProString::prepareExtend(int extraLen, int thisTarget, int extraTarget)
 {
     if (m_string.isDetached() && m_length + extraLen <= m_string.capacity()) {
         m_string.reserve(0); // Prevent the resize() below from reallocating
         QChar *ptr = (QChar *)m_string.constData();
-        if (m_offset)
-            memmove(ptr, ptr + m_offset, m_length * 2);
-        ptr += m_length;
+        if (m_offset != thisTarget)
+            memmove(ptr + thisTarget, ptr + m_offset, m_length * 2);
+        ptr += extraTarget;
         m_offset = 0;
         m_length += extraLen;
         m_string.resize(m_length);
@@ -196,13 +183,51 @@ QChar *ProString::prepareAppend(int extraLen)
     } else {
         QString neu(m_length + extraLen, Qt::Uninitialized);
         QChar *ptr = (QChar *)neu.constData();
-        memcpy(ptr, m_string.constData() + m_offset, m_length * 2);
-        ptr += m_length;
-        *this = ProString(neu, NoHash);
+        memcpy(ptr + thisTarget, m_string.constData() + m_offset, m_length * 2);
+        ptr += extraTarget;
+        *this = ProString(neu);
         return ptr;
     }
 }
 
+ProString &ProString::prepend(const ProString &other)
+{
+    if (other.m_length) {
+        if (!m_length) {
+            *this = other;
+        } else {
+            QChar *ptr = prepareExtend(other.m_length, other.m_length, 0);
+            memcpy(ptr, other.constData(), other.m_length * 2);
+            if (!m_file)
+                m_file = other.m_file;
+        }
+    }
+    return *this;
+}
+
+ProString &ProString::append(const QLatin1String other)
+{
+    const char *latin1 = other.latin1();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+    int size = other.size();
+#else
+    int size = strlen(latin1);
+#endif
+    if (size) {
+        QChar *ptr = prepareExtend(size, 0, m_length);
+        for (int i = 0; i < size; i++)
+            *ptr++ = QLatin1Char(latin1[i]);
+    }
+    return *this;
+}
+
+ProString &ProString::append(QChar other)
+{
+    QChar *ptr = prepareExtend(1, 0, m_length);
+    *ptr = other;
+    return *this;
+}
+
 // If pending != 0, prefix with space if appending to non-empty non-pending
 ProString &ProString::append(const ProString &other, bool *pending)
 {
@@ -212,10 +237,10 @@ ProString &ProString::append(const ProString &other, bool *pending)
         } else {
             QChar *ptr;
             if (pending && !*pending) {
-                ptr = prepareAppend(1 + other.m_length);
+                ptr = prepareExtend(1 + other.m_length, 0, m_length);
                 *ptr++ = 32;
             } else {
-                ptr = prepareAppend(other.m_length);
+                ptr = prepareExtend(other.m_length, 0, m_length);
             }
             memcpy(ptr, other.m_string.constData() + other.m_offset, other.m_length * 2);
             if (other.m_file)
@@ -248,7 +273,7 @@ ProString &ProString::append(const ProStringList &other, bool *pending, bool ski
             else
                 totalLength--;
 
-            QChar *ptr = prepareAppend(totalLength);
+            QChar *ptr = prepareExtend(totalLength, 0, m_length);
             for (int i = startIdx; i < sz; ++i) {
                 if (putSpace)
                     *ptr++ = 32;
@@ -291,7 +316,7 @@ ProString ProString::mid(int off, int len) const
         off = m_length;
     ret.m_offset += off;
     ret.m_length -= off;
-    if (ret.m_length > len)
+    if ((uint)ret.m_length > (uint)len)  // Unsigned comparison to interpret < 0 as infinite
         ret.m_length = len;
     return ret;
 }
@@ -314,6 +339,12 @@ ProString ProString::trimmed() const
     return ret;
 }
 
+QTextStream &operator<<(QTextStream &t, const ProString &str)
+{
+    t << str.toQString(); // XXX optimize ... somehow
+    return t;
+}
+
 QString ProStringList::join(const QString &sep) const
 {
     int totalLength = 0;
@@ -338,6 +369,20 @@ QString ProStringList::join(const QString &sep) const
     return res;
 }
 
+void ProStringList::removeAll(const ProString &str)
+{
+    for (int i = size(); --i >= 0; )
+        if (at(i) == str)
+            remove(i);
+}
+
+void ProStringList::removeAll(const char *str)
+{
+    for (int i = size(); --i >= 0; )
+        if (at(i) == str)
+            remove(i);
+}
+
 void ProStringList::removeDuplicates()
 {
     int n = size();
@@ -357,10 +402,43 @@ void ProStringList::removeDuplicates()
         erase(begin() + j, end());
 }
 
+ProStringList::ProStringList(const QStringList &list)
+{
+    reserve(list.size());
+    foreach (const QString &str, list)
+        *this << ProString(str);
+}
+
+QStringList ProStringList::toQStringList() const
+{
+    QStringList ret;
+    ret.reserve(size());
+    foreach (const ProString &str, *this)
+        ret << str.toQString();
+    return ret;
+}
+
+bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const
+{
+    for (int i = 0; i < size(); i++)
+        if (!at(i).compare(str, cs))
+            return true;
+    return false;
+}
+
+bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const
+{
+    for (int i = 0; i < size(); i++)
+        if (!at(i).compare(str, cs))
+            return true;
+    return false;
+}
+
 ProFile::ProFile(const QString &fileName)
     : m_refCount(1),
       m_fileName(fileName),
-      m_ok(true)
+      m_ok(true),
+      m_hostBuild(false)
 {
     if (!fileName.startsWith(QLatin1Char('(')))
         m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory!
index 144dda6..402bd78 100644 (file)
 #ifndef PROITEMS_H
 #define PROITEMS_H
 
-#include "proparser_global.h"
-#include <QtCore/QString>
-#include <QtCore/QVector>
+#include "qmake_global.h"
+
+#include <qstring.h>
+#include <qvector.h>
+#include <qhash.h>
 
 QT_BEGIN_NAMESPACE
 
+class QTextStream;
+
 #ifdef PROPARSER_THREAD_SAFE
 typedef QAtomicInt ProItemRefCount;
 #else
@@ -62,10 +66,13 @@ private:
 };
 #endif
 
-namespace ProStringConstants {
-enum OmitPreHashing { NoHash };
-}
+#ifndef QT_BUILD_QMAKE
+#  define PROITEM_EXPLICIT explicit
+#else
+#  define PROITEM_EXPLICIT
+#endif
 
+class ProKey;
 class ProStringList;
 class ProFile;
 
@@ -73,67 +80,188 @@ class ProString {
 public:
     ProString();
     ProString(const ProString &other);
-    ProString(const ProString &other, ProStringConstants::OmitPreHashing);
-    explicit ProString(const QString &str);
-    ProString(const QString &str, ProStringConstants::OmitPreHashing);
-    explicit ProString(const char *str);
-    ProString(const char *str, ProStringConstants::OmitPreHashing);
+    PROITEM_EXPLICIT ProString(const QString &str);
+    PROITEM_EXPLICIT ProString(const char *str);
     ProString(const QString &str, int offset, int length);
-    ProString(const QString &str, int offset, int length, uint hash);
-    ProString(const QString &str, int offset, int length, ProStringConstants::OmitPreHashing);
     void setValue(const QString &str);
-    void setValue(const QString &str, ProStringConstants::OmitPreHashing);
+    void clear() { m_string.clear(); m_length = 0; }
     ProString &setSource(const ProString &other) { m_file = other.m_file; return *this; }
     ProString &setSource(const ProFile *pro) { m_file = pro; return *this; }
     const ProFile *sourceFile() const { return m_file; }
-    QString toQString() const;
-    QString &toQString(QString &tmp) const;
-    ProString &operator+=(const ProString &other);
+
+    ProString &prepend(const ProString &other);
     ProString &append(const ProString &other, bool *pending = 0);
+    ProString &append(const QString &other) { return append(ProString(other)); }
+    ProString &append(const QLatin1String other);
+    ProString &append(const char *other) { return append(QLatin1String(other)); }
+    ProString &append(QChar other);
     ProString &append(const ProStringList &other, bool *pending = 0, bool skipEmpty1st = false);
-    bool operator==(const ProString &other) const;
-    bool operator==(const QString &other) const;
-    bool operator==(const QLatin1String &other) const;
+    ProString &operator+=(const ProString &other) { return append(other); }
+    ProString &operator+=(const QString &other) { return append(other); }
+    ProString &operator+=(const QLatin1String other) { return append(other); }
+    ProString &operator+=(const char *other) { return append(other); }
+    ProString &operator+=(QChar other) { return append(other); }
+
+    void chop(int n) { Q_ASSERT(n <= m_length); m_length -= n; }
+    void chopFront(int n) { Q_ASSERT(n <= m_length); m_offset += n; m_length -= n; }
+
+    bool operator==(const ProString &other) const { return toQStringRef() == other.toQStringRef(); }
+    bool operator==(const QString &other) const { return toQStringRef() == other; }
+    bool operator==(QLatin1String other) const  { return toQStringRef() == other; }
+    bool operator==(const char *other) const { return toQStringRef() == QLatin1String(other); }
     bool operator!=(const ProString &other) const { return !(*this == other); }
     bool operator!=(const QString &other) const { return !(*this == other); }
-    bool operator!=(const QLatin1String &other) const { return !(*this == other); }
+    bool operator!=(QLatin1String other) const { return !(*this == other); }
+    bool operator!=(const char *other) const { return !(*this == other); }
+    bool isNull() const { return m_string.isNull(); }
     bool isEmpty() const { return !m_length; }
+    int length() const { return m_length; }
     int size() const { return m_length; }
+    QChar at(int i) const { Q_ASSERT((uint)i < (uint)m_length); return constData()[i]; }
     const QChar *constData() const { return m_string.constData() + m_offset; }
     ProString mid(int off, int len = -1) const;
     ProString left(int len) const { return mid(0, len); }
     ProString right(int len) const { return mid(qMax(0, size() - len)); }
     ProString trimmed() const;
-    void clear() { m_string.clear(); m_length = 0; }
+    int compare(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(sub.toQStringRef(), cs); }
+    int compare(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(sub, cs); }
+    int compare(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(QLatin1String(sub), cs); }
+    bool startsWith(const ProString &sub) const { return toQStringRef().startsWith(sub.toQStringRef()); }
+    bool startsWith(const QString &sub) const { return toQStringRef().startsWith(sub); }
+    bool startsWith(const char *sub) const { return toQStringRef().startsWith(QLatin1String(sub)); }
+    bool startsWith(QChar c) const { return toQStringRef().startsWith(c); }
+    bool endsWith(const ProString &sub) const { return toQStringRef().endsWith(sub.toQStringRef()); }
+    bool endsWith(const QString &sub) const { return toQStringRef().endsWith(sub); }
+    bool endsWith(const char *sub) const { return toQStringRef().endsWith(QLatin1String(sub)); }
+    bool endsWith(QChar c) const { return toQStringRef().endsWith(c); }
+    int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(s, from, cs); }
+    int indexOf(const char *s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(QLatin1String(s), from, cs); }
+    int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(c, from, cs); }
+    int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(s, from, cs); }
+    int lastIndexOf(const char *s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(QLatin1String(s), from, cs); }
+    int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(c, from, cs); }
+    bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(s, 0, cs) >= 0; }
+    bool contains(const char *s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(QLatin1String(s), 0, cs) >= 0; }
+    bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(c, 0, cs) >= 0; }
+    int toInt(bool *ok = 0) const { return toQString().toInt(ok); } // XXX optimize
+    short toShort(bool *ok = 0) const { return toQString().toShort(ok); } // XXX optimize
 
     static uint hash(const QChar *p, int n);
 
+    ALWAYS_INLINE QStringRef toQStringRef() const { return QStringRef(&m_string, m_offset, m_length); }
+
+    ALWAYS_INLINE ProKey &toKey() { return *(ProKey *)this; }
+    ALWAYS_INLINE const ProKey &toKey() const { return *(const ProKey *)this; }
+
+    QString toQString() const;
+    QString &toQString(QString &tmp) const;
+
+    QByteArray toLatin1() const { return toQStringRef().toLatin1(); }
+
 private:
+    ProString(const ProKey &other);
+    ProString &operator=(const ProKey &other);
+
+    enum OmitPreHashing { NoHash };
+    ProString(const ProString &other, OmitPreHashing);
+
+    enum DoPreHashing { DoHash };
+    ALWAYS_INLINE ProString(const QString &str, DoPreHashing);
+    ALWAYS_INLINE ProString(const char *str, DoPreHashing);
+    ALWAYS_INLINE ProString(const QString &str, int offset, int length, DoPreHashing);
+    ALWAYS_INLINE ProString(const QString &str, int offset, int length, uint hash);
+
     QString m_string;
     int m_offset, m_length;
     const ProFile *m_file;
     mutable uint m_hash;
-    QChar *prepareAppend(int extraLen);
+    QChar *prepareExtend(int extraLen, int thisTarget, int extraTarget);
     uint updatedHash() const;
     friend uint qHash(const ProString &str);
     friend QString operator+(const ProString &one, const ProString &two);
+    friend class ProKey;
 };
 Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE);
 
+class ProKey : public ProString {
+public:
+    ALWAYS_INLINE ProKey() : ProString() {}
+    explicit ProKey(const QString &str);
+    PROITEM_EXPLICIT ProKey(const char *str);
+    ProKey(const QString &str, int off, int len);
+    ProKey(const QString &str, int off, int len, uint hash);
+    void setValue(const QString &str);
+
+#ifdef Q_CC_MSVC
+    // Workaround strange MSVC behaviour when exporting classes with ProKey members.
+    ALWAYS_INLINE ProKey(const ProKey &other) : ProString(other.toString()) {}
+    ALWAYS_INLINE ProKey &operator=(const ProKey &other)
+    {
+        toString() = other.toString();
+        return *this;
+    }
+#endif
+
+    ALWAYS_INLINE ProString &toString() { return *(ProString *)this; }
+    ALWAYS_INLINE const ProString &toString() const { return *(const ProString *)this; }
+
+private:
+    ProKey(const ProString &other);
+};
+Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE);
+
 uint qHash(const ProString &str);
 QString operator+(const ProString &one, const ProString &two);
 inline QString operator+(const ProString &one, const QString &two)
-    { return one + ProString(two, ProStringConstants::NoHash); }
+    { return one + ProString(two); }
 inline QString operator+(const QString &one, const ProString &two)
-    { return ProString(one, ProStringConstants::NoHash) + two; }
+    { return ProString(one) + two; }
+
+inline QString operator+(const ProString &one, const char *two)
+    { return one + ProString(two); } // XXX optimize
+inline QString operator+(const char *one, const ProString &two)
+    { return ProString(one) + two; } // XXX optimize
+
+inline QString &operator+=(QString &that, const ProString &other)
+    { return that += other.toQStringRef(); }
+
+inline bool operator==(const QString &that, const ProString &other)
+    { return other == that; }
+inline bool operator!=(const QString &that, const ProString &other)
+    { return !(other == that); }
+
+QTextStream &operator<<(QTextStream &t, const ProString &str);
 
 class ProStringList : public QVector<ProString> {
 public:
     ProStringList() {}
     ProStringList(const ProString &str) { *this << str; }
+    explicit ProStringList(const QStringList &list);
+    QStringList toQStringList() const;
+
+    ProStringList &operator<<(const ProString &str)
+        { QVector<ProString>::operator<<(str); return *this; }
+
+    int length() const { return size(); }
+
     QString join(const QString &sep) const;
+
+    void removeAll(const ProString &str);
+    void removeAll(const char *str);
+    void removeAt(int idx) { remove(idx); }
     void removeDuplicates();
+
+    bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+    bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+        { return contains(ProString(str), cs); }
+    bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
 };
+Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE);
+
+inline ProStringList operator+(const ProStringList &one, const ProStringList &two)
+    { ProStringList ret = one; ret += two; return ret; }
+
+typedef QHash<ProKey, ProStringList> ProValueMap;
 
 // These token definitions affect both ProFileEvaluator and ProWriter
 enum ProToken {
@@ -160,6 +288,7 @@ enum ProToken {
                         // - name length (1)
                         // - name (name length; unterminated)
     TokProperty,        // qmake property expansion
+                        // - hash (2)
                         // - name length (1)
                         // - name (name length; unterminated)
     TokEnvVar,          // environment variable expansion
@@ -200,7 +329,7 @@ enum ProToken {
     TokNewStr = 0x200   // Next stringlist element
 };
 
-class PROPARSER_EXPORT ProFile
+class QMAKE_EXPORT ProFile
 {
 public:
     explicit ProFile(const QString &fileName);
@@ -218,12 +347,45 @@ public:
     bool isOk() const { return m_ok; }
     void setOk(bool ok) { m_ok = ok; }
 
+    bool isHostBuild() const { return m_hostBuild; }
+    void setHostBuild(bool host_build) { m_hostBuild = host_build; }
+
 private:
     ProItemRefCount m_refCount;
     QString m_proitems;
     QString m_fileName;
     QString m_directoryName;
     bool m_ok;
+    bool m_hostBuild;
+};
+
+class ProFunctionDef {
+public:
+    ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); }
+    ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
+    ~ProFunctionDef() { m_pro->deref(); }
+    ProFunctionDef &operator=(const ProFunctionDef &o)
+    {
+        if (this != &o) {
+            m_pro->deref();
+            m_pro = o.m_pro;
+            m_pro->ref();
+            m_offset = o.m_offset;
+        }
+        return *this;
+    }
+    ProFile *pro() const { return m_pro; }
+    const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; }
+private:
+    ProFile *m_pro;
+    int m_offset;
+};
+
+Q_DECLARE_TYPEINFO(ProFunctionDef, Q_MOVABLE_TYPE);
+
+struct ProFunctionDefs {
+    QHash<ProKey, ProFunctionDef> testFunctions;
+    QHash<ProKey, ProFunctionDef> replaceFunctions;
 };
 
 QT_END_NAMESPACE
index 829c8cd..69e09c8 100644 (file)
@@ -4,14 +4,20 @@ INCLUDEPATH *= $$PWD
 DEFINES += PROEVALUATOR_CUMULATIVE PROEVALUATOR_INIT_PROPS
 
 HEADERS += \
-    $$PWD/proparser_global.h \
+    $$PWD/qmake_global.h \
     $$PWD/ioutils.h \
     $$PWD/proitems.h \
-    $$PWD/profileparser.h \
+    $$PWD/qmakeglobals.h \
+    $$PWD/qmakeparser.h \
+    $$PWD/qmakeevaluator.h \
+    $$PWD/qmakeevaluator_p.h \
     $$PWD/profileevaluator.h
 
 SOURCES += \
     $$PWD/ioutils.cpp \
     $$PWD/proitems.cpp \
-    $$PWD/profileparser.cpp \
+    $$PWD/qmakeglobals.cpp \
+    $$PWD/qmakeparser.cpp \
+    $$PWD/qmakeevaluator.cpp \
+    $$PWD/qmakebuiltins.cpp \
     $$PWD/profileevaluator.cpp
similarity index 74%
rename from src/linguist/shared/proparser_global.h
rename to src/linguist/shared/qmake_global.h
index 2a8c243..f794a98 100644 (file)
 **
 ****************************************************************************/
 
-#ifndef PROPARSER_GLOBAL_H
-#define PROPARSER_GLOBAL_H
+#ifndef QMAKE_GLOBAL_H
+#define QMAKE_GLOBAL_H
 
-#include <QtCore/qglobal.h>
+#include <qglobal.h>
 
-#if defined(PROPARSER_AS_LIBRARY)
-#  if defined(PROPARSER_LIBRARY)
-#    define PROPARSER_EXPORT Q_DECL_EXPORT
+#if defined(QMAKE_AS_LIBRARY)
+#  if defined(QMAKE_LIBRARY)
+#    define QMAKE_EXPORT Q_DECL_EXPORT
 #  else
-#    define PROPARSER_EXPORT Q_DECL_IMPORT
+#    define QMAKE_EXPORT Q_DECL_IMPORT
 #  endif
 #else
-#  define PROPARSER_EXPORT
+#  define QMAKE_EXPORT
+#endif
+
+// Be fast even for debug builds
+// MinGW GCC 4.5+ has a problem with always_inline putTok and putBlockLen
+#if defined(__GNUC__) && !(defined(__MINGW32__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define ALWAYS_INLINE inline __attribute__((always_inline))
+#elif defined(_MSC_VER)
+# define ALWAYS_INLINE __forceinline
+#else
+# define ALWAYS_INLINE inline
 #endif
 
 #endif
diff --git a/src/linguist/shared/qmakebuiltins.cpp b/src/linguist/shared/qmakebuiltins.cpp
new file mode 100644 (file)
index 0000000..97a92d8
--- /dev/null
@@ -0,0 +1,1669 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeevaluator.h"
+
+#include "qmakeevaluator_p.h"
+#include "qmakeglobals.h"
+#include "qmakeparser.h"
+#include "ioutils.h"
+
+#include <qbytearray.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qregexp.h>
+#include <qset.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+
+#ifdef Q_OS_UNIX
+#include <time.h>
+#include <utime.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#else
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#define QT_PCLOSE _pclose
+#else
+#define QT_POPEN popen
+#define QT_PCLOSE pclose
+#endif
+
+using namespace QMakeInternal;
+
+QT_BEGIN_NAMESPACE
+
+#define fL1S(s) QString::fromLatin1(s)
+
+enum ExpandFunc {
+    E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
+    E_SPRINTF, E_FORMAT_NUMBER, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
+    E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND,
+    E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE,
+    E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS,
+    E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH,
+    E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE
+};
+
+enum TestFunc {
+    T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
+    T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
+    T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
+    T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
+    T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE
+};
+
+void QMakeEvaluator::initFunctionStatics()
+{
+    static const struct {
+        const char * const name;
+        const ExpandFunc func;
+    } expandInits[] = {
+        { "member", E_MEMBER },
+        { "first", E_FIRST },
+        { "last", E_LAST },
+        { "size", E_SIZE },
+        { "cat", E_CAT },
+        { "fromfile", E_FROMFILE },
+        { "eval", E_EVAL },
+        { "list", E_LIST },
+        { "sprintf", E_SPRINTF },
+        { "format_number", E_FORMAT_NUMBER },
+        { "join", E_JOIN },
+        { "split", E_SPLIT },
+        { "basename", E_BASENAME },
+        { "dirname", E_DIRNAME },
+        { "section", E_SECTION },
+        { "find", E_FIND },
+        { "system", E_SYSTEM },
+        { "unique", E_UNIQUE },
+        { "reverse", E_REVERSE },
+        { "quote", E_QUOTE },
+        { "escape_expand", E_ESCAPE_EXPAND },
+        { "upper", E_UPPER },
+        { "lower", E_LOWER },
+        { "re_escape", E_RE_ESCAPE },
+        { "val_escape", E_VAL_ESCAPE },
+        { "files", E_FILES },
+        { "prompt", E_PROMPT },
+        { "replace", E_REPLACE },
+        { "sort_depends", E_SORT_DEPENDS },
+        { "resolve_depends", E_RESOLVE_DEPENDS },
+        { "enumerate_vars", E_ENUMERATE_VARS },
+        { "shadowed", E_SHADOWED },
+        { "absolute_path", E_ABSOLUTE_PATH },
+        { "relative_path", E_RELATIVE_PATH },
+        { "clean_path", E_CLEAN_PATH },
+        { "system_path", E_SYSTEM_PATH },
+        { "shell_path", E_SHELL_PATH },
+        { "system_quote", E_SYSTEM_QUOTE },
+        { "shell_quote", E_SHELL_QUOTE },
+    };
+    for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
+        statics.expands.insert(ProKey(expandInits[i].name), expandInits[i].func);
+
+    static const struct {
+        const char * const name;
+        const TestFunc func;
+    } testInits[] = {
+        { "requires", T_REQUIRES },
+        { "greaterThan", T_GREATERTHAN },
+        { "lessThan", T_LESSTHAN },
+        { "equals", T_EQUALS },
+        { "isEqual", T_EQUALS },
+        { "exists", T_EXISTS },
+        { "export", T_EXPORT },
+        { "clear", T_CLEAR },
+        { "unset", T_UNSET },
+        { "eval", T_EVAL },
+        { "CONFIG", T_CONFIG },
+        { "if", T_IF },
+        { "isActiveConfig", T_CONFIG },
+        { "system", T_SYSTEM },
+        { "return", T_RETURN },
+        { "break", T_BREAK },
+        { "next", T_NEXT },
+        { "defined", T_DEFINED },
+        { "contains", T_CONTAINS },
+        { "infile", T_INFILE },
+        { "count", T_COUNT },
+        { "isEmpty", T_ISEMPTY },
+        { "load", T_LOAD },
+        { "include", T_INCLUDE },
+        { "debug", T_DEBUG },
+        { "log", T_LOG },
+        { "message", T_MESSAGE },
+        { "warning", T_WARNING },
+        { "error", T_ERROR },
+        { "mkpath", T_MKPATH },
+        { "write_file", T_WRITE_FILE },
+        { "touch", T_TOUCH },
+        { "cache", T_CACHE },
+    };
+    for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
+        statics.functions.insert(ProKey(testInits[i].name), testInits[i].func);
+}
+
+static bool isTrue(const ProString &_str, QString &tmp)
+{
+    const QString &str = _str.toQString(tmp);
+    return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
+}
+
+#ifdef Q_OS_WIN
+static QString windowsErrorCode()
+{
+    wchar_t *string = 0;
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+                  NULL,
+                  GetLastError(),
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (LPWSTR)&string,
+                  0,
+                  NULL);
+    QString ret = QString::fromWCharArray(string);
+    LocalFree((HLOCAL)string);
+    return ret;
+}
+#endif
+
+static QString
+quoteValue(const ProString &val)
+{
+    QString ret;
+    ret.reserve(val.size());
+    const QChar *chars = val.constData();
+    bool quote = val.isEmpty();
+    bool escaping = false;
+    for (int i = 0, l = val.size(); i < l; i++) {
+        QChar c = chars[i];
+        ushort uc = c.unicode();
+        if (uc < 32) {
+            if (!escaping) {
+                escaping = true;
+                ret += QLatin1String("$$escape_expand(");
+            }
+            switch (uc) {
+            case '\r':
+                ret += QLatin1String("\\\\r");
+                break;
+            case '\n':
+                ret += QLatin1String("\\\\n");
+                break;
+            case '\t':
+                ret += QLatin1String("\\\\t");
+                break;
+            default:
+                ret += QString::fromLatin1("\\\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
+                break;
+            }
+        } else {
+            if (escaping) {
+                escaping = false;
+                ret += QLatin1Char(')');
+            }
+            switch (uc) {
+            case '\\':
+                ret += QLatin1String("\\\\");
+                break;
+            case '"':
+                ret += QLatin1String("\\\"");
+                break;
+            case '\'':
+                ret += QLatin1String("\\'");
+                break;
+            case '$':
+                ret += QLatin1String("\\$");
+                break;
+            case '#':
+                ret += QLatin1String("$${LITERAL_HASH}");
+                break;
+            case 32:
+                quote = true;
+                // fallthrough
+            default:
+                ret += c;
+                break;
+            }
+        }
+    }
+    if (escaping)
+        ret += QLatin1Char(')');
+    if (quote) {
+        ret.prepend(QLatin1Char('"'));
+        ret.append(QLatin1Char('"'));
+    }
+    return ret;
+}
+
+static bool
+doWriteFile(const QString &name, QIODevice::OpenMode mode, const QString &contents, QString *errStr)
+{
+    QByteArray bytes = contents.toLocal8Bit();
+    QFile cfile(name);
+    if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        if (cfile.readAll() == bytes)
+            return true;
+        cfile.close();
+    }
+    if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
+        *errStr = cfile.errorString();
+        return false;
+    }
+    cfile.write(bytes);
+    cfile.close();
+    if (cfile.error() != QFile::NoError) {
+        *errStr = cfile.errorString();
+        return false;
+    }
+    return true;
+}
+
+QMakeEvaluator::VisitReturn
+QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
+                          const QString &contents)
+{
+    QFileInfo qfi(fn);
+    if (!QDir::current().mkpath(qfi.path())) {
+        evalError(fL1S("Cannot create %1directory %2.")
+                  .arg(ctx, QDir::toNativeSeparators(qfi.path())));
+        return ReturnFalse;
+    }
+    QString errStr;
+    if (!doWriteFile(qfi.filePath(), mode, contents, &errStr)) {
+        evalError(fL1S("Cannot write %1file %2: %3.")
+                  .arg(ctx, QDir::toNativeSeparators(qfi.filePath()), errStr));
+        return ReturnFalse;
+    }
+    return ReturnTrue;
+}
+
+#ifndef QT_BOOTSTRAPPED
+void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
+{
+    proc->setWorkingDirectory(currentDirectory());
+# ifdef PROEVALUATOR_SETENV
+    if (!m_option->environment.isEmpty())
+        proc->setProcessEnvironment(m_option->environment);
+# endif
+# ifdef Q_OS_WIN
+    proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
+    proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
+# else
+    proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
+# endif
+    proc->waitForFinished(-1);
+}
+#endif
+
+QByteArray QMakeEvaluator::getCommandOutput(const QString &args) const
+{
+    QByteArray out;
+#ifndef QT_BOOTSTRAPPED
+    QProcess proc;
+    runProcess(&proc, args);
+    QByteArray errout = proc.readAllStandardError();
+# ifdef PROEVALUATOR_FULL
+    // FIXME: Qt really should have the option to set forwarding per channel
+    fputs(errout.constData(), stderr);
+# else
+    if (!errout.isEmpty()) {
+        if (errout.endsWith('\n'))
+            errout.chop(1);
+        m_handler->message(QMakeHandler::EvalError, QString::fromLocal8Bit(errout));
+    }
+# endif
+    out = proc.readAllStandardOutput();
+# ifdef Q_OS_WIN
+    // FIXME: Qt's line end conversion on sequential files should really be fixed
+    out.replace("\r\n", "\n");
+# endif
+#else
+    if (FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
+                               + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
+                               + QLatin1String(" && ") + args).toLocal8Bit().constData(), "r")) {
+        while (!feof(proc)) {
+            char buff[10 * 1024];
+            int read_in = int(fread(buff, 1, sizeof(buff), proc));
+            if (!read_in)
+                break;
+            out += QByteArray(buff, read_in);
+        }
+        QT_PCLOSE(proc);
+    }
+#endif
+    return out;
+}
+
+void QMakeEvaluator::populateDeps(
+        const ProStringList &deps, const ProString &prefix,
+        QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees,
+        ProStringList &rootSet) const
+{
+    foreach (const ProString &item, deps)
+        if (!dependencies.contains(item.toKey())) {
+            QSet<ProKey> &dset = dependencies[item.toKey()]; // Always create entry
+            ProStringList depends = values(ProKey(prefix + item + QString::fromLatin1(".depends")));
+            if (depends.isEmpty()) {
+                rootSet << item;
+            } else {
+                foreach (const ProString &dep, depends) {
+                    dset.insert(dep.toKey());
+                    dependees[dep.toKey()] << item;
+                }
+                populateDeps(depends, prefix, dependencies, dependees, rootSet);
+            }
+        }
+}
+
+ProStringList QMakeEvaluator::evaluateBuiltinExpand(
+        int func_t, const ProKey &func, const ProStringList &args)
+{
+    ProStringList ret;
+
+    traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args));
+
+    switch (func_t) {
+    case E_BASENAME:
+    case E_DIRNAME:
+    case E_SECTION: {
+        bool regexp = false;
+        QString sep;
+        ProString var;
+        int beg = 0;
+        int end = -1;
+        if (func_t == E_SECTION) {
+            if (args.count() != 3 && args.count() != 4) {
+                evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
+                               " three or four arguments.").arg(func.toQString(m_tmp1)));
+            } else {
+                var = args[0];
+                sep = args.at(1).toQString();
+                beg = args.at(2).toQString(m_tmp2).toInt();
+                if (args.count() == 4)
+                    end = args.at(3).toQString(m_tmp2).toInt();
+            }
+        } else {
+            if (args.count() != 1) {
+                evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
+            } else {
+                var = args[0];
+                regexp = true;
+                sep = QLatin1String("[\\\\/]");
+                if (func_t == E_DIRNAME)
+                    end = -2;
+                else
+                    beg = -1;
+            }
+        }
+        if (!var.isEmpty()) {
+            if (regexp) {
+                QRegExp sepRx(sep);
+                foreach (const ProString &str, values(map(var))) {
+                    const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
+                    ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str));
+                }
+            } else {
+                foreach (const ProString &str, values(map(var))) {
+                    const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
+                    ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str));
+                }
+            }
+        }
+        break;
+    }
+    case E_SPRINTF:
+        if (args.count() < 1) {
+            evalError(fL1S("sprintf(format, ...) requires at least one argument."));
+        } else {
+            QString tmp = args.at(0).toQString(m_tmp1);
+            for (int i = 1; i < args.count(); ++i)
+                tmp = tmp.arg(args.at(i).toQString(m_tmp2));
+            // Note: this depends on split_value_list() making a deep copy
+            ret = split_value_list(tmp);
+        }
+        break;
+    case E_FORMAT_NUMBER:
+        if (args.count() > 2) {
+            evalError(fL1S("format_number(number[, options...]) requires one or two arguments."));
+        } else {
+            int ibase = 10;
+            int obase = 10;
+            int width = 0;
+            bool zeropad = false;
+            bool leftalign = false;
+            enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign;
+            if (args.count() >= 2) {
+                foreach (const ProString &opt, split_value_list(args.at(1).toQString(m_tmp2))) {
+                    opt.toQString(m_tmp3);
+                    if (m_tmp3.startsWith(QLatin1String("ibase="))) {
+                        ibase = m_tmp3.mid(6).toInt();
+                    } else if (m_tmp3.startsWith(QLatin1String("obase="))) {
+                        obase = m_tmp3.mid(6).toInt();
+                    } else if (m_tmp3.startsWith(QLatin1String("width="))) {
+                        width = m_tmp3.mid(6).toInt();
+                    } else if (m_tmp3 == QLatin1String("zeropad")) {
+                        zeropad = true;
+                    } else if (m_tmp3 == QLatin1String("padsign")) {
+                        sign = PadSign;
+                    } else if (m_tmp3 == QLatin1String("alwayssign")) {
+                        sign = AlwaysSign;
+                    } else if (m_tmp3 == QLatin1String("leftalign")) {
+                        leftalign = true;
+                    } else {
+                        evalError(fL1S("format_number(): invalid format option %1.").arg(m_tmp3));
+                        goto formfail;
+                    }
+                }
+            }
+            args.at(0).toQString(m_tmp3);
+            if (m_tmp3.contains(QLatin1Char('.'))) {
+                evalError(fL1S("format_number(): floats are currently not supported."));
+                break;
+            }
+            bool ok;
+            qlonglong num = m_tmp3.toLongLong(&ok, ibase);
+            if (!ok) {
+                evalError(fL1S("format_number(): malformed number %2 for base %1.")
+                          .arg(ibase).arg(m_tmp3));
+                break;
+            }
+            QString outstr;
+            if (num < 0) {
+                num = -num;
+                outstr = QLatin1Char('-');
+            } else if (sign == AlwaysSign) {
+                outstr = QLatin1Char('+');
+            } else if (sign == PadSign) {
+                outstr = QLatin1Char(' ');
+            }
+            QString numstr = QString::number(num, obase);
+            int space = width - outstr.length() - numstr.length();
+            if (space <= 0) {
+                outstr += numstr;
+            } else if (leftalign) {
+                outstr += numstr + QString(space, QLatin1Char(' '));
+            } else if (zeropad) {
+                outstr += QString(space, QLatin1Char('0')) + numstr;
+            } else {
+                outstr.prepend(QString(space, QLatin1Char(' ')));
+                outstr += numstr;
+            }
+            ret += ProString(outstr);
+        }
+      formfail:
+        break;
+    case E_JOIN: {
+        if (args.count() < 1 || args.count() > 4) {
+            evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
+        } else {
+            QString glue;
+            ProString before, after;
+            if (args.count() >= 2)
+                glue = args.at(1).toQString(m_tmp1);
+            if (args.count() >= 3)
+                before = args[2];
+            if (args.count() == 4)
+                after = args[3];
+            const ProStringList &var = values(map(args.at(0)));
+            if (!var.isEmpty()) {
+                const ProFile *src = currentProFile();
+                foreach (const ProString &v, var)
+                    if (const ProFile *s = v.sourceFile()) {
+                        src = s;
+                        break;
+                    }
+                ret = split_value_list(before + var.join(glue) + after, src);
+            }
+        }
+        break;
+    }
+    case E_SPLIT:
+        if (args.count() < 1 || args.count() > 2) {
+            evalError(fL1S("split(var, sep) requires one or two arguments."));
+        } else {
+            const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
+            foreach (const ProString &var, values(map(args.at(0))))
+                foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
+                    ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt).setSource(var));
+        }
+        break;
+    case E_MEMBER:
+        if (args.count() < 1 || args.count() > 3) {
+            evalError(fL1S("member(var, start, end) requires one to three arguments."));
+        } else {
+            bool ok = true;
+            const ProStringList &var = values(map(args.at(0)));
+            int start = 0, end = 0;
+            if (args.count() >= 2) {
+                const QString &start_str = args.at(1).toQString(m_tmp1);
+                start = start_str.toInt(&ok);
+                if (!ok) {
+                    if (args.count() == 2) {
+                        int dotdot = start_str.indexOf(statics.strDotDot);
+                        if (dotdot != -1) {
+                            start = start_str.left(dotdot).toInt(&ok);
+                            if (ok)
+                                end = start_str.mid(dotdot+2).toInt(&ok);
+                        }
+                    }
+                    if (!ok)
+                        evalError(fL1S("member() argument 2 (start) '%2' invalid.")
+                                  .arg(start_str));
+                } else {
+                    end = start;
+                    if (args.count() == 3)
+                        end = args.at(2).toQString(m_tmp1).toInt(&ok);
+                    if (!ok)
+                        evalError(fL1S("member() argument 3 (end) '%2' invalid.")
+                                  .arg(args.at(2).toQString(m_tmp1)));
+                }
+            }
+            if (ok) {
+                if (start < 0)
+                    start += var.count();
+                if (end < 0)
+                    end += var.count();
+                if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
+                    //nothing
+                } else if (start < end) {
+                    for (int i = start; i <= end && var.count() >= i; i++)
+                        ret.append(var[i]);
+                } else {
+                    for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
+                        ret += var[i];
+                }
+            }
+        }
+        break;
+    case E_FIRST:
+    case E_LAST:
+        if (args.count() != 1) {
+            evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
+        } else {
+            const ProStringList &var = values(map(args.at(0)));
+            if (!var.isEmpty()) {
+                if (func_t == E_FIRST)
+                    ret.append(var[0]);
+                else
+                    ret.append(var.last());
+            }
+        }
+        break;
+    case E_SIZE:
+        if (args.count() != 1)
+            evalError(fL1S("size(var) requires one argument."));
+        else
+            ret.append(ProString(QString::number(values(map(args.at(0))).size())));
+        break;
+    case E_CAT:
+        if (args.count() < 1 || args.count() > 2) {
+            evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
+        } else {
+            const QString &file = args.at(0).toQString(m_tmp1);
+
+            bool blob = false;
+            bool lines = false;
+            bool singleLine = true;
+            if (args.count() > 1) {
+                args.at(1).toQString(m_tmp2);
+                if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive))
+                    singleLine = false;
+                else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive))
+                    blob = true;
+                else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive))
+                    lines = true;
+            }
+
+            QFile qfile(resolvePath(m_option->expandEnvVars(file)));
+            if (qfile.open(QIODevice::ReadOnly)) {
+                QTextStream stream(&qfile);
+                if (blob) {
+                    ret += ProString(stream.readAll());
+                } else {
+                    while (!stream.atEnd()) {
+                        if (lines) {
+                            ret += ProString(stream.readLine());
+                        } else {
+                            ret += split_value_list(stream.readLine().trimmed());
+                            if (!singleLine)
+                                ret += ProString("\n");
+                        }
+                    }
+                }
+            }
+        }
+        break;
+    case E_FROMFILE:
+        if (args.count() != 2) {
+            evalError(fL1S("fromfile(file, variable) requires two arguments."));
+        } else {
+            ProValueMap vars;
+            QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+            fn.detach();
+            if (evaluateFileInto(fn, &vars, LoadProOnly))
+                ret = vars.value(map(args.at(1)));
+        }
+        break;
+    case E_EVAL:
+        if (args.count() != 1) {
+            evalError(fL1S("eval(variable) requires one argument."));
+        } else {
+            ret += values(map(args.at(0)));
+        }
+        break;
+    case E_LIST: {
+        QString tmp;
+        tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
+        ret = ProStringList(ProString(tmp));
+        ProStringList lst;
+        foreach (const ProString &arg, args)
+            lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy
+        m_valuemapStack.top()[ret.at(0).toKey()] = lst;
+        break; }
+    case E_FIND:
+        if (args.count() != 2) {
+            evalError(fL1S("find(var, str) requires two arguments."));
+        } else {
+            QRegExp regx(args.at(1).toQString());
+            int t = 0;
+            foreach (const ProString &val, values(map(args.at(0)))) {
+                if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
+                    ret += val;
+                t ^= 1;
+            }
+        }
+        break;
+    case E_SYSTEM:
+        if (!m_skipLevel) {
+            if (args.count() < 1 || args.count() > 2) {
+                evalError(fL1S("system(execute) requires one or two arguments."));
+            } else {
+                bool blob = false;
+                bool lines = false;
+                bool singleLine = true;
+                if (args.count() > 1) {
+                    args.at(1).toQString(m_tmp2);
+                    if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive))
+                        singleLine = false;
+                    else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive))
+                        blob = true;
+                    else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive))
+                        lines = true;
+                }
+                QByteArray bytes = getCommandOutput(args.at(0).toQString(m_tmp2));
+                if (lines) {
+                    QTextStream stream(bytes);
+                    while (!stream.atEnd())
+                        ret += ProString(stream.readLine());
+                } else {
+                    QString output = QString::fromLocal8Bit(bytes);
+                    if (blob) {
+                        ret += ProString(output);
+                    } else {
+                        output.replace(QLatin1Char('\t'), QLatin1Char(' '));
+                        if (singleLine)
+                            output.replace(QLatin1Char('\n'), QLatin1Char(' '));
+                        ret += split_value_list(output);
+                    }
+                }
+            }
+        }
+        break;
+    case E_UNIQUE:
+        if (args.count() != 1) {
+            evalError(fL1S("unique(var) requires one argument."));
+        } else {
+            ret = values(map(args.at(0)));
+            ret.removeDuplicates();
+        }
+        break;
+    case E_REVERSE:
+        if (args.count() != 1) {
+            evalError(fL1S("reverse(var) requires one argument."));
+        } else {
+            ProStringList var = values(args.at(0).toKey());
+            for (int i = 0; i < var.size() / 2; i++)
+                qSwap(var[i], var[var.size() - i - 1]);
+            ret += var;
+        }
+        break;
+    case E_QUOTE:
+        ret += args;
+        break;
+    case E_ESCAPE_EXPAND:
+        for (int i = 0; i < args.size(); ++i) {
+            QString str = args.at(i).toQString();
+            QChar *i_data = str.data();
+            int i_len = str.length();
+            for (int x = 0; x < i_len; ++x) {
+                if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
+                    if (*(i_data+x+1) == QLatin1Char('\\')) {
+                        ++x;
+                    } else {
+                        struct {
+                            char in, out;
+                        } mapped_quotes[] = {
+                            { 'n', '\n' },
+                            { 't', '\t' },
+                            { 'r', '\r' },
+                            { 0, 0 }
+                        };
+                        for (int i = 0; mapped_quotes[i].in; ++i) {
+                            if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
+                                *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
+                                if (x < i_len-2)
+                                    memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
+                                --i_len;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            ret.append(ProString(QString(i_data, i_len)).setSource(args.at(i)));
+        }
+        break;
+    case E_RE_ESCAPE:
+        for (int i = 0; i < args.size(); ++i) {
+            const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i)));
+        }
+        break;
+    case E_VAL_ESCAPE:
+        if (args.count() != 1) {
+            evalError(fL1S("val_escape(var) requires one argument."));
+        } else {
+            const ProStringList &vals = values(args.at(0).toKey());
+            ret.reserve(vals.size());
+            foreach (const ProString &str, vals)
+                ret += ProString(quoteValue(str));
+        }
+        break;
+    case E_UPPER:
+    case E_LOWER:
+        for (int i = 0; i < args.count(); ++i) {
+            QString rstr = args.at(i).toQString(m_tmp1);
+            rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i)));
+        }
+        break;
+    case E_FILES:
+        if (args.count() != 1 && args.count() != 2) {
+            evalError(fL1S("files(pattern, recursive=false) requires one or two arguments."));
+        } else {
+            bool recursive = false;
+            if (args.count() == 2)
+                recursive = isTrue(args.at(1), m_tmp2);
+            QStringList dirs;
+            QString r = m_option->expandEnvVars(args.at(0).toQString(m_tmp1))
+                        .replace(QLatin1Char('\\'), QLatin1Char('/'));
+            QString pfx;
+            if (IoUtils::isRelativePath(r)) {
+                pfx = currentDirectory();
+                if (!pfx.endsWith(QLatin1Char('/')))
+                    pfx += QLatin1Char('/');
+            }
+            int slash = r.lastIndexOf(QLatin1Char('/'));
+            if (slash != -1) {
+                dirs.append(r.left(slash+1));
+                r = r.mid(slash+1);
+            } else {
+                dirs.append(QString());
+            }
+
+            r.detach(); // Keep m_tmp out of QRegExp's cache
+            QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
+            for (int d = 0; d < dirs.count(); d++) {
+                QString dir = dirs[d];
+                QDir qdir(pfx + dir);
+                for (int i = 0; i < (int)qdir.count(); ++i) {
+                    if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
+                        continue;
+                    QString fname = dir + qdir[i];
+                    if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
+                        if (recursive)
+                            dirs.append(fname + QLatin1Char('/'));
+                    }
+                    if (regex.exactMatch(qdir[i]))
+                        ret += ProString(fname).setSource(currentProFile());
+                }
+            }
+        }
+        break;
+#ifdef PROEVALUATOR_FULL
+    case E_PROMPT: {
+        if (args.count() != 1) {
+            evalError(fL1S("prompt(question) requires one argument."));
+//        } else if (currentFileName() == QLatin1String("-")) {
+//            evalError(fL1S("prompt(question) cannot be used when '-o -' is used"));
+        } else {
+            QString msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp1));
+            if (!msg.endsWith(QLatin1Char('?')))
+                msg += QLatin1Char('?');
+            fprintf(stderr, "Project PROMPT: %s ", qPrintable(msg));
+
+            QFile qfile;
+            if (qfile.open(stdin, QIODevice::ReadOnly)) {
+                QTextStream t(&qfile);
+                ret = split_value_list(t.readLine());
+            }
+        }
+        break; }
+#endif
+    case E_REPLACE:
+        if (args.count() != 3 ) {
+            evalError(fL1S("replace(var, before, after) requires three arguments."));
+        } else {
+            const QRegExp before(args.at(1).toQString());
+            const QString &after(args.at(2).toQString(m_tmp2));
+            foreach (const ProString &val, values(map(args.at(0)))) {
+                QString rstr = val.toQString(m_tmp1);
+                QString copy = rstr; // Force a detach on modify
+                rstr.replace(before, after);
+                ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr).setSource(val));
+            }
+        }
+        break;
+    case E_SORT_DEPENDS:
+    case E_RESOLVE_DEPENDS:
+        if (args.count() < 1 || args.count() > 2) {
+            evalError(fL1S("%1(var, prefix) requires one or two arguments.").arg(func.toQString(m_tmp1)));
+        } else {
+            QHash<ProKey, QSet<ProKey> > dependencies;
+            ProValueMap dependees;
+            ProStringList rootSet;
+            ProStringList orgList = values(args.at(0).toKey());
+            populateDeps(orgList, (args.count() < 2 ? ProString() : args.at(1)),
+                         dependencies, dependees, rootSet);
+            for (int i = 0; i < rootSet.size(); ++i) {
+                const ProString &item = rootSet.at(i);
+                if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item))
+                    ret.prepend(item);
+                foreach (const ProString &dep, dependees[item.toKey()]) {
+                    QSet<ProKey> &dset = dependencies[dep.toKey()];
+                    dset.remove(rootSet.at(i).toKey()); // *Don't* use 'item' - rootSet may have changed!
+                    if (dset.isEmpty())
+                        rootSet << dep;
+                }
+            }
+        }
+        break;
+    case E_ENUMERATE_VARS: {
+        QSet<ProString> keys;
+        foreach (const ProValueMap &vmap, m_valuemapStack)
+            for (ProValueMap::ConstIterator it = vmap.constBegin(); it != vmap.constEnd(); ++it)
+                keys.insert(it.key());
+        ret.reserve(keys.size());
+        foreach (const ProString &key, keys)
+            ret << key;
+        break; }
+    case E_SHADOWED:
+        if (args.count() != 1) {
+            evalError(fL1S("shadowed(path) requires one argument."));
+        } else {
+            QString rstr = m_option->shadowedPath(resolvePath(args.at(0).toQString(m_tmp1)));
+            if (rstr.isEmpty())
+                break;
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_ABSOLUTE_PATH:
+        if (args.count() > 2) {
+            evalError(fL1S("absolute_path(path[, base]) requires one or two arguments."));
+        } else {
+            QString rstr = QDir::cleanPath(
+                    QDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory())
+                    .absoluteFilePath(args.at(0).toQString(m_tmp1)));
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_RELATIVE_PATH:
+        if (args.count() > 2) {
+            evalError(fL1S("relative_path(path[, base]) requires one or two arguments."));
+        } else {
+            QDir baseDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory());
+            QString rstr = baseDir.relativeFilePath(baseDir.absoluteFilePath(
+                                args.at(0).toQString(m_tmp1)));
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_CLEAN_PATH:
+        if (args.count() != 1) {
+            evalError(fL1S("clean_path(path) requires one argument."));
+        } else {
+            QString rstr = QDir::cleanPath(args.at(0).toQString(m_tmp1));
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_SYSTEM_PATH:
+        if (args.count() != 1) {
+            evalError(fL1S("system_path(path) requires one argument."));
+        } else {
+            QString rstr = args.at(0).toQString(m_tmp1);
+#ifdef Q_OS_WIN
+            rstr.replace(QLatin1Char('/'), QLatin1Char('\\'));
+#else
+            rstr.replace(QLatin1Char('\\'), QLatin1Char('/'));
+#endif
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_SHELL_PATH:
+        if (args.count() != 1) {
+            evalError(fL1S("shell_path(path) requires one argument."));
+        } else {
+            QString rstr = args.at(0).toQString(m_tmp1);
+            if (m_dirSep.startsWith(QLatin1Char('\\')))
+                rstr.replace(QLatin1Char('/'), QLatin1Char('\\'));
+            else
+                rstr.replace(QLatin1Char('\\'), QLatin1Char('/'));
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_SYSTEM_QUOTE:
+        if (args.count() != 1) {
+            evalError(fL1S("system_quote(arg) requires one argument."));
+        } else {
+            QString rstr = IoUtils::shellQuote(args.at(0).toQString(m_tmp1));
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    case E_SHELL_QUOTE:
+        if (args.count() != 1) {
+            evalError(fL1S("shell_quote(arg) requires one argument."));
+        } else {
+            QString rstr = args.at(0).toQString(m_tmp1);
+            if (m_dirSep.startsWith(QLatin1Char('\\')))
+                rstr = IoUtils::shellQuoteWin(rstr);
+            else
+                rstr = IoUtils::shellQuoteUnix(rstr);
+            ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0)));
+        }
+        break;
+    default:
+        evalError(fL1S("Function '%1' is not implemented.").arg(func.toQString(m_tmp1)));
+        break;
+    }
+
+    return ret;
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
+        int func_t, const ProKey &function, const ProStringList &args)
+{
+    traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args));
+
+    switch (func_t) {
+    case T_DEFINED: {
+        if (args.count() < 1 || args.count() > 2) {
+            evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
+                           " requires one or two arguments."));
+            return ReturnFalse;
+        }
+        const ProKey &var = args.at(0).toKey();
+        if (args.count() > 1) {
+            if (args[1] == QLatin1String("test")) {
+                return returnBool(m_functionDefs.testFunctions.contains(var));
+            } else if (args[1] == QLatin1String("replace")) {
+                return returnBool(m_functionDefs.replaceFunctions.contains(var));
+            } else if (args[1] == QLatin1String("var")) {
+                ProValueMap::Iterator it;
+                return returnBool(findValues(var, &it));
+            }
+            evalError(fL1S("defined(function, type): unexpected type [%1].")
+                      .arg(args.at(1).toQString(m_tmp1)));
+            return ReturnFalse;
+        }
+        return returnBool(m_functionDefs.replaceFunctions.contains(var)
+                          || m_functionDefs.testFunctions.contains(var));
+    }
+    case T_RETURN:
+        m_returnValue = args;
+        // It is "safe" to ignore returns - due to qmake brokeness
+        // they cannot be used to terminate loops anyway.
+        if (m_cumulative)
+            return ReturnTrue;
+        if (m_valuemapStack.size() == 1) {
+            evalError(fL1S("unexpected return()."));
+            return ReturnFalse;
+        }
+        return ReturnReturn;
+    case T_EXPORT: {
+        if (args.count() != 1) {
+            evalError(fL1S("export(variable) requires one argument."));
+            return ReturnFalse;
+        }
+        const ProKey &var = map(args.at(0));
+        for (ProValueMapStack::Iterator vmi = m_valuemapStack.end();
+             --vmi != m_valuemapStack.begin(); ) {
+            ProValueMap::Iterator it = (*vmi).find(var);
+            if (it != (*vmi).end()) {
+                if (it->constBegin() == statics.fakeValue.constBegin()) {
+                    // This is stupid, but qmake doesn't propagate deletions
+                    m_valuemapStack.first()[var] = ProStringList();
+                } else {
+                    m_valuemapStack.first()[var] = *it;
+                }
+                (*vmi).erase(it);
+                while (--vmi != m_valuemapStack.begin())
+                    (*vmi).remove(var);
+                break;
+            }
+        }
+        return ReturnTrue;
+    }
+    case T_INFILE:
+        if (args.count() < 2 || args.count() > 3) {
+            evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
+        } else {
+            ProValueMap vars;
+            QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+            fn.detach();
+            if (!evaluateFileInto(fn, &vars, LoadProOnly))
+                return ReturnFalse;
+            if (args.count() == 2)
+                return returnBool(vars.contains(map(args.at(1))));
+            QRegExp regx;
+            const QString &qry = args.at(2).toQString(m_tmp1);
+            if (qry != QRegExp::escape(qry)) {
+                QString copy = qry;
+                copy.detach();
+                regx.setPattern(copy);
+            }
+            int t = 0;
+            foreach (const ProString &s, vars.value(map(args.at(1)))) {
+                if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
+                    return ReturnTrue;
+                t ^= 1;
+            }
+        }
+        return ReturnFalse;
+#ifdef PROEVALUATOR_FULL
+    case T_REQUIRES:
+        checkRequirements(args);
+        return ReturnFalse; // Another qmake breakage
+#endif
+    case T_EVAL: {
+            VisitReturn ret = ReturnFalse;
+            ProFile *pro = m_parser->parsedProBlock(args.join(statics.field_sep),
+                                                    m_current.pro->fileName(), m_current.line);
+            if (pro) {
+                if (m_cumulative || pro->isOk()) {
+                    m_locationStack.push(m_current);
+                    visitProBlock(pro, pro->tokPtr());
+                    ret = ReturnTrue; // This return value is not too useful, but that's qmake
+                    m_current = m_locationStack.pop();
+                }
+                pro->deref();
+            }
+            return ret;
+        }
+    case T_BREAK:
+        if (m_skipLevel)
+            return ReturnFalse;
+        if (m_loopLevel)
+            return ReturnBreak;
+        evalError(fL1S("Unexpected break()."));
+        return ReturnFalse;
+    case T_NEXT:
+        if (m_skipLevel)
+            return ReturnFalse;
+        if (m_loopLevel)
+            return ReturnNext;
+        evalError(fL1S("Unexpected next()."));
+        return ReturnFalse;
+    case T_IF: {
+        if (args.count() != 1) {
+            evalError(fL1S("if(condition) requires one argument."));
+            return ReturnFalse;
+        }
+        return returnBool(evaluateConditional(args.at(0).toQString(),
+                                              m_current.pro->fileName(), m_current.line));
+    }
+    case T_CONFIG: {
+        if (args.count() < 1 || args.count() > 2) {
+            evalError(fL1S("CONFIG(config) requires one or two arguments."));
+            return ReturnFalse;
+        }
+        if (args.count() == 1)
+            return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
+        const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
+        const ProStringList &configs = values(statics.strCONFIG);
+
+        for (int i = configs.size() - 1; i >= 0; i--) {
+            for (int mut = 0; mut < mutuals.count(); mut++) {
+                if (configs[i] == mutuals[mut].trimmed()) {
+                    return returnBool(configs[i] == args[0]);
+                }
+            }
+        }
+        return ReturnFalse;
+    }
+    case T_CONTAINS: {
+        if (args.count() < 2 || args.count() > 3) {
+            evalError(fL1S("contains(var, val) requires two or three arguments."));
+            return ReturnFalse;
+        }
+
+        const QString &qry = args.at(1).toQString(m_tmp1);
+        QRegExp regx;
+        if (qry != QRegExp::escape(qry)) {
+            QString copy = qry;
+            copy.detach();
+            regx.setPattern(copy);
+        }
+        const ProStringList &l = values(map(args.at(0)));
+        if (args.count() == 2) {
+            int t = 0;
+            for (int i = 0; i < l.size(); ++i) {
+                const ProString &val = l[i];
+                if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
+                    return ReturnTrue;
+                t ^= 1;
+            }
+        } else {
+            const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
+            for (int i = l.size() - 1; i >= 0; i--) {
+                const ProString val = l[i];
+                for (int mut = 0; mut < mutuals.count(); mut++) {
+                    if (val == mutuals[mut].trimmed()) {
+                        return returnBool((!regx.isEmpty()
+                                           && regx.exactMatch(val.toQString(m_tmp2)))
+                                          || val == qry);
+                    }
+                }
+            }
+        }
+        return ReturnFalse;
+    }
+    case T_COUNT: {
+        if (args.count() != 2 && args.count() != 3) {
+            evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
+            return ReturnFalse;
+        }
+        int cnt = values(map(args.at(0))).count();
+        if (args.count() == 3) {
+            const ProString &comp = args.at(2);
+            const int val = args.at(1).toQString(m_tmp1).toInt();
+            if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
+                return returnBool(cnt > val);
+            } else if (comp == QLatin1String(">=")) {
+                return returnBool(cnt >= val);
+            } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
+                return returnBool(cnt < val);
+            } else if (comp == QLatin1String("<=")) {
+                return returnBool(cnt <= val);
+            } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
+                       || comp == QLatin1String("=") || comp == QLatin1String("==")) {
+                return returnBool(cnt == val);
+            } else {
+                evalError(fL1S("Unexpected modifier to count(%2).").arg(comp.toQString(m_tmp1)));
+                return ReturnFalse;
+            }
+        }
+        return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
+    }
+    case T_GREATERTHAN:
+    case T_LESSTHAN: {
+        if (args.count() != 2) {
+            evalError(fL1S("%1(variable, value) requires two arguments.")
+                      .arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
+        }
+        const QString &rhs(args.at(1).toQString(m_tmp1)),
+                      &lhs(values(map(args.at(0))).join(statics.field_sep));
+        bool ok;
+        int rhs_int = rhs.toInt(&ok);
+        if (ok) { // do integer compare
+            int lhs_int = lhs.toInt(&ok);
+            if (ok) {
+                if (func_t == T_GREATERTHAN)
+                    return returnBool(lhs_int > rhs_int);
+                return returnBool(lhs_int < rhs_int);
+            }
+        }
+        if (func_t == T_GREATERTHAN)
+            return returnBool(lhs > rhs);
+        return returnBool(lhs < rhs);
+    }
+    case T_EQUALS:
+        if (args.count() != 2) {
+            evalError(fL1S("%1(variable, value) requires two arguments.")
+                      .arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
+        }
+        return returnBool(values(map(args.at(0))).join(statics.field_sep)
+                          == args.at(1).toQString(m_tmp1));
+    case T_CLEAR: {
+        if (args.count() != 1) {
+            evalError(fL1S("%1(variable) requires one argument.")
+                      .arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
+        }
+        ProValueMap *hsh;
+        ProValueMap::Iterator it;
+        const ProKey &var = map(args.at(0));
+        if (!(hsh = findValues(var, &it)))
+            return ReturnFalse;
+        if (hsh == &m_valuemapStack.top())
+            it->clear();
+        else
+            m_valuemapStack.top()[var].clear();
+        return ReturnTrue;
+    }
+    case T_UNSET: {
+        if (args.count() != 1) {
+            evalError(fL1S("%1(variable) requires one argument.")
+                      .arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
+        }
+        ProValueMap *hsh;
+        ProValueMap::Iterator it;
+        const ProKey &var = map(args.at(0));
+        if (!(hsh = findValues(var, &it)))
+            return ReturnFalse;
+        if (m_valuemapStack.size() == 1)
+            hsh->erase(it);
+        else if (hsh == &m_valuemapStack.top())
+            *it = statics.fakeValue;
+        else
+            m_valuemapStack.top()[var] = statics.fakeValue;
+        return ReturnTrue;
+    }
+    case T_INCLUDE: {
+        if (args.count() < 1 || args.count() > 3) {
+            evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments."));
+            return ReturnFalse;
+        }
+        QString parseInto;
+        LoadFlags flags = 0;
+        if (args.count() >= 2) {
+            parseInto = args.at(1).toQString(m_tmp2);
+            if (args.count() >= 3 && isTrue(args.at(2), m_tmp3))
+                flags = LoadSilent;
+        }
+        QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+        fn.detach();
+        bool ok;
+        if (parseInto.isEmpty()) {
+            ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags);
+        } else {
+            ProValueMap symbols;
+            if ((ok = evaluateFileInto(fn, &symbols, LoadAll | flags))) {
+                ProValueMap newMap;
+                for (ProValueMap::ConstIterator
+                        it = m_valuemapStack.top().constBegin(),
+                        end = m_valuemapStack.top().constEnd();
+                        it != end; ++it) {
+                    const QString &ky = it.key().toQString(m_tmp1);
+                    if (!(ky.startsWith(parseInto) &&
+                          (ky.length() == parseInto.length()
+                           || ky.at(parseInto.length()) == QLatin1Char('.'))))
+                        newMap[it.key()] = it.value();
+                }
+                for (ProValueMap::ConstIterator it = symbols.constBegin();
+                     it != symbols.constEnd(); ++it) {
+                    const QString &ky = it.key().toQString(m_tmp1);
+                    if (!ky.startsWith(QLatin1Char('.')))
+                        newMap.insert(ProKey(parseInto + QLatin1Char('.') + ky), it.value());
+                }
+                m_valuemapStack.top() = newMap;
+            }
+        }
+        return returnBool(ok || (flags & LoadSilent));
+    }
+    case T_LOAD: {
+        bool ignore_error = false;
+        if (args.count() == 2) {
+            ignore_error = isTrue(args.at(1), m_tmp2);
+        } else if (args.count() != 1) {
+            evalError(fL1S("load(feature) requires one or two arguments."));
+            return ReturnFalse;
+        }
+        return returnBool(evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()),
+                                              ignore_error) || ignore_error);
+    }
+    case T_DEBUG: {
+#ifdef PROEVALUATOR_DEBUG
+        if (args.count() != 2) {
+            evalError(fL1S("debug(level, message) requires two arguments."));
+            return ReturnFalse;
+        }
+        int level = args.at(0).toInt();
+        if (level <= m_debugLevel) {
+            const QString &msg = m_option->expandEnvVars(args.at(1).toQString(m_tmp2));
+            debugMsg(level, "Project DEBUG: %s", qPrintable(msg));
+        }
+#endif
+        return ReturnTrue;
+    }
+    case T_LOG:
+    case T_ERROR:
+    case T_WARNING:
+    case T_MESSAGE: {
+        if (args.count() != 1) {
+            evalError(fL1S("%1(message) requires one argument.")
+                      .arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
+        }
+        const QString &msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp2));
+        if (!m_skipLevel) {
+            if (func_t == T_LOG) {
+#ifdef PROEVALUATOR_FULL
+                fputs(msg.toLatin1().constData(), stderr);
+#endif
+            } else {
+                m_handler->fileMessage(fL1S("Project %1: %2")
+                                       .arg(function.toQString(m_tmp1).toUpper(), msg));
+            }
+        }
+        return (func_t == T_ERROR && !m_cumulative) ? ReturnError : ReturnTrue;
+    }
+#ifdef PROEVALUATOR_FULL
+    case T_SYSTEM: {
+        if (m_cumulative) // Anything else would be insanity
+            return ReturnFalse;
+        if (args.count() != 1) {
+            evalError(fL1S("system(exec) requires one argument."));
+            return ReturnFalse;
+        }
+#ifndef QT_BOOTSTRAPPED
+        QProcess proc;
+        proc.setProcessChannelMode(QProcess::ForwardedChannels);
+        runProcess(&proc, args.at(0).toQString(m_tmp2));
+        return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
+#else
+        return returnBool(system((QLatin1String("cd ")
+                                  + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
+                                  + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
+#endif
+    }
+#endif
+    case T_ISEMPTY: {
+        if (args.count() != 1) {
+            evalError(fL1S("isEmpty(var) requires one argument."));
+            return ReturnFalse;
+        }
+        return returnBool(values(map(args.at(0))).isEmpty());
+    }
+    case T_EXISTS: {
+        if (args.count() != 1) {
+            evalError(fL1S("exists(file) requires one argument."));
+            return ReturnFalse;
+        }
+        const QString &file = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+
+        if (IoUtils::exists(file)) {
+            return ReturnTrue;
+        }
+        int slsh = file.lastIndexOf(QLatin1Char('/'));
+        QString fn = file.mid(slsh+1);
+        if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
+            QString dirstr = file.left(slsh+1);
+            if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
+                return ReturnTrue;
+        }
+
+        return ReturnFalse;
+    }
+#ifdef PROEVALUATOR_FULL
+    case T_MKPATH: {
+        if (args.count() != 1) {
+            evalError(fL1S("mkpath(file) requires one argument."));
+            return ReturnFalse;
+        }
+        const QString &fn = resolvePath(args.at(0).toQString(m_tmp1));
+        if (!QDir::current().mkpath(fn)) {
+            evalError(fL1S("Cannot create directory %1.").arg(QDir::toNativeSeparators(fn)));
+            return ReturnFalse;
+        }
+        return ReturnTrue;
+    }
+    case T_WRITE_FILE: {
+        if (args.count() > 3) {
+            evalError(fL1S("write_file(name, [content var, [append]]) requires one to three arguments."));
+            return ReturnFalse;
+        }
+        QIODevice::OpenMode mode = QIODevice::Truncate;
+        QString contents;
+        if (args.count() >= 2) {
+            const ProStringList &vals = values(args.at(1).toKey());
+            if (!vals.isEmpty())
+                contents = vals.join(fL1S("\n")) + QLatin1Char('\n');
+            if (args.count() >= 3)
+                if (!args.at(2).toQString(m_tmp1).compare(fL1S("append"), Qt::CaseInsensitive))
+                    mode = QIODevice::Append;
+        }
+        return writeFile(QString(), resolvePath(args.at(0).toQString(m_tmp1)), mode, contents);
+    }
+    case T_TOUCH: {
+        if (args.count() != 2) {
+            evalError(fL1S("touch(file, reffile) requires two arguments."));
+            return ReturnFalse;
+        }
+        const QString &tfn = resolvePath(args.at(0).toQString(m_tmp1));
+        const QString &rfn = resolvePath(args.at(1).toQString(m_tmp2));
+#ifdef Q_OS_UNIX
+        struct stat st;
+        if (stat(rfn.toLocal8Bit().constData(), &st)) {
+            evalError(fL1S("Cannot stat() reference file %1: %2.").arg(rfn, fL1S(strerror(errno))));
+            return ReturnFalse;
+        }
+        struct utimbuf utb;
+        utb.actime = time(0);
+        utb.modtime = st.st_mtime;
+        if (utime(tfn.toLocal8Bit().constData(), &utb)) {
+            evalError(fL1S("Cannot touch %1: %2.").arg(tfn, fL1S(strerror(errno))));
+            return ReturnFalse;
+        }
+#else
+        HANDLE rHand = CreateFile((wchar_t*)rfn.utf16(),
+                                  GENERIC_READ, FILE_SHARE_READ,
+                                  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+        if (rHand == INVALID_HANDLE_VALUE) {
+            evalError(fL1S("Cannot open() reference file %1: %2.").arg(rfn, windowsErrorCode()));
+            return ReturnFalse;
+        }
+        FILETIME ft;
+        GetFileTime(rHand, 0, 0, &ft);
+        CloseHandle(rHand);
+        HANDLE wHand = CreateFile((wchar_t*)tfn.utf16(),
+                                  GENERIC_WRITE, FILE_SHARE_READ,
+                                  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+        if (wHand == INVALID_HANDLE_VALUE) {
+            evalError(fL1S("Cannot open() %1: %2.").arg(tfn, windowsErrorCode()));
+            return ReturnFalse;
+        }
+        SetFileTime(wHand, 0, 0, &ft);
+        CloseHandle(wHand);
+#endif
+        return ReturnTrue;
+    }
+    case T_CACHE: {
+        if (args.count() > 3) {
+            evalError(fL1S("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments."));
+            return ReturnFalse;
+        }
+        bool persist = true;
+        bool super = false;
+        enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
+        ProKey srcvar;
+        if (args.count() >= 2) {
+            foreach (const ProString &opt, split_value_list(args.at(1).toQString(m_tmp2))) {
+                opt.toQString(m_tmp3);
+                if (m_tmp3 == QLatin1String("transient")) {
+                    persist = false;
+                } else if (m_tmp3 == QLatin1String("super")) {
+                    super = true;
+                } else if (m_tmp3 == QLatin1String("set")) {
+                    mode = CacheSet;
+                } else if (m_tmp3 == QLatin1String("add")) {
+                    mode = CacheAdd;
+                } else if (m_tmp3 == QLatin1String("sub")) {
+                    mode = CacheSub;
+                } else {
+                    evalError(fL1S("cache(): invalid flag %1.").arg(m_tmp3));
+                    return ReturnFalse;
+                }
+            }
+            if (args.count() >= 3) {
+                srcvar = args.at(2).toKey();
+            } else if (mode != CacheSet) {
+                evalError(fL1S("cache(): modes other than 'set' require a source variable."));
+                return ReturnFalse;
+            }
+        }
+        QString varstr;
+        ProKey dstvar = args.at(0).toKey();
+        if (!dstvar.isEmpty()) {
+            if (srcvar.isEmpty())
+                srcvar = dstvar;
+            ProValueMap::Iterator srcvarIt;
+            if (!findValues(srcvar, &srcvarIt)) {
+                evalError(fL1S("Variable %1 is not defined.").arg(srcvar.toQString(m_tmp1)));
+                return ReturnFalse;
+            }
+            // The caches for the host and target may differ (e.g., when we are manipulating
+            // CONFIG), so we cannot compute a common new value for both.
+            const ProStringList &diffval = *srcvarIt;
+            ProStringList newval;
+            bool changed = false;
+            for (bool hostBuild = false; ; hostBuild = true) {
+                if (QMakeBaseEnv *baseEnv = m_option->baseEnvs.value(
+                            QMakeBaseKey(m_buildRoot, hostBuild))) {
+                    QMakeEvaluator *baseEval = baseEnv->evaluator;
+                    const ProStringList &oldval = baseEval->values(dstvar);
+                    if (mode == CacheSet) {
+                        newval = diffval;
+                    } else {
+                        newval = oldval;
+                        if (mode == CacheAdd)
+                            newval += diffval;
+                        else
+                            removeEach(&newval, diffval);
+                    }
+                    if (oldval != newval) {
+                        baseEval->valuesRef(dstvar) = newval;
+                        if (super) {
+                            do {
+                                if (dstvar == QLatin1String("QMAKEPATH")) {
+                                    baseEval->m_qmakepath = newval.toQStringList();
+                                    baseEval->updateMkspecPaths();
+                                } else if (dstvar == QLatin1String("QMAKEFEATURES")) {
+                                    baseEval->m_qmakefeatures = newval.toQStringList();
+                                } else {
+                                    break;
+                                }
+                                baseEval->updateFeaturePaths();
+                                if (hostBuild == m_hostBuild)
+                                    m_featureRoots = baseEval->m_featureRoots;
+                            } while (false);
+                        }
+                        changed = true;
+                    }
+                }
+                if (hostBuild)
+                    break;
+            }
+            // We assume that whatever got the cached value to be what it is now will do so
+            // the next time as well, so we just skip the persisting if nothing changed.
+            if (!persist || !changed)
+                return ReturnTrue;
+            varstr = dstvar.toQString();
+            if (mode == CacheAdd)
+                varstr += QLatin1String(" +=");
+            else if (mode == CacheSub)
+                varstr += QLatin1String(" -=");
+            else
+                varstr += QLatin1String(" =");
+            if (diffval.count() == 1) {
+                varstr += QLatin1Char(' ');
+                varstr += quoteValue(diffval.at(0));
+            } else if (!diffval.isEmpty()) {
+                foreach (const ProString &vval, diffval) {
+                    varstr += QLatin1String(" \\\n    ");
+                    varstr += quoteValue(vval);
+                }
+            }
+            varstr += QLatin1Char('\n');
+        }
+        QString fn;
+        if (super) {
+            if (m_superfile.isEmpty()) {
+                m_superfile = m_outputDir + QLatin1String("/.qmake.super");
+                printf("Info: creating super cache file %s\n", qPrintable(m_superfile));
+                valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
+            }
+            fn = m_superfile;
+        } else {
+            if (m_cachefile.isEmpty()) {
+                m_cachefile = m_outputDir + QLatin1String("/.qmake.cache");
+                printf("Info: creating cache file %s\n", qPrintable(m_cachefile));
+                valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
+                // We could update m_{source,build}Root and m_featureRoots here, or even
+                // "re-home" our rootEnv, but this doesn't sound too useful - if somebody
+                // wanted qmake to find something in the build directory, he could have
+                // done so "from the outside".
+                // The sub-projects will find the new cache all by themselves.
+            }
+            fn = m_cachefile;
+        }
+        return writeFile(fL1S("cache "), fn, QIODevice::Append, varstr);
+    }
+#endif
+    default:
+        evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1)));
+        return ReturnFalse;
+    }
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/qmakeevaluator.cpp b/src/linguist/shared/qmakeevaluator.cpp
new file mode 100644 (file)
index 0000000..9e91c08
--- /dev/null
@@ -0,0 +1,1993 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeevaluator.h"
+
+#include "qmakeglobals.h"
+#include "qmakeparser.h"
+#include "qmakeevaluator_p.h"
+#include "ioutils.h"
+
+#include <qbytearray.h>
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qregexp.h>
+#include <qset.h>
+#include <qstack.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qthreadpool.h>
+#endif
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#include <sys/utsname.h>
+#else
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace QMakeInternal;
+
+QT_BEGIN_NAMESPACE
+
+#define fL1S(s) QString::fromLatin1(s)
+
+
+QMakeBaseKey::QMakeBaseKey(const QString &_root, bool _hostBuild)
+    : root(_root), hostBuild(_hostBuild)
+{
+}
+
+uint qHash(const QMakeBaseKey &key)
+{
+    return qHash(key.root) ^ (uint)key.hostBuild;
+}
+
+bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
+{
+    return one.root == two.root && one.hostBuild == two.hostBuild;
+}
+
+QMakeBaseEnv::QMakeBaseEnv()
+    : evaluator(0)
+{
+#ifdef PROEVALUATOR_THREAD_SAFE
+    inProgress = false;
+#endif
+}
+
+QMakeBaseEnv::~QMakeBaseEnv()
+{
+    delete evaluator;
+}
+
+namespace QMakeInternal {
+QMakeStatics statics;
+}
+
+void QMakeEvaluator::initStatics()
+{
+    if (!statics.field_sep.isNull())
+        return;
+
+    statics.field_sep = QLatin1String(" ");
+    statics.strtrue = QLatin1String("true");
+    statics.strfalse = QLatin1String("false");
+    statics.strCONFIG = ProKey("CONFIG");
+    statics.strARGS = ProKey("ARGS");
+    statics.strDot = QLatin1String(".");
+    statics.strDotDot = QLatin1String("..");
+    statics.strever = QLatin1String("ever");
+    statics.strforever = QLatin1String("forever");
+    statics.strhost_build = QLatin1String("host_build");
+    statics.strTEMPLATE = ProKey("TEMPLATE");
+#ifdef PROEVALUATOR_FULL
+    statics.strREQUIRES = ProKey("REQUIRES");
+#endif
+
+    statics.fakeValue = ProStringList(ProString("_FAKE_")); // It has to have a unique begin() value
+
+    initFunctionStatics();
+
+    static const struct {
+        const char * const oldname, * const newname;
+    } mapInits[] = {
+        { "INTERFACES", "FORMS" },
+        { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
+        { "TARGETDEPS", "POST_TARGETDEPS" },
+        { "LIBPATH", "QMAKE_LIBDIR" },
+        { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
+        { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
+        { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
+        { "PRECOMPH", "PRECOMPILED_HEADER" },
+        { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
+        { "INCPATH", "INCLUDEPATH" },
+        { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
+        { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
+        { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
+        { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
+        { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
+        { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
+        { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
+        { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
+        { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" },
+        { "IN_PWD", "PWD" }
+    };
+    for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
+        statics.varMap.insert(ProKey(mapInits[i].oldname), ProKey(mapInits[i].newname));
+}
+
+const ProKey &QMakeEvaluator::map(const ProKey &var)
+{
+    QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(var);
+    if (it == statics.varMap.constEnd())
+        return var;
+    deprecationWarning(fL1S("Variable %1 is deprecated; use %2 instead.")
+                       .arg(var.toQString(), it.value().toQString()));
+    return it.value();
+}
+
+
+QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option,
+                               QMakeParser *parser, QMakeHandler *handler)
+  :
+#ifdef PROEVALUATOR_DEBUG
+    m_debugLevel(option->debugLevel),
+#endif
+    m_option(option), m_parser(parser), m_handler(handler)
+{
+    // So that single-threaded apps don't have to call initialize() for now.
+    initStatics();
+
+    // Configuration, more or less
+    m_caller = 0;
+#ifdef PROEVALUATOR_CUMULATIVE
+    m_cumulative = false;
+#endif
+    m_hostBuild = false;
+
+    // Evaluator state
+#ifdef PROEVALUATOR_CUMULATIVE
+    m_skipLevel = 0;
+#endif
+    m_loopLevel = 0;
+    m_listCount = 0;
+    m_valuemapStack.push(ProValueMap());
+    m_valuemapInited = false;
+}
+
+QMakeEvaluator::~QMakeEvaluator()
+{
+}
+
+void QMakeEvaluator::initFrom(const QMakeEvaluator &other)
+{
+    Q_ASSERT_X(&other, "QMakeEvaluator::visitProFile", "Project not prepared");
+    m_functionDefs = other.m_functionDefs;
+    m_valuemapStack = other.m_valuemapStack;
+    m_valuemapInited = true;
+    m_qmakespec = other.m_qmakespec;
+    m_qmakespecFull = other.m_qmakespecFull;
+    m_qmakespecName = other.m_qmakespecName;
+    m_mkspecPaths = other.m_mkspecPaths;
+    m_featureRoots = other.m_featureRoots;
+    m_dirSep = other.m_dirSep;
+}
+
+//////// Evaluator tools /////////
+
+uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr)
+{
+    uint len = *tokPtr++;
+    len |= (uint)*tokPtr++ << 16;
+    return len;
+}
+
+ProString QMakeEvaluator::getStr(const ushort *&tokPtr)
+{
+    uint len = *tokPtr++;
+    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len);
+    ret.setSource(m_current.pro);
+    tokPtr += len;
+    return ret;
+}
+
+ProKey QMakeEvaluator::getHashStr(const ushort *&tokPtr)
+{
+    uint hash = getBlockLen(tokPtr);
+    uint len = *tokPtr++;
+    ProKey ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
+    tokPtr += len;
+    return ret;
+}
+
+void QMakeEvaluator::skipStr(const ushort *&tokPtr)
+{
+    uint len = *tokPtr++;
+    tokPtr += len;
+}
+
+void QMakeEvaluator::skipHashStr(const ushort *&tokPtr)
+{
+    tokPtr += 2;
+    uint len = *tokPtr++;
+    tokPtr += len;
+}
+
+// FIXME: this should not build new strings for direct sections.
+// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
+ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFile *source)
+{
+    QString build;
+    ProStringList ret;
+    QStack<char> quote;
+
+    const ushort SPACE = ' ';
+    const ushort LPAREN = '(';
+    const ushort RPAREN = ')';
+    const ushort SINGLEQUOTE = '\'';
+    const ushort DOUBLEQUOTE = '"';
+    const ushort BACKSLASH = '\\';
+
+    if (!source)
+        source = currentProFile();
+
+    ushort unicode;
+    const QChar *vals_data = vals.data();
+    const int vals_len = vals.length();
+    int parens = 0;
+    for (int x = 0; x < vals_len; x++) {
+        unicode = vals_data[x].unicode();
+        if (x != (int)vals_len-1 && unicode == BACKSLASH &&
+            (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
+            build += vals_data[x++]; //get that 'escape'
+        } else if (!quote.isEmpty() && unicode == quote.top()) {
+            quote.pop();
+        } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
+            quote.push(unicode);
+        } else if (unicode == RPAREN) {
+            --parens;
+        } else if (unicode == LPAREN) {
+            ++parens;
+        }
+
+        if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
+            ret << ProString(build).setSource(source);
+            build.clear();
+        } else {
+            build += vals_data[x];
+        }
+    }
+    if (!build.isEmpty())
+        ret << ProString(build).setSource(source);
+    if (parens)
+        deprecationWarning(fL1S("Unmatched parentheses are deprecated."));
+    return ret;
+}
+
+static void zipEmpty(ProStringList *value)
+{
+    for (int i = value->size(); --i >= 0;)
+        if (value->at(i).isEmpty())
+            value->remove(i);
+}
+
+static void insertUnique(ProStringList *varlist, const ProStringList &value)
+{
+    foreach (const ProString &str, value)
+        if (!str.isEmpty() && !varlist->contains(str))
+            varlist->append(str);
+}
+
+static void removeAll(ProStringList *varlist, const ProString &value)
+{
+    for (int i = varlist->size(); --i >= 0; )
+        if (varlist->at(i) == value)
+            varlist->remove(i);
+}
+
+void QMakeEvaluator::removeEach(ProStringList *varlist, const ProStringList &value)
+{
+    foreach (const ProString &str, value)
+        if (!str.isEmpty())
+            removeAll(varlist, str);
+}
+
+static void replaceInList(ProStringList *varlist,
+        const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
+{
+    for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
+        QString val = varit->toQString(tmp);
+        QString copy = val; // Force detach and have a reference value
+        val.replace(regexp, replace);
+        if (!val.isSharedWith(copy) && val != copy) {
+            if (val.isEmpty()) {
+                varit = varlist->erase(varit);
+            } else {
+                (*varit).setValue(val);
+                ++varit;
+            }
+            if (!global)
+                break;
+        } else {
+            ++varit;
+        }
+    }
+}
+
+//////// Evaluator /////////
+
+static ALWAYS_INLINE void addStr(
+        const ProString &str, ProStringList *ret, bool &pending, bool joined)
+{
+    if (joined) {
+        ret->last().append(str, &pending);
+    } else {
+        if (!pending) {
+            pending = true;
+            *ret << str;
+        } else {
+            ret->last().append(str);
+        }
+    }
+}
+
+static ALWAYS_INLINE void addStrList(
+        const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
+{
+    if (!list.isEmpty()) {
+        if (joined) {
+            ret->last().append(list, &pending, !(tok & TokQuoted));
+        } else {
+            if (tok & TokQuoted) {
+                if (!pending) {
+                    pending = true;
+                    *ret << ProString();
+                }
+                ret->last().append(list);
+            } else {
+                if (!pending) {
+                    // Another qmake bizzarity: if nothing is pending and the
+                    // first element is empty, it will be eaten
+                    if (!list.at(0).isEmpty()) {
+                        // The common case
+                        pending = true;
+                        *ret += list;
+                        return;
+                    }
+                } else {
+                    ret->last().append(list.at(0));
+                }
+                // This is somewhat slow, but a corner case
+                for (int j = 1; j < list.size(); ++j) {
+                    pending = true;
+                    *ret << list.at(j);
+                }
+            }
+        }
+    }
+}
+
+void QMakeEvaluator::evaluateExpression(
+        const ushort *&tokPtr, ProStringList *ret, bool joined)
+{
+    debugMsg(2, joined ? "evaluating joined expression" : "evaluating expression");
+    if (joined)
+        *ret << ProString();
+    bool pending = false;
+    forever {
+        ushort tok = *tokPtr++;
+        if (tok & TokNewStr) {
+            debugMsg(2, "new string");
+            pending = false;
+        }
+        ushort maskedTok = tok & TokMask;
+        switch (maskedTok) {
+        case TokLine:
+            m_current.line = *tokPtr++;
+            break;
+        case TokLiteral: {
+            const ProString &val = getStr(tokPtr);
+            debugMsg(2, "literal %s", dbgStr(val));
+            addStr(val, ret, pending, joined);
+            break; }
+        case TokHashLiteral: {
+            const ProKey &val = getHashStr(tokPtr);
+            debugMsg(2, "hashed literal %s", dbgStr(val.toString()));
+            addStr(val, ret, pending, joined);
+            break; }
+        case TokVariable: {
+            const ProKey &var = getHashStr(tokPtr);
+            const ProStringList &val = values(map(var));
+            debugMsg(2, "variable %s => %s", dbgKey(var), dbgStrList(val));
+            addStrList(val, tok, ret, pending, joined);
+            break; }
+        case TokProperty: {
+            const ProKey &var = getHashStr(tokPtr);
+            const ProString &val = propertyValue(var);
+            debugMsg(2, "property %s => %s", dbgKey(var), dbgStr(val));
+            addStr(val, ret, pending, joined);
+            break; }
+        case TokEnvVar: {
+            const ProString &var = getStr(tokPtr);
+            const ProStringList &val = split_value_list(m_option->getEnv(var.toQString(m_tmp1)));
+            debugMsg(2, "env var %s => %s", dbgStr(var), dbgStrList(val));
+            addStrList(val, tok, ret, pending, joined);
+            break; }
+        case TokFuncName: {
+            const ProKey &func = getHashStr(tokPtr);
+            debugMsg(2, "function %s", dbgKey(func));
+            addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
+            break; }
+        default:
+            debugMsg(2, "evaluated expression => %s", dbgStrList(*ret));
+            tokPtr--;
+            return;
+        }
+    }
+}
+
+void QMakeEvaluator::skipExpression(const ushort *&pTokPtr)
+{
+    const ushort *tokPtr = pTokPtr;
+    forever {
+        ushort tok = *tokPtr++;
+        switch (tok) {
+        case TokLine:
+            m_current.line = *tokPtr++;
+            break;
+        case TokValueTerminator:
+        case TokFuncTerminator:
+            pTokPtr = tokPtr;
+            return;
+        case TokArgSeparator:
+            break;
+        default:
+            switch (tok & TokMask) {
+            case TokLiteral:
+            case TokEnvVar:
+                skipStr(tokPtr);
+                break;
+            case TokHashLiteral:
+            case TokVariable:
+            case TokProperty:
+                skipHashStr(tokPtr);
+                break;
+            case TokFuncName:
+                skipHashStr(tokPtr);
+                pTokPtr = tokPtr;
+                skipExpression(pTokPtr);
+                tokPtr = pTokPtr;
+                break;
+            default:
+                Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
+                break;
+            }
+        }
+    }
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
+        ProFile *pro, const ushort *tokPtr)
+{
+    m_current.pro = pro;
+    m_current.line = 0;
+    return visitProBlock(tokPtr);
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
+        const ushort *tokPtr)
+{
+    traceMsg("entering block");
+    ProStringList curr;
+    bool okey = true, or_op = false, invert = false;
+    uint blockLen;
+    while (ushort tok = *tokPtr++) {
+        VisitReturn ret;
+        switch (tok) {
+        case TokLine:
+            m_current.line = *tokPtr++;
+            continue;
+        case TokAssign:
+        case TokAppend:
+        case TokAppendUnique:
+        case TokRemove:
+        case TokReplace:
+            visitProVariable(tok, curr, tokPtr);
+            curr.clear();
+            continue;
+        case TokBranch:
+            blockLen = getBlockLen(tokPtr);
+            if (m_cumulative) {
+#ifdef PROEVALUATOR_CUMULATIVE
+                if (!okey)
+                    m_skipLevel++;
+                ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
+                tokPtr += blockLen;
+                blockLen = getBlockLen(tokPtr);
+                if (!okey)
+                    m_skipLevel--;
+                else
+                    m_skipLevel++;
+                if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
+                    ret = visitProBlock(tokPtr);
+                if (okey)
+                    m_skipLevel--;
+#endif
+            } else {
+                if (okey) {
+                    traceMsg("taking 'then' branch");
+                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
+                    traceMsg("finished 'then' branch");
+                }
+                tokPtr += blockLen;
+                blockLen = getBlockLen(tokPtr);
+                if (!okey) {
+                    traceMsg("taking 'else' branch");
+                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
+                    traceMsg("finished 'else' branch");
+                }
+            }
+            tokPtr += blockLen;
+            okey = true, or_op = false; // force next evaluation
+            break;
+        case TokForLoop:
+            if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
+                skipHashStr(tokPtr);
+                uint exprLen = getBlockLen(tokPtr);
+                tokPtr += exprLen;
+                blockLen = getBlockLen(tokPtr);
+                ret = visitProBlock(tokPtr);
+            } else if (okey != or_op) {
+                const ProKey &variable = getHashStr(tokPtr);
+                uint exprLen = getBlockLen(tokPtr);
+                const ushort *exprPtr = tokPtr;
+                tokPtr += exprLen;
+                blockLen = getBlockLen(tokPtr);
+                ret = visitProLoop(variable, exprPtr, tokPtr);
+            } else {
+                skipHashStr(tokPtr);
+                uint exprLen = getBlockLen(tokPtr);
+                tokPtr += exprLen;
+                blockLen = getBlockLen(tokPtr);
+                traceMsg("skipped loop");
+                ret = ReturnTrue;
+            }
+            tokPtr += blockLen;
+            okey = true, or_op = false; // force next evaluation
+            break;
+        case TokTestDef:
+        case TokReplaceDef:
+            if (m_cumulative || okey != or_op) {
+                const ProKey &name = getHashStr(tokPtr);
+                blockLen = getBlockLen(tokPtr);
+                visitProFunctionDef(tok, name, tokPtr);
+                traceMsg("defined %s function %s",
+                      tok == TokTestDef ? "test" : "replace", dbgKey(name));
+            } else {
+                traceMsg("skipped function definition");
+                skipHashStr(tokPtr);
+                blockLen = getBlockLen(tokPtr);
+            }
+            tokPtr += blockLen;
+            okey = true, or_op = false; // force next evaluation
+            continue;
+        case TokNot:
+            traceMsg("NOT");
+            invert ^= true;
+            continue;
+        case TokAnd:
+            traceMsg("AND");
+            or_op = false;
+            continue;
+        case TokOr:
+            traceMsg("OR");
+            or_op = true;
+            continue;
+        case TokCondition:
+            if (!m_skipLevel && okey != or_op) {
+                if (curr.size() != 1) {
+                    if (!m_cumulative || !curr.isEmpty())
+                        evalError(fL1S("Conditional must expand to exactly one word."));
+                    okey = false;
+                } else {
+                    okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true);
+                    traceMsg("condition %s is %s", dbgStr(curr.at(0)), dbgBool(okey));
+                    okey ^= invert;
+                }
+            } else {
+                traceMsg("skipped condition %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>");
+            }
+            or_op = !okey; // tentatively force next evaluation
+            invert = false;
+            curr.clear();
+            continue;
+        case TokTestCall:
+            if (!m_skipLevel && okey != or_op) {
+                if (curr.size() != 1) {
+                    if (!m_cumulative || !curr.isEmpty())
+                        evalError(fL1S("Test name must expand to exactly one word."));
+                    skipExpression(tokPtr);
+                    okey = false;
+                } else {
+                    traceMsg("evaluating test function %s", dbgStr(curr.at(0)));
+                    ret = evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
+                    switch (ret) {
+                    case ReturnTrue: okey = true; break;
+                    case ReturnFalse: okey = false; break;
+                    default:
+                        traceMsg("aborting block, function status: %s", dbgReturn(ret));
+                        return ret;
+                    }
+                    traceMsg("test function returned %s", dbgBool(okey));
+                    okey ^= invert;
+                }
+            } else if (m_cumulative) {
+#ifdef PROEVALUATOR_CUMULATIVE
+                m_skipLevel++;
+                if (curr.size() != 1)
+                    skipExpression(tokPtr);
+                else
+                    evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
+                m_skipLevel--;
+#endif
+            } else {
+                skipExpression(tokPtr);
+                traceMsg("skipped test function %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>");
+            }
+            or_op = !okey; // tentatively force next evaluation
+            invert = false;
+            curr.clear();
+            continue;
+        default: {
+                const ushort *oTokPtr = --tokPtr;
+                evaluateExpression(tokPtr, &curr, false);
+                if (tokPtr != oTokPtr)
+                    continue;
+            }
+            Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
+            continue;
+        }
+        if (ret != ReturnTrue && ret != ReturnFalse) {
+            traceMsg("aborting block, status: %s", dbgReturn(ret));
+            return ret;
+        }
+    }
+    traceMsg("leaving block, okey=%s", dbgBool(okey));
+    return returnBool(okey);
+}
+
+
+void QMakeEvaluator::visitProFunctionDef(
+        ushort tok, const ProKey &name, const ushort *tokPtr)
+{
+    QHash<ProKey, ProFunctionDef> *hash =
+            (tok == TokTestDef
+             ? &m_functionDefs.testFunctions
+             : &m_functionDefs.replaceFunctions);
+    hash->insert(name, ProFunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
+        const ProKey &_variable, const ushort *exprPtr, const ushort *tokPtr)
+{
+    VisitReturn ret = ReturnTrue;
+    bool infinite = false;
+    int index = 0;
+    ProKey variable;
+    ProStringList oldVarVal;
+    ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
+    if (_variable.isEmpty()) {
+        if (it_list != statics.strever) {
+            evalError(fL1S("Invalid loop expression."));
+            return ReturnFalse;
+        }
+        it_list = ProString(statics.strforever);
+    } else {
+        variable = map(_variable);
+        oldVarVal = values(variable);
+    }
+    ProStringList list = values(it_list.toKey());
+    if (list.isEmpty()) {
+        if (it_list == statics.strforever) {
+            infinite = true;
+        } else {
+            const QString &itl = it_list.toQString(m_tmp1);
+            int dotdot = itl.indexOf(statics.strDotDot);
+            if (dotdot != -1) {
+                bool ok;
+                int start = itl.left(dotdot).toInt(&ok);
+                if (ok) {
+                    int end = itl.mid(dotdot+2).toInt(&ok);
+                    if (ok) {
+                        if (start < end) {
+                            for (int i = start; i <= end; i++)
+                                list << ProString(QString::number(i));
+                        } else {
+                            for (int i = start; i >= end; i--)
+                                list << ProString(QString::number(i));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if (infinite)
+        traceMsg("entering infinite loop for %s", dbgKey(variable));
+    else
+        traceMsg("entering loop for %s over %s", dbgKey(variable), dbgStrList(list));
+
+    m_loopLevel++;
+    forever {
+        if (infinite) {
+            if (!variable.isEmpty())
+                m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++)));
+            if (index > 1000) {
+                evalError(fL1S("Ran into infinite loop (> 1000 iterations)."));
+                break;
+            }
+            traceMsg("loop iteration %d", index);
+        } else {
+            ProString val;
+            do {
+                if (index >= list.count())
+                    goto do_break;
+                val = list.at(index++);
+            } while (val.isEmpty()); // stupid, but qmake is like that
+            traceMsg("loop iteration %s", dbgStr(val));
+            m_valuemapStack.top()[variable] = ProStringList(val);
+        }
+
+        ret = visitProBlock(tokPtr);
+        switch (ret) {
+        case ReturnTrue:
+        case ReturnFalse:
+            break;
+        case ReturnNext:
+            ret = ReturnTrue;
+            break;
+        case ReturnBreak:
+            ret = ReturnTrue;
+            goto do_break;
+        default:
+            goto do_break;
+        }
+    }
+  do_break:
+    m_loopLevel--;
+
+    traceMsg("done looping");
+
+    if (!variable.isEmpty())
+        m_valuemapStack.top()[variable] = oldVarVal;
+    return ret;
+}
+
+void QMakeEvaluator::visitProVariable(
+        ushort tok, const ProStringList &curr, const ushort *&tokPtr)
+{
+    int sizeHint = *tokPtr++;
+
+    if (curr.size() != 1) {
+        skipExpression(tokPtr);
+        if (!m_cumulative || !curr.isEmpty())
+            evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
+        return;
+    }
+    const ProKey &varName = map(curr.first());
+
+    if (tok == TokReplace) {      // ~=
+        // DEFINES ~= s/a/b/?[gqi]
+
+        const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
+        const QString &val = varVal.at(0).toQString(m_tmp1);
+        if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
+            evalError(fL1S("The ~= operator can handle only the s/// function."));
+            return;
+        }
+        QChar sep = val.at(1);
+        QStringList func = val.split(sep);
+        if (func.count() < 3 || func.count() > 4) {
+            evalError(fL1S("The s/// function expects 3 or 4 arguments."));
+            return;
+        }
+
+        bool global = false, quote = false, case_sense = false;
+        if (func.count() == 4) {
+            global = func[3].indexOf(QLatin1Char('g')) != -1;
+            case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
+            quote = func[3].indexOf(QLatin1Char('q')) != -1;
+        }
+        QString pattern = func[1];
+        QString replace = func[2];
+        if (quote)
+            pattern = QRegExp::escape(pattern);
+
+        QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
+
+        // We could make a union of modified and unmodified values,
+        // but this will break just as much as it fixes, so leave it as is.
+        replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
+        debugMsg(2, "replaced %s with %s", dbgQStr(pattern), dbgQStr(replace));
+    } else {
+        ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
+        switch (tok) {
+        default: // whatever - cannot happen
+        case TokAssign:          // =
+            zipEmpty(&varVal);
+            if (!m_cumulative) {
+                // FIXME: add check+warning about accidental value removal.
+                // This may be a bit too noisy, though.
+                m_valuemapStack.top()[varName] = varVal;
+            } else {
+                if (!varVal.isEmpty()) {
+                    // We are greedy for values. But avoid exponential growth.
+                    ProStringList &v = valuesRef(varName);
+                    if (v.isEmpty()) {
+                        v = varVal;
+                    } else {
+                        ProStringList old = v;
+                        v = varVal;
+                        QSet<ProString> has;
+                        has.reserve(v.size());
+                        foreach (const ProString &s, v)
+                            has.insert(s);
+                        v.reserve(v.size() + old.size());
+                        foreach (const ProString &s, old)
+                            if (!has.contains(s))
+                                v << s;
+                    }
+                }
+            }
+            debugMsg(2, "assigning");
+            break;
+        case TokAppendUnique:    // *=
+            insertUnique(&valuesRef(varName), varVal);
+            debugMsg(2, "appending unique");
+            break;
+        case TokAppend:          // +=
+            zipEmpty(&varVal);
+            valuesRef(varName) += varVal;
+            debugMsg(2, "appending");
+            break;
+        case TokRemove:       // -=
+            if (!m_cumulative) {
+                removeEach(&valuesRef(varName), varVal);
+            } else {
+                // We are stingy with our values, too.
+            }
+            debugMsg(2, "removing");
+            break;
+        }
+    }
+    traceMsg("%s := %s", dbgKey(varName), dbgStrList(values(varName)));
+
+    if (varName == statics.strTEMPLATE)
+        setTemplate();
+#ifdef PROEVALUATOR_FULL
+    else if (varName == statics.strREQUIRES)
+        checkRequirements(values(varName));
+#endif
+}
+
+void QMakeEvaluator::setTemplate()
+{
+    ProStringList &values = valuesRef(statics.strTEMPLATE);
+    if (!m_option->user_template.isEmpty()) {
+        // Don't allow override
+        values = ProStringList(ProString(m_option->user_template));
+    } else {
+        if (values.isEmpty())
+            values.append(ProString("app"));
+        else
+            values.erase(values.begin() + 1, values.end());
+    }
+    if (!m_option->user_template_prefix.isEmpty()) {
+        QString val = values.first().toQString(m_tmp1);
+        if (!val.startsWith(m_option->user_template_prefix)) {
+            val.prepend(m_option->user_template_prefix);
+            values = ProStringList(ProString(val));
+        }
+    }
+}
+
+void QMakeEvaluator::loadDefaults()
+{
+    ProValueMap &vars = m_valuemapStack.top();
+
+    vars[ProKey("DIR_SEPARATOR")] << ProString(m_option->dir_sep);
+    vars[ProKey("DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep);
+    vars[ProKey("_DATE_")] << ProString(QDateTime::currentDateTime().toString());
+    if (!m_option->qmake_abslocation.isEmpty())
+        vars[ProKey("QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation);
+#if defined(Q_OS_WIN32)
+    vars[ProKey("QMAKE_HOST.os")] << ProString("Windows");
+
+    DWORD name_length = 1024;
+    wchar_t name[1024];
+    if (GetComputerName(name, &name_length))
+        vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name));
+
+    QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
+    vars[ProKey("QMAKE_HOST.version")] << ProString(QString::number(ver));
+    ProString verStr;
+    switch (ver) {
+    case QSysInfo::WV_Me: verStr = ProString("WinMe"); break;
+    case QSysInfo::WV_95: verStr = ProString("Win95"); break;
+    case QSysInfo::WV_98: verStr = ProString("Win98"); break;
+    case QSysInfo::WV_NT: verStr = ProString("WinNT"); break;
+    case QSysInfo::WV_2000: verStr = ProString("Win2000"); break;
+    case QSysInfo::WV_2003: verStr = ProString("Win2003"); break;
+    case QSysInfo::WV_XP: verStr = ProString("WinXP"); break;
+    case QSysInfo::WV_VISTA: verStr = ProString("WinVista"); break;
+    default: verStr = ProString("Unknown"); break;
+    }
+    vars[ProKey("QMAKE_HOST.version_string")] << verStr;
+
+    SYSTEM_INFO info;
+    GetSystemInfo(&info);
+    ProString archStr;
+    switch (info.wProcessorArchitecture) {
+# ifdef PROCESSOR_ARCHITECTURE_AMD64
+    case PROCESSOR_ARCHITECTURE_AMD64:
+        archStr = ProString("x86_64");
+        break;
+# endif
+    case PROCESSOR_ARCHITECTURE_INTEL:
+        archStr = ProString("x86");
+        break;
+    case PROCESSOR_ARCHITECTURE_IA64:
+# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
+    case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
+# endif
+        archStr = ProString("IA64");
+        break;
+    default:
+        archStr = ProString("Unknown");
+        break;
+    }
+    vars[ProKey("QMAKE_HOST.arch")] << archStr;
+
+# if defined(Q_CC_MSVC) // ### bogus condition, but nobody x-builds for msvc with a different qmake
+    QLatin1Char backslash('\\');
+    QString paths = m_option->getEnv(QLatin1String("PATH"));
+    QString vcBin64 = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
+    if (!vcBin64.endsWith(backslash))
+        vcBin64.append(backslash);
+    vcBin64.append(QLatin1String("bin\\amd64"));
+    QString vcBinX86_64 = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
+    if (!vcBinX86_64.endsWith(backslash))
+        vcBinX86_64.append(backslash);
+    vcBinX86_64.append(QLatin1String("bin\\x86_amd64"));
+    if (paths.contains(vcBin64, Qt::CaseInsensitive)
+            || paths.contains(vcBinX86_64, Qt::CaseInsensitive))
+        vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86_64");
+    else
+        vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86");
+# endif
+#elif defined(Q_OS_UNIX)
+    struct utsname name;
+    if (!uname(&name)) {
+        vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
+        vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename));
+        vars[ProKey("QMAKE_HOST.version")] << ProString(name.release);
+        vars[ProKey("QMAKE_HOST.version_string")] << ProString(name.version);
+        vars[ProKey("QMAKE_HOST.arch")] << ProString(name.machine);
+    }
+#endif
+
+    m_valuemapInited = true;
+}
+
+bool QMakeEvaluator::prepareProject(const QString &inDir)
+{
+    QString superdir;
+    if (m_option->do_cache) {
+        QString conffile;
+        QString cachefile = m_option->cachefile;
+        if (cachefile.isEmpty())  { //find it as it has not been specified
+            if (m_outputDir.isEmpty())
+                goto no_cache;
+            superdir = m_outputDir;
+            forever {
+                QString superfile = superdir + QLatin1String("/.qmake.super");
+                if (IoUtils::exists(superfile)) {
+                    m_superfile = superfile;
+                    break;
+                }
+                QFileInfo qdfi(superdir);
+                if (qdfi.isRoot()) {
+                    superdir.clear();
+                    break;
+                }
+                superdir = qdfi.path();
+            }
+            QString sdir = inDir;
+            QString dir = m_outputDir;
+            forever {
+                conffile = sdir + QLatin1String("/.qmake.conf");
+                if (!IoUtils::exists(conffile))
+                    conffile.clear();
+                cachefile = dir + QLatin1String("/.qmake.cache");
+                if (!IoUtils::exists(cachefile))
+                    cachefile.clear();
+                if (!conffile.isEmpty() || !cachefile.isEmpty()) {
+                    if (dir != sdir)
+                        m_sourceRoot = sdir;
+                    m_buildRoot = dir;
+                    break;
+                }
+                if (dir == superdir)
+                    goto no_cache;
+                QFileInfo qsdfi(sdir);
+                QFileInfo qdfi(dir);
+                if (qsdfi.isRoot() || qdfi.isRoot())
+                    goto no_cache;
+                sdir = qsdfi.path();
+                dir = qdfi.path();
+            }
+        } else {
+            m_buildRoot = QFileInfo(cachefile).path();
+        }
+        m_conffile = conffile;
+        m_cachefile = cachefile;
+    }
+  no_cache:
+
+    // Look for mkspecs/ in source and build. First to win determines the root.
+    QString sdir = inDir;
+    QString dir = m_outputDir;
+    while (dir != m_buildRoot) {
+        if ((dir != sdir && QFileInfo(sdir, QLatin1String("mkspecs")).isDir())
+                || QFileInfo(dir, QLatin1String("mkspecs")).isDir()) {
+            if (dir != sdir)
+                m_sourceRoot = sdir;
+            m_buildRoot = dir;
+            break;
+        }
+        if (dir == superdir)
+            break;
+        QFileInfo qsdfi(sdir);
+        QFileInfo qdfi(dir);
+        if (qsdfi.isRoot() || qdfi.isRoot())
+            break;
+        sdir = qsdfi.path();
+        dir = qdfi.path();
+    }
+
+    return true;
+}
+
+bool QMakeEvaluator::loadSpecInternal()
+{
+    if (!evaluateFeatureFile(QLatin1String("spec_pre.prf")))
+        return false;
+    QString spec = m_qmakespec + QLatin1String("/qmake.conf");
+    if (!evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+        evalError(fL1S("Could not read qmake configuration file %1.").arg(spec));
+        return false;
+    }
+#ifdef Q_OS_UNIX
+    m_qmakespecFull = QFileInfo(m_qmakespec).canonicalFilePath();
+#else
+    // We can't resolve symlinks as they do on Unix, so configure.exe puts
+    // the source of the qmake.conf at the end of the default/qmake.conf in
+    // the QMAKESPEC_ORIGINAL variable.
+    const ProString &orig_spec = first(ProKey("QMAKESPEC_ORIGINAL"));
+    m_qmakespecFull = orig_spec.isEmpty() ? m_qmakespec : orig_spec.toQString();
+#endif
+    valuesRef(ProKey("QMAKESPEC")) << ProString(m_qmakespecFull);
+    m_qmakespecName = IoUtils::fileName(m_qmakespecFull).toString();
+    if (!evaluateFeatureFile(QLatin1String("spec_post.prf")))
+        return false;
+    // The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it
+    m_dirSep = first(ProKey("QMAKE_DIR_SEP"));
+    return true;
+}
+
+bool QMakeEvaluator::loadSpec()
+{
+    QString qmakespec = m_option->expandEnvVars(
+                m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
+
+    {
+        QMakeEvaluator evaluator(m_option, m_parser, m_handler);
+        if (!m_superfile.isEmpty()) {
+            valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
+            if (!evaluator.evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly))
+                return false;
+        }
+        if (!m_conffile.isEmpty()) {
+            valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
+            if (!evaluator.evaluateFile(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly))
+                return false;
+        }
+        if (!m_cachefile.isEmpty()) {
+            valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
+            if (!evaluator.evaluateFile(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly))
+                return false;
+        }
+        if (qmakespec.isEmpty()) {
+            if (!m_hostBuild)
+                qmakespec = evaluator.first(ProKey("XQMAKESPEC")).toQString();
+            if (qmakespec.isEmpty())
+                qmakespec = evaluator.first(ProKey("QMAKESPEC")).toQString();
+        }
+        m_qmakepath = evaluator.values(ProKey("QMAKEPATH")).toQStringList();
+        m_qmakefeatures = evaluator.values(ProKey("QMAKEFEATURES")).toQStringList();
+    }
+
+    updateMkspecPaths();
+    if (qmakespec.isEmpty())
+        qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
+    if (IoUtils::isRelativePath(qmakespec)) {
+        foreach (const QString &root, m_mkspecPaths) {
+            QString mkspec = root + QLatin1Char('/') + qmakespec;
+            if (IoUtils::exists(mkspec)) {
+                qmakespec = mkspec;
+                goto cool;
+            }
+        }
+        evalError(fL1S("Could not find qmake configuration file %1.").arg(qmakespec));
+        return false;
+    }
+  cool:
+    m_qmakespec = QDir::cleanPath(qmakespec);
+
+    if (!m_superfile.isEmpty()
+        && !evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+        return false;
+    }
+    if (!loadSpecInternal())
+        return false;
+    updateFeaturePaths(); // The spec extends the feature search path, so rebuild the cache.
+    if (!m_conffile.isEmpty()
+        && !evaluateFile(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+        return false;
+    }
+    if (!m_cachefile.isEmpty()
+        && !evaluateFile(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly)) {
+        return false;
+    }
+    return true;
+}
+
+void QMakeEvaluator::setupProject()
+{
+    setTemplate();
+    ProValueMap &vars = m_valuemapStack.top();
+    vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName());
+    vars[ProKey("_PRO_FILE_")] << ProString(currentFileName());
+    vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory());
+    vars[ProKey("OUT_PWD")] << ProString(m_outputDir);
+}
+
+void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where)
+{
+    if (!cmds.isEmpty()) {
+        if (ProFile *pro = m_parser->parsedProBlock(cmds, where, -1)) {
+            if (pro->isOk()) {
+                m_locationStack.push(m_current);
+                visitProBlock(pro, pro->tokPtr());
+                m_current = m_locationStack.pop();
+            }
+            pro->deref();
+        }
+    }
+}
+
+void QMakeEvaluator::evaluateConfigFeatures()
+{
+    QSet<QString> processed;
+    forever {
+        bool finished = true;
+        ProStringList configs = values(statics.strCONFIG);
+        for (int i = configs.size() - 1; i >= 0; --i) {
+            QString config = configs.at(i).toQString(m_tmp1).toLower();
+            if (!processed.contains(config)) {
+                config.detach();
+                processed.insert(config);
+                if (evaluateFeatureFile(config, true)) {
+                    finished = false;
+                    break;
+                }
+            }
+        }
+        if (finished)
+            break;
+    }
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
+        ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+    if (!m_cumulative && !pro->isOk())
+        return ReturnFalse;
+
+    if (flags & LoadPreFiles) {
+        if (!prepareProject(pro->directoryName()))
+            return ReturnFalse;
+
+        m_hostBuild = pro->isHostBuild();
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+        m_option->mutex.lock();
+#endif
+        QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_hostBuild)];
+        if (!*baseEnvPtr)
+            *baseEnvPtr = new QMakeBaseEnv;
+        QMakeBaseEnv *baseEnv = *baseEnvPtr;
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+        {
+            QMutexLocker locker(&baseEnv->mutex);
+            m_option->mutex.unlock();
+            if (baseEnv->inProgress) {
+                QThreadPool::globalInstance()->releaseThread();
+                baseEnv->cond.wait(&baseEnv->mutex);
+                QThreadPool::globalInstance()->reserveThread();
+                if (!baseEnv->isOk)
+                    return ReturnFalse;
+            } else
+#endif
+            if (!baseEnv->evaluator) {
+#ifdef PROEVALUATOR_THREAD_SAFE
+                baseEnv->inProgress = true;
+                locker.unlock();
+#endif
+
+                QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_handler);
+                baseEnv->evaluator = baseEval;
+                baseEval->m_superfile = m_superfile;
+                baseEval->m_conffile = m_conffile;
+                baseEval->m_cachefile = m_cachefile;
+                baseEval->m_sourceRoot = m_sourceRoot;
+                baseEval->m_buildRoot = m_buildRoot;
+                baseEval->m_hostBuild = m_hostBuild;
+                bool ok = baseEval->loadSpec();
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+                locker.relock();
+                baseEnv->isOk = ok;
+                baseEnv->inProgress = false;
+                baseEnv->cond.wakeAll();
+#endif
+
+                if (!ok)
+                    return ReturnFalse;
+            }
+#ifdef PROEVALUATOR_THREAD_SAFE
+        }
+#endif
+
+        initFrom(*baseEnv->evaluator);
+    } else {
+        if (!m_valuemapInited)
+            loadDefaults();
+    }
+
+#ifdef QT_BUILD_QMAKE
+    for (ProValueMap::ConstIterator it = m_extraVars.constBegin();
+         it != m_extraVars.constEnd(); ++it)
+        m_valuemapStack.first().insert(it.key(), it.value());
+#endif
+
+    m_handler->aboutToEval(currentProFile(), pro, type);
+    m_profileStack.push(pro);
+    valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
+    if (flags & LoadPreFiles) {
+        setupProject();
+
+        evaluateFeatureFile(QLatin1String("default_pre.prf"));
+
+        evaluateCommand(m_option->precmds, fL1S("(command line)"));
+
+#ifdef QT_BUILD_QMAKE
+        // After user configs, to override them
+        if (!m_extraConfigs.isEmpty())
+            evaluateCommand("CONFIG += " + m_extraConfigs.join(" "), fL1S("(extra configs)"));
+#endif
+    }
+
+    debugMsg(1, "visiting file %s", qPrintable(pro->fileName()));
+    visitProBlock(pro, pro->tokPtr());
+    debugMsg(1, "done visiting file %s", qPrintable(pro->fileName()));
+
+    if (flags & LoadPostFiles) {
+        evaluateCommand(m_option->postcmds, fL1S("(command line -after)"));
+
+#ifdef QT_BUILD_QMAKE
+        // Again, to ensure the project does not mess with us.
+        // Specifically, do not allow a project to override debug/release within a
+        // debug_and_release build pass - it's too late for that at this point anyway.
+        if (!m_extraConfigs.isEmpty())
+            evaluateCommand("CONFIG += " + m_extraConfigs.join(" "), fL1S("(extra configs)"));
+#endif
+
+        evaluateFeatureFile(QLatin1String("default_post.prf"));
+
+        evaluateConfigFeatures();
+    }
+    m_profileStack.pop();
+    valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
+    m_handler->doneWithEval(currentProFile());
+
+    return ReturnTrue;
+}
+
+
+void QMakeEvaluator::updateMkspecPaths()
+{
+    QStringList ret;
+    const QString concat = QLatin1String("/mkspecs");
+
+    foreach (const QString &it, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))
+        ret << it + concat;
+
+    foreach (const QString &it, m_qmakepath)
+        ret << it + concat;
+
+    if (!m_buildRoot.isEmpty())
+        ret << m_buildRoot + concat;
+    if (!m_sourceRoot.isEmpty())
+        ret << m_sourceRoot + concat;
+
+    ret << m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + concat;
+
+    ret.removeDuplicates();
+    m_mkspecPaths = ret;
+}
+
+void QMakeEvaluator::updateFeaturePaths()
+{
+    QString mkspecs_concat = QLatin1String("/mkspecs");
+    QString features_concat = QLatin1String("/features/");
+
+    QStringList feature_roots;
+
+    foreach (const QString &f, m_option->getPathListEnv(QLatin1String("QMAKEFEATURES")))
+        feature_roots += f;
+
+    feature_roots += m_qmakefeatures;
+
+    feature_roots += m_option->propertyValue(ProKey("QMAKEFEATURES")).toQString(m_mtmp).split(
+            m_option->dirlist_sep, QString::SkipEmptyParts);
+
+    QStringList feature_bases;
+    if (!m_buildRoot.isEmpty())
+        feature_bases << m_buildRoot;
+    if (!m_sourceRoot.isEmpty())
+        feature_bases << m_sourceRoot;
+
+    foreach (const QString &item, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))
+        feature_bases << (item + mkspecs_concat);
+
+    foreach (const QString &item, m_qmakepath)
+        feature_bases << (item + mkspecs_concat);
+
+    if (!m_qmakespecFull.isEmpty()) {
+        // The spec is already platform-dependent, so no subdirs here.
+        feature_roots << (m_qmakespecFull + features_concat);
+
+        // Also check directly under the root directory of the mkspecs collection
+        QDir specdir(m_qmakespecFull);
+        while (!specdir.isRoot() && specdir.cdUp()) {
+            const QString specpath = specdir.path();
+            if (specpath.endsWith(mkspecs_concat)) {
+                if (IoUtils::exists(specpath + features_concat))
+                    feature_bases << specpath;
+                break;
+            }
+        }
+    }
+
+    feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")).toQString(m_mtmp)
+                      + mkspecs_concat);
+
+    foreach (const QString &fb, feature_bases) {
+        foreach (const ProString &sfx, values(ProKey("QMAKE_PLATFORM")))
+            feature_roots << (fb + features_concat + sfx + QLatin1Char('/'));
+        feature_roots << (fb + features_concat);
+    }
+
+    for (int i = 0; i < feature_roots.count(); ++i)
+        if (!feature_roots.at(i).endsWith((ushort)'/'))
+            feature_roots[i].append((ushort)'/');
+
+    feature_roots.removeDuplicates();
+
+    QStringList ret;
+    foreach (const QString &root, feature_roots)
+        if (IoUtils::exists(root))
+            ret << root;
+    m_featureRoots = ret;
+}
+
+ProString QMakeEvaluator::propertyValue(const ProKey &name) const
+{
+    if (name == QLatin1String("QMAKE_MKSPECS"))
+        return ProString(m_mkspecPaths.join(m_option->dirlist_sep));
+    ProString ret = m_option->propertyValue(name);
+//    if (ret.isNull())
+//        evalError(fL1S("Querying unknown property %1").arg(name.toQString(m_mtmp)));
+    return ret;
+}
+
+ProFile *QMakeEvaluator::currentProFile() const
+{
+    if (m_profileStack.count() > 0)
+        return m_profileStack.top();
+    return 0;
+}
+
+QString QMakeEvaluator::currentFileName() const
+{
+    ProFile *pro = currentProFile();
+    if (pro)
+        return pro->fileName();
+    return QString();
+}
+
+QString QMakeEvaluator::currentDirectory() const
+{
+    ProFile *pro = currentProFile();
+    if (pro)
+        return pro->directoryName();
+    return QString();
+}
+
+bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex)
+{
+    // magic types for easy flipping
+    if (config == statics.strtrue)
+        return true;
+    if (config == statics.strfalse)
+        return false;
+
+    if (config == statics.strhost_build)
+        return m_hostBuild;
+
+    if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
+        QString cfg = config;
+        cfg.detach(); // Keep m_tmp out of QRegExp's cache
+        QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
+
+        // mkspecs
+        if (re.exactMatch(m_qmakespecName))
+            return true;
+
+        // CONFIG variable
+        int t = 0;
+        foreach (const ProString &configValue, values(statics.strCONFIG)) {
+            if (re.exactMatch(configValue.toQString(m_tmp[t])))
+                return true;
+            t ^= 1;
+        }
+    } else {
+        // mkspecs
+        if (m_qmakespecName == config)
+            return true;
+
+        // CONFIG variable
+        if (values(statics.strCONFIG).contains(ProString(config)))
+            return true;
+    }
+
+    return false;
+}
+
+ProStringList QMakeEvaluator::expandVariableReferences(
+        const ushort *&tokPtr, int sizeHint, bool joined)
+{
+    ProStringList ret;
+    ret.reserve(sizeHint);
+    forever {
+        evaluateExpression(tokPtr, &ret, joined);
+        switch (*tokPtr) {
+        case TokValueTerminator:
+        case TokFuncTerminator:
+            tokPtr++;
+            return ret;
+        case TokArgSeparator:
+            if (joined) {
+                tokPtr++;
+                continue;
+            }
+            // fallthrough
+        default:
+            Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
+            break;
+        }
+    }
+}
+
+QList<ProStringList> QMakeEvaluator::prepareFunctionArgs(const ushort *&tokPtr)
+{
+    QList<ProStringList> args_list;
+    if (*tokPtr != TokFuncTerminator) {
+        for (;; tokPtr++) {
+            ProStringList arg;
+            evaluateExpression(tokPtr, &arg, false);
+            args_list << arg;
+            if (*tokPtr == TokFuncTerminator)
+                break;
+            Q_ASSERT(*tokPtr == TokArgSeparator);
+        }
+    }
+    tokPtr++;
+    return args_list;
+}
+
+ProStringList QMakeEvaluator::evaluateFunction(
+        const ProFunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
+{
+    bool oki;
+    ProStringList ret;
+
+    if (m_valuemapStack.count() >= 100) {
+        evalError(fL1S("Ran into infinite recursion (depth > 100)."));
+        oki = false;
+    } else {
+        m_valuemapStack.push(ProValueMap());
+        m_locationStack.push(m_current);
+        int loopLevel = m_loopLevel;
+        m_loopLevel = 0;
+
+        ProStringList args;
+        for (int i = 0; i < argumentsList.count(); ++i) {
+            args += argumentsList[i];
+            m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i];
+        }
+        m_valuemapStack.top()[statics.strARGS] = args;
+        VisitReturn vr = visitProBlock(func.pro(), func.tokPtr());
+        oki = (vr != ReturnFalse && vr != ReturnError); // True || Return
+        ret = m_returnValue;
+        m_returnValue.clear();
+
+        m_loopLevel = loopLevel;
+        m_current = m_locationStack.pop();
+        m_valuemapStack.pop();
+    }
+    if (ok)
+        *ok = oki;
+    if (oki)
+        return ret;
+    return ProStringList();
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
+        const ProFunctionDef &func, const QList<ProStringList> &argumentsList,
+        const ProString &function)
+{
+    bool ok;
+    ProStringList ret = evaluateFunction(func, argumentsList, &ok);
+    if (ok) {
+        if (ret.isEmpty())
+            return ReturnTrue;
+        if (ret.at(0) != statics.strfalse) {
+            if (ret.at(0) == statics.strtrue)
+                return ReturnTrue;
+            int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
+            if (ok) {
+                if (val)
+                    return ReturnTrue;
+            } else {
+                evalError(fL1S("Unexpected return value from test '%1': %2.")
+                          .arg(function.toQString(m_tmp1))
+                          .arg(ret.join(QLatin1String(" :: "))));
+            }
+        }
+    }
+    return ReturnFalse;
+}
+
+QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
+        const ProKey &func, const ushort *&tokPtr)
+{
+    if (int func_t = statics.functions.value(func)) {
+        //why don't the builtin functions just use args_list? --Sam
+        return evaluateBuiltinConditional(func_t, func, expandVariableReferences(tokPtr, 5, true));
+    }
+
+    QHash<ProKey, ProFunctionDef>::ConstIterator it =
+            m_functionDefs.testFunctions.constFind(func);
+    if (it != m_functionDefs.testFunctions.constEnd()) {
+        const QList<ProStringList> args = prepareFunctionArgs(tokPtr);
+        traceMsg("calling %s(%s)", dbgKey(func), dbgStrListList(args));
+        return evaluateBoolFunction(*it, args, func);
+    }
+
+    skipExpression(tokPtr);
+    evalError(fL1S("'%1' is not a recognized test function.").arg(func.toQString(m_tmp1)));
+    return ReturnFalse;
+}
+
+ProStringList QMakeEvaluator::evaluateExpandFunction(
+        const ProKey &func, const ushort *&tokPtr)
+{
+    if (int func_t = statics.expands.value(func)) {
+        //why don't the builtin functions just use args_list? --Sam
+        return evaluateBuiltinExpand(func_t, func, expandVariableReferences(tokPtr, 5, true));
+    }
+
+    QHash<ProKey, ProFunctionDef>::ConstIterator it =
+            m_functionDefs.replaceFunctions.constFind(func);
+    if (it != m_functionDefs.replaceFunctions.constEnd()) {
+        const QList<ProStringList> args = prepareFunctionArgs(tokPtr);
+        traceMsg("calling $$%s(%s)", dbgKey(func), dbgStrListList(args));
+        return evaluateFunction(*it, args, 0);
+    }
+
+    skipExpression(tokPtr);
+    evalError(fL1S("'%1' is not a recognized replace function.").arg(func.toQString(m_tmp1)));
+    return ProStringList();
+}
+
+bool QMakeEvaluator::evaluateConditional(const QString &cond, const QString &where, int line)
+{
+    bool ret = false;
+    ProFile *pro = m_parser->parsedProBlock(cond, where, line, QMakeParser::TestGrammar);
+    if (pro) {
+        if (pro->isOk()) {
+            m_locationStack.push(m_current);
+            ret = visitProBlock(pro, pro->tokPtr()) == ReturnTrue;
+            m_current = m_locationStack.pop();
+        }
+        pro->deref();
+    }
+    return ret;
+}
+
+#ifdef PROEVALUATOR_FULL
+void QMakeEvaluator::checkRequirements(const ProStringList &deps)
+{
+    ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS"));
+    foreach (const ProString &dep, deps)
+        if (!evaluateConditional(dep.toQString(), m_current.pro->fileName(), m_current.line))
+            failed << dep;
+}
+#endif
+
+ProValueMap *QMakeEvaluator::findValues(const ProKey &variableName, ProValueMap::Iterator *rit)
+{
+    ProValueMapStack::Iterator vmi = m_valuemapStack.end();
+    do {
+        --vmi;
+        ProValueMap::Iterator it = (*vmi).find(variableName);
+        if (it != (*vmi).end()) {
+            if (it->constBegin() == statics.fakeValue.constBegin())
+                return 0;
+            *rit = it;
+            return &(*vmi);
+        }
+    } while (vmi != m_valuemapStack.begin());
+    return 0;
+}
+
+ProStringList &QMakeEvaluator::valuesRef(const ProKey &variableName)
+{
+    ProValueMap::Iterator it = m_valuemapStack.top().find(variableName);
+    if (it != m_valuemapStack.top().end()) {
+        if (it->constBegin() == statics.fakeValue.constBegin())
+            it->clear();
+        return *it;
+    }
+    ProValueMapStack::Iterator vmi = m_valuemapStack.end();
+    if (--vmi != m_valuemapStack.begin()) {
+        do {
+            --vmi;
+            ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
+            if (it != (*vmi).constEnd()) {
+                ProStringList &ret = m_valuemapStack.top()[variableName];
+                if (it->constBegin() != statics.fakeValue.constBegin())
+                    ret = *it;
+                return ret;
+            }
+        } while (vmi != m_valuemapStack.begin());
+    }
+    return m_valuemapStack.top()[variableName];
+}
+
+ProStringList QMakeEvaluator::values(const ProKey &variableName) const
+{
+    ProValueMapStack::ConstIterator vmi = m_valuemapStack.constEnd();
+    do {
+        --vmi;
+        ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
+        if (it != (*vmi).constEnd()) {
+            if (it->constBegin() == statics.fakeValue.constBegin())
+                break;
+            return *it;
+        }
+    } while (vmi != m_valuemapStack.constBegin());
+    return ProStringList();
+}
+
+ProString QMakeEvaluator::first(const ProKey &variableName) const
+{
+    const ProStringList &vals = values(variableName);
+    if (!vals.isEmpty())
+        return vals.first();
+    return ProString();
+}
+
+bool QMakeEvaluator::evaluateFile(
+        const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+    if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
+        m_locationStack.push(m_current);
+        bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
+        m_current = m_locationStack.pop();
+        pro->deref();
+#ifdef PROEVALUATOR_FULL
+        if (ok) {
+            ProStringList &iif = m_valuemapStack.first()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
+            ProString ifn(fileName);
+            if (!iif.contains(ifn))
+                iif << ifn;
+        }
+#endif
+        return ok;
+    } else {
+        if (!(flags & LoadSilent) && !IoUtils::exists(fileName))
+            evalError(fL1S("WARNING: Include file %1 not found").arg(fileName));
+        return false;
+    }
+}
+
+bool QMakeEvaluator::evaluateFileChecked(
+        const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
+{
+    if (fileName.isEmpty())
+        return false;
+    QMakeEvaluator *ref = this;
+    do {
+        foreach (const ProFile *pf, ref->m_profileStack)
+            if (pf->fileName() == fileName) {
+                evalError(fL1S("Circular inclusion of %1.").arg(fileName));
+                return false;
+            }
+    } while ((ref = ref->m_caller));
+    return evaluateFile(fileName, type, flags);
+}
+
+bool QMakeEvaluator::evaluateFeatureFile(const QString &fileName, bool silent)
+{
+    QString fn = fileName;
+    if (!fn.endsWith(QLatin1String(".prf")))
+        fn += QLatin1String(".prf");
+
+    if (m_featureRoots.isEmpty())
+        updateFeaturePaths();
+    int start_root = 0;
+    QString currFn = currentFileName();
+    if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
+        for (int root = 0; root < m_featureRoots.size(); ++root)
+            if (currFn == m_featureRoots.at(root) + fn) {
+                start_root = root + 1;
+                break;
+            }
+    }
+    for (int root = start_root; root < m_featureRoots.size(); ++root) {
+        QString fname = m_featureRoots.at(root) + fn;
+        if (IoUtils::exists(fname)) {
+            fn = fname;
+            goto cool;
+        }
+    }
+#ifdef QMAKE_BUILTIN_PRFS
+    fn.prepend(QLatin1String(":/qmake/features/"));
+    if (QFileInfo(fn).exists())
+        goto cool;
+#endif
+    if (!silent)
+        evalError(fL1S("Cannot find feature %1").arg(fileName));
+    return false;
+
+  cool:
+    ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
+    ProString afn(fn);
+    if (already.contains(afn)) {
+        if (!silent)
+            languageWarning(fL1S("Feature %1 already included").arg(fileName));
+        return true;
+    }
+    already.append(afn);
+
+#ifdef PROEVALUATOR_CUMULATIVE
+    bool cumulative = m_cumulative;
+    m_cumulative = false;
+#endif
+
+    // The path is fully normalized already.
+    bool ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
+
+#ifdef PROEVALUATOR_CUMULATIVE
+    m_cumulative = cumulative;
+#endif
+    return ok;
+}
+
+bool QMakeEvaluator::evaluateFileInto(const QString &fileName, ProValueMap *values, LoadFlags flags)
+{
+    QMakeEvaluator visitor(m_option, m_parser, m_handler);
+    visitor.m_caller = this;
+    visitor.m_outputDir = m_outputDir;
+    visitor.m_featureRoots = m_featureRoots;
+    if (!visitor.evaluateFileChecked(fileName, QMakeHandler::EvalAuxFile, flags))
+        return false;
+    *values = visitor.m_valuemapStack.top();
+#ifdef PROEVALUATOR_FULL
+    ProKey qiif("QMAKE_INTERNAL_INCLUDED_FILES");
+    ProStringList &iif = m_valuemapStack.first()[qiif];
+    foreach (const ProString &ifn, values->value(qiif))
+        if (!iif.contains(ifn))
+            iif << ifn;
+#endif
+    return true;
+}
+
+void QMakeEvaluator::message(int type, const QString &msg) const
+{
+    if (!m_skipLevel)
+        m_handler->message(type, msg,
+                m_current.line ? m_current.pro->fileName() : QString(),
+                m_current.line != 0xffff ? m_current.line : -1);
+}
+
+#ifdef PROEVALUATOR_DEBUG
+void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const
+{
+    va_list ap;
+
+    if (level <= m_debugLevel) {
+        fprintf(stderr, "DEBUG %d: ", level);
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+        fputc('\n', stderr);
+    }
+}
+
+void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const
+{
+    va_list ap;
+
+    if (!m_current.pro)
+        fprintf(stderr, "DEBUG 1: ");
+    else if (m_current.line <= 0)
+        fprintf(stderr, "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName()));
+    else
+        fprintf(stderr, "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line);
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    fputc('\n', stderr);
+}
+
+QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote)
+{
+    QString ret;
+    ret.reserve(val.size() + 2);
+    const QChar *chars = val.constData();
+    bool quote = forceQuote || val.isEmpty();
+    for (int i = 0, l = val.size(); i < l; i++) {
+        QChar c = chars[i];
+        ushort uc = c.unicode();
+        if (uc < 32) {
+            switch (uc) {
+            case '\r':
+                ret += QLatin1String("\\r");
+                break;
+            case '\n':
+                ret += QLatin1String("\\n");
+                break;
+            case '\t':
+                ret += QLatin1String("\\t");
+                break;
+            default:
+                ret += QString::fromLatin1("\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
+                break;
+            }
+        } else {
+            switch (uc) {
+            case '\\':
+                ret += QLatin1String("\\\\");
+                break;
+            case '"':
+                ret += QLatin1String("\\\"");
+                break;
+            case '\'':
+                ret += QLatin1String("\\'");
+                break;
+            case 32:
+                quote = true;
+                // fallthrough
+            default:
+                ret += c;
+                break;
+            }
+        }
+    }
+    if (quote) {
+        ret.prepend(QLatin1Char('"'));
+        ret.append(QLatin1Char('"'));
+    }
+    return ret;
+}
+
+QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas)
+{
+    QString ret;
+
+    foreach (const ProString &str, vals) {
+        if (!ret.isEmpty()) {
+            if (commas)
+                ret += QLatin1Char(',');
+            ret += QLatin1Char(' ');
+        }
+        ret += formatValue(str);
+    }
+    return ret;
+}
+
+QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists)
+{
+    QString ret;
+
+    foreach (const ProStringList &list, lists) {
+        if (!ret.isEmpty())
+            ret += QLatin1String(", ");
+        ret += formatValueList(list);
+    }
+    return ret;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/qmakeevaluator.h b/src/linguist/shared/qmakeevaluator.h
new file mode 100644 (file)
index 0000000..e1d3f61
--- /dev/null
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEEVALUATOR_H
+#define QMAKEEVALUATOR_H
+
+#if defined(PROEVALUATOR_FULL) && defined(PROEVALUATOR_THREAD_SAFE)
+#  error PROEVALUATOR_FULL is incompatible with PROEVALUATOR_THREAD_SAFE due to cache() implementation
+#endif
+
+#include "qmakeparser.h"
+#include "ioutils.h"
+
+#include <qlist.h>
+#include <qlinkedlist.h>
+#include <qset.h>
+#include <qstack.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#ifndef QT_BOOTSTRAPPED
+# include <qprocess.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMakeGlobals;
+
+class QMAKE_EXPORT QMakeHandler : public QMakeParserHandler
+{
+public:
+    enum {
+        SourceEvaluator = 0x10,
+
+        EvalWarnLanguage = SourceEvaluator |  WarningMessage | WarnLanguage,
+        EvalWarnDeprecated = SourceEvaluator | WarningMessage | WarnDeprecated,
+
+        EvalError = ErrorMessage | SourceEvaluator
+    };
+
+    // error(), warning() and message() from .pro file
+    virtual void fileMessage(const QString &msg) = 0;
+
+    enum EvalFileType { EvalProjectFile, EvalIncludeFile, EvalConfigFile, EvalFeatureFile, EvalAuxFile };
+    virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) = 0;
+    virtual void doneWithEval(ProFile *parent) = 0;
+};
+
+// We use a QLinkedList based stack instead of a QVector based one (QStack), so that
+// the addresses of value maps stay constant. The qmake generators rely on that.
+class QMAKE_EXPORT ProValueMapStack : public QLinkedList<ProValueMap>
+{
+public:
+    inline void push(const ProValueMap &t) { append(t); }
+    inline ProValueMap pop() { return takeLast(); }
+    ProValueMap &top() { return last(); }
+    const ProValueMap &top() const { return last(); }
+};
+
+class QMAKE_EXPORT QMakeEvaluator
+{
+public:
+    enum LoadFlag {
+        LoadProOnly = 0,
+        LoadPreFiles = 1,
+        LoadPostFiles = 2,
+        LoadAll = LoadPreFiles|LoadPostFiles,
+        LoadSilent = 0x10
+    };
+    Q_DECLARE_FLAGS(LoadFlags, LoadFlag)
+
+    static void initStatics();
+    static void initFunctionStatics();
+    QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser,
+                   QMakeHandler *handler);
+    ~QMakeEvaluator();
+
+#ifdef QT_BUILD_QMAKE
+    void setExtraVars(const ProValueMap &extraVars) { m_extraVars = extraVars; }
+    void setExtraConfigs(const ProStringList &extraConfigs) { m_extraConfigs = extraConfigs; }
+#endif
+    void setOutputDir(const QString &outputDir) { m_outputDir = outputDir; }
+
+    ProStringList values(const ProKey &variableName) const;
+    ProStringList &valuesRef(const ProKey &variableName);
+    ProString first(const ProKey &variableName) const;
+    ProString propertyValue(const ProKey &val) const;
+
+    ProString dirSep() const { return m_dirSep; }
+    bool isHostBuild() const { return m_hostBuild; }
+
+    enum VisitReturn {
+        ReturnFalse,
+        ReturnTrue,
+        ReturnError,
+        ReturnBreak,
+        ReturnNext,
+        ReturnReturn
+    };
+
+    static ALWAYS_INLINE VisitReturn returnBool(bool b)
+        { return b ? ReturnTrue : ReturnFalse; }
+
+    static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
+    ProString getStr(const ushort *&tokPtr);
+    ProKey getHashStr(const ushort *&tokPtr);
+    void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
+    static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
+    static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
+    void skipExpression(const ushort *&tokPtr);
+
+    void loadDefaults();
+    bool prepareProject(const QString &inDir);
+    bool loadSpecInternal();
+    bool loadSpec();
+    void initFrom(const QMakeEvaluator &other);
+    void setupProject();
+    void evaluateCommand(const QString &cmds, const QString &where);
+    VisitReturn visitProFile(ProFile *pro, QMakeHandler::EvalFileType type,
+                             LoadFlags flags);
+    VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
+    VisitReturn visitProBlock(const ushort *tokPtr);
+    VisitReturn visitProLoop(const ProKey &variable, const ushort *exprPtr,
+                             const ushort *tokPtr);
+    void visitProFunctionDef(ushort tok, const ProKey &name, const ushort *tokPtr);
+    void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
+
+    ALWAYS_INLINE const ProKey &map(const ProString &var) { return map(var.toKey()); }
+    const ProKey &map(const ProKey &var);
+    ProValueMap *findValues(const ProKey &variableName, ProValueMap::Iterator *it);
+
+    void setTemplate();
+
+    ProStringList split_value_list(const QString &vals, const ProFile *source = 0);
+    ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
+    ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
+
+    QString currentFileName() const;
+    QString currentDirectory() const;
+    ProFile *currentProFile() const;
+    QString resolvePath(const QString &fileName) const
+        { return QMakeInternal::IoUtils::resolvePath(currentDirectory(), fileName); }
+
+    bool evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type,
+                      LoadFlags flags);
+    bool evaluateFileChecked(const QString &fileName, QMakeHandler::EvalFileType type,
+                             LoadFlags flags);
+    bool evaluateFeatureFile(const QString &fileName, bool silent = false);
+    bool evaluateFileInto(const QString &fileName,
+                          ProValueMap *values, // output-only
+                          LoadFlags flags);
+    void evaluateConfigFeatures();
+    void message(int type, const QString &msg) const;
+    void evalError(const QString &msg) const
+            { message(QMakeHandler::EvalError, msg); }
+    void languageWarning(const QString &msg) const
+            { message(QMakeHandler::EvalWarnLanguage, msg); }
+    void deprecationWarning(const QString &msg) const
+            { message(QMakeHandler::EvalWarnDeprecated, msg); }
+
+    QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
+    ProStringList evaluateFunction(const ProFunctionDef &func,
+                                   const QList<ProStringList> &argumentsList, bool *ok);
+    VisitReturn evaluateBoolFunction(const ProFunctionDef &func,
+                                     const QList<ProStringList> &argumentsList,
+                                     const ProString &function);
+
+    ProStringList evaluateExpandFunction(const ProKey &function, const ushort *&tokPtr);
+    VisitReturn evaluateConditionalFunction(const ProKey &function, const ushort *&tokPtr);
+
+    ProStringList evaluateBuiltinExpand(int func_t, const ProKey &function, const ProStringList &args);
+    VisitReturn evaluateBuiltinConditional(int func_t, const ProKey &function, const ProStringList &args);
+
+    bool evaluateConditional(const QString &cond, const QString &where, int line = -1);
+#ifdef PROEVALUATOR_FULL
+    void checkRequirements(const ProStringList &deps);
+#endif
+
+    void updateMkspecPaths();
+    void updateFeaturePaths();
+
+    bool isActiveConfig(const QString &config, bool regex = false);
+
+    void populateDeps(
+            const ProStringList &deps, const ProString &prefix,
+            QHash<ProKey, QSet<ProKey> > &dependencies,
+            ProValueMap &dependees, ProStringList &rootSet) const;
+
+    VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
+                          const QString &contents);
+#ifndef QT_BOOTSTRAPPED
+    void runProcess(QProcess *proc, const QString &command) const;
+#endif
+    QByteArray getCommandOutput(const QString &args) const;
+
+    static void removeEach(ProStringList *varlist, const ProStringList &value);
+
+    QMakeEvaluator *m_caller;
+    int m_loopLevel; // To report unexpected break() and next()s
+#ifdef PROEVALUATOR_CUMULATIVE
+    bool m_cumulative;
+    int m_skipLevel;
+#else
+    enum { m_cumulative = 0 };
+    enum { m_skipLevel = 0 };
+#endif
+
+#ifdef PROEVALUATOR_DEBUG
+    void debugMsgInternal(int level, const char *fmt, ...) const;
+    void traceMsgInternal(const char *fmt, ...) const;
+    static QString formatValue(const ProString &val, bool forceQuote = false);
+    static QString formatValueList(const ProStringList &vals, bool commas = false);
+    static QString formatValueListList(const QList<ProStringList> &vals);
+
+    const int m_debugLevel;
+#else
+    ALWAYS_INLINE void debugMsgInternal(int, const char *, ...) const {}
+    ALWAYS_INLINE void traceMsgInternal(const char *, ...) const {}
+
+    enum { m_debugLevel = 0 };
+#endif
+
+    struct Location {
+        Location() : pro(0), line(0) {}
+        Location(ProFile *_pro, ushort _line) : pro(_pro), line(_line) {}
+        void clear() { pro = 0; line = 0; }
+        ProFile *pro;
+        ushort line;
+    };
+
+    Location m_current; // Currently evaluated location
+    QStack<Location> m_locationStack; // All execution location changes
+    QStack<ProFile *> m_profileStack; // Includes only
+
+#ifdef QT_BUILD_QMAKE
+    ProValueMap m_extraVars;
+    ProStringList m_extraConfigs;
+#endif
+    QString m_outputDir;
+
+    int m_listCount;
+    bool m_valuemapInited;
+    bool m_hostBuild;
+    QString m_qmakespec;
+    QString m_qmakespecFull;
+    QString m_qmakespecName;
+    QString m_superfile;
+    QString m_conffile;
+    QString m_cachefile;
+    QString m_sourceRoot;
+    QString m_buildRoot;
+    QStringList m_qmakepath;
+    QStringList m_qmakefeatures;
+    QStringList m_mkspecPaths;
+    QStringList m_featureRoots;
+    ProString m_dirSep;
+    ProFunctionDefs m_functionDefs;
+    ProStringList m_returnValue;
+    ProValueMapStack m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii.
+    QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
+    mutable QString m_mtmp;
+
+    QMakeGlobals *m_option;
+    QMakeParser *m_parser;
+    QMakeHandler *m_handler;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags)
+
+QT_END_NAMESPACE
+
+#endif // QMAKEEVALUATOR_H
diff --git a/src/linguist/shared/qmakeevaluator_p.h b/src/linguist/shared/qmakeevaluator_p.h
new file mode 100644 (file)
index 0000000..825fdf5
--- /dev/null
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEEVALUATOR_P_H
+#define QMAKEEVALUATOR_P_H
+
+#include "proitems.h"
+
+#include <qregexp.h>
+
+#define debugMsg if (!m_debugLevel) {} else debugMsgInternal
+#define traceMsg if (!m_debugLevel) {} else traceMsgInternal
+#ifdef PROEVALUATOR_DEBUG
+#  define dbgBool(b) (b ? "true" : "false")
+#  define dbgReturn(r) \
+    (r == ReturnError ? "error" : \
+     r == ReturnBreak ? "break" : \
+     r == ReturnNext ? "next" : \
+     r == ReturnReturn ? "return" : \
+     "<invalid>")
+#  define dbgKey(s) qPrintable(s.toString().toQString())
+#  define dbgStr(s) qPrintable(formatValue(s, true))
+#  define dbgStrList(s) qPrintable(formatValueList(s))
+#  define dbgSepStrList(s) qPrintable(formatValueList(s, true))
+#  define dbgStrListList(s) qPrintable(formatValueListList(s))
+#  define dbgQStr(s) dbgStr(ProString(s))
+#else
+#  define dbgBool(b) 0
+#  define dbgReturn(r) 0
+#  define dbgKey(s) 0
+#  define dbgStr(s) 0
+#  define dbgStrList(s) 0
+#  define dbgSepStrList(s) 0
+#  define dbgStrListList(s) 0
+#  define dbgQStr(s) 0
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QMakeInternal {
+
+struct QMakeStatics {
+    QString field_sep;
+    QString strtrue;
+    QString strfalse;
+    ProKey strCONFIG;
+    ProKey strARGS;
+    QString strDot;
+    QString strDotDot;
+    QString strever;
+    QString strforever;
+    QString strhost_build;
+    ProKey strTEMPLATE;
+#ifdef PROEVALUATOR_FULL
+    ProKey strREQUIRES;
+#endif
+    QHash<ProKey, int> expands;
+    QHash<ProKey, int> functions;
+    QHash<ProKey, ProKey> varMap;
+    ProStringList fakeValue;
+};
+
+extern QMakeStatics statics;
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMAKEEVALUATOR_P_H
diff --git a/src/linguist/shared/qmakeglobals.cpp b/src/linguist/shared/qmakeglobals.cpp
new file mode 100644 (file)
index 0000000..c326d0a
--- /dev/null
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakeglobals.h"
+
+#include "qmakeevaluator.h"
+#include "ioutils.h"
+
+#include <qbytearray.h>
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qregexp.h>
+#include <qset.h>
+#include <qstack.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qthreadpool.h>
+#endif
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#include <sys/utsname.h>
+#else
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#define QT_PCLOSE _pclose
+#else
+#define QT_POPEN popen
+#define QT_PCLOSE pclose
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define fL1S(s) QString::fromLatin1(s)
+
+namespace { // MSVC doesn't seem to know the semantics of "static" ...
+
+static struct {
+    QRegExp reg_variableName;
+} statics;
+
+}
+
+static void initStatics()
+{
+    if (!statics.reg_variableName.isEmpty())
+        return;
+
+    statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
+    statics.reg_variableName.setMinimal(true);
+}
+
+QMakeGlobals::QMakeGlobals()
+{
+    initStatics();
+
+    do_cache = true;
+
+#ifdef PROEVALUATOR_DEBUG
+    debugLevel = 0;
+#endif
+#ifdef Q_OS_WIN
+    dirlist_sep = QLatin1Char(';');
+    dir_sep = QLatin1Char('\\');
+#else
+    dirlist_sep = QLatin1Char(':');
+    dir_sep = QLatin1Char('/');
+#endif
+    qmakespec = getEnv(QLatin1String("QMAKESPEC"));
+}
+
+QMakeGlobals::~QMakeGlobals()
+{
+    qDeleteAll(baseEnvs);
+}
+
+QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec)
+{
+    QString ret = QDir::cleanPath(spec);
+    if (ret.contains(QLatin1Char('/'))) {
+        QString absRet = QDir(state.pwd).absoluteFilePath(ret);
+        if (QFile::exists(absRet))
+            ret = QDir::cleanPath(absRet);
+    }
+    return ret;
+}
+
+QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
+        QMakeCmdLineParserState &state, QStringList &args, int *pos)
+{
+    enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache } argState = ArgNone;
+    for (; *pos < args.count(); (*pos)++) {
+        QString arg = args.at(*pos);
+        switch (argState) {
+        case ArgConfig:
+            if (state.after)
+                state.postconfigs << arg;
+            else
+                state.preconfigs << arg;
+            break;
+        case ArgSpec:
+            qmakespec = args[*pos] = cleanSpec(state, arg);
+            break;
+        case ArgXSpec:
+            xqmakespec = args[*pos] = cleanSpec(state, arg);
+            break;
+        case ArgTmpl:
+            user_template = arg;
+            break;
+        case ArgTmplPfx:
+            user_template_prefix = arg;
+            break;
+        case ArgCache:
+            cachefile = args[*pos] = QDir::cleanPath(QDir(state.pwd).absoluteFilePath(arg));
+            break;
+        default:
+            if (arg.startsWith(QLatin1Char('-'))) {
+                if (arg == QLatin1String("-after")) {
+                    state.after = true;
+                } else if (arg == QLatin1String("-config")) {
+                    argState = ArgConfig;
+                } else if (arg == QLatin1String("-nocache")) {
+                    do_cache = false;
+                } else if (arg == QLatin1String("-cache")) {
+                    argState = ArgCache;
+                } else if (arg == QLatin1String("-platform") || arg == QLatin1String("-spec")) {
+                    argState = ArgSpec;
+                } else if (arg == QLatin1String("-xplatform") || arg == QLatin1String("-xspec")) {
+                    argState = ArgXSpec;
+                } else if (arg == QLatin1String("-template") || arg == QLatin1String("-t")) {
+                    argState = ArgTmpl;
+                } else if (arg == QLatin1String("-template_prefix") || arg == QLatin1String("-tp")) {
+                    argState = ArgTmplPfx;
+                } else if (arg == QLatin1String("-win32")) {
+                    dir_sep = QLatin1Char('\\');
+                } else if (arg == QLatin1String("-unix")) {
+                    dir_sep = QLatin1Char('/');
+                } else {
+                    return ArgumentUnknown;
+                }
+            } else if (arg.contains(QLatin1Char('='))) {
+                if (state.after)
+                    state.postcmds << arg;
+                else
+                    state.precmds << arg;
+            } else {
+                return ArgumentUnknown;
+            }
+            continue;
+        }
+        argState = ArgNone;
+    }
+    if (argState != ArgNone)
+        return ArgumentMalformed;
+    return ArgumentsOk;
+}
+
+void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state)
+{
+    if (!state.preconfigs.isEmpty())
+        state.precmds << (fL1S("CONFIG += ") + state.preconfigs.join(fL1S(" ")));
+    precmds = state.precmds.join(fL1S("\n"));
+    if (!state.postconfigs.isEmpty())
+        state.postcmds << (fL1S("CONFIG += ") + state.postconfigs.join(fL1S(" ")));
+    postcmds = state.postcmds.join(fL1S("\n"));
+
+    if (xqmakespec.isEmpty())
+        xqmakespec = qmakespec;
+}
+
+void QMakeGlobals::useEnvironment()
+{
+    if (xqmakespec.isEmpty())
+        xqmakespec = getEnv(QLatin1String("XQMAKESPEC"));
+    if (qmakespec.isEmpty()) {
+        qmakespec = getEnv(QLatin1String("QMAKESPEC"));
+        if (xqmakespec.isEmpty())
+            xqmakespec = qmakespec;
+    }
+}
+
+void QMakeGlobals::setCommandLineArguments(const QString &pwd, const QStringList &_args)
+{
+    QStringList args = _args;
+
+    QMakeCmdLineParserState state(pwd);
+    for (int pos = 0; pos < args.size(); pos++)
+        addCommandLineArguments(state, args, &pos);
+    commitCommandLineArguments(state);
+    useEnvironment();
+}
+
+void QMakeGlobals::setDirectories(const QString &input_dir, const QString &output_dir)
+{
+    if (input_dir != output_dir && !output_dir.isEmpty()) {
+        QString srcpath = input_dir;
+        if (!srcpath.endsWith(QLatin1Char('/')))
+            srcpath += QLatin1Char('/');
+        QString dstpath = output_dir;
+        if (!dstpath.endsWith(QLatin1Char('/')))
+            dstpath += QLatin1Char('/');
+        int srcLen = srcpath.length();
+        int dstLen = dstpath.length();
+        int lastSl = -1;
+        while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen))
+            if (srcpath.at(srcLen) == QLatin1Char('/'))
+                lastSl = 0;
+        source_root = srcpath.left(srcLen + lastSl);
+        build_root = dstpath.left(dstLen + lastSl);
+    }
+}
+
+QString QMakeGlobals::shadowedPath(const QString &fileName) const
+{
+    if (source_root.isEmpty())
+        return fileName;
+    if (fileName.startsWith(source_root)
+        && (fileName.length() == source_root.length()
+            || fileName.at(source_root.length()) == QLatin1Char('/'))) {
+        return build_root + fileName.mid(source_root.length());
+    }
+    return QString();
+}
+
+QString QMakeGlobals::getEnv(const QString &var) const
+{
+#ifdef PROEVALUATOR_SETENV
+    return environment.value(var);
+#else
+    return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
+#endif
+}
+
+QStringList QMakeGlobals::getPathListEnv(const QString &var) const
+{
+    QStringList ret;
+    QString val = getEnv(var);
+    if (!val.isEmpty()) {
+        QDir bdir;
+        QStringList vals = val.split(dirlist_sep);
+        ret.reserve(vals.length());
+        foreach (const QString &it, vals)
+            ret << QDir::cleanPath(bdir.absoluteFilePath(it));
+    }
+    return ret;
+}
+
+QString QMakeGlobals::expandEnvVars(const QString &str) const
+{
+    QString string = str;
+    int rep;
+    QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
+    while ((rep = reg_variableName.indexIn(string)) != -1)
+        string.replace(rep, reg_variableName.matchedLength(),
+                       getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3)));
+    return string;
+}
+
+#ifndef QT_BUILD_QMAKE
+#ifdef PROEVALUATOR_INIT_PROPS
+bool QMakeGlobals::initProperties()
+{
+    QByteArray data;
+#ifndef QT_BOOTSTRAPPED
+    QProcess proc;
+    proc.start(qmake_abslocation, QStringList() << QLatin1String("-query"));
+    if (!proc.waitForFinished())
+        return false;
+    data = proc.readAll();
+#else
+    if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake_abslocation) + QLatin1String(" -query"))
+                              .toLocal8Bit(), "r")) {
+        char buff[1024];
+        while (!feof(proc))
+            data.append(buff, int(fread(buff, 1, 1023, proc)));
+        QT_PCLOSE(proc);
+    }
+#endif
+    foreach (QByteArray line, data.split('\n'))
+        if (!line.startsWith("QMAKE_")) {
+            int off = line.indexOf(':');
+            if (off < 0) // huh?
+                continue;
+            if (line.endsWith('\r'))
+                line.chop(1);
+            QString name = QString::fromLatin1(line.left(off));
+            ProString value = ProString(QDir::fromNativeSeparators(
+                        QString::fromLocal8Bit(line.mid(off + 1))));
+            properties.insert(ProKey(name), value);
+            if (name.startsWith(QLatin1String("QT_")) && !name.contains(QLatin1Char('/'))) {
+                if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
+                    properties.insert(ProKey(name + QLatin1String("/raw")), value);
+                    properties.insert(ProKey(name + QLatin1String("/get")), value);
+                    if (name == QLatin1String("QT_INSTALL_PREFIX")
+                        || name == QLatin1String("QT_INSTALL_DATA")
+                        || name == QLatin1String("QT_INSTALL_BINS")) {
+                        name.replace(3, 7, QLatin1String("HOST"));
+                        properties.insert(ProKey(name), value);
+                        properties.insert(ProKey(name + QLatin1String("/get")), value);
+                    }
+                } else if (name.startsWith(QLatin1String("QT_HOST_"))) {
+                    properties.insert(ProKey(name + QLatin1String("/get")), value);
+                }
+            }
+        }
+    properties.insert(ProKey("QMAKE_VERSION"), ProString("2.01a"));
+    return true;
+}
+#else
+void QMakeGlobals::setProperties(const QHash<QString, QString> &props)
+{
+    QHash<QString, QString>::ConstIterator it = props.constBegin(), eit = props.constEnd();
+    for (; it != eit; ++it)
+        properties.insert(ProKey(it.key()), ProString(it.value()));
+}
+#endif
+#endif // QT_BUILD_QMAKE
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/qmakeglobals.h b/src/linguist/shared/qmakeglobals.h
new file mode 100644 (file)
index 0000000..a40d4c0
--- /dev/null
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMAKEGLOBALS_H
+#define QMAKEGLOBALS_H
+
+#include "qmake_global.h"
+#include "proitems.h"
+
+#ifdef QT_BUILD_QMAKE
+#  include <property.h>
+#endif
+
+#include <qhash.h>
+#include <qstringlist.h>
+#ifndef QT_BOOTSTRAPPED
+# include <qprocess.h>
+#endif
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qmutex.h>
+# include <qwaitcondition.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMakeEvaluator;
+
+class QMakeBaseKey
+{
+public:
+    QMakeBaseKey(const QString &_root, bool _hostBuild);
+
+    QString root;
+    bool hostBuild;
+};
+
+uint qHash(const QMakeBaseKey &key);
+bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two);
+
+class QMakeBaseEnv
+{
+public:
+    QMakeBaseEnv();
+    ~QMakeBaseEnv();
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+    QMutex mutex;
+    QWaitCondition cond;
+    bool inProgress;
+    // The coupling of this flag to thread safety exists because for other
+    // use cases failure is immediately fatal anyway.
+    bool isOk;
+#endif
+    QMakeEvaluator *evaluator;
+};
+
+class QMAKE_EXPORT QMakeCmdLineParserState
+{
+public:
+    QMakeCmdLineParserState(const QString &_pwd) : pwd(_pwd), after(false) {}
+    QString pwd;
+    QStringList precmds, preconfigs, postcmds, postconfigs;
+    bool after;
+};
+
+class QMAKE_EXPORT QMakeGlobals
+{
+public:
+    QMakeGlobals();
+    ~QMakeGlobals();
+
+    bool do_cache;
+    QString dir_sep;
+    QString dirlist_sep;
+    QString cachefile;
+#ifdef PROEVALUATOR_SETENV
+    QProcessEnvironment environment;
+#endif
+    QString qmake_abslocation;
+
+    QString qmakespec, xqmakespec;
+    QString user_template, user_template_prefix;
+    QString precmds, postcmds;
+
+#ifdef PROEVALUATOR_DEBUG
+    int debugLevel;
+#endif
+
+    enum ArgumentReturn { ArgumentUnknown, ArgumentMalformed, ArgumentsOk };
+    ArgumentReturn addCommandLineArguments(QMakeCmdLineParserState &state,
+                                           QStringList &args, int *pos);
+    void commitCommandLineArguments(QMakeCmdLineParserState &state);
+    void setCommandLineArguments(const QString &pwd, const QStringList &args);
+    void useEnvironment();
+    void setDirectories(const QString &input_dir, const QString &output_dir);
+#ifdef QT_BUILD_QMAKE
+    void setQMakeProperty(QMakeProperty *prop) { property = prop; }
+    ProString propertyValue(const ProKey &name) const { return property->value(name); }
+#else
+#  ifdef PROEVALUATOR_INIT_PROPS
+    bool initProperties();
+#  else
+    void setProperties(const QHash<QString, QString> &props);
+#  endif
+    ProString propertyValue(const ProKey &name) const { return properties.value(name); }
+#endif
+
+    QString expandEnvVars(const QString &str) const;
+    QString shadowedPath(const QString &fileName) const;
+
+private:
+    QString getEnv(const QString &) const;
+    QStringList getPathListEnv(const QString &var) const;
+
+    QString cleanSpec(QMakeCmdLineParserState &state, const QString &spec);
+
+    QString source_root, build_root;
+
+#ifdef QT_BUILD_QMAKE
+    QMakeProperty *property;
+#else
+    QHash<ProKey, ProString> properties;
+#endif
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+    QMutex mutex;
+#endif
+    QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs;
+
+    friend class QMakeEvaluator;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMAKEGLOBALS_H
similarity index 76%
rename from src/linguist/shared/profileparser.cpp
rename to src/linguist/shared/qmakeparser.cpp
index 96786ef..9b2983e 100644 (file)
 **
 ****************************************************************************/
 
-#include "profileparser.h"
+#include "qmakeparser.h"
 
 #include "ioutils.h"
-using namespace ProFileEvaluatorInternal;
+using namespace QMakeInternal;
 
-#include <QtCore/QFile>
+#include <qfile.h>
 #ifdef PROPARSER_THREAD_SAFE
-# include <QtCore/QThreadPool>
+# include <qthreadpool.h>
 #endif
 
 QT_BEGIN_NAMESPACE
@@ -107,11 +107,18 @@ static struct {
     QString strfor;
     QString strdefineTest;
     QString strdefineReplace;
+    QString stroption;
+    QString strhost_build;
+    QString strLINE;
+    QString strFILE;
+    QString strLITERAL_HASH;
+    QString strLITERAL_DOLLAR;
+    QString strLITERAL_WHITESPACE;
 } statics;
 
 }
 
-void ProFileParser::initialize()
+void QMakeParser::initialize()
 {
     if (!statics.strelse.isNull())
         return;
@@ -120,9 +127,16 @@ void ProFileParser::initialize()
     statics.strfor = QLatin1String("for");
     statics.strdefineTest = QLatin1String("defineTest");
     statics.strdefineReplace = QLatin1String("defineReplace");
+    statics.stroption = QLatin1String("option");
+    statics.strhost_build = QLatin1String("host_build");
+    statics.strLINE = QLatin1String("_LINE_");
+    statics.strFILE = QLatin1String("_FILE_");
+    statics.strLITERAL_HASH = QLatin1String("LITERAL_HASH");
+    statics.strLITERAL_DOLLAR = QLatin1String("LITERAL_DOLLAR");
+    statics.strLITERAL_WHITESPACE = QLatin1String("LITERAL_WHITESPACE");
 }
 
-ProFileParser::ProFileParser(ProFileCache *cache, ProFileParserHandler *handler)
+QMakeParser::QMakeParser(ProFileCache *cache, QMakeParserHandler *handler)
     : m_cache(cache)
     , m_handler(handler)
 {
@@ -130,7 +144,7 @@ ProFileParser::ProFileParser(ProFileCache *cache, ProFileParserHandler *handler)
     initialize();
 }
 
-ProFile *ProFileParser::parsedProFile(const QString &fileName, bool cache, const QString *contents)
+ProFile *QMakeParser::parsedProFile(const QString &fileName, bool cache)
 {
     ProFile *pro;
     if (cache && m_cache) {
@@ -162,10 +176,11 @@ ProFile *ProFileParser::parsedProFile(const QString &fileName, bool cache, const
             locker.unlock();
 #endif
             pro = new ProFile(fileName);
-            if (!(!contents ? read(pro) : read(pro, *contents))) {
+            if (!read(pro)) {
                 delete pro;
                 pro = 0;
             } else {
+                pro->itemsRef()->squeeze();
                 pro->ref();
             }
             ent->pro = pro;
@@ -182,7 +197,7 @@ ProFile *ProFileParser::parsedProFile(const QString &fileName, bool cache, const
         }
     } else {
         pro = new ProFile(fileName);
-        if (!(!contents ? read(pro) : read(pro, *contents))) {
+        if (!read(pro)) {
             delete pro;
             pro = 0;
         }
@@ -190,38 +205,58 @@ ProFile *ProFileParser::parsedProFile(const QString &fileName, bool cache, const
     return pro;
 }
 
-bool ProFileParser::read(ProFile *pro)
+ProFile *QMakeParser::parsedProBlock(
+        const QString &contents, const QString &name, int line, SubGrammar grammar)
+{
+    ProFile *pro = new ProFile(name);
+    if (!read(pro, contents, line, grammar)) {
+        delete pro;
+        pro = 0;
+    }
+    return pro;
+}
+
+bool QMakeParser::read(ProFile *pro)
 {
     QFile file(pro->fileName());
     if (!file.open(QIODevice::ReadOnly)) {
         if (m_handler && IoUtils::exists(pro->fileName()))
-            m_handler->parseError(QString(), 0, fL1S("%1 not readable.").arg(pro->fileName()));
+            m_handler->message(QMakeParserHandler::ParserIoError,
+                               fL1S("Cannot read %1: %2").arg(pro->fileName(), file.errorString()));
         return false;
     }
 
-    QString content(QString::fromLocal8Bit(file.readAll()));
+    QByteArray bcont = file.readAll();
+    if (bcont.startsWith(QByteArray("\xef\xbb\xbf"))) {
+        // UTF-8 BOM will cause subtle errors
+        m_handler->message(QMakeParserHandler::ParserIoError,
+                           fL1S("Unexpected UTF-8 BOM in %1").arg(pro->fileName()));
+        return false;
+    }
+    QString content(QString::fromLocal8Bit(bcont));
+    bcont.clear();
     file.close();
-    return read(pro, content);
+    return read(pro, content, 1, FullGrammar);
 }
 
-void ProFileParser::putTok(ushort *&tokPtr, ushort tok)
+void QMakeParser::putTok(ushort *&tokPtr, ushort tok)
 {
     *tokPtr++ = tok;
 }
 
-void ProFileParser::putBlockLen(ushort *&tokPtr, uint len)
+void QMakeParser::putBlockLen(ushort *&tokPtr, uint len)
 {
     *tokPtr++ = (ushort)len;
     *tokPtr++ = (ushort)(len >> 16);
 }
 
-void ProFileParser::putBlock(ushort *&tokPtr, const ushort *buf, uint len)
+void QMakeParser::putBlock(ushort *&tokPtr, const ushort *buf, uint len)
 {
     memcpy(tokPtr, buf, len * 2);
     tokPtr += len;
 }
 
-void ProFileParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len)
+void QMakeParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len)
 {
     uint hash = ProString::hash((const QChar *)buf, len);
     ushort *tokPtr = pTokPtr;
@@ -232,7 +267,7 @@ void ProFileParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len)
     pTokPtr = tokPtr + len;
 }
 
-void ProFileParser::finalizeHashStr(ushort *buf, uint len)
+void QMakeParser::finalizeHashStr(ushort *buf, uint len)
 {
     buf[-4] = TokHashLiteral;
     buf[-1] = len;
@@ -241,10 +276,10 @@ void ProFileParser::finalizeHashStr(ushort *buf, uint len)
     buf[-2] = (ushort)(hash >> 16);
 }
 
-bool ProFileParser::read(ProFile *pro, const QString &in)
+bool QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar grammar)
 {
     m_proFile = pro;
-    m_lineNo = 1;
+    m_lineNo = line;
 
     // Final precompiled token stream buffer
     QString tokBuff;
@@ -277,7 +312,7 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
     // Expression precompiler buffer.
     QString xprBuff;
     xprBuff.reserve(tokBuff.capacity()); // Excessive, but simple
-    ushort * const buf = (ushort *)xprBuff.constData();
+    ushort *buf = (ushort *)xprBuff.constData();
 
     // Parser state
     m_blockstack.clear();
@@ -295,18 +330,24 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
     m_operator = NoOperator;
     m_markLine = m_lineNo;
     m_inError = false;
-    Context context = CtxTest;
     int parens = 0; // Braces in value context
     int argc = 0;
     int wordCount = 0; // Number of words in currently accumulated expression
-    bool putSpace = false; // Only ever true inside quoted string
+    int lastIndent = 0; // Previous line's indentation, to detect accidental continuation abuse
     bool lineMarked = true; // For in-expression markers
-    ushort needSep = TokNewStr; // Complementary to putSpace: separator outside quotes
+    ushort needSep = TokNewStr; // Met unquoted whitespace
     ushort quote = 0;
     ushort term = 0;
 
-    ushort *ptr = buf;
-    ptr += 4;
+    Context context;
+    ushort *ptr;
+    if (grammar == ValueGrammar) {
+        context = CtxPureValue;
+        ptr = tokPtr + 2;
+    } else {
+        context = CtxTest;
+        ptr = buf + 4;
+    }
     ushort *xprPtr = ptr;
 
 #define FLUSH_LHS_LITERAL() \
@@ -358,16 +399,30 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
         putTok(tokPtr, TokValueTerminator); \
     } while (0)
 
+    const ushort *end; // End of this line
+    const ushort *cptr; // Start of next line
+    bool lineCont;
+    int indent;
+
+    if (context == CtxPureValue) {
+        end = (const ushort *)in.unicode() + in.length();
+        cptr = 0;
+        lineCont = false;
+        indent = 0; // just gcc being stupid
+        goto nextChr;
+    }
+
     forever {
         ushort c;
 
         // First, skip leading whitespace
-        for (;; ++cur) {
+        for (indent = 0; ; ++cur, ++indent) {
             c = *cur;
             if (c == '\n') {
                 ++cur;
                 goto flushLine;
             } else if (!c) {
+                cur = 0;
                 goto flushLine;
             } else if (c != ' ' && c != '\t' && c != '\r') {
                 break;
@@ -375,8 +430,6 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
         }
 
         // Then strip comments. Yep - no escaping is possible.
-        const ushort *end; // End of this line
-        const ushort *cptr; // Start of next line
         for (cptr = cur;; ++cptr) {
             c = *cptr;
             if (c == '#') {
@@ -405,7 +458,6 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
         }
 
         // Then look for line continuations. Yep - no escaping here as well.
-        bool lineCont;
         forever {
             // We don't have to check for underrun here, as we already determined
             // that the line is non-empty.
@@ -435,10 +487,6 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                 if (c == '$') {
                     if (*cur == '$') { // may be EOF, EOL, WS, '#' or '\\' if past end
                         cur++;
-                        if (putSpace) {
-                            putSpace = false;
-                            *ptr++ = ' ';
-                        }
                         FLUSH_LITERAL();
                         if (!lineMarked) {
                             lineMarked = true;
@@ -449,7 +497,7 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         tok = TokVariable;
                         c = *cur;
                         if (c == '[') {
-                            ptr += 2;
+                            ptr += 4;
                             tok = TokProperty;
                             term = ']';
                             c = *++cur;
@@ -458,7 +506,6 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                             term = '}';
                             c = *++cur;
                         } else if (c == '(') {
-                            // FIXME: could/should expand this immediately
                             ptr += 2;
                             tok = TokEnvVar;
                             term = ')';
@@ -470,7 +517,7 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         rtok = tok;
                         while ((c & 0xFF00) || c == '.' || c == '_' ||
                                (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
-                               (c >= '0' && c <= '9')) {
+                               (c >= '0' && c <= '9') || (c == '/' && term)) {
                             *ptr++ = c;
                             if (++cur == end) {
                                 c = 0;
@@ -481,6 +528,8 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         if (tok == TokVariable && c == '(')
                             tok = TokFuncName;
                       notfunc:
+                        if (ptr == xprPtr)
+                            languageWarning(fL1S("Missing name in expansion"));
                         if (quote)
                             tok |= TokQuoted;
                         if (needSep) {
@@ -488,15 +537,20 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                             wordCount++;
                         }
                         tlen = ptr - xprPtr;
-                        if (rtok == TokVariable) {
-                            xprPtr[-4] = tok;
-                            uint hash = ProString::hash((const QChar *)xprPtr, tlen);
-                            xprPtr[-3] = (ushort)hash;
-                            xprPtr[-2] = (ushort)(hash >> 16);
-                        } else {
-                            xprPtr[-2] = tok;
+                        if (rtok != TokVariable
+                            || !resolveVariable(xprPtr, tlen, needSep, &ptr,
+                                                &buf, &xprBuff, &tokPtr, &tokBuff, cur, in)) {
+                            if (rtok == TokVariable || rtok == TokProperty) {
+                                xprPtr[-4] = tok;
+                                uint hash = ProString::hash((const QChar *)xprPtr, tlen);
+                                xprPtr[-3] = (ushort)hash;
+                                xprPtr[-2] = (ushort)(hash >> 16);
+                                xprPtr[-1] = tlen;
+                            } else {
+                                xprPtr[-2] = tok;
+                                xprPtr[-1] = tlen;
+                            }
                         }
-                        xprPtr[-1] = tlen;
                         if ((tok & TokMask) == TokFuncName) {
                             cur++;
                           funcCall:
@@ -524,7 +578,6 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                             goto newWord;
                         }
                         if (term) {
-                            cur++;
                           checkTerm:
                             if (c != term) {
                                 parseError(fL1S("Missing %1 terminator [found %2]")
@@ -532,9 +585,9 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                                     .arg(c ? QString(c) : QString::fromLatin1("end-of-line")));
                                 pro->setOk(false);
                                 m_inError = true;
-                                if (c)
-                                    cur--;
                                 // Just parse on, as if there was a terminator ...
+                            } else {
+                                cur++;
                             }
                         }
                       joinToken:
@@ -543,23 +596,18 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         needSep = 0;
                         goto nextChr;
                     }
-                } else if (c == '\\' && cur != end) {
+                } else if (c == '\\') {
                     static const char symbols[] = "[]{}()$\\'\"";
-                    ushort c2 = *cur;
-                    if (!(c2 & 0xff00) && strchr(symbols, c2)) {
+                    ushort c2;
+                    if (cur != end && !((c2 = *cur) & 0xff00) && strchr(symbols, c2)) {
                         c = c2;
                         cur++;
+                    } else {
+                        deprecationWarning(fL1S("Unescaped backslashes are deprecated"));
                     }
                 } else if (quote) {
                     if (c == quote) {
                         quote = 0;
-                        if (putSpace) {
-                            putSpace = false;
-                            *ptr++ = ' ';
-                        }
-                        goto nextChr;
-                    } else if (c == ' ' || c == '\t') {
-                        putSpace = true;
                         goto nextChr;
                     } else if (c == '!' && ptr == xprPtr && context == CtxTest) {
                         m_invert ^= true;
@@ -568,12 +616,12 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                 } else if (c == '\'' || c == '"') {
                     quote = c;
                     goto nextChr;
-                } else if (c == ' ' || c == '\t') {
-                    FLUSH_LITERAL();
-                    goto nextWord;
                 } else if (context == CtxArgs) {
                     // Function arg context
-                    if (c == '(') {
+                    if (c == ' ' || c == '\t') {
+                        FLUSH_RHS_LITERAL();
+                        goto nextWord;
+                    } else if (c == '(') {
                         ++parens;
                     } else if (c == ')') {
                         if (--parens < 0) {
@@ -594,7 +642,7 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                                 finalizeCall(tokPtr, buf, ptr, theargc);
                                 goto nextItem;
                             } else if (term == '}') {
-                                c = (cur == end) ? 0 : *cur++;
+                                c = (cur == end) ? 0 : *cur;
                                 goto checkTerm;
                             } else {
                                 Q_ASSERT(!term);
@@ -609,7 +657,10 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                     }
                 } else if (context == CtxTest) {
                     // Test or LHS context
-                    if (c == '(') {
+                    if (c == ' ' || c == '\t') {
+                        FLUSH_LHS_LITERAL();
+                        goto nextWord;
+                    } else if (c == '(') {
                         FLUSH_LHS_LITERAL();
                         if (wordCount != 1) {
                             if (wordCount)
@@ -648,6 +699,10 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         finalizeCond(tokPtr, buf, ptr, wordCount);
                         flushCond(tokPtr);
                         ++m_blockstack.top().braceLevel;
+                        if (grammar == TestGrammar) {
+                            parseError(fL1S("Opening scope not permitted in this context."));
+                            pro->setOk(false);
+                        }
                         goto nextItem;
                     } else if (c == '}') {
                         FLUSH_LHS_LITERAL();
@@ -686,7 +741,10 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         FLUSH_LHS_LITERAL();
                         flushCond(tokPtr);
                         putLineMarker(tokPtr);
-                        if (wordCount != 1) {
+                        if (grammar == TestGrammar) {
+                            parseError(fL1S("Assignment not permitted in this context."));
+                            pro->setOk(false);
+                        } else if (wordCount != 1) {
                             parseError(fL1S("Assignment needs exactly one word on the left hand side."));
                             pro->setOk(false);
                             // Put empty variable name.
@@ -698,8 +756,11 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                         ptr = ++tokPtr;
                         goto nextToken;
                     }
-                } else { // context == CtxValue
-                    if (c == '{') {
+                } else if (context == CtxValue) {
+                    if (c == ' ' || c == '\t') {
+                        FLUSH_RHS_LITERAL();
+                        goto nextWord;
+                    } else if (c == '{') {
                         ++parens;
                     } else if (c == '}') {
                         if (!parens) {
@@ -709,12 +770,11 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                             goto closeScope;
                         }
                         --parens;
+                    } else if (c == '=') {
+                        if (indent < lastIndent)
+                            languageWarning(fL1S("Possible accidental line continuation"));
                     }
                 }
-                if (putSpace) {
-                    putSpace = false;
-                    *ptr++ = ' ';
-                }
                 *ptr++ = c;
               nextChr:
                 if (cur == end)
@@ -725,7 +785,7 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
           lineEnd:
             if (lineCont) {
                 if (quote) {
-                    putSpace = true;
+                    *ptr++ = ' ';
                 } else {
                     FLUSH_LITERAL();
                     needSep = TokNewStr;
@@ -733,7 +793,6 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                     xprPtr = ptr;
                 }
             } else {
-                c = '\n';
                 cur = cptr;
               flushLine:
                 FLUSH_LITERAL();
@@ -753,25 +812,28 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
                     if (context == CtxValue) {
                         tokPtr[-1] = 0; // sizehint
                         putTok(tokPtr, TokValueTerminator);
+                    } else if (context == CtxPureValue) {
+                        putTok(tokPtr, TokValueTerminator);
                     } else {
                         bogusTest(tokPtr);
                     }
                 } else if (context == CtxValue) {
                     FLUSH_VALUE_LIST();
+                    if (parens)
+                        languageWarning(fL1S("Possible braces mismatch"));
+                } else if (context == CtxPureValue) {
+                    tokPtr = ptr;
+                    putTok(tokPtr, TokValueTerminator);
                 } else {
                     finalizeCond(tokPtr, buf, ptr, wordCount);
                 }
-                if (!c)
+                if (!cur)
                     break;
                 ++m_lineNo;
                 goto freshLine;
             }
 
-        if (!lineCont) {
-            cur = cptr;
-            ++m_lineNo;
-            goto freshLine;
-        }
+        lastIndent = indent;
         lineMarked = false;
       ignore:
         cur = cptr;
@@ -785,8 +847,8 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
     }
     while (m_blockstack.size())
         leaveScope(tokPtr);
-    xprBuff.clear();
-    *pro->itemsRef() = QString(tokBuff.constData(), tokPtr - (ushort *)tokBuff.constData());
+    tokBuff.resize(tokPtr - (ushort *)tokBuff.constData()); // Reserved capacity stays
+    *pro->itemsRef() = tokBuff;
     return true;
 
 #undef FLUSH_VALUE_LIST
@@ -795,7 +857,7 @@ bool ProFileParser::read(ProFile *pro, const QString &in)
 #undef FLUSH_RHS_LITERAL
 }
 
-void ProFileParser::putLineMarker(ushort *&tokPtr)
+void QMakeParser::putLineMarker(ushort *&tokPtr)
 {
     if (m_markLine) {
         *tokPtr++ = TokLine;
@@ -804,7 +866,7 @@ void ProFileParser::putLineMarker(ushort *&tokPtr)
     }
 }
 
-void ProFileParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
+void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
 {
     m_blockstack.resize(m_blockstack.size() + 1);
     m_blockstack.top().special = special;
@@ -816,7 +878,7 @@ void ProFileParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
         m_markLine = m_lineNo;
 }
 
-void ProFileParser::leaveScope(ushort *&tokPtr)
+void QMakeParser::leaveScope(ushort *&tokPtr)
 {
     if (m_blockstack.top().inBranch) {
         // Put empty else block
@@ -832,7 +894,7 @@ void ProFileParser::leaveScope(ushort *&tokPtr)
 }
 
 // If we are on a fresh line, close all open one-line scopes.
-void ProFileParser::flushScopes(ushort *&tokPtr)
+void QMakeParser::flushScopes(ushort *&tokPtr)
 {
     if (m_state == StNew) {
         while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1)
@@ -847,7 +909,7 @@ void ProFileParser::flushScopes(ushort *&tokPtr)
 }
 
 // If there is a pending conditional, enter a new scope, otherwise flush scopes.
-void ProFileParser::flushCond(ushort *&tokPtr)
+void QMakeParser::flushCond(ushort *&tokPtr)
 {
     if (m_state == StCond) {
         putTok(tokPtr, TokBranch);
@@ -858,7 +920,7 @@ void ProFileParser::flushCond(ushort *&tokPtr)
     }
 }
 
-void ProFileParser::finalizeTest(ushort *&tokPtr)
+void QMakeParser::finalizeTest(ushort *&tokPtr)
 {
     flushScopes(tokPtr);
     putLineMarker(tokPtr);
@@ -874,7 +936,7 @@ void ProFileParser::finalizeTest(ushort *&tokPtr)
     m_canElse = true;
 }
 
-void ProFileParser::bogusTest(ushort *&tokPtr)
+void QMakeParser::bogusTest(ushort *&tokPtr)
 {
     flushScopes(tokPtr);
     m_operator = NoOperator;
@@ -884,7 +946,7 @@ void ProFileParser::bogusTest(ushort *&tokPtr)
     m_proFile->setOk(false);
 }
 
-void ProFileParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount)
+void QMakeParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount)
 {
     if (wordCount != 1) {
         if (wordCount) {
@@ -937,7 +999,7 @@ void ProFileParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int w
     putTok(tokPtr, TokCondition);
 }
 
-void ProFileParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc)
+void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc)
 {
     // Check for magic tokens
     if (*uc == TokHashLiteral) {
@@ -1019,6 +1081,26 @@ void ProFileParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int a
                 }
                 parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
                 return;
+            } else if (m_tmp == statics.stroption) {
+                if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1
+                        || m_invert || m_operator != NoOperator) {
+                    parseError(fL1S("option() must appear outside any control structures."));
+                    return;
+                }
+                if (*uce == (TokLiteral|TokNewStr)) {
+                    uint nlen = uce[1];
+                    if (uce[nlen + 2] == TokFuncTerminator) {
+                        m_tmp.setRawData((QChar *)uce + 2, nlen);
+                        if (m_tmp == statics.strhost_build) {
+                            m_proFile->setHostBuild(true);
+                        } else {
+                            parseError(fL1S("Unknown option() %1.").arg(m_tmp));
+                        }
+                        return;
+                    }
+                }
+                parseError(fL1S("option() requires one literal argument."));
+                return;
             }
         }
     }
@@ -1027,10 +1109,61 @@ void ProFileParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int a
     putBlock(tokPtr, uc, ptr - uc);
 }
 
-void ProFileParser::parseError(const QString &msg) const
+bool QMakeParser::resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
+                                  ushort **buf, QString *xprBuff,
+                                  ushort **tokPtr, QString *tokBuff,
+                                  const ushort *cur, const QString &in)
+{
+    QString out;
+    m_tmp.setRawData((const QChar *)xprPtr, tlen);
+    if (m_tmp == statics.strLINE) {
+        out.setNum(m_lineNo);
+    } else if (m_tmp == statics.strFILE) {
+        out = m_proFile->fileName();
+        // The string is typically longer than the variable reference, so we need
+        // to ensure that there is enough space in the output buffer - as unlikely
+        // as an overflow is to actually happen in practice.
+        int need = (in.length() - (cur - (const ushort *)in.constData()) + 2) * 5 + out.length();
+        int tused = *tokPtr - (ushort *)tokBuff->constData();
+        int xused;
+        int total;
+        bool ptrFinal = xprPtr >= (ushort *)tokBuff->constData()
+                && xprPtr < (ushort *)tokBuff->constData() + tokBuff->capacity();
+        if (ptrFinal) {
+            xused = xprPtr - (ushort *)tokBuff->constData();
+            total = xused + need;
+        } else {
+            xused = xprPtr - *buf;
+            total = tused + xused + need;
+        }
+        if (tokBuff->capacity() < total) {
+            tokBuff->reserve(total);
+            *tokPtr = (ushort *)tokBuff->constData() + tused;
+            xprBuff->reserve(total);
+            *buf = (ushort *)xprBuff->constData();
+            xprPtr = (ptrFinal ? (ushort *)tokBuff->constData() : *buf) + xused;
+        }
+    } else if (m_tmp == statics.strLITERAL_HASH) {
+        out = QLatin1String("#");
+    } else if (m_tmp == statics.strLITERAL_DOLLAR) {
+        out = QLatin1String("$");
+    } else if (m_tmp == statics.strLITERAL_WHITESPACE) {
+        out = QLatin1String("\t");
+    } else {
+        return false;
+    }
+    xprPtr -= 2; // Was set up for variable reference
+    xprPtr[-2] = TokLiteral | needSep;
+    xprPtr[-1] = out.length();
+    memcpy(xprPtr, out.constData(), out.length() * 2);
+    *ptr = xprPtr + out.length();
+    return true;
+}
+
+void QMakeParser::message(int type, const QString &msg) const
 {
     if (!m_inError && m_handler)
-        m_handler->parseError(m_proFile->fileName(), m_lineNo, msg);
+        m_handler->message(type, msg, m_proFile->fileName(), m_lineNo);
 }
 
 QT_END_NAMESPACE
similarity index 70%
rename from src/linguist/shared/profileparser.h
rename to src/linguist/shared/qmakeparser.h
index ad223b8..3a89bf7 100644 (file)
 **
 ****************************************************************************/
 
-#ifndef PROFILEPARSER_H
-#define PROFILEPARSER_H
+#ifndef QMAKEPARSER_H
+#define QMAKEPARSER_H
 
-#include "proparser_global.h"
+#include "qmake_global.h"
 #include "proitems.h"
-#include <QtCore/QHash>
-#include <QtCore/QStack>
-#ifdef PROPARSER_THREAD_SAFE
-# include <QtCore/QMutex>
-# include <QtCore/QWaitCondition>
-#endif
 
-// Be fast even for debug builds
-// MinGW GCC 4.5+ has a problem with always_inline putTok and putBlockLen
-#if defined(__GNUC__) && !(defined(__MINGW32__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5)
-# define ALWAYS_INLINE inline __attribute__((always_inline))
-#elif defined(_MSC_VER)
-# define ALWAYS_INLINE __forceinline
-#else
-# define ALWAYS_INLINE inline
+#include <qhash.h>
+#include <qstack.h>
+#ifdef PROPARSER_THREAD_SAFE
+# include <qmutex.h>
+# include <qwaitcondition.h>
 #endif
 
 QT_BEGIN_NAMESPACE
-class PROPARSER_EXPORT ProFileParserHandler
+class QMAKE_EXPORT QMakeParserHandler
 {
 public:
-    // Some error during parsing
-    virtual void parseError(const QString &filename, int lineNo, const QString &msg) = 0;
+    enum {
+        CategoryMask = 0xf00,
+        WarningMessage = 0x000,
+        ErrorMessage = 0x100,
+
+        SourceMask = 0xf0,
+        SourceParser = 0,
+
+        CodeMask = 0xf,
+        WarnLanguage = 0,
+        WarnDeprecated,
+
+        ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage,
+        ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated,
+
+        ParserIoError = ErrorMessage | SourceParser,
+        ParserError
+    };
+    virtual void message(int type, const QString &msg,
+                         const QString &fileName = QString(), int lineNo = 0) = 0;
 };
 
 class ProFileCache;
 
-class PROPARSER_EXPORT ProFileParser
+class QMAKE_EXPORT QMakeParser
 {
 public:
     // Call this from a concurrency-free context
     static void initialize();
 
-    ProFileParser(ProFileCache *cache, ProFileParserHandler *handler);
+    QMakeParser(ProFileCache *cache, QMakeParserHandler *handler);
 
+    enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
     // fileName is expected to be absolute and cleanPath()ed.
-    // If contents is non-null, it will be used instead of the file's actual content
-    ProFile *parsedProFile(const QString &fileName, bool cache = false,
-                           const QString *contents = 0);
-    ProFile *parsedProBlock(const QString &name, const QString &contents)
-        { return parsedProFile(name, false, &contents); }
+    ProFile *parsedProFile(const QString &fileName, bool cache = false);
+    ProFile *parsedProBlock(const QString &contents, const QString &name, int line = 0,
+                            SubGrammar grammar = FullGrammar);
 
 private:
     struct BlockScope {
@@ -102,7 +110,7 @@ private:
         StCond  // Conditionals met on current line
     };
 
-    enum Context { CtxTest, CtxValue, CtxArgs };
+    enum Context { CtxTest, CtxValue, CtxPureValue, CtxArgs };
     struct ParseCtx {
         int parens; // Nesting of non-functional parentheses
         int argc; // Number of arguments in current function call
@@ -113,7 +121,7 @@ private:
     };
 
     bool read(ProFile *pro);
-    bool read(ProFile *pro, const QString &content);
+    bool read(ProFile *pro, const QString &content, int line, SubGrammar grammar);
 
     ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok);
     ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len);
@@ -121,6 +129,10 @@ private:
     void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len);
     void finalizeHashStr(ushort *buf, uint len);
     void putLineMarker(ushort *&tokPtr);
+    ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
+                                       ushort **buf, QString *xprBuff,
+                                       ushort **tokPtr, QString *tokBuff,
+                                       const ushort *cur, const QString &in);
     void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount);
     void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc);
     void finalizeTest(ushort *&tokPtr);
@@ -130,7 +142,13 @@ private:
     void flushCond(ushort *&tokPtr);
     void flushScopes(ushort *&tokPtr);
 
-    void parseError(const QString &msg) const;
+    void message(int type, const QString &msg) const;
+    void parseError(const QString &msg) const
+            { message(QMakeParserHandler::ParserError, msg); }
+    void languageWarning(const QString &msg) const
+            { message(QMakeParserHandler::ParserWarnLanguage, msg); }
+    void deprecationWarning(const QString &msg) const
+            { message(QMakeParserHandler::ParserWarnDeprecated, msg); }
 
     // Current location
     ProFile *m_proFile;
@@ -147,7 +165,7 @@ private:
     QString m_tmp; // Temporary for efficient toQString
 
     ProFileCache *m_cache;
-    ProFileParserHandler *m_handler;
+    QMakeParserHandler *m_handler;
 
     // This doesn't help gcc 3.3 ...
     template<typename T> friend class QTypeInfo;
@@ -155,7 +173,7 @@ private:
     friend class ProFileCache;
 };
 
-class PROPARSER_EXPORT ProFileCache
+class QMAKE_EXPORT ProFileCache
 {
 public:
     ProFileCache() {}
@@ -183,12 +201,12 @@ private:
     QMutex mutex;
 #endif
 
-    friend class ProFileParser;
+    friend class QMakeParser;
 };
 
 #if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
-Q_DECLARE_TYPEINFO(ProFileParser::BlockScope, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(ProFileParser::Context, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QMakeParser::BlockScope, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QMakeParser::Context, Q_PRIMITIVE_TYPE);
 #endif
 
 QT_END_NAMESPACE