summaryrefslogtreecommitdiffstats
path: root/qmake/library/qmakeevaluator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qmake/library/qmakeevaluator.cpp')
-rw-r--r--qmake/library/qmakeevaluator.cpp1981
1 files changed, 1981 insertions, 0 deletions
diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp
new file mode 100644
index 0000000000..4376865820
--- /dev/null
+++ b/qmake/library/qmakeevaluator.cpp
@@ -0,0 +1,1981 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the qmake application 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)
+{
+ 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);
+ }
+
+ //why don't the builtin functions just use args_list? --Sam
+ return evaluateBuiltinConditional(func, expandVariableReferences(tokPtr, 5, true));
+}
+
+ProStringList QMakeEvaluator::evaluateExpandFunction(
+ const ProKey &func, const ushort *&tokPtr)
+{
+ 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);
+ }
+
+ //why don't the builtin functions just use args_list? --Sam
+ return evaluateBuiltinExpand(func, expandVariableReferences(tokPtr, 5, true));
+}
+
+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