diff options
Diffstat (limited to 'src/shared/proparser')
-rw-r--r-- | src/shared/proparser/qmake_global.h | 4 | ||||
-rw-r--r-- | src/shared/proparser/qmakebuiltins.cpp | 33 | ||||
-rw-r--r-- | src/shared/proparser/qmakeevaluator.cpp | 219 | ||||
-rw-r--r-- | src/shared/proparser/qmakeevaluator.h | 15 | ||||
-rw-r--r-- | src/shared/proparser/qmakeevaluator_p.h | 27 | ||||
-rw-r--r-- | src/shared/proparser/qmakeglobals.cpp | 3 | ||||
-rw-r--r-- | src/shared/proparser/qmakeglobals.h | 4 |
7 files changed, 275 insertions, 30 deletions
diff --git a/src/shared/proparser/qmake_global.h b/src/shared/proparser/qmake_global.h index db0b175f748..2a3d9e817e0 100644 --- a/src/shared/proparser/qmake_global.h +++ b/src/shared/proparser/qmake_global.h @@ -53,4 +53,8 @@ # define ALWAYS_INLINE inline #endif +#ifdef PROEVALUATOR_FULL +# define PROEVALUATOR_DEBUG +#endif + #endif diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp index 38ebcdc4c77..1941b5b80e4 100644 --- a/src/shared/proparser/qmakebuiltins.cpp +++ b/src/shared/proparser/qmakebuiltins.cpp @@ -392,8 +392,11 @@ ProStringList QMakeEvaluator::evaluateExpandFunction( { QHash<ProKey, ProFunctionDef>::ConstIterator it = m_functionDefs.replaceFunctions.constFind(func); - if (it != m_functionDefs.replaceFunctions.constEnd()) - return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0); + 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); + } ExpandFunc func_t = ExpandFunc(statics.expands.value(func)); if (func_t == 0) { @@ -408,6 +411,7 @@ ProStringList QMakeEvaluator::evaluateExpandFunction( //why don't the builtin functions just use args_list? --Sam const ProStringList &args = expandVariableReferences(tokPtr, 5, true); + traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args)); ProStringList ret; switch (func_t) { @@ -1053,13 +1057,17 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( { QHash<ProKey, ProFunctionDef>::ConstIterator it = m_functionDefs.testFunctions.constFind(function); - if (it != m_functionDefs.testFunctions.constEnd()) - return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function); + if (it != m_functionDefs.testFunctions.constEnd()) { + const QList<ProStringList> args = prepareFunctionArgs(tokPtr); + traceMsg("calling %s(%s)", dbgKey(function), dbgStrListList(args)); + return evaluateBoolFunction(*it, args, function); + } TestFunc func_t = (TestFunc)statics.functions.value(function); //why don't the builtin functions just use args_list? --Sam const ProStringList &args = expandVariableReferences(tokPtr, 5, true); + traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args)); switch (func_t) { case T_DEFINED: { @@ -1389,9 +1397,20 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( return returnBool(evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()), ignore_error) || ignore_error); } - case T_DEBUG: - // Yup - do nothing. Nothing is going to enable debug output anyway. - return ReturnFalse; + case T_DEBUG: { +#ifdef PROEVALUATOR_DEBUG + if (args.count() != 2) { + evalError(fL1S("debug(level, message) requires two arguments.")); + return ReturnFalse; + } + int level = args.at(0).toInt(); + if (level <= m_debugLevel) { + const QString &msg = m_option->expandEnvVars(args.at(1).toQString(m_tmp2)); + debugMsg(level, "Project DEBUG: %s", qPrintable(msg)); + } +#endif + return ReturnTrue; + } case T_LOG: case T_ERROR: case T_WARNING: diff --git a/src/shared/proparser/qmakeevaluator.cpp b/src/shared/proparser/qmakeevaluator.cpp index 65d2200e018..223835506f5 100644 --- a/src/shared/proparser/qmakeevaluator.cpp +++ b/src/shared/proparser/qmakeevaluator.cpp @@ -164,7 +164,11 @@ const ProKey &QMakeEvaluator::map(const ProKey &var) QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeHandler *handler) - : m_option(option), m_parser(parser), m_handler(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(); @@ -401,39 +405,56 @@ static ALWAYS_INLINE void addStrList( 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) + if (tok & TokNewStr) { + debugMsg(2, "new string"); pending = false; + } ushort maskedTok = tok & TokMask; switch (maskedTok) { case TokLine: m_current.line = *tokPtr++; break; - case TokLiteral: - addStr(getStr(tokPtr), ret, pending, joined); - break; - case TokHashLiteral: - addStr(getHashStr(tokPtr), ret, pending, joined); - break; - case TokVariable: - addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined); - break; - case TokProperty: - addStr(propertyValue(getHashStr(tokPtr)), ret, pending, joined); - break; - case TokEnvVar: - addStrList(split_value_list(m_option->getEnv(getStr(tokPtr).toQString(m_tmp1))), - tok, ret, pending, joined); - 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; } @@ -491,6 +512,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( const ushort *tokPtr) { + traceMsg("entering block"); ProStringList curr; bool okey = true, or_op = false, invert = false; uint blockLen; @@ -527,12 +549,18 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( m_skipLevel--; #endif } else { - if (okey) + if (okey) { + traceMsg("taking 'then' branch"); ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; + traceMsg("finished 'then' branch"); + } tokPtr += blockLen; blockLen = getBlockLen(tokPtr); - if (!okey) + 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 @@ -556,6 +584,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( uint exprLen = getBlockLen(tokPtr); tokPtr += exprLen; blockLen = getBlockLen(tokPtr); + traceMsg("skipped loop"); ret = ReturnTrue; } tokPtr += blockLen; @@ -567,7 +596,10 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( 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); } @@ -575,12 +607,15 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( 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: @@ -590,8 +625,12 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( evalError(fL1S("Conditional must expand to exactly one word.")); okey = false; } else { - okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert; + 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; @@ -605,12 +644,16 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( 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: return ret; + default: + traceMsg("aborting block, function status: %s", dbgReturn(ret)); + return ret; } + traceMsg("test function returned %s", dbgBool(okey)); okey ^= invert; } } else if (m_cumulative) { @@ -624,6 +667,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( #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; @@ -638,9 +682,12 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( Q_ASSERT_X(false, "visitProBlock", "unexpected item type"); continue; } - if (ret != ReturnTrue && ret != ReturnFalse) + if (ret != ReturnTrue && ret != ReturnFalse) { + traceMsg("aborting block, status: %s", dbgReturn(ret)); return ret; + } } + traceMsg("leaving block, okey=%s", dbgBool(okey)); return returnBool(okey); } @@ -700,6 +747,11 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( } } + 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) { @@ -709,6 +761,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( evalError(fL1S("Ran into infinite loop (> 1000 iterations).")); break; } + traceMsg("loop iteration %d", index); } else { ProString val; do { @@ -716,6 +769,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( 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); } @@ -737,6 +791,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( do_break: m_loopLevel--; + traceMsg("done looping"); + if (!variable.isEmpty()) m_valuemapStack.top()[variable] = oldVarVal; return ret; @@ -787,6 +843,7 @@ void QMakeEvaluator::visitProVariable( // 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) { @@ -817,13 +874,16 @@ void QMakeEvaluator::visitProVariable( } } } + 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) { @@ -831,9 +891,11 @@ void QMakeEvaluator::visitProVariable( } 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(); @@ -1228,7 +1290,9 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile( visitCmdLine(m_option->precmds); } + debugMsg(1, "visiting file %s", qPrintable(pro->fileName())); visitProBlock(pro, pro->tokPtr()); + debugMsg(1, "done visiting file %s", qPrintable(pro->fileName())); if (flags & LoadPostFiles) { visitCmdLine(m_option->postcmds); @@ -1731,4 +1795,113 @@ void QMakeEvaluator::message(int type, const QString &msg) const m_current.line ? m_current.pro->fileName() : QString(), m_current.line); } +#ifdef PROEVALUATOR_DEBUG +void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const +{ + va_list ap; + + if (level <= m_debugLevel) { + fprintf(stderr, "DEBUG %d: ", level); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + } +} + +void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const +{ + va_list ap; + + if (!m_current.pro) + fprintf(stderr, "DEBUG 1: "); + else if (m_current.line <= 0) + fprintf(stderr, "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName())); + else + fprintf(stderr, "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote) +{ + QString ret; + ret.reserve(val.size() + 2); + const QChar *chars = val.constData(); + bool quote = forceQuote || val.isEmpty(); + for (int i = 0, l = val.size(); i < l; i++) { + QChar c = chars[i]; + ushort uc = c.unicode(); + if (uc < 32) { + switch (uc) { + case '\r': + ret += QLatin1String("\\r"); + break; + case '\n': + ret += QLatin1String("\\n"); + break; + case '\t': + ret += QLatin1String("\\t"); + break; + default: + ret += QString::fromLatin1("\\x%1").arg(uc, 2, 16, QLatin1Char('0')); + break; + } + } else { + switch (uc) { + case '\\': + ret += QLatin1String("\\\\"); + break; + case '"': + ret += QLatin1String("\\\""); + break; + case '\'': + ret += QLatin1String("\\'"); + break; + case 32: + quote = true; + // fallthrough + default: + ret += c; + break; + } + } + } + if (quote) { + ret.prepend(QLatin1Char('"')); + ret.append(QLatin1Char('"')); + } + return ret; +} + +QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas) +{ + QString ret; + + foreach (const ProString &str, vals) { + if (!ret.isEmpty()) { + if (commas) + ret += QLatin1Char(','); + ret += QLatin1Char(' '); + } + ret += formatValue(str); + } + return ret; +} + +QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists) +{ + QString ret; + + foreach (const ProStringList &list, lists) { + if (!ret.isEmpty()) + ret += QLatin1String(", "); + ret += formatValueList(list); + } + return ret; +} +#endif + QT_END_NAMESPACE diff --git a/src/shared/proparser/qmakeevaluator.h b/src/shared/proparser/qmakeevaluator.h index 92d6cc329f0..c6dcb60406a 100644 --- a/src/shared/proparser/qmakeevaluator.h +++ b/src/shared/proparser/qmakeevaluator.h @@ -206,6 +206,21 @@ public: enum { m_skipLevel = 0 }; #endif +#ifdef PROEVALUATOR_DEBUG + void debugMsgInternal(int level, const char *fmt, ...) const; + void traceMsgInternal(const char *fmt, ...) const; + static QString formatValue(const ProString &val, bool forceQuote = false); + static QString formatValueList(const ProStringList &vals, bool commas = false); + static QString formatValueListList(const QList<ProStringList> &vals); + + const int m_debugLevel; +#else + ALWAYS_INLINE void debugMsgInternal(int, const char *, ...) const {} + ALWAYS_INLINE void traceMsgInternal(const char *, ...) const {} + + enum { m_debugLevel = 0 }; +#endif + struct Location { Location() : pro(0), line(0) {} Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {} diff --git a/src/shared/proparser/qmakeevaluator_p.h b/src/shared/proparser/qmakeevaluator_p.h index d4fba31a173..59f96589184 100644 --- a/src/shared/proparser/qmakeevaluator_p.h +++ b/src/shared/proparser/qmakeevaluator_p.h @@ -35,6 +35,33 @@ #include <QRegExp> +#define debugMsg if (!m_debugLevel) {} else debugMsgInternal +#define traceMsg if (!m_debugLevel) {} else traceMsgInternal +#ifdef PROEVALUATOR_DEBUG +# define dbgBool(b) (b ? "true" : "false") +# define dbgReturn(r) \ + (r == ReturnError ? "error" : \ + r == ReturnBreak ? "break" : \ + r == ReturnNext ? "next" : \ + r == ReturnReturn ? "return" : \ + "<invalid>") +# define dbgKey(s) qPrintable(s.toString().toQString()) +# define dbgStr(s) qPrintable(formatValue(s, true)) +# define dbgStrList(s) qPrintable(formatValueList(s)) +# define dbgSepStrList(s) qPrintable(formatValueList(s, true)) +# define dbgStrListList(s) qPrintable(formatValueListList(s)) +# define dbgQStr(s) dbgStr(ProString(s)) +#else +# define dbgBool(b) 0 +# define dbgReturn(r) 0 +# define dbgKey(s) 0 +# define dbgStr(s) 0 +# define dbgStrList(s) 0 +# define dbgSepStrList(s) 0 +# define dbgStrListList(s) 0 +# define dbgQStr(s) 0 +#endif + QT_BEGIN_NAMESPACE namespace ProFileEvaluatorInternal { diff --git a/src/shared/proparser/qmakeglobals.cpp b/src/shared/proparser/qmakeglobals.cpp index aaa56855f6a..95e7f9a12c4 100644 --- a/src/shared/proparser/qmakeglobals.cpp +++ b/src/shared/proparser/qmakeglobals.cpp @@ -94,6 +94,9 @@ QMakeGlobals::QMakeGlobals() do_cache = true; +#ifdef PROEVALUATOR_DEBUG + debugLevel = 0; +#endif #ifdef Q_OS_WIN dirlist_sep = QLatin1Char(';'); dir_sep = QLatin1Char('\\'); diff --git a/src/shared/proparser/qmakeglobals.h b/src/shared/proparser/qmakeglobals.h index 761fbede4e8..f4f5e55ac45 100644 --- a/src/shared/proparser/qmakeglobals.h +++ b/src/shared/proparser/qmakeglobals.h @@ -106,6 +106,10 @@ public: QString user_template, user_template_prefix; QString precmds, postcmds; +#ifdef PROEVALUATOR_DEBUG + int debugLevel; +#endif + enum ArgumentReturn { ArgumentUnknown, ArgumentMalformed, ArgumentsOk }; ArgumentReturn addCommandLineArguments(QMakeCmdLineParserState &state, QStringList &args, int *pos); |