An error occurred while loading the file. Please try again.
-
Paul Wilkins authored
Some basic plumbing added for a range of segment level features. MB_LVL_* changed to SEG_LVL_* to better reflect meaning. Change-Id: Iac96da36990aa0e40afc0d86e990df337fd0c50b
dfbc61f3
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the qmake application of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia 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.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmakeevaluator.h"
#include "qmakeevaluator_p.h"
#include "qmakeglobals.h"
#include "qmakeparser.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
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
#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");
statics.strQMAKE_PLATFORM = ProKey("QMAKE_PLATFORM");
#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[] = {
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
{ "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_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");
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
m_functionDefs = other.m_functionDefs;
m_valuemapStack = other.m_valuemapStack;
m_valuemapInited = true;
m_qmakespec = other.m_qmakespec;
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;
if (!source)
source = currentProFile();
const QChar *vals_data = vals.data();
const int vals_len = vals.length();
ushort quote = 0;
bool hadWord = false;
for (int x = 0; x < vals_len; x++) {
ushort unicode = vals_data[x].unicode();
if (unicode == quote) {
quote = 0;
continue;
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
switch (unicode) {
case '"':
case '\'':
quote = unicode;
hadWord = true;
continue;
case ' ':
case '\t':
if (!quote) {
if (hadWord) {
ret << ProString(build).setSource(source);
build.clear();
hadWord = false;
}
continue;
}
build += QChar(unicode);
break;
case '\\':
if (x + 1 != vals_len) {
ushort next = vals_data[++x].unicode();
if (next == '\'' || next == '"' || next == '\\')
unicode = next;
else
--x;
}
// fallthrough
default:
hadWord = true;
build += QChar(unicode);
break;
}
}
if (hadWord)
ret << ProString(build).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);
}
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 ®exp, const QString &replace, bool global, QString &tmp)
{
for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
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);
}
}
}
}
}
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
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 ProString &val = ProString(m_option->getEnv(var.toQString(m_tmp1)));
debugMsg(2, "env var %s => %s", dbgStr(var), dbgStr(val));
addStr(val, 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:
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
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--;
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
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:
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
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;
case TokReturn:
m_returnValue = curr;
curr.clear();
ret = ReturnReturn;
goto ctrlstm;
case TokBreak:
ret = ReturnBreak;
goto ctrlstm;
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
case TokNext:
ret = ReturnNext;
ctrlstm:
if (!m_skipLevel && okey != or_op) {
traceMsg("flow control statement '%s', aborting block", dbgReturn(ret));
return ret;
}
traceMsg("skipped flow control statement '%s'", dbgReturn(ret));
okey = false, or_op = true; // force next evaluation
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);
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
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));
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:
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);
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
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");
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
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();
else if (varName == statics.strQMAKE_PLATFORM)
updateFeaturePaths();
#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));
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
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;
}