• Paul Wilkins's avatar
    Segment Features: · dfbc61f3
    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
qmakeevaluator.cpp 66.47 KiB
/****************************************************************************
**
** 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 &regexp, 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; }