summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2012-09-06 21:50:09 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2012-09-20 14:34:56 +0200
commitbf4fc2e5fca65204665ef2ad23a9792b72f727ab (patch)
treea6c323e8804181324d6cda3ddc806ad66d3bd080 /src
parente69551f23e47632d517f7ec7d6efc25580df8df0 (diff)
downloadqttools-bf4fc2e5fca65204665ef2ad23a9792b72f727ab.tar.gz
update qmake evaluator to newest version from qt creator
Change-Id: I66ec46dd87f922094f6937b50cc40e02ef08cc86 Reviewed-by: Daniel Teske <daniel.teske@digia.com> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/linguist/lrelease/main.cpp44
-rw-r--r--src/linguist/lupdate/main.cpp57
-rw-r--r--src/linguist/shared/ioutils.cpp71
-rw-r--r--src/linguist/shared/ioutils.h19
-rw-r--r--src/linguist/shared/profileevaluator.cpp3412
-rw-r--r--src/linguist/shared/profileevaluator.h155
-rw-r--r--src/linguist/shared/proitems.cpp186
-rw-r--r--src/linguist/shared/proitems.h214
-rw-r--r--src/linguist/shared/proparser.pri12
-rw-r--r--src/linguist/shared/qmake_global.h (renamed from src/linguist/shared/proparser_global.h)26
-rw-r--r--src/linguist/shared/qmakebuiltins.cpp1669
-rw-r--r--src/linguist/shared/qmakeevaluator.cpp1993
-rw-r--r--src/linguist/shared/qmakeevaluator.h309
-rw-r--r--src/linguist/shared/qmakeevaluator_p.h107
-rw-r--r--src/linguist/shared/qmakeglobals.cpp368
-rw-r--r--src/linguist/shared/qmakeglobals.h173
-rw-r--r--src/linguist/shared/qmakeparser.cpp (renamed from src/linguist/shared/profileparser.cpp)317
-rw-r--r--src/linguist/shared/qmakeparser.h (renamed from src/linguist/shared/profileparser.h)88
18 files changed, 5404 insertions, 3816 deletions
diff --git a/src/linguist/lrelease/main.cpp b/src/linguist/lrelease/main.cpp
index c282f6610..1a0fd9962 100644
--- a/src/linguist/lrelease/main.cpp
+++ b/src/linguist/lrelease/main.cpp
@@ -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())))) {
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
index a90e1128a..eb225d9ec 100644
--- a/src/linguist/lupdate/main.cpp
+++ b/src/linguist/lupdate/main.cpp
@@ -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;
diff --git a/src/linguist/shared/ioutils.cpp b/src/linguist/shared/ioutils.cpp
index 7f7a69401..4038b8520 100644
--- a/src/linguist/shared/ioutils.cpp
+++ b/src/linguist/shared/ioutils.cpp
@@ -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
diff --git a/src/linguist/shared/ioutils.h b/src/linguist/shared/ioutils.h
index 84b0fd471..86e098fcc 100644
--- a/src/linguist/shared/ioutils.h
+++ b/src/linguist/shared/ioutils.h
@@ -42,9 +42,11 @@
#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
diff --git a/src/linguist/shared/profileevaluator.cpp b/src/linguist/shared/profileevaluator.cpp
index 266bfa4c4..0ad9e507a 100644
--- a/src/linguist/shared/profileevaluator.cpp
+++ b/src/linguist/shared/profileevaluator.cpp
@@ -41,3373 +41,23 @@
#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>
+#include <QDir>
-#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;
+using namespace QMakeInternal;
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);
-
- // mkspecs
- if (re.exactMatch(m_option->qmakespec_name))
- return true;
-
- // 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
-//
-///////////////////////////////////////////////////////////////////////
-
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
diff --git a/src/linguist/shared/profileevaluator.h b/src/linguist/shared/profileevaluator.h
index 2b92603f7..2de40a78f 100644
--- a/src/linguist/shared/profileevaluator.h
+++ b/src/linguist/shared/profileevaluator.h
@@ -42,72 +42,29 @@
#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
diff --git a/src/linguist/shared/proitems.cpp b/src/linguist/shared/proitems.cpp
index 43ab9059d..8377a9a52 100644
--- a/src/linguist/shared/proitems.cpp
+++ b/src/linguist/shared/proitems.cpp
@@ -41,13 +41,13 @@
#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!
diff --git a/src/linguist/shared/proitems.h b/src/linguist/shared/proitems.h
index 144dda6ac..402bd7891 100644
--- a/src/linguist/shared/proitems.h
+++ b/src/linguist/shared/proitems.h
@@ -42,12 +42,16 @@
#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
diff --git a/src/linguist/shared/proparser.pri b/src/linguist/shared/proparser.pri
index 829c8cde7..69e09c8d7 100644
--- a/src/linguist/shared/proparser.pri
+++ b/src/linguist/shared/proparser.pri
@@ -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
diff --git a/src/linguist/shared/proparser_global.h b/src/linguist/shared/qmake_global.h
index 2a8c2439b..f794a9843 100644
--- a/src/linguist/shared/proparser_global.h
+++ b/src/linguist/shared/qmake_global.h
@@ -39,19 +39,29 @@
**
****************************************************************************/
-#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
index 000000000..97a92d80d
--- /dev/null
+++ b/src/linguist/shared/qmakebuiltins.cpp
@@ -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
index 000000000..9e91c0898
--- /dev/null
+++ b/src/linguist/shared/qmakeevaluator.cpp
@@ -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
index 000000000..e1d3f61f1
--- /dev/null
+++ b/src/linguist/shared/qmakeevaluator.h
@@ -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
index 000000000..825fdf505
--- /dev/null
+++ b/src/linguist/shared/qmakeevaluator_p.h
@@ -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
index 000000000..c326d0a8f
--- /dev/null
+++ b/src/linguist/shared/qmakeglobals.cpp
@@ -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
index 000000000..a40d4c055
--- /dev/null
+++ b/src/linguist/shared/qmakeglobals.h
@@ -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
diff --git a/src/linguist/shared/profileparser.cpp b/src/linguist/shared/qmakeparser.cpp
index 96786ef43..9b2983e03 100644
--- a/src/linguist/shared/profileparser.cpp
+++ b/src/linguist/shared/qmakeparser.cpp
@@ -39,14 +39,14 @@
**
****************************************************************************/
-#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
diff --git a/src/linguist/shared/profileparser.h b/src/linguist/shared/qmakeparser.h
index ad223b851..3a89bf7f9 100644
--- a/src/linguist/shared/profileparser.h
+++ b/src/linguist/shared/qmakeparser.h
@@ -39,52 +39,60 @@
**
****************************************************************************/
-#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