1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "profileevaluator.h"
32 #include "profileparser.h"
35 #include <QtCore/QByteArray>
36 #include <QtCore/QDateTime>
37 #include <QtCore/QDebug>
38 #include <QtCore/QDir>
39 #include <QtCore/QFile>
40 #include <QtCore/QFileInfo>
41 #include <QtCore/QList>
42 #include <QtCore/QRegExp>
43 #include <QtCore/QSet>
44 #include <QtCore/QStack>
45 #include <QtCore/QString>
46 #include <QtCore/QStringList>
47 #include <QtCore/QTextStream>
48 #ifdef PROEVALUATOR_THREAD_SAFE
49 # include <QtCore/QThreadPool>
54 #include <sys/utsname.h>
62 #define QT_POPEN _popen
63 #define QT_PCLOSE _pclose
65 #define QT_POPEN popen
66 #define QT_PCLOSE pclose
69 using namespace ProFileEvaluatorInternal;
73 using namespace ProStringConstants;
76 ///////////////////////////////////////////////////////////////////////
80 ///////////////////////////////////////////////////////////////////////
82 ProFileOption::ProFileOption()
85 dirlist_sep = QLatin1Char(';');
86 dir_sep = QLatin1Char('\\');
88 dirlist_sep = QLatin1Char(':');
89 dir_sep = QLatin1Char('/');
91 qmakespec = QString::fromLocal8Bit(qgetenv("QMAKESPEC").data());
93 #if defined(Q_OS_WIN32)
94 target_mode = TARG_WIN_MODE;
95 #elif defined(Q_OS_MAC)
96 target_mode = TARG_MACX_MODE;
97 #elif defined(Q_OS_QNX6)
98 target_mode = TARG_QNX6_MODE;
100 target_mode = TARG_UNIX_MODE;
103 #ifdef PROEVALUATOR_THREAD_SAFE
104 base_inProgress = false;
108 ProFileOption::~ProFileOption()
112 ///////////////////////////////////////////////////////////////////////
114 // ProFileEvaluator::Private
116 ///////////////////////////////////////////////////////////////////////
118 #define fL1S(s) QString::fromLatin1(s)
120 class ProFileEvaluator::Private
123 static void initStatics();
124 Private(ProFileEvaluator *q_, ProFileOption *option, ProFileParser *parser,
125 ProFileEvaluatorHandler *handler);
138 static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
139 ProString getStr(const ushort *&tokPtr);
140 ProString getHashStr(const ushort *&tokPtr);
141 void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
142 static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
143 static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
144 void skipExpression(const ushort *&tokPtr);
146 VisitReturn visitProFile(ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
147 ProFileEvaluator::LoadFlags flags);
148 VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
149 VisitReturn visitProBlock(const ushort *tokPtr);
150 VisitReturn visitProLoop(const ProString &variable, const ushort *exprPtr,
151 const ushort *tokPtr);
152 void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr);
153 void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
155 static inline const ProString &map(const ProString &var);
156 QHash<ProString, ProStringList> *findValues(const ProString &variableName,
157 QHash<ProString, ProStringList>::Iterator *it);
158 ProStringList &valuesRef(const ProString &variableName);
159 ProStringList valuesDirect(const ProString &variableName) const;
160 ProStringList values(const ProString &variableName) const;
161 QString propertyValue(const QString &val, bool complain = true) const;
163 static ProStringList split_value_list(const QString &vals);
164 bool isActiveConfig(const QString &config, bool regex = false);
165 ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
166 ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
167 ProStringList evaluateExpandFunction(const ProString &function, const ProString &arguments);
168 ProStringList evaluateExpandFunction(const ProString &function, const ushort *&tokPtr);
169 ProStringList evaluateExpandFunction(const ProString &function, const ProStringList &args);
170 void evalError(const QString &msg) const;
172 QString currentFileName() const;
173 QString currentDirectory() const;
174 ProFile *currentProFile() const;
175 QString resolvePath(const QString &fileName) const
176 { return IoUtils::resolvePath(currentDirectory(), fileName); }
178 VisitReturn evaluateConditionalFunction(const ProString &function, const ProString &arguments);
179 VisitReturn evaluateConditionalFunction(const ProString &function, const ushort *&tokPtr);
180 VisitReturn evaluateConditionalFunction(const ProString &function, const ProStringList &args);
181 bool evaluateFileDirect(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
182 ProFileEvaluator::LoadFlags flags);
183 bool evaluateFile(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
184 ProFileEvaluator::LoadFlags flags);
185 bool evaluateFeatureFile(const QString &fileName);
186 enum EvalIntoMode { EvalProOnly, EvalWithDefaults, EvalWithSetup };
187 bool evaluateFileInto(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
188 QHash<ProString, ProStringList> *values, FunctionDefs *defs,
189 EvalIntoMode mode); // values are output-only, defs are input-only
191 static ALWAYS_INLINE VisitReturn returnBool(bool b)
192 { return b ? ReturnTrue : ReturnFalse; }
194 QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
195 QList<ProStringList> prepareFunctionArgs(const ProString &arguments);
196 ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok);
197 VisitReturn evaluateBoolFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList,
198 const ProString &function);
200 QStringList qmakeMkspecPaths() const;
201 QStringList qmakeFeaturePaths() const;
204 int m_loopLevel; // To report unexpected break() and next()s
208 Location() : pro(0), line(0) {}
209 Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {}
214 Location m_current; // Currently evaluated location
215 QStack<Location> m_locationStack; // All execution location changes
216 QStack<ProFile *> m_profileStack; // Includes only
221 FunctionDefs m_functionDefs;
222 ProStringList m_returnValue;
223 QStack<QHash<ProString, ProStringList> > m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii.
224 QHash<const ProFile*, QHash<ProString, ProStringList> > m_filevaluemap; // Variables per include file
225 QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
227 QStringList m_addUserConfigCmdArgs;
228 QStringList m_removeUserConfigCmdArgs;
230 ProFileOption *m_option;
231 ProFileParser *m_parser;
232 ProFileEvaluatorHandler *m_handler;
235 E_MEMBER=1, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
236 E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
237 E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
238 E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
243 T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
244 T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
245 T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
246 T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF
250 V_LITERAL_DOLLAR, V_LITERAL_HASH, V_LITERAL_WHITESPACE,
251 V_DIRLIST_SEPARATOR, V_DIR_SEPARATOR,
252 V_OUT_PWD, V_PWD, V_IN_PWD,
253 V__FILE_, V__LINE_, V__PRO_FILE_, V__PRO_FILE_PWD_,
254 V_QMAKE_HOST_arch, V_QMAKE_HOST_name, V_QMAKE_HOST_os,
255 V_QMAKE_HOST_version, V_QMAKE_HOST_version_string,
256 V__DATE_, V__QMAKE_CACHE_
276 ProString strTEMPLATE;
277 ProString strQMAKE_DIR_SEP;
278 QHash<ProString, int> expands;
279 QHash<ProString, int> functions;
280 QHash<ProString, int> varList;
281 QHash<ProString, ProString> varMap;
282 QRegExp reg_variableName;
283 ProStringList fakeValue;
286 void ProFileEvaluator::Private::initStatics()
288 if (!statics.field_sep.isNull())
291 statics.field_sep = QLatin1String(" ");
292 statics.strtrue = QLatin1String("true");
293 statics.strfalse = QLatin1String("false");
294 statics.strunix = QLatin1String("unix");
295 statics.strmacx = QLatin1String("macx");
296 statics.strqnx6 = QLatin1String("qnx6");
297 statics.strmac9 = QLatin1String("mac9");
298 statics.strmac = QLatin1String("mac");
299 statics.strwin32 = QLatin1String("win32");
300 statics.strCONFIG = ProString("CONFIG");
301 statics.strARGS = ProString("ARGS");
302 statics.strDot = QLatin1String(".");
303 statics.strDotDot = QLatin1String("..");
304 statics.strever = QLatin1String("ever");
305 statics.strforever = QLatin1String("forever");
306 statics.strTEMPLATE = ProString("TEMPLATE");
307 statics.strQMAKE_DIR_SEP = ProString("QMAKE_DIR_SEP");
309 statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
310 statics.reg_variableName.setMinimal(true);
312 statics.fakeValue.detach(); // It has to have a unique begin() value
314 static const struct {
315 const char * const name;
316 const ExpandFunc func;
318 { "member", E_MEMBER },
319 { "first", E_FIRST },
323 { "fromfile", E_FROMFILE },
326 { "sprintf", E_SPRINTF },
328 { "split", E_SPLIT },
329 { "basename", E_BASENAME },
330 { "dirname", E_DIRNAME },
331 { "section", E_SECTION },
333 { "system", E_SYSTEM },
334 { "unique", E_UNIQUE },
335 { "quote", E_QUOTE },
336 { "escape_expand", E_ESCAPE_EXPAND },
337 { "upper", E_UPPER },
338 { "lower", E_LOWER },
339 { "re_escape", E_RE_ESCAPE },
340 { "files", E_FILES },
341 { "prompt", E_PROMPT }, // interactive, so cannot be implemented
342 { "replace", E_REPLACE }
344 for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
345 statics.expands.insert(ProString(expandInits[i].name), expandInits[i].func);
347 static const struct {
348 const char * const name;
351 { "requires", T_REQUIRES },
352 { "greaterThan", T_GREATERTHAN },
353 { "lessThan", T_LESSTHAN },
354 { "equals", T_EQUALS },
355 { "isEqual", T_EQUALS },
356 { "exists", T_EXISTS },
357 { "export", T_EXPORT },
358 { "clear", T_CLEAR },
359 { "unset", T_UNSET },
361 { "CONFIG", T_CONFIG },
363 { "isActiveConfig", T_CONFIG },
364 { "system", T_SYSTEM },
365 { "return", T_RETURN },
366 { "break", T_BREAK },
368 { "defined", T_DEFINED },
369 { "contains", T_CONTAINS },
370 { "infile", T_INFILE },
371 { "count", T_COUNT },
372 { "isEmpty", T_ISEMPTY },
374 { "include", T_INCLUDE },
375 { "debug", T_DEBUG },
376 { "message", T_MESSAGE },
377 { "warning", T_MESSAGE },
378 { "error", T_MESSAGE },
380 for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
381 statics.functions.insert(ProString(testInits[i].name), testInits[i].func);
383 static const char * const names[] = {
384 "LITERAL_DOLLAR", "LITERAL_HASH", "LITERAL_WHITESPACE",
385 "DIRLIST_SEPARATOR", "DIR_SEPARATOR",
386 "OUT_PWD", "PWD", "IN_PWD",
387 "_FILE_", "_LINE_", "_PRO_FILE_", "_PRO_FILE_PWD_",
388 "QMAKE_HOST.arch", "QMAKE_HOST.name", "QMAKE_HOST.os",
389 "QMAKE_HOST.version", "QMAKE_HOST.version_string",
390 "_DATE_", "_QMAKE_CACHE_"
392 for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
393 statics.varList.insert(ProString(names[i]), i);
395 static const struct {
396 const char * const oldname, * const newname;
398 { "INTERFACES", "FORMS" },
399 { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
400 { "TARGETDEPS", "POST_TARGETDEPS" },
401 { "LIBPATH", "QMAKE_LIBDIR" },
402 { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
403 { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
404 { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
405 { "PRECOMPH", "PRECOMPILED_HEADER" },
406 { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
407 { "INCPATH", "INCLUDEPATH" },
408 { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
409 { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
410 { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
411 { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
412 { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
413 { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
414 { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
415 { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
416 { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }
418 for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
419 statics.varMap.insert(ProString(mapInits[i].oldname),
420 ProString(mapInits[i].newname));
423 const ProString &ProFileEvaluator::Private::map(const ProString &var)
425 QHash<ProString, ProString>::ConstIterator it = statics.varMap.constFind(var);
426 return (it != statics.varMap.constEnd()) ? it.value() : var;
430 ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option,
431 ProFileParser *parser, ProFileEvaluatorHandler *handler)
432 : q(q_), m_option(option), m_parser(parser), m_handler(handler)
434 // So that single-threaded apps don't have to call initialize() for now.
437 // Configuration, more or less
444 m_valuemapStack.push(QHash<ProString, ProStringList>());
447 ProFileEvaluator::Private::~Private()
451 //////// Evaluator tools /////////
453 uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr)
455 uint len = *tokPtr++;
456 len |= (uint)*tokPtr++ << 16;
460 ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr)
462 uint len = *tokPtr++;
463 ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, NoHash);
468 ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr)
470 uint hash = getBlockLen(tokPtr);
471 uint len = *tokPtr++;
472 ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
477 void ProFileEvaluator::Private::skipStr(const ushort *&tokPtr)
479 uint len = *tokPtr++;
483 void ProFileEvaluator::Private::skipHashStr(const ushort *&tokPtr)
486 uint len = *tokPtr++;
490 // FIXME: this should not build new strings for direct sections.
491 // Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
492 ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals)
498 const ushort SPACE = ' ';
499 const ushort LPAREN = '(';
500 const ushort RPAREN = ')';
501 const ushort SINGLEQUOTE = '\'';
502 const ushort DOUBLEQUOTE = '"';
503 const ushort BACKSLASH = '\\';
506 const QChar *vals_data = vals.data();
507 const int vals_len = vals.length();
508 for (int x = 0, parens = 0; x < vals_len; x++) {
509 unicode = vals_data[x].unicode();
510 if (x != (int)vals_len-1 && unicode == BACKSLASH &&
511 (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
512 build += vals_data[x++]; //get that 'escape'
513 } else if (!quote.isEmpty() && unicode == quote.top()) {
515 } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
517 } else if (unicode == RPAREN) {
519 } else if (unicode == LPAREN) {
523 if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
524 ret << ProString(build, NoHash);
527 build += vals_data[x];
530 if (!build.isEmpty())
531 ret << ProString(build, NoHash);
535 static void zipEmpty(ProStringList *value)
537 for (int i = value->size(); --i >= 0;)
538 if (value->at(i).isEmpty())
542 static void insertUnique(ProStringList *varlist, const ProStringList &value)
544 foreach (const ProString &str, value)
545 if (!str.isEmpty() && !varlist->contains(str))
546 varlist->append(str);
549 static void removeAll(ProStringList *varlist, const ProString &value)
551 for (int i = varlist->size(); --i >= 0; )
552 if (varlist->at(i) == value)
556 static void removeEach(ProStringList *varlist, const ProStringList &value)
558 foreach (const ProString &str, value)
560 removeAll(varlist, str);
563 static void replaceInList(ProStringList *varlist,
564 const QRegExp ®exp, const QString &replace, bool global, QString &tmp)
566 for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
567 QString val = varit->toQString(tmp);
568 QString copy = val; // Force detach and have a reference value
569 val.replace(regexp, replace);
570 if (!val.isSharedWith(copy)) {
572 varit = varlist->erase(varit);
574 *varit = ProString(val);
585 static QString expandEnvVars(const QString &str)
587 QString string = str;
589 QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
590 while ((rep = reg_variableName.indexIn(string)) != -1)
591 string.replace(rep, reg_variableName.matchedLength(),
592 QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
596 // This is braindead, but we want qmake compat
597 static QString fixPathToLocalOS(const QString &str)
599 QString string = expandEnvVars(str);
601 if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
602 string[0] = string[0].toLower();
604 #if defined(Q_OS_WIN32)
605 string.replace(QLatin1Char('/'), QLatin1Char('\\'));
607 string.replace(QLatin1Char('\\'), QLatin1Char('/'));
612 static bool isTrue(const ProString &_str, QString &tmp)
614 const QString &str = _str.toQString(tmp);
615 return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
618 //////// Evaluator /////////
620 static ALWAYS_INLINE void addStr(
621 const ProString &str, ProStringList *ret, bool &pending, bool joined)
624 ret->last().append(str, &pending);
630 ret->last().append(str);
635 static ALWAYS_INLINE void addStrList(
636 const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
638 if (!list.isEmpty()) {
640 ret->last().append(list, &pending, !(tok & TokQuoted));
642 if (tok & TokQuoted) {
647 ret->last().append(list);
650 // Another qmake bizzarity: if nothing is pending and the
651 // first element is empty, it will be eaten
652 if (!list.at(0).isEmpty()) {
659 ret->last().append(list.at(0));
661 // This is somewhat slow, but a corner case
662 for (int j = 1; j < list.size(); ++j) {
671 void ProFileEvaluator::Private::evaluateExpression(
672 const ushort *&tokPtr, ProStringList *ret, bool joined)
676 bool pending = false;
678 ushort tok = *tokPtr++;
681 ushort maskedTok = tok & TokMask;
684 m_current.line = *tokPtr++;
687 addStr(getStr(tokPtr), ret, pending, joined);
690 addStr(getHashStr(tokPtr), ret, pending, joined);
693 addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined);
696 addStr(ProString(propertyValue(
697 getStr(tokPtr).toQString(m_tmp1)), NoHash), ret, pending, joined);
700 addStrList(split_value_list(QString::fromLocal8Bit(qgetenv(
701 getStr(tokPtr).toQString(m_tmp1).toLatin1().constData()))), tok, ret, pending, joined);
704 ProString func = getHashStr(tokPtr);
705 addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
714 void ProFileEvaluator::Private::skipExpression(const ushort *&pTokPtr)
716 const ushort *tokPtr = pTokPtr;
718 ushort tok = *tokPtr++;
721 m_current.line = *tokPtr++;
723 case TokValueTerminator:
724 case TokFuncTerminator:
727 case TokArgSeparator:
730 switch (tok & TokMask) {
743 skipExpression(pTokPtr);
747 Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
754 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
755 ProFile *pro, const ushort *tokPtr)
759 return visitProBlock(tokPtr);
762 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
763 const ushort *tokPtr)
766 bool okey = true, or_op = false, invert = false;
768 VisitReturn ret = ReturnTrue;
769 while (ushort tok = *tokPtr++) {
772 m_current.line = *tokPtr++;
776 case TokAppendUnique:
779 visitProVariable(tok, curr, tokPtr);
783 blockLen = getBlockLen(tokPtr);
787 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
789 blockLen = getBlockLen(tokPtr);
794 if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
795 ret = visitProBlock(tokPtr);
800 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
802 blockLen = getBlockLen(tokPtr);
804 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
807 okey = true, or_op = false; // force next evaluation
810 if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
812 uint exprLen = getBlockLen(tokPtr);
814 blockLen = getBlockLen(tokPtr);
815 ret = visitProBlock(tokPtr);
816 } else if (okey != or_op) {
817 const ProString &variable = getHashStr(tokPtr);
818 uint exprLen = getBlockLen(tokPtr);
819 const ushort *exprPtr = tokPtr;
821 blockLen = getBlockLen(tokPtr);
822 ret = visitProLoop(variable, exprPtr, tokPtr);
825 uint exprLen = getBlockLen(tokPtr);
827 blockLen = getBlockLen(tokPtr);
831 okey = true, or_op = false; // force next evaluation
835 if (m_cumulative || okey != or_op) {
836 const ProString &name = getHashStr(tokPtr);
837 blockLen = getBlockLen(tokPtr);
838 visitProFunctionDef(tok, name, tokPtr);
841 blockLen = getBlockLen(tokPtr);
844 okey = true, or_op = false; // force next evaluation
856 if (!m_skipLevel && okey != or_op) {
857 if (curr.size() != 1) {
858 evalError(fL1S("Conditional must expand to exactly one word."));
861 okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert;
864 or_op = !okey; // tentatively force next evaluation
869 if (!m_skipLevel && okey != or_op) {
870 if (curr.size() != 1) {
871 evalError(fL1S("Test name must expand to exactly one word."));
872 skipExpression(tokPtr);
875 ret = evaluateConditionalFunction(curr.at(0), tokPtr);
877 case ReturnTrue: okey = true; break;
878 case ReturnFalse: okey = false; break;
883 } else if (m_cumulative) {
885 if (curr.size() != 1)
886 skipExpression(tokPtr);
888 evaluateConditionalFunction(curr.at(0), tokPtr);
891 skipExpression(tokPtr);
893 or_op = !okey; // tentatively force next evaluation
898 const ushort *oTokPtr = --tokPtr;
899 evaluateExpression(tokPtr, &curr, false);
900 if (tokPtr != oTokPtr)
903 Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
905 if (ret != ReturnTrue && ret != ReturnFalse)
912 void ProFileEvaluator::Private::visitProFunctionDef(
913 ushort tok, const ProString &name, const ushort *tokPtr)
915 QHash<ProString, FunctionDef> *hash =
917 ? &m_functionDefs.testFunctions
918 : &m_functionDefs.replaceFunctions);
919 hash->insert(name, FunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
922 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(
923 const ProString &_variable, const ushort *exprPtr, const ushort *tokPtr)
925 VisitReturn ret = ReturnTrue;
926 bool infinite = false;
929 ProStringList oldVarVal;
930 ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
931 if (_variable.isEmpty()) {
932 if (it_list != statics.strever) {
933 evalError(fL1S("Invalid loop expression."));
936 it_list = ProString(statics.strforever);
938 variable = map(_variable);
939 oldVarVal = valuesDirect(variable);
941 ProStringList list = valuesDirect(it_list);
942 if (list.isEmpty()) {
943 if (it_list == statics.strforever) {
946 const QString &itl = it_list.toQString(m_tmp1);
947 int dotdot = itl.indexOf(statics.strDotDot);
950 int start = itl.left(dotdot).toInt(&ok);
952 int end = itl.mid(dotdot+2).toInt(&ok);
955 for (int i = start; i <= end; i++)
956 list << ProString(QString::number(i), NoHash);
958 for (int i = start; i >= end; i--)
959 list << ProString(QString::number(i), NoHash);
970 if (!variable.isEmpty())
971 m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++), NoHash));
973 evalError(fL1S("ran into infinite loop (> 1000 iterations)."));
979 if (index >= list.count())
981 val = list.at(index++);
982 } while (val.isEmpty()); // stupid, but qmake is like that
983 m_valuemapStack.top()[variable] = ProStringList(val);
986 ret = visitProBlock(tokPtr);
1004 if (!variable.isEmpty())
1005 m_valuemapStack.top()[variable] = oldVarVal;
1009 void ProFileEvaluator::Private::visitProVariable(
1010 ushort tok, const ProStringList &curr, const ushort *&tokPtr)
1012 int sizeHint = *tokPtr++;
1014 if (curr.size() != 1) {
1015 skipExpression(tokPtr);
1016 evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
1019 const ProString &varName = map(curr.first());
1021 if (tok == TokReplace) { // ~=
1022 // DEFINES ~= s/a/b/?[gqi]
1024 const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
1025 const QString &val = varVal.at(0).toQString(m_tmp1);
1026 if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
1027 evalError(fL1S("the ~= operator can handle only the s/// function."));
1030 QChar sep = val.at(1);
1031 QStringList func = val.split(sep);
1032 if (func.count() < 3 || func.count() > 4) {
1033 evalError(fL1S("the s/// function expects 3 or 4 arguments."));
1037 bool global = false, quote = false, case_sense = false;
1038 if (func.count() == 4) {
1039 global = func[3].indexOf(QLatin1Char('g')) != -1;
1040 case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
1041 quote = func[3].indexOf(QLatin1Char('q')) != -1;
1043 QString pattern = func[1];
1044 QString replace = func[2];
1046 pattern = QRegExp::escape(pattern);
1048 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
1050 if (!m_skipLevel || m_cumulative) {
1051 // We could make a union of modified and unmodified values,
1052 // but this will break just as much as it fixes, so leave it as is.
1053 replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
1054 replaceInList(&m_filevaluemap[currentProFile()][varName], regexp, replace, global, m_tmp2);
1057 ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
1059 default: // whatever - cannot happen
1060 case TokAssign: // =
1061 if (!m_cumulative) {
1064 m_valuemapStack.top()[varName] = varVal;
1065 m_filevaluemap[currentProFile()][varName] = varVal;
1069 if (!varVal.isEmpty()) {
1070 // We are greedy for values. But avoid exponential growth.
1071 ProStringList &v = valuesRef(varName);
1075 ProStringList old = v;
1077 QSet<ProString> has;
1078 has.reserve(v.size());
1079 foreach (const ProString &s, v)
1081 v.reserve(v.size() + old.size());
1082 foreach (const ProString &s, old)
1083 if (!has.contains(s))
1086 // These values will not be used for further processing inside
1087 // the evaluator. Duplicate elimination happens later.
1088 m_filevaluemap[currentProFile()][varName] += varVal;
1092 case TokAppendUnique: // *=
1093 if (!m_skipLevel || m_cumulative) {
1094 insertUnique(&valuesRef(varName), varVal);
1095 insertUnique(&m_filevaluemap[currentProFile()][varName], varVal);
1098 case TokAppend: // +=
1099 if (!m_skipLevel || m_cumulative) {
1101 valuesRef(varName) += varVal;
1102 m_filevaluemap[currentProFile()][varName] += varVal;
1105 case TokRemove: // -=
1106 if (!m_cumulative) {
1108 removeEach(&valuesRef(varName), varVal);
1109 removeEach(&m_filevaluemap[currentProFile()][varName], varVal);
1112 // We are stingy with our values, too.
1119 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(
1120 ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
1121 ProFileEvaluator::LoadFlags flags)
1123 m_handler->aboutToEval(currentProFile(), pro, type);
1124 m_profileStack.push(pro);
1125 if (flags & LoadPreFiles) {
1126 #ifdef PROEVALUATOR_THREAD_SAFE
1128 QMutexLocker locker(&m_option->mutex);
1129 if (m_option->base_inProgress) {
1130 QThreadPool::globalInstance()->releaseThread();
1131 m_option->cond.wait(&m_option->mutex);
1132 QThreadPool::globalInstance()->reserveThread();
1135 if (m_option->base_valuemap.isEmpty()) {
1136 #ifdef PROEVALUATOR_THREAD_SAFE
1137 m_option->base_inProgress = true;
1141 bool cumulative = m_cumulative;
1142 m_cumulative = false;
1144 // ### init QMAKE_QMAKE, QMAKE_SH
1145 // ### init QMAKE_EXT_{C,H,CPP,OBJ}
1146 // ### init TEMPLATE_PREFIX
1148 QString qmake_cache = m_option->cachefile;
1149 if (qmake_cache.isEmpty() && !m_outputDir.isEmpty()) { //find it as it has not been specified
1150 QDir dir(m_outputDir);
1152 qmake_cache = dir.path() + QLatin1String("/.qmake.cache");
1153 if (IoUtils::exists(qmake_cache))
1155 if (!dir.cdUp() || dir.isRoot()) {
1156 qmake_cache.clear();
1161 if (!qmake_cache.isEmpty()) {
1162 qmake_cache = resolvePath(qmake_cache);
1163 QHash<ProString, ProStringList> cache_valuemap;
1164 if (evaluateFileInto(qmake_cache, ProFileEvaluatorHandler::EvalConfigFile,
1165 &cache_valuemap, 0, EvalProOnly)) {
1166 if (m_option->qmakespec.isEmpty()) {
1167 const ProStringList &vals = cache_valuemap.value(ProString("QMAKESPEC"));
1168 if (!vals.isEmpty())
1169 m_option->qmakespec = vals.first().toQString();
1172 qmake_cache.clear();
1175 m_option->cachefile = qmake_cache;
1177 QStringList mkspec_roots = qmakeMkspecPaths();
1179 QString qmakespec = expandEnvVars(m_option->qmakespec);
1180 if (qmakespec.isEmpty()) {
1181 foreach (const QString &root, mkspec_roots) {
1182 QString mkspec = root + QLatin1String("/default");
1183 if (IoUtils::fileType(mkspec) == IoUtils::FileIsDir) {
1188 if (qmakespec.isEmpty()) {
1189 m_handler->configError(fL1S("Could not find qmake configuration directory"));
1190 // Unlike in qmake, not finding the spec is not critical ...
1194 if (IoUtils::isRelativePath(qmakespec)) {
1195 if (IoUtils::exists(currentDirectory() + QLatin1Char('/') + qmakespec
1196 + QLatin1String("/qmake.conf"))) {
1197 qmakespec = currentDirectory() + QLatin1Char('/') + qmakespec;
1198 } else if (!m_outputDir.isEmpty()
1199 && IoUtils::exists(m_outputDir + QLatin1Char('/') + qmakespec
1200 + QLatin1String("/qmake.conf"))) {
1201 qmakespec = m_outputDir + QLatin1Char('/') + qmakespec;
1203 foreach (const QString &root, mkspec_roots) {
1204 QString mkspec = root + QLatin1Char('/') + qmakespec;
1205 if (IoUtils::exists(mkspec)) {
1210 m_handler->configError(fL1S("Could not find qmake configuration file"));
1211 // Unlike in qmake, a missing config is not critical ...
1217 if (!qmakespec.isEmpty()) {
1218 m_option->qmakespec = QDir::cleanPath(qmakespec);
1220 QString spec = m_option->qmakespec + QLatin1String("/qmake.conf");
1221 if (!evaluateFileDirect(spec, ProFileEvaluatorHandler::EvalConfigFile,
1222 ProFileEvaluator::LoadProOnly)) {
1223 m_handler->configError(
1224 fL1S("Could not read qmake configuration file %1").arg(spec));
1225 } else if (!m_option->cachefile.isEmpty()) {
1226 evaluateFileDirect(m_option->cachefile,
1227 ProFileEvaluatorHandler::EvalConfigFile,
1228 ProFileEvaluator::LoadProOnly);
1230 m_option->qmakespec_name = IoUtils::fileName(m_option->qmakespec).toString();
1231 if (m_option->qmakespec_name == QLatin1String("default")) {
1234 int l = ::readlink(m_option->qmakespec.toLocal8Bit().constData(), buffer, 1024);
1236 m_option->qmakespec_name =
1237 IoUtils::fileName(QString::fromLocal8Bit(buffer, l)).toString();
1239 // We can't resolve symlinks as they do on Unix, so configure.exe puts
1240 // the source of the qmake.conf at the end of the default/qmake.conf in
1241 // the QMAKESPEC_ORG variable.
1242 const ProStringList &spec_org =
1243 m_option->base_valuemap.value(ProString("QMAKESPEC_ORIGINAL"));
1244 if (!spec_org.isEmpty())
1245 m_option->qmakespec_name =
1246 IoUtils::fileName(spec_org.first().toQString()).toString();
1251 evaluateFeatureFile(QLatin1String("default_pre.prf"));
1253 m_option->base_valuemap = m_valuemapStack.top();
1254 m_option->base_functions = m_functionDefs;
1256 m_cumulative = cumulative;
1258 #ifdef PROEVALUATOR_THREAD_SAFE
1260 m_option->base_inProgress = false;
1261 m_option->cond.wakeAll();
1265 #ifdef PROEVALUATOR_THREAD_SAFE
1269 m_valuemapStack.top() = m_option->base_valuemap;
1270 m_functionDefs = m_option->base_functions;
1273 ProStringList &tgt = m_valuemapStack.top()[ProString("TARGET")];
1275 tgt.append(ProString(QFileInfo(pro->fileName()).baseName(), NoHash));
1277 ProStringList &tmp = m_valuemapStack.top()[ProString("CONFIG")];
1278 foreach (const QString &add, m_addUserConfigCmdArgs)
1279 tmp.append(ProString(add, NoHash));
1280 foreach (const QString &remove, m_removeUserConfigCmdArgs)
1281 removeAll(&tmp, ProString(remove, NoHash));
1284 visitProBlock(pro, pro->tokPtr());
1286 if (flags & LoadPostFiles) {
1287 evaluateFeatureFile(QLatin1String("default_post.prf"));
1289 QSet<QString> processed;
1291 bool finished = true;
1292 ProStringList configs = valuesDirect(statics.strCONFIG);
1293 for (int i = configs.size() - 1; i >= 0; --i) {
1294 QString config = configs.at(i).toQString(m_tmp1).toLower();
1295 if (!processed.contains(config)) {
1297 processed.insert(config);
1298 if (evaluateFeatureFile(config)) {
1308 m_profileStack.pop();
1309 m_handler->doneWithEval(currentProFile());
1315 QStringList ProFileEvaluator::Private::qmakeMkspecPaths() const
1318 const QString concat = QLatin1String("/mkspecs");
1320 QByteArray qmakepath = qgetenv("QMAKEPATH");
1321 if (!qmakepath.isEmpty())
1322 foreach (const QString &it, QString::fromLocal8Bit(qmakepath).split(m_option->dirlist_sep))
1323 ret << QDir::cleanPath(it) + concat;
1325 QString builtIn = propertyValue(QLatin1String("QT_INSTALL_DATA")) + concat;
1326 if (!ret.contains(builtIn))
1332 QStringList ProFileEvaluator::Private::qmakeFeaturePaths() const
1334 QString mkspecs_concat = QLatin1String("/mkspecs");
1335 QString features_concat = QLatin1String("/features");
1337 switch (m_option->target_mode) {
1338 case ProFileOption::TARG_MACX_MODE:
1339 concat << QLatin1String("/features/mac");
1340 concat << QLatin1String("/features/macx");
1341 concat << QLatin1String("/features/unix");
1343 case ProFileOption::TARG_UNIX_MODE:
1344 concat << QLatin1String("/features/unix");
1346 case ProFileOption::TARG_WIN_MODE:
1347 concat << QLatin1String("/features/win32");
1349 case ProFileOption::TARG_MAC9_MODE:
1350 concat << QLatin1String("/features/mac");
1351 concat << QLatin1String("/features/mac9");
1353 case ProFileOption::TARG_QNX6_MODE:
1354 concat << QLatin1String("/features/qnx6");
1355 concat << QLatin1String("/features/unix");
1358 concat << features_concat;
1360 QStringList feature_roots;
1362 QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
1363 if (!mkspec_path.isEmpty())
1364 foreach (const QString &f, QString::fromLocal8Bit(mkspec_path).split(m_option->dirlist_sep))
1365 feature_roots += resolvePath(f);
1367 feature_roots += propertyValue(QLatin1String("QMAKEFEATURES"), false).split(
1368 m_option->dirlist_sep, QString::SkipEmptyParts);
1370 if (!m_option->cachefile.isEmpty()) {
1371 QString path = m_option->cachefile.left(m_option->cachefile.lastIndexOf((ushort)'/'));
1372 foreach (const QString &concat_it, concat)
1373 feature_roots << (path + concat_it);
1376 QByteArray qmakepath = qgetenv("QMAKEPATH");
1377 if (!qmakepath.isNull()) {
1378 const QStringList lst = QString::fromLocal8Bit(qmakepath).split(m_option->dirlist_sep);
1379 foreach (const QString &item, lst) {
1380 QString citem = resolvePath(item);
1381 foreach (const QString &concat_it, concat)
1382 feature_roots << (citem + mkspecs_concat + concat_it);
1386 if (!m_option->qmakespec.isEmpty()) {
1387 QString qmakespec = resolvePath(m_option->qmakespec);
1388 feature_roots << (qmakespec + features_concat);
1390 QDir specdir(qmakespec);
1391 while (!specdir.isRoot()) {
1392 if (!specdir.cdUp() || specdir.isRoot())
1394 if (IoUtils::exists(specdir.path() + features_concat)) {
1395 foreach (const QString &concat_it, concat)
1396 feature_roots << (specdir.path() + concat_it);
1402 foreach (const QString &concat_it, concat)
1403 feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
1404 mkspecs_concat + concat_it);
1405 foreach (const QString &concat_it, concat)
1406 feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
1407 mkspecs_concat + concat_it);
1409 for (int i = 0; i < feature_roots.count(); ++i)
1410 if (!feature_roots.at(i).endsWith((ushort)'/'))
1411 feature_roots[i].append((ushort)'/');
1413 feature_roots.removeDuplicates();
1415 return feature_roots;
1418 QString ProFileEvaluator::Private::propertyValue(const QString &name, bool complain) const
1420 if (m_option->properties.contains(name))
1421 return m_option->properties.value(name);
1422 if (name == QLatin1String("QMAKE_MKSPECS"))
1423 return qmakeMkspecPaths().join(m_option->dirlist_sep);
1424 if (name == QLatin1String("QMAKE_VERSION"))
1425 return QLatin1String("1.0"); //### FIXME
1427 evalError(fL1S("Querying unknown property %1").arg(name));
1431 ProFile *ProFileEvaluator::Private::currentProFile() const
1433 if (m_profileStack.count() > 0)
1434 return m_profileStack.top();
1438 QString ProFileEvaluator::Private::currentFileName() const
1440 ProFile *pro = currentProFile();
1442 return pro->fileName();
1446 QString ProFileEvaluator::Private::currentDirectory() const
1448 ProFile *cur = m_profileStack.top();
1449 return cur->directoryName();
1452 // The (QChar*)current->constData() constructs below avoid pointless detach() calls
1453 // FIXME: This is inefficient. Should not make new string if it is a straight subsegment
1454 static ALWAYS_INLINE void appendChar(ushort unicode,
1455 QString *current, QChar **ptr, ProString *pending)
1457 if (!pending->isEmpty()) {
1458 int len = pending->size();
1459 current->resize(current->size() + len);
1460 ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
1462 *ptr = (QChar*)current->constData() + len;
1464 *(*ptr)++ = QChar(unicode);
1467 static void appendString(const ProString &string,
1468 QString *current, QChar **ptr, ProString *pending)
1470 if (string.isEmpty())
1472 QChar *uc = (QChar*)current->constData();
1476 current->resize(current->size() + string.size());
1477 } else if (!pending->isEmpty()) {
1478 len = pending->size();
1479 current->resize(current->size() + len + string.size());
1480 ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
1486 *ptr = (QChar*)current->constData() + len;
1487 ::memcpy(*ptr, string.constData(), string.size() * 2);
1488 *ptr += string.size();
1491 static void flushCurrent(ProStringList *ret,
1492 QString *current, QChar **ptr, ProString *pending, bool joined)
1494 QChar *uc = (QChar*)current->constData();
1495 int len = *ptr - uc;
1497 ret->append(ProString(QString(uc, len), NoHash));
1499 } else if (!pending->isEmpty()) {
1500 ret->append(*pending);
1502 } else if (joined) {
1503 ret->append(ProString());
1507 static inline void flushFinal(ProStringList *ret,
1508 const QString ¤t, const QChar *ptr, const ProString &pending,
1509 const ProString &str, bool replaced, bool joined)
1511 int len = ptr - current.data();
1513 if (!replaced && len == str.size())
1516 ret->append(ProString(QString(current.data(), len), NoHash));
1517 } else if (!pending.isEmpty()) {
1518 ret->append(pending);
1519 } else if (joined) {
1520 ret->append(ProString());
1524 ProStringList ProFileEvaluator::Private::expandVariableReferences(
1525 const ProString &str, int *pos, bool joined)
1530 if (str.isEmpty() && !pos)
1533 const ushort LSQUARE = '[';
1534 const ushort RSQUARE = ']';
1535 const ushort LCURLY = '{';
1536 const ushort RCURLY = '}';
1537 const ushort LPAREN = '(';
1538 const ushort RPAREN = ')';
1539 const ushort DOLLAR = '$';
1540 const ushort BACKSLASH = '\\';
1541 const ushort UNDERSCORE = '_';
1542 const ushort DOT = '.';
1543 const ushort SPACE = ' ';
1544 const ushort TAB = '\t';
1545 const ushort COMMA = ',';
1546 const ushort SINGLEQUOTE = '\'';
1547 const ushort DOUBLEQUOTE = '"';
1549 ushort unicode, quote = 0, parens = 0;
1550 const ushort *str_data = (const ushort *)str.constData();
1551 const int str_len = str.size();
1553 ProString var, args;
1555 bool replaced = false;
1556 bool putSpace = false;
1557 QString current; // Buffer for successively assembled string segments
1558 current.resize(str.size());
1559 QChar *ptr = current.data();
1560 ProString pending; // Buffer for string segments from variables
1561 // Only one of the above buffers can be filled at a given time.
1562 for (int i = pos ? *pos : 0; i < str_len; ++i) {
1563 unicode = str_data[i];
1564 if (unicode == DOLLAR) {
1565 if (str_len > i+2 && str_data[i+1] == DOLLAR) {
1568 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
1569 unicode = str_data[++i];
1570 if (unicode == LSQUARE) {
1571 unicode = str_data[++i];
1573 var_type = PROPERTY;
1574 } else if (unicode == LCURLY) {
1575 unicode = str_data[++i];
1578 } else if (unicode == LPAREN) {
1579 unicode = str_data[++i];
1585 if (!(unicode & (0xFF<<8)) &&
1586 unicode != DOT && unicode != UNDERSCORE &&
1587 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
1588 (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
1589 (unicode < '0' || unicode > '9'))
1593 unicode = str_data[i];
1594 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
1596 var = str.mid(name_start, i - name_start);
1597 if (var_type == VAR && unicode == LPAREN) {
1598 var_type = FUNCTION;
1604 unicode = str_data[i];
1605 if (unicode == LPAREN) {
1607 } else if (unicode == RPAREN) {
1613 args = str.mid(name_start, i - name_start);
1615 unicode = str_data[i];
1618 // at this point i is pointing to the 'next' character (which is in unicode)
1619 // this might actually be a term character since you can do $${func()}
1622 if (unicode != term) {
1623 evalError(fL1S("Missing %1 terminator [found %2]")
1625 .arg(unicode ? QString(unicode) : fL1S("end-of-line")));
1630 return ProStringList();
1633 // move the 'cursor' back to the last char of the thing we were looking at
1637 ProStringList replacement;
1638 if (var_type == ENVIRON) {
1639 replacement = split_value_list(QString::fromLocal8Bit(qgetenv(
1640 var.toQString(m_tmp1).toLocal8Bit().constData())));
1641 } else if (var_type == PROPERTY) {
1642 replacement << ProString(propertyValue(var.toQString(m_tmp1)), NoHash);
1643 } else if (var_type == FUNCTION) {
1644 replacement += evaluateExpandFunction(var, args);
1645 } else if (var_type == VAR) {
1646 replacement = values(map(var));
1648 if (!replacement.isEmpty()) {
1649 if (quote || joined) {
1652 if (!replacement.at(0).isEmpty()) // Bizarre, indeed
1653 appendChar(' ', ¤t, &ptr, &pending);
1655 appendString(ProString(replacement.join(statics.field_sep), NoHash),
1656 ¤t, &ptr, &pending);
1658 appendString(replacement.at(0), ¤t, &ptr, &pending);
1659 if (replacement.size() > 1) {
1660 flushCurrent(&ret, ¤t, &ptr, &pending, false);
1662 if (replacement.size() > 2) {
1663 // FIXME: ret.reserve(ret.size() + replacement.size() - 2);
1664 for (; j < replacement.size() - 1; ++j)
1665 ret << replacement.at(j);
1667 pending = replacement.at(j);
1674 } else if (unicode == BACKSLASH) {
1675 static const char symbols[] = "[]{}()$\\'\"";
1676 ushort unicode2 = str_data[i+1];
1677 if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
1682 if (unicode == quote) {
1687 if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
1690 } else if (unicode == SPACE || unicode == TAB) {
1692 flushCurrent(&ret, ¤t, &ptr, &pending, false);
1693 else if ((ptr - (QChar*)current.constData()) || !pending.isEmpty())
1697 if (unicode == LPAREN) {
1699 } else if (unicode == RPAREN) {
1701 } else if (!parens && unicode == COMMA) {
1704 flushFinal(&ret, current, ptr, pending, str, replaced, false);
1707 flushCurrent(&ret, ¤t, &ptr, &pending, true);
1715 appendChar(' ', ¤t, &ptr, &pending);
1717 appendChar(unicode, ¤t, &ptr, &pending);
1721 flushFinal(&ret, current, ptr, pending, str, replaced, joined);
1725 bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
1727 // magic types for easy flipping
1728 if (config == statics.strtrue)
1730 if (config == statics.strfalse)
1734 if ((m_option->target_mode == m_option->TARG_MACX_MODE
1735 || m_option->target_mode == m_option->TARG_QNX6_MODE
1736 || m_option->target_mode == m_option->TARG_UNIX_MODE)
1737 && config == statics.strunix)
1739 if (m_option->target_mode == m_option->TARG_MACX_MODE && config == statics.strmacx)
1741 if (m_option->target_mode == m_option->TARG_QNX6_MODE && config == statics.strqnx6)
1743 if (m_option->target_mode == m_option->TARG_MAC9_MODE && config == statics.strmac9)
1745 if ((m_option->target_mode == m_option->TARG_MAC9_MODE
1746 || m_option->target_mode == m_option->TARG_MACX_MODE)
1747 && config == statics.strmac)
1749 if (m_option->target_mode == m_option->TARG_WIN_MODE && config == statics.strwin32)
1752 if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
1753 QString cfg = config;
1754 cfg.detach(); // Keep m_tmp out of QRegExp's cache
1755 QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
1757 if (re.exactMatch(m_option->qmakespec_name))
1762 foreach (const ProString &configValue, valuesDirect(statics.strCONFIG)) {
1763 if (re.exactMatch(configValue.toQString(m_tmp[t])))
1769 if (m_option->qmakespec_name == config)
1773 if (valuesDirect(statics.strCONFIG).contains(ProString(config, NoHash)))
1780 ProStringList ProFileEvaluator::Private::expandVariableReferences(
1781 const ushort *&tokPtr, int sizeHint, bool joined)
1784 ret.reserve(sizeHint);
1786 evaluateExpression(tokPtr, &ret, joined);
1788 case TokValueTerminator:
1789 case TokFuncTerminator:
1792 case TokArgSeparator:
1799 Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
1805 QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ushort *&tokPtr)
1807 QList<ProStringList> args_list;
1808 if (*tokPtr != TokFuncTerminator) {
1811 evaluateExpression(tokPtr, &arg, false);
1813 if (*tokPtr == TokFuncTerminator)
1815 Q_ASSERT(*tokPtr == TokArgSeparator);
1822 QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ProString &arguments)
1824 QList<ProStringList> args_list;
1825 for (int pos = 0; pos < arguments.size(); )
1826 args_list << expandVariableReferences(arguments, &pos);
1830 ProStringList ProFileEvaluator::Private::evaluateFunction(
1831 const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
1836 if (m_valuemapStack.count() >= 100) {
1837 evalError(fL1S("ran into infinite recursion (depth > 100)."));
1840 m_valuemapStack.push(QHash<ProString, ProStringList>());
1841 m_locationStack.push(m_current);
1842 int loopLevel = m_loopLevel;
1846 for (int i = 0; i < argumentsList.count(); ++i) {
1847 args += argumentsList[i];
1848 m_valuemapStack.top()[ProString(QString::number(i+1))] = argumentsList[i];
1850 m_valuemapStack.top()[statics.strARGS] = args;
1851 oki = (visitProBlock(func.pro(), func.tokPtr()) != ReturnFalse); // True || Return
1852 ret = m_returnValue;
1853 m_returnValue.clear();
1855 m_loopLevel = loopLevel;
1856 m_current = m_locationStack.pop();
1857 m_valuemapStack.pop();
1863 return ProStringList();
1866 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateBoolFunction(
1867 const FunctionDef &func, const QList<ProStringList> &argumentsList,
1868 const ProString &function)
1871 ProStringList ret = evaluateFunction(func, argumentsList, &ok);
1875 if (ret.at(0) != statics.strfalse) {
1876 if (ret.at(0) == statics.strtrue)
1878 int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
1883 evalError(fL1S("Unexpected return value from test '%1': %2")
1884 .arg(function.toQString(m_tmp1))
1885 .arg(ret.join(QLatin1String(" :: "))));
1892 ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
1893 const ProString &func, const ushort *&tokPtr)
1895 QHash<ProString, FunctionDef>::ConstIterator it =
1896 m_functionDefs.replaceFunctions.constFind(func);
1897 if (it != m_functionDefs.replaceFunctions.constEnd())
1898 return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0);
1900 //why don't the builtin functions just use args_list? --Sam
1901 return evaluateExpandFunction(func, expandVariableReferences(tokPtr, 5, true));
1904 ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
1905 const ProString &func, const ProString &arguments)
1907 QHash<ProString, FunctionDef>::ConstIterator it =
1908 m_functionDefs.replaceFunctions.constFind(func);
1909 if (it != m_functionDefs.replaceFunctions.constEnd())
1910 return evaluateFunction(*it, prepareFunctionArgs(arguments), 0);
1912 //why don't the builtin functions just use args_list? --Sam
1914 return evaluateExpandFunction(func, expandVariableReferences(arguments, &pos, true));
1917 ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
1918 const ProString &func, const ProStringList &args)
1920 ExpandFunc func_t = ExpandFunc(statics.expands.value(func));
1922 const QString &fn = func.toQString(m_tmp1);
1923 const QString &lfn = fn.toLower();
1924 if (!fn.isSharedWith(lfn))
1925 func_t = ExpandFunc(statics.expands.value(ProString(lfn)));
1934 bool regexp = false;
1939 if (func_t == E_SECTION) {
1940 if (args.count() != 3 && args.count() != 4) {
1941 evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
1942 " three or four arguments.").arg(func.toQString(m_tmp1)));
1945 sep = args.at(1).toQString();
1946 beg = args.at(2).toQString(m_tmp2).toInt();
1947 if (args.count() == 4)
1948 end = args.at(3).toQString(m_tmp2).toInt();
1951 if (args.count() != 1) {
1952 evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
1956 sep = QLatin1String("[\\\\/]");
1957 if (func_t == E_DIRNAME)
1963 if (!var.isEmpty()) {
1966 foreach (const ProString &str, values(map(var))) {
1967 const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
1968 ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash));
1971 foreach (const ProString &str, values(map(var))) {
1972 const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
1973 ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash));
1980 if(args.count() < 1) {
1981 evalError(fL1S("sprintf(format, ...) requires at least one argument"));
1983 QString tmp = args.at(0).toQString(m_tmp1);
1984 for (int i = 1; i < args.count(); ++i)
1985 tmp = tmp.arg(args.at(i).toQString(m_tmp2));
1986 // Note: this depends on split_value_list() making a deep copy
1987 ret = split_value_list(tmp);
1991 if (args.count() < 1 || args.count() > 4) {
1992 evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
1995 ProString before, after;
1996 if (args.count() >= 2)
1997 glue = args.at(1).toQString(m_tmp1);
1998 if (args.count() >= 3)
2000 if (args.count() == 4)
2002 const ProStringList &var = values(map(args.at(0)));
2004 ret.append(ProString(before + var.join(glue) + after, NoHash));
2009 if (args.count() != 2) {
2010 evalError(fL1S("split(var, sep) requires one or two arguments"));
2012 const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
2013 foreach (const ProString &var, values(map(args.at(0))))
2014 foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
2015 ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt, NoHash));
2019 if (args.count() < 1 || args.count() > 3) {
2020 evalError(fL1S("member(var, start, end) requires one to three arguments."));
2023 const ProStringList &var = values(map(args.at(0)));
2024 int start = 0, end = 0;
2025 if (args.count() >= 2) {
2026 const QString &start_str = args.at(1).toQString(m_tmp1);
2027 start = start_str.toInt(&ok);
2029 if (args.count() == 2) {
2030 int dotdot = start_str.indexOf(statics.strDotDot);
2032 start = start_str.left(dotdot).toInt(&ok);
2034 end = start_str.mid(dotdot+2).toInt(&ok);
2038 evalError(fL1S("member() argument 2 (start) '%2' invalid.")
2042 if (args.count() == 3)
2043 end = args.at(2).toQString(m_tmp1).toInt(&ok);
2045 evalError(fL1S("member() argument 3 (end) '%2' invalid.\n")
2046 .arg(args.at(2).toQString(m_tmp1)));
2051 start += var.count();
2054 if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
2056 } else if (start < end) {
2057 for (int i = start; i <= end && var.count() >= i; i++)
2060 for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
2068 if (args.count() != 1) {
2069 evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
2071 const ProStringList &var = values(map(args.at(0)));
2072 if (!var.isEmpty()) {
2073 if (func_t == E_FIRST)
2076 ret.append(var.last());
2081 if(args.count() != 1)
2082 evalError(fL1S("size(var) requires one argument."));
2084 ret.append(ProString(QString::number(values(map(args.at(0))).size()), NoHash));
2087 if (args.count() < 1 || args.count() > 2) {
2088 evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
2090 const QString &file = args.at(0).toQString(m_tmp1);
2092 bool singleLine = true;
2093 if (args.count() > 1)
2094 singleLine = isTrue(args.at(1), m_tmp2);
2096 QFile qfile(resolvePath(expandEnvVars(file)));
2097 if (qfile.open(QIODevice::ReadOnly)) {
2098 QTextStream stream(&qfile);
2099 while (!stream.atEnd()) {
2100 ret += split_value_list(stream.readLine().trimmed());
2102 ret += ProString("\n", NoHash);
2109 if (args.count() != 2) {
2110 evalError(fL1S("fromfile(file, variable) requires two arguments."));
2112 QHash<ProString, ProStringList> vars;
2113 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2115 if (evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2116 &vars, &m_functionDefs, EvalWithDefaults))
2117 ret = vars.value(map(args.at(1)));
2121 if (args.count() != 1) {
2122 evalError(fL1S("eval(variable) requires one argument"));
2124 ret += values(map(args.at(0)));
2129 tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
2130 ret = ProStringList(ProString(tmp, NoHash));
2132 foreach (const ProString &arg, args)
2133 lst += split_value_list(arg.toQString(m_tmp1)); // Relies on deep copy
2134 m_valuemapStack.top()[ret.at(0)] = lst;
2137 if (args.count() != 2) {
2138 evalError(fL1S("find(var, str) requires two arguments."));
2140 QRegExp regx(args.at(1).toQString());
2142 foreach (const ProString &val, values(map(args.at(0)))) {
2143 if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
2151 if (args.count() < 1 || args.count() > 2) {
2152 evalError(fL1S("system(execute) requires one or two arguments."));
2155 FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
2156 + IoUtils::shellQuote(currentDirectory())
2157 + QLatin1String(" && ") + args[0]).toLocal8Bit(), "r");
2158 bool singleLine = true;
2159 if (args.count() > 1)
2160 singleLine = isTrue(args.at(1), m_tmp2);
2162 while (proc && !feof(proc)) {
2163 int read_in = int(fread(buff, 1, 255, proc));
2166 for (int i = 0; i < read_in; i++) {
2167 if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
2170 buff[read_in] = '\0';
2171 output += QString::fromLocal8Bit(buff);
2173 ret += split_value_list(output);
2180 if(args.count() != 1) {
2181 evalError(fL1S("unique(var) requires one argument."));
2183 ret = values(map(args.at(0)));
2184 ret.removeDuplicates();
2190 case E_ESCAPE_EXPAND:
2191 for (int i = 0; i < args.size(); ++i) {
2192 QString str = args.at(i).toQString();
2193 QChar *i_data = str.data();
2194 int i_len = str.length();
2195 for (int x = 0; x < i_len; ++x) {
2196 if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
2197 if (*(i_data+x+1) == QLatin1Char('\\')) {
2202 } mapped_quotes[] = {
2208 for (int i = 0; mapped_quotes[i].in; ++i) {
2209 if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
2210 *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
2212 memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
2220 ret.append(ProString(QString(i_data, i_len), NoHash));
2224 for (int i = 0; i < args.size(); ++i) {
2225 const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
2226 ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash));
2231 for (int i = 0; i < args.count(); ++i) {
2232 QString rstr = args.at(i).toQString(m_tmp1);
2233 rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
2234 ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash));
2238 if (args.count() != 1 && args.count() != 2) {
2239 evalError(fL1S("files(pattern, recursive=false) requires one or two arguments"));
2241 bool recursive = false;
2242 if (args.count() == 2)
2243 recursive = isTrue(args.at(1), m_tmp2);
2245 QString r = fixPathToLocalOS(args.at(0).toQString(m_tmp1));
2247 if (IoUtils::isRelativePath(r)) {
2248 pfx = currentDirectory();
2249 if (!pfx.endsWith(QLatin1Char('/')))
2250 pfx += QLatin1Char('/');
2252 int slash = r.lastIndexOf(QDir::separator());
2254 dirs.append(r.left(slash+1));
2257 dirs.append(QString());
2260 r.detach(); // Keep m_tmp out of QRegExp's cache
2261 const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
2262 for (int d = 0; d < dirs.count(); d++) {
2263 QString dir = dirs[d];
2264 QDir qdir(pfx + dir);
2265 for (int i = 0; i < (int)qdir.count(); ++i) {
2266 if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
2268 QString fname = dir + qdir[i];
2269 if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
2271 dirs.append(fname + QDir::separator());
2273 if (regex.exactMatch(qdir[i]))
2274 ret += ProString(fname, NoHash);
2280 if(args.count() != 3 ) {
2281 evalError(fL1S("replace(var, before, after) requires three arguments"));
2283 const QRegExp before(args.at(1).toQString());
2284 const QString &after(args.at(2).toQString(m_tmp2));
2285 foreach (const ProString &val, values(map(args.at(0)))) {
2286 QString rstr = val.toQString(m_tmp1);
2287 QString copy = rstr; // Force a detach on modify
2288 rstr.replace(before, after);
2289 ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr, NoHash));
2294 evalError(fL1S("'%1' is not a recognized replace function")
2295 .arg(func.toQString(m_tmp1)));
2298 evalError(fL1S("Function '%1' is not implemented").arg(func.toQString(m_tmp1)));
2305 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2306 const ProString &function, const ProString &arguments)
2308 QHash<ProString, FunctionDef>::ConstIterator it =
2309 m_functionDefs.testFunctions.constFind(function);
2310 if (it != m_functionDefs.testFunctions.constEnd())
2311 return evaluateBoolFunction(*it, prepareFunctionArgs(arguments), function);
2313 //why don't the builtin functions just use args_list? --Sam
2315 return evaluateConditionalFunction(function, expandVariableReferences(arguments, &pos, true));
2318 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2319 const ProString &function, const ushort *&tokPtr)
2321 QHash<ProString, FunctionDef>::ConstIterator it =
2322 m_functionDefs.testFunctions.constFind(function);
2323 if (it != m_functionDefs.testFunctions.constEnd())
2324 return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function);
2326 //why don't the builtin functions just use args_list? --Sam
2327 return evaluateConditionalFunction(function, expandVariableReferences(tokPtr, 5, true));
2330 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2331 const ProString &function, const ProStringList &args)
2333 TestFunc func_t = (TestFunc)statics.functions.value(function);
2337 if (args.count() < 1 || args.count() > 2) {
2338 evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
2339 " requires one or two arguments."));
2342 if (args.count() > 1) {
2343 if (args[1] == QLatin1String("test"))
2344 return returnBool(m_functionDefs.testFunctions.contains(args[0]));
2345 else if (args[1] == QLatin1String("replace"))
2346 return returnBool(m_functionDefs.replaceFunctions.contains(args[0]));
2347 evalError(fL1S("defined(function, type): unexpected type [%1].\n")
2348 .arg(args.at(1).toQString(m_tmp1)));
2351 return returnBool(m_functionDefs.replaceFunctions.contains(args[0])
2352 || m_functionDefs.testFunctions.contains(args[0]));
2354 m_returnValue = args;
2355 // It is "safe" to ignore returns - due to qmake brokeness
2356 // they cannot be used to terminate loops anyway.
2357 if (m_skipLevel || m_cumulative)
2359 if (m_valuemapStack.isEmpty()) {
2360 evalError(fL1S("unexpected return()."));
2363 return ReturnReturn;
2365 if (m_skipLevel && !m_cumulative)
2367 if (args.count() != 1) {
2368 evalError(fL1S("export(variable) requires one argument."));
2371 const ProString &var = map(args.at(0));
2372 for (int i = m_valuemapStack.size(); --i > 0; ) {
2373 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(var);
2374 if (it != m_valuemapStack.at(i).end()) {
2375 if (it->constBegin() == statics.fakeValue.constBegin()) {
2376 // This is stupid, but qmake doesn't propagate deletions
2377 m_valuemapStack[0][var] = ProStringList();
2379 m_valuemapStack[0][var] = *it;
2381 m_valuemapStack[i].erase(it);
2383 m_valuemapStack[i].remove(var);
2390 if (args.count() < 2 || args.count() > 3) {
2391 evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
2393 QHash<ProString, ProStringList> vars;
2394 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2396 if (!evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2397 &vars, &m_functionDefs, EvalWithDefaults))
2399 if (args.count() == 2)
2400 return returnBool(vars.contains(args.at(1)));
2402 const QString &qry = args.at(2).toQString(m_tmp1);
2403 if (qry != QRegExp::escape(qry)) {
2406 regx.setPattern(copy);
2409 foreach (const ProString &s, vars.value(map(args.at(1)))) {
2410 if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
2420 ProFile *pro = m_parser->parsedProFile(fL1S("(eval)"), false,
2421 args.join(statics.field_sep));
2424 m_locationStack.push(m_current);
2425 VisitReturn ret = visitProBlock(pro, pro->tokPtr());
2426 m_current = m_locationStack.pop();
2435 evalError(fL1S("unexpected break()."));
2442 evalError(fL1S("unexpected next()."));
2445 if (m_skipLevel && !m_cumulative)
2447 if (args.count() != 1) {
2448 evalError(fL1S("if(condition) requires one argument."));
2451 const ProString &cond = args.at(0);
2452 bool quoted = false;
2455 bool invert = false;
2456 bool isFunc = false;
2461 argsString.reserve(50);
2462 const QChar *d = cond.constData();
2463 const QChar *ed = d + cond.size();
2465 ushort c = (d++)->unicode();
2470 else if (c == '!' && test.isEmpty())
2474 } else if (c == '(') {
2479 } else if (c == ')') {
2483 } else if (!parens) {
2486 else if (c == ':' || c == '|')
2488 else if (c == '!' && test.isEmpty())
2495 if (!quoted && !parens && (isOp || d == ed)) {
2496 if (m_cumulative || (orOp != ret)) {
2497 test = test.trimmed();
2499 ret = evaluateConditionalFunction(ProString(test), ProString(argsString, NoHash));
2501 ret = isActiveConfig(test, true);
2511 return returnBool(ret);
2514 if (args.count() < 1 || args.count() > 2) {
2515 evalError(fL1S("CONFIG(config) requires one or two arguments."));
2518 if (args.count() == 1)
2519 return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
2520 const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
2521 const ProStringList &configs = valuesDirect(statics.strCONFIG);
2523 for (int i = configs.size() - 1; i >= 0; i--) {
2524 for (int mut = 0; mut < mutuals.count(); mut++) {
2525 if (configs[i] == mutuals[mut].trimmed()) {
2526 return returnBool(configs[i] == args[0]);
2533 if (args.count() < 2 || args.count() > 3) {
2534 evalError(fL1S("contains(var, val) requires two or three arguments."));
2538 const QString &qry = args.at(1).toQString(m_tmp1);
2540 if (qry != QRegExp::escape(qry)) {
2543 regx.setPattern(copy);
2545 const ProStringList &l = values(map(args.at(0)));
2546 if (args.count() == 2) {
2548 for (int i = 0; i < l.size(); ++i) {
2549 const ProString &val = l[i];
2550 if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
2555 const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
2556 for (int i = l.size() - 1; i >= 0; i--) {
2557 const ProString val = l[i];
2558 for (int mut = 0; mut < mutuals.count(); mut++) {
2559 if (val == mutuals[mut].trimmed()) {
2560 return returnBool((!regx.isEmpty()
2561 && regx.exactMatch(val.toQString(m_tmp2)))
2570 if (args.count() != 2 && args.count() != 3) {
2571 evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
2574 int cnt = values(map(args.at(0))).count();
2575 if (args.count() == 3) {
2576 const ProString &comp = args.at(2);
2577 const int val = args.at(1).toQString(m_tmp1).toInt();
2578 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
2579 return returnBool(cnt > val);
2580 } else if (comp == QLatin1String(">=")) {
2581 return returnBool(cnt >= val);
2582 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
2583 return returnBool(cnt < val);
2584 } else if (comp == QLatin1String("<=")) {
2585 return returnBool(cnt <= val);
2586 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
2587 || comp == QLatin1String("=") || comp == QLatin1String("==")) {
2588 return returnBool(cnt == val);
2590 evalError(fL1S("unexpected modifier to count(%2)").arg(comp.toQString(m_tmp1)));
2594 return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
2598 if (args.count() != 2) {
2599 evalError(fL1S("%1(variable, value) requires two arguments.")
2600 .arg(function.toQString(m_tmp1)));
2603 const QString &rhs(args.at(1).toQString(m_tmp1)),
2604 &lhs(values(map(args.at(0))).join(statics.field_sep));
2606 int rhs_int = rhs.toInt(&ok);
2607 if (ok) { // do integer compare
2608 int lhs_int = lhs.toInt(&ok);
2610 if (func_t == T_GREATERTHAN)
2611 return returnBool(lhs_int > rhs_int);
2612 return returnBool(lhs_int < rhs_int);
2615 if (func_t == T_GREATERTHAN)
2616 return returnBool(lhs > rhs);
2617 return returnBool(lhs < rhs);
2620 if (args.count() != 2) {
2621 evalError(fL1S("%1(variable, value) requires two arguments.")
2622 .arg(function.toQString(m_tmp1)));
2625 return returnBool(values(map(args.at(0))).join(statics.field_sep)
2626 == args.at(1).toQString(m_tmp1));
2628 if (m_skipLevel && !m_cumulative)
2630 if (args.count() != 1) {
2631 evalError(fL1S("%1(variable) requires one argument.")
2632 .arg(function.toQString(m_tmp1)));
2635 QHash<ProString, ProStringList> *hsh;
2636 QHash<ProString, ProStringList>::Iterator it;
2637 const ProString &var = map(args.at(0));
2638 if (!(hsh = findValues(var, &it)))
2640 if (hsh == &m_valuemapStack.top())
2643 m_valuemapStack.top()[var].clear();
2647 if (m_skipLevel && !m_cumulative)
2649 if (args.count() != 1) {
2650 evalError(fL1S("%1(variable) requires one argument.")
2651 .arg(function.toQString(m_tmp1)));
2654 QHash<ProString, ProStringList> *hsh;
2655 QHash<ProString, ProStringList>::Iterator it;
2656 const ProString &var = map(args.at(0));
2657 if (!(hsh = findValues(var, &it)))
2659 if (m_valuemapStack.size() == 1)
2661 else if (hsh == &m_valuemapStack.top())
2662 *it = statics.fakeValue;
2664 m_valuemapStack.top()[var] = statics.fakeValue;
2668 if (m_skipLevel && !m_cumulative)
2671 // the third optional argument to include() controls warnings
2672 // and is not used here
2673 if ((args.count() == 2) || (args.count() == 3) ) {
2674 parseInto = args.at(1).toQString(m_tmp2);
2675 } else if (args.count() != 1) {
2676 evalError(fL1S("include(file, into, silent) requires one, two or three arguments."));
2679 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2682 if (parseInto.isEmpty()) {
2683 ok = evaluateFile(fn, ProFileEvaluatorHandler::EvalIncludeFile,
2684 ProFileEvaluator::LoadProOnly);
2686 QHash<ProString, ProStringList> symbols;
2687 if ((ok = evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2688 &symbols, 0, EvalWithSetup))) {
2689 QHash<ProString, ProStringList> newMap;
2690 for (QHash<ProString, ProStringList>::ConstIterator
2691 it = m_valuemapStack.top().constBegin(),
2692 end = m_valuemapStack.top().constEnd();
2694 const QString &ky = it.key().toQString(m_tmp1);
2695 if (!(ky.startsWith(parseInto) &&
2696 (ky.length() == parseInto.length()
2697 || ky.at(parseInto.length()) == QLatin1Char('.'))))
2698 newMap[it.key()] = it.value();
2700 for (QHash<ProString, ProStringList>::ConstIterator it = symbols.constBegin();
2701 it != symbols.constEnd(); ++it) {
2702 const QString &ky = it.key().toQString(m_tmp1);
2703 if (!ky.startsWith(QLatin1Char('.')))
2704 newMap.insert(ProString(parseInto + QLatin1Char('.') + ky), it.value());
2706 m_valuemapStack.top() = newMap;
2709 return returnBool(ok);
2712 if (m_skipLevel && !m_cumulative)
2714 bool ignore_error = false;
2715 if (args.count() == 2) {
2716 ignore_error = isTrue(args.at(1), m_tmp2);
2717 } else if (args.count() != 1) {
2718 evalError(fL1S("load(feature) requires one or two arguments."));
2721 // XXX ignore_error unused
2722 return returnBool(evaluateFeatureFile(expandEnvVars(args.at(0).toQString())));
2725 // Yup - do nothing. Nothing is going to enable debug output anyway.
2728 if (args.count() != 1) {
2729 evalError(fL1S("%1(message) requires one argument.")
2730 .arg(function.toQString(m_tmp1)));
2733 const QString &msg = expandEnvVars(args.at(0).toQString(m_tmp2));
2735 m_handler->fileMessage(fL1S("Project %1: %2")
2736 .arg(function.toQString(m_tmp1).toUpper(), msg));
2737 // ### Consider real termination in non-cumulative mode
2738 return returnBool(function != QLatin1String("error"));
2740 #if 0 // Way too dangerous to enable.
2742 if (args.count() != 1) {
2743 evalError(fL1S("system(exec) requires one argument."));
2746 return returnBool(system((QLatin1String("cd ")
2747 + IoUtils::shellQuote(currentDirectory())
2748 + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
2752 if (args.count() != 1) {
2753 evalError(fL1S("isEmpty(var) requires one argument."));
2756 const ProStringList &sl = values(map(args.at(0)));
2757 if (sl.count() == 0) {
2759 } else if (sl.count() > 0) {
2760 const ProString &var = sl.first();
2767 if (args.count() != 1) {
2768 evalError(fL1S("exists(file) requires one argument."));
2771 const QString &file = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2773 if (IoUtils::exists(file)) {
2776 int slsh = file.lastIndexOf(QLatin1Char('/'));
2777 QString fn = file.mid(slsh+1);
2778 if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
2779 QString dirstr = file.left(slsh+1);
2780 if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
2787 evalError(fL1S("'%1' is not a recognized test function")
2788 .arg(function.toQString(m_tmp1)));
2791 evalError(fL1S("Function '%1' is not implemented").arg(function.toQString(m_tmp1)));
2796 QHash<ProString, ProStringList> *ProFileEvaluator::Private::findValues(
2797 const ProString &variableName, QHash<ProString, ProStringList>::Iterator *rit)
2799 for (int i = m_valuemapStack.size(); --i >= 0; ) {
2800 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(variableName);
2801 if (it != m_valuemapStack[i].end()) {
2802 if (it->constBegin() == statics.fakeValue.constBegin())
2805 return &m_valuemapStack[i];
2811 ProStringList &ProFileEvaluator::Private::valuesRef(const ProString &variableName)
2813 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack.top().find(variableName);
2814 if (it != m_valuemapStack.top().end())
2816 for (int i = m_valuemapStack.size() - 1; --i >= 0; ) {
2817 QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
2818 if (it != m_valuemapStack.at(i).constEnd()) {
2819 ProStringList &ret = m_valuemapStack.top()[variableName];
2824 return m_valuemapStack.top()[variableName];
2827 ProStringList ProFileEvaluator::Private::valuesDirect(const ProString &variableName) const
2829 for (int i = m_valuemapStack.size(); --i >= 0; ) {
2830 QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
2831 if (it != m_valuemapStack.at(i).constEnd()) {
2832 if (it->constBegin() == statics.fakeValue.constBegin())
2837 return ProStringList();
2840 ProStringList ProFileEvaluator::Private::values(const ProString &variableName) const
2842 QHash<ProString, int>::ConstIterator vli = statics.varList.find(variableName);
2843 if (vli != statics.varList.constEnd()) {
2846 switch ((VarName)vlidx) {
2847 case V_LITERAL_WHITESPACE: ret = QLatin1String("\t"); break;
2848 case V_LITERAL_DOLLAR: ret = QLatin1String("$"); break;
2849 case V_LITERAL_HASH: ret = QLatin1String("#"); break;
2850 case V_OUT_PWD: // the outgoing dir (shadow of _PRO_FILE_PWD_)
2853 case V_PWD: // containing directory of most nested project/include file
2855 ret = currentDirectory();
2857 case V_DIR_SEPARATOR:
2858 ret = m_option->dir_sep;
2860 case V_DIRLIST_SEPARATOR:
2861 ret = m_option->dirlist_sep;
2863 case V__LINE_: // currently executed line number
2864 ret = QString::number(m_current.line);
2866 case V__FILE_: // currently executed file
2867 ret = m_current.pro->fileName();
2869 case V__DATE_: //current date/time
2870 ret = QDateTime::currentDateTime().toString();
2873 ret = m_profileStack.first()->fileName();
2875 case V__PRO_FILE_PWD_:
2876 ret = m_profileStack.first()->directoryName();
2878 case V__QMAKE_CACHE_:
2879 ret = m_option->cachefile;
2881 #if defined(Q_OS_WIN32)
2882 case V_QMAKE_HOST_os: ret = QLatin1String("Windows"); break;
2883 case V_QMAKE_HOST_name: {
2884 DWORD name_length = 1024;
2886 if (GetComputerName(name, &name_length))
2887 ret = QString::fromUtf16((ushort*)name, name_length);
2890 case V_QMAKE_HOST_version:
2891 ret = QString::number(QSysInfo::WindowsVersion);
2893 case V_QMAKE_HOST_version_string:
2894 switch (QSysInfo::WindowsVersion) {
2895 case QSysInfo::WV_Me: ret = QLatin1String("WinMe"); break;
2896 case QSysInfo::WV_95: ret = QLatin1String("Win95"); break;
2897 case QSysInfo::WV_98: ret = QLatin1String("Win98"); break;
2898 case QSysInfo::WV_NT: ret = QLatin1String("WinNT"); break;
2899 case QSysInfo::WV_2000: ret = QLatin1String("Win2000"); break;
2900 case QSysInfo::WV_2003: ret = QLatin1String("Win2003"); break;
2901 case QSysInfo::WV_XP: ret = QLatin1String("WinXP"); break;
2902 case QSysInfo::WV_VISTA: ret = QLatin1String("WinVista"); break;
2903 default: ret = QLatin1String("Unknown"); break;
2906 case V_QMAKE_HOST_arch:
2908 GetSystemInfo(&info);
2909 switch(info.wProcessorArchitecture) {
2910 #ifdef PROCESSOR_ARCHITECTURE_AMD64
2911 case PROCESSOR_ARCHITECTURE_AMD64:
2912 ret = QLatin1String("x86_64");
2915 case PROCESSOR_ARCHITECTURE_INTEL:
2916 ret = QLatin1String("x86");
2918 case PROCESSOR_ARCHITECTURE_IA64:
2919 #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
2920 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
2922 ret = QLatin1String("IA64");
2925 ret = QLatin1String("Unknown");
2929 #elif defined(Q_OS_UNIX)
2930 case V_QMAKE_HOST_os:
2931 case V_QMAKE_HOST_name:
2932 case V_QMAKE_HOST_version:
2933 case V_QMAKE_HOST_version_string:
2934 case V_QMAKE_HOST_arch:
2936 struct utsname name;
2938 if (!uname(&name)) {
2940 case V_QMAKE_HOST_os: what = name.sysname; break;
2941 case V_QMAKE_HOST_name: what = name.nodename; break;
2942 case V_QMAKE_HOST_version: what = name.release; break;
2943 case V_QMAKE_HOST_version_string: what = name.version; break;
2944 case V_QMAKE_HOST_arch: what = name.machine; break;
2946 ret = QString::fromLocal8Bit(what);
2951 return ProStringList(ProString(ret, NoHash));
2954 ProStringList result = valuesDirect(variableName);
2955 if (result.isEmpty()) {
2956 if (variableName == statics.strTEMPLATE) {
2957 result.append(ProString("app", NoHash));
2958 } else if (variableName == statics.strQMAKE_DIR_SEP) {
2959 result.append(ProString(m_option->dirlist_sep, NoHash));
2965 bool ProFileEvaluator::Private::evaluateFileDirect(
2966 const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
2967 ProFileEvaluator::LoadFlags flags)
2969 if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
2970 m_locationStack.push(m_current);
2971 bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
2972 m_current = m_locationStack.pop();
2980 bool ProFileEvaluator::Private::evaluateFile(
2981 const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
2982 ProFileEvaluator::LoadFlags flags)
2984 if (fileName.isEmpty())
2986 foreach (const ProFile *pf, m_profileStack)
2987 if (pf->fileName() == fileName) {
2988 evalError(fL1S("circular inclusion of %1").arg(fileName));
2991 return evaluateFileDirect(fileName, type, flags);
2994 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
2996 QString fn = fileName;
2997 if (!fn.endsWith(QLatin1String(".prf")))
2998 fn += QLatin1String(".prf");
3000 if (!fileName.contains((ushort)'/') || !IoUtils::exists(resolvePath(fn))) {
3001 if (m_option->feature_roots.isEmpty())
3002 m_option->feature_roots = qmakeFeaturePaths();
3004 QString currFn = currentFileName();
3005 if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
3006 for (int root = 0; root < m_option->feature_roots.size(); ++root)
3007 if (currFn == m_option->feature_roots.at(root) + fn) {
3008 start_root = root + 1;
3012 for (int root = start_root; root < m_option->feature_roots.size(); ++root) {
3013 QString fname = m_option->feature_roots.at(root) + fn;
3014 if (IoUtils::exists(fname)) {
3022 // It's beyond me why qmake has this inside this if ...
3023 ProStringList &already = valuesRef(ProString("QMAKE_INTERNAL_INCLUDED_FEATURES"));
3024 ProString afn(fn, NoHash);
3025 if (already.contains(afn))
3027 already.append(afn);
3029 fn = resolvePath(fn);
3032 bool cumulative = m_cumulative;
3033 m_cumulative = false;
3035 // The path is fully normalized already.
3036 bool ok = evaluateFileDirect(fn, ProFileEvaluatorHandler::EvalFeatureFile,
3037 ProFileEvaluator::LoadProOnly);
3039 m_cumulative = cumulative;
3043 bool ProFileEvaluator::Private::evaluateFileInto(
3044 const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
3045 QHash<ProString, ProStringList> *values, FunctionDefs *funcs, EvalIntoMode mode)
3047 ProFileEvaluator visitor(m_option, m_parser, m_handler);
3048 visitor.d->m_cumulative = false;
3049 visitor.d->m_outputDir = m_outputDir;
3050 // visitor.d->m_valuemapStack.top() = *values;
3052 visitor.d->m_functionDefs = *funcs;
3053 if (mode == EvalWithDefaults)
3054 visitor.d->evaluateFeatureFile(QLatin1String("default_pre.prf"));
3055 if (!visitor.d->evaluateFile(fileName, type,
3056 (mode == EvalWithSetup) ? ProFileEvaluator::LoadAll : ProFileEvaluator::LoadProOnly))
3058 *values = visitor.d->m_valuemapStack.top();
3060 // *funcs = visitor.d->m_functionDefs;
3064 void ProFileEvaluator::Private::evalError(const QString &message) const
3067 m_handler->evalError(m_current.pro->fileName(), m_current.line, message);
3071 ///////////////////////////////////////////////////////////////////////
3075 ///////////////////////////////////////////////////////////////////////
3077 void ProFileEvaluator::initialize()
3079 Private::initStatics();
3082 ProFileEvaluator::ProFileEvaluator(ProFileOption *option, ProFileParser *parser,
3083 ProFileEvaluatorHandler *handler)
3084 : d(new Private(this, option, parser, handler))
3088 ProFileEvaluator::~ProFileEvaluator()
3093 bool ProFileEvaluator::contains(const QString &variableName) const
3095 return d->m_valuemapStack.top().contains(ProString(variableName));
3098 static QStringList expandEnvVars(const ProStringList &x)
3101 foreach (const ProString &str, x)
3102 ret << expandEnvVars(str.toQString());
3106 QString ProFileEvaluator::value(const QString &variable) const
3108 const QStringList &vals = values(variable);
3109 if (!vals.isEmpty())
3110 return vals.first();
3115 QStringList ProFileEvaluator::values(const QString &variableName) const
3117 return expandEnvVars(d->values(ProString(variableName)));
3120 QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
3122 // It makes no sense to put any kind of magic into expanding these
3123 return expandEnvVars(d->m_filevaluemap.value(pro).value(ProString(variableName)));
3126 QStringList ProFileEvaluator::absolutePathValues(
3127 const QString &variable, const QString &baseDirectory) const
3130 foreach (const QString &el, values(variable)) {
3131 QString absEl = IoUtils::resolvePath(baseDirectory, el);
3132 if (IoUtils::fileType(absEl) == IoUtils::FileIsDir)
3133 result << QDir::cleanPath(absEl);
3138 QStringList ProFileEvaluator::absoluteFileValues(
3139 const QString &variable, const QString &baseDirectory, const QStringList &searchDirs,
3140 const ProFile *pro) const
3143 foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
3145 if (IoUtils::isAbsolutePath(el)) {
3146 if (IoUtils::exists(el)) {
3147 result << QDir::cleanPath(el);
3152 foreach (const QString &dir, searchDirs) {
3153 QString fn = dir + QLatin1Char('/') + el;
3154 if (IoUtils::exists(fn)) {
3155 result << QDir::cleanPath(fn);
3159 if (baseDirectory.isEmpty())
3161 absEl = baseDirectory + QLatin1Char('/') + el;
3164 absEl = QDir::cleanPath(absEl);
3165 int nameOff = absEl.lastIndexOf(QLatin1Char('/'));
3166 QString absDir = d->m_tmp1.setRawData(absEl.constData(), nameOff);
3167 if (IoUtils::exists(absDir)) {
3168 QString wildcard = d->m_tmp2.setRawData(absEl.constData() + nameOff + 1,
3169 absEl.length() - nameOff - 1);
3170 if (wildcard.contains(QLatin1Char('*')) || wildcard.contains(QLatin1Char('?'))) {
3171 QDir theDir(absDir);
3172 foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
3173 if (fn != statics.strDot && fn != statics.strDotDot)
3174 result << absDir + QLatin1Char('/') + fn;
3175 } // else if (acceptMissing)
3183 ProFileEvaluator::TemplateType ProFileEvaluator::templateType() const
3185 const ProStringList &templ = d->values(statics.strTEMPLATE);
3186 if (templ.count() >= 1) {
3187 const QString &t = templ.at(0).toQString();
3188 if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
3189 return TT_Application;
3190 if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
3192 if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
3194 if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
3200 bool ProFileEvaluator::accept(ProFile *pro, LoadFlags flags)
3202 return d->visitProFile(pro, ProFileEvaluatorHandler::EvalProjectFile, flags);
3205 QString ProFileEvaluator::propertyValue(const QString &name) const
3207 return d->propertyValue(name, false);
3210 void ProFileEvaluator::setCumulative(bool on)
3212 d->m_cumulative = on;
3215 void ProFileEvaluator::setOutputDir(const QString &dir)
3217 d->m_outputDir = dir;
3220 void ProFileEvaluator::setConfigCommandLineArguments(const QStringList &addUserConfigCmdArgs, const QStringList &removeUserConfigCmdArgs)
3222 d->m_addUserConfigCmdArgs = addUserConfigCmdArgs;
3223 d->m_removeUserConfigCmdArgs = removeUserConfigCmdArgs;