1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** 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.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "profileevaluator.h"
43
44#include "profileparser.h"
45#include "ioutils.h"
46
47#include <QtCore/QByteArray>
48#include <QtCore/QDateTime>
49#include <QtCore/QDebug>
50#include <QtCore/QDir>
51#include <QtCore/QFile>
52#include <QtCore/QFileInfo>
53#include <QtCore/QList>
54#include <QtCore/QRegExp>
55#include <QtCore/QSet>
56#include <QtCore/QStack>
57#include <QtCore/QString>
58#include <QtCore/QStringList>
59#include <QtCore/QTextStream>
60#ifdef PROEVALUATOR_THREAD_SAFE
61# include <QtCore/QThreadPool>
62#endif
63
64#ifdef Q_OS_UNIX
65#include <unistd.h>
66#include <sys/utsname.h>
67#else
68#include <Windows.h>
69#endif
70#include <stdio.h>
71#include <stdlib.h>
72
73#ifdef Q_OS_WIN32
74#define QT_POPEN _popen
75#define QT_PCLOSE _pclose
76#else
77#define QT_POPEN popen
78#define QT_PCLOSE pclose
79#endif
80
81using namespace ProFileEvaluatorInternal;
82
83QT_BEGIN_NAMESPACE
84
85using namespace ProStringConstants;
86
87
88#define fL1S(s) QString::fromLatin1(s)
89
90///////////////////////////////////////////////////////////////////////
91//
92// ProFileOption
93//
94///////////////////////////////////////////////////////////////////////
95
96ProFileOption::ProFileOption()
97{
98#ifdef Q_OS_WIN
99 dirlist_sep = QLatin1Char(';');
100 dir_sep = QLatin1Char('\\');
101#else
102 dirlist_sep = QLatin1Char(':');
103 dir_sep = QLatin1Char('/');
104#endif
105 qmakespec = getEnv(QLatin1String("QMAKESPEC"));
106
107 host_mode = HOST_UNKNOWN_MODE;
108 target_mode = TARG_UNKNOWN_MODE;
109
110#ifdef PROEVALUATOR_THREAD_SAFE
111 base_inProgress = false;
112#endif
113}
114
115ProFileOption::~ProFileOption()
116{
117}
118
119void ProFileOption::setCommandLineArguments(const QStringList &args)
120{
121 QStringList _precmds, _preconfigs, _postcmds, _postconfigs;
122 bool after = false;
123
124 bool isConf = false;
125 foreach (const QString &arg, args) {
126 if (isConf) {
127 isConf = false;
128 if (after)
129 _postconfigs << arg;
130 else
131 _preconfigs << arg;
132 } else if (arg.startsWith(QLatin1Char('-'))) {
133 if (arg == QLatin1String("-after")) {
134 after = true;
135 } else if (arg == QLatin1String("-config")) {
136 isConf = true;
137 } else if (arg == QLatin1String("-win32")) {
138 host_mode = HOST_WIN_MODE;
139 target_mode = TARG_WIN_MODE;
140 } else if (arg == QLatin1String("-unix")) {
141 host_mode = HOST_UNIX_MODE;
142 target_mode = TARG_UNIX_MODE;
143 } else if (arg == QLatin1String("-macx")) {
144 host_mode = HOST_MACX_MODE;
145 target_mode = TARG_MACX_MODE;
146 }
147 } else if (arg.contains(QLatin1Char('='))) {
148 if (after)
149 _postcmds << arg;
150 else
151 _precmds << arg;
152 }
153 }
154
155 if (!_preconfigs.isEmpty())
156 _precmds << (fL1S("CONFIG += ") + _preconfigs.join(fL1S(" ")));
157 precmds = _precmds.join(fL1S("\n"));
158 if (!_postconfigs.isEmpty())
159 _postcmds << (fL1S("CONFIG += ") + _postconfigs.join(fL1S(" ")));
160 postcmds = _postcmds.join(fL1S("\n"));
161
162 if (host_mode != HOST_UNKNOWN_MODE)
163 applyHostMode();
164}
165
166void ProFileOption::applyHostMode()
167{
168 if (host_mode == HOST_WIN_MODE) {
169 dir_sep = fL1S("\\");
170 } else {
171 dir_sep = fL1S("/");
172 }
173}
174
175QString ProFileOption::getEnv(const QString &var) const
176{
177#ifndef QT_BOOTSTRAPPED
178 if (!environment.isEmpty())
179 return environment.value(var);
180#endif
181 return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
182}
183
184#ifdef PROEVALUATOR_INIT_PROPS
185bool ProFileOption::initProperties(const QString &qmake)
186{
187 QByteArray data;
188#ifndef QT_BOOTSTRAPPED
189 QProcess proc;
190 proc.start(qmake, QStringList() << QLatin1String("-query"));
191 if (!proc.waitForFinished())
192 return false;
193 data = proc.readAll();
194#else
195 if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake) + QLatin1String(" -query"))
196 .toLocal8Bit(), "r")) {
197 char buff[1024];
198 while (!feof(proc))
199 data.append(buff, int(fread(buff, 1, 1023, proc)));
200 QT_PCLOSE(proc);
201 }
202#endif
203 foreach (QByteArray line, data.split('\n'))
204 if (!line.startsWith("QMAKE_")) {
205 int off = line.indexOf(':');
206 if (off < 0) // huh?
207 continue;
208 if (line.endsWith('\r'))
209 line.chop(1);
210 properties.insert(QString::fromLatin1(line.left(off)),
211 QString::fromLocal8Bit(line.mid(off + 1)));
212 }
213 return true;
214}
215#endif
216
217///////////////////////////////////////////////////////////////////////
218//
219// ProFileEvaluator::Private
220//
221///////////////////////////////////////////////////////////////////////
222
223class ProFileEvaluator::Private
224{
225public:
226 static void initStatics();
227 Private(ProFileEvaluator *q_, ProFileOption *option, ProFileParser *parser,
228 ProFileEvaluatorHandler *handler);
229 ~Private();
230
231 ProFileEvaluator *q;
232
233 enum VisitReturn {
234 ReturnFalse,
235 ReturnTrue,
236 ReturnBreak,
237 ReturnNext,
238 ReturnReturn
239 };
240
241 static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
242 ProString getStr(const ushort *&tokPtr);
243 ProString getHashStr(const ushort *&tokPtr);
244 void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
245 static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
246 static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
247 void skipExpression(const ushort *&tokPtr);
248
249 void visitCmdLine(const QString &cmds);
250 VisitReturn visitProFile(ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
251 ProFileEvaluator::LoadFlags flags);
252 VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
253 VisitReturn visitProBlock(const ushort *tokPtr);
254 VisitReturn visitProLoop(const ProString &variable, const ushort *exprPtr,
255 const ushort *tokPtr);
256 void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr);
257 void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
258
259 static inline const ProString &map(const ProString &var);
260 QHash<ProString, ProStringList> *findValues(const ProString &variableName,
261 QHash<ProString, ProStringList>::Iterator *it);
262 ProStringList &valuesRef(const ProString &variableName);
263 ProStringList valuesDirect(const ProString &variableName) const;
264 ProStringList values(const ProString &variableName) const;
265 QString propertyValue(const QString &val, bool complain) const;
266
267 ProStringList split_value_list(const QString &vals, const ProFile *source = 0);
268 bool isActiveConfig(const QString &config, bool regex = false);
269 ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
270 ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
271 ProStringList evaluateExpandFunction(const ProString &function, const ProString &arguments);
272 ProStringList evaluateExpandFunction(const ProString &function, const ushort *&tokPtr);
273 ProStringList evaluateExpandFunction(const ProString &function, const ProStringList &args);
274 void evalError(const QString &msg) const;
275
276 QString currentFileName() const;
277 QString currentDirectory() const;
278 ProFile *currentProFile() const;
279 QString resolvePath(const QString &fileName) const
280 { return IoUtils::resolvePath(currentDirectory(), fileName); }
281
282 VisitReturn evaluateConditionalFunction(const ProString &function, const ProString &arguments);
283 VisitReturn evaluateConditionalFunction(const ProString &function, const ushort *&tokPtr);
284 VisitReturn evaluateConditionalFunction(const ProString &function, const ProStringList &args);
285 bool evaluateFileDirect(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
286 ProFileEvaluator::LoadFlags flags);
287 bool evaluateFile(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
288 ProFileEvaluator::LoadFlags flags);
289 bool evaluateFeatureFile(const QString &fileName);
290 enum EvalIntoMode { EvalProOnly, EvalWithDefaults, EvalWithSetup };
291 bool evaluateFileInto(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
292 QHash<ProString, ProStringList> *values, FunctionDefs *defs,
293 EvalIntoMode mode); // values are output-only, defs are input-only
294
295 static ALWAYS_INLINE VisitReturn returnBool(bool b)
296 { return b ? ReturnTrue : ReturnFalse; }
297
298 QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
299 QList<ProStringList> prepareFunctionArgs(const ProString &arguments);
300 ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok);
301 VisitReturn evaluateBoolFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList,
302 const ProString &function);
303
304 bool modesForGenerator(const QString &gen,
305 ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const;
306 void validateModes() const;
307 QStringList qmakeMkspecPaths() const;
308 QStringList qmakeFeaturePaths() const;
309
310 QString expandEnvVars(const QString &str) const;
311 QString fixPathToLocalOS(const QString &str) const;
312 QString sysrootify(const QString &path, const QString &baseDir) const;
313
314#ifndef QT_BOOTSTRAPPED
315 void runProcess(QProcess *proc, const QString &command, QProcess::ProcessChannel chan) const;
316#endif
317
318 int m_skipLevel;
319 int m_loopLevel; // To report unexpected break() and next()s
320#ifdef PROEVALUATOR_CUMULATIVE
321 bool m_cumulative;
322#else
323 enum { m_cumulative = 0 };
324#endif
325
326 struct Location {
327 Location() : pro(0), line(0) {}
328 Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {}
329 ProFile *pro;
330 int line;
331 };
332
333 Location m_current; // Currently evaluated location
334 QStack<Location> m_locationStack; // All execution location changes
335 QStack<ProFile *> m_profileStack; // Includes only
336
337 QString m_outputDir;
338
339 int m_listCount;
340 FunctionDefs m_functionDefs;
341 ProStringList m_returnValue;
342 QStack<QHash<ProString, ProStringList> > m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii.
343 QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
344
345 ProFileOption *m_option;
346 ProFileParser *m_parser;
347 ProFileEvaluatorHandler *m_handler;
348
349 enum ExpandFunc {
350 E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
351 E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
352 E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
353 E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
354 E_REPLACE
355 };
356
357 enum TestFunc {
358 T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
359 T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
360 T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
361 T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF
362 };
363
364 enum VarName {
365 V_LITERAL_DOLLAR, V_LITERAL_HASH, V_LITERAL_WHITESPACE,
366 V_DIRLIST_SEPARATOR, V_DIR_SEPARATOR,
367 V_OUT_PWD, V_PWD, V_IN_PWD,
368 V__FILE_, V__LINE_, V__PRO_FILE_, V__PRO_FILE_PWD_,
369 V_QMAKE_HOST_arch, V_QMAKE_HOST_name, V_QMAKE_HOST_os,
370 V_QMAKE_HOST_version, V_QMAKE_HOST_version_string,
371 V__DATE_, V__QMAKE_CACHE_
372 };
373};
374
375static struct {
376 QString field_sep;
377 QString strtrue;
378 QString strfalse;
379 QString strunix;
380 QString strmacx;
381 QString strmac;
382 QString strwin32;
383 QString strsymbian;
384 ProString strCONFIG;
385 ProString strARGS;
386 QString strDot;
387 QString strDotDot;
388 QString strever;
389 QString strforever;
390 ProString strTEMPLATE;
391 ProString strQMAKE_DIR_SEP;
392 QHash<ProString, int> expands;
393 QHash<ProString, int> functions;
394 QHash<ProString, int> varList;
395 QHash<ProString, ProString> varMap;
396 QRegExp reg_variableName;
397 ProStringList fakeValue;
398} statics;
399
400void ProFileEvaluator::Private::initStatics()
401{
402 if (!statics.field_sep.isNull())
403 return;
404
405 statics.field_sep = QLatin1String(" ");
406 statics.strtrue = QLatin1String("true");
407 statics.strfalse = QLatin1String("false");
408 statics.strunix = QLatin1String("unix");
409 statics.strmacx = QLatin1String("macx");
410 statics.strmac = QLatin1String("mac");
411 statics.strwin32 = QLatin1String("win32");
412 statics.strsymbian = QLatin1String("symbian");
413 statics.strCONFIG = ProString("CONFIG");
414 statics.strARGS = ProString("ARGS");
415 statics.strDot = QLatin1String(".");
416 statics.strDotDot = QLatin1String("..");
417 statics.strever = QLatin1String("ever");
418 statics.strforever = QLatin1String("forever");
419 statics.strTEMPLATE = ProString("TEMPLATE");
420 statics.strQMAKE_DIR_SEP = ProString("QMAKE_DIR_SEP");
421
422 statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
423 statics.reg_variableName.setMinimal(true);
424
425 statics.fakeValue.detach(); // It has to have a unique begin() value
426
427 static const struct {
428 const char * const name;
429 const ExpandFunc func;
430 } expandInits[] = {
431 { "member", E_MEMBER },
432 { "first", E_FIRST },
433 { "last", E_LAST },
434 { "size", E_SIZE },
435 { "cat", E_CAT },
436 { "fromfile", E_FROMFILE },
437 { "eval", E_EVAL },
438 { "list", E_LIST },
439 { "sprintf", E_SPRINTF },
440 { "join", E_JOIN },
441 { "split", E_SPLIT },
442 { "basename", E_BASENAME },
443 { "dirname", E_DIRNAME },
444 { "section", E_SECTION },
445 { "find", E_FIND },
446 { "system", E_SYSTEM },
447 { "unique", E_UNIQUE },
448 { "quote", E_QUOTE },
449 { "escape_expand", E_ESCAPE_EXPAND },
450 { "upper", E_UPPER },
451 { "lower", E_LOWER },
452 { "re_escape", E_RE_ESCAPE },
453 { "files", E_FILES },
454 { "prompt", E_PROMPT }, // interactive, so cannot be implemented
455 { "replace", E_REPLACE }
456 };
457 for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
458 statics.expands.insert(ProString(expandInits[i].name), expandInits[i].func);
459
460 static const struct {
461 const char * const name;
462 const TestFunc func;
463 } testInits[] = {
464 { "requires", T_REQUIRES },
465 { "greaterThan", T_GREATERTHAN },
466 { "lessThan", T_LESSTHAN },
467 { "equals", T_EQUALS },
468 { "isEqual", T_EQUALS },
469 { "exists", T_EXISTS },
470 { "export", T_EXPORT },
471 { "clear", T_CLEAR },
472 { "unset", T_UNSET },
473 { "eval", T_EVAL },
474 { "CONFIG", T_CONFIG },
475 { "if", T_IF },
476 { "isActiveConfig", T_CONFIG },
477 { "system", T_SYSTEM },
478 { "return", T_RETURN },
479 { "break", T_BREAK },
480 { "next", T_NEXT },
481 { "defined", T_DEFINED },
482 { "contains", T_CONTAINS },
483 { "infile", T_INFILE },
484 { "count", T_COUNT },
485 { "isEmpty", T_ISEMPTY },
486 { "load", T_LOAD },
487 { "include", T_INCLUDE },
488 { "debug", T_DEBUG },
489 { "message", T_MESSAGE },
490 { "warning", T_MESSAGE },
491 { "error", T_MESSAGE },
492 };
493 for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
494 statics.functions.insert(ProString(testInits[i].name), testInits[i].func);
495
496 static const char * const names[] = {
497 "LITERAL_DOLLAR", "LITERAL_HASH", "LITERAL_WHITESPACE",
498 "DIRLIST_SEPARATOR", "DIR_SEPARATOR",
499 "OUT_PWD", "PWD", "IN_PWD",
500 "_FILE_", "_LINE_", "_PRO_FILE_", "_PRO_FILE_PWD_",
501 "QMAKE_HOST.arch", "QMAKE_HOST.name", "QMAKE_HOST.os",
502 "QMAKE_HOST.version", "QMAKE_HOST.version_string",
503 "_DATE_", "_QMAKE_CACHE_"
504 };
505 for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
506 statics.varList.insert(ProString(names[i]), i);
507
508 static const struct {
509 const char * const oldname, * const newname;
510 } mapInits[] = {
511 { "INTERFACES", "FORMS" },
512 { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
513 { "TARGETDEPS", "POST_TARGETDEPS" },
514 { "LIBPATH", "QMAKE_LIBDIR" },
515 { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
516 { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
517 { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
518 { "PRECOMPH", "PRECOMPILED_HEADER" },
519 { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
520 { "INCPATH", "INCLUDEPATH" },
521 { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
522 { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
523 { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
524 { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
525 { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
526 { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
527 { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
528 { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
529 { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }
530 };
531 for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
532 statics.varMap.insert(ProString(mapInits[i].oldname),
533 ProString(mapInits[i].newname));
534}
535
536const ProString &ProFileEvaluator::Private::map(const ProString &var)
537{
538 QHash<ProString, ProString>::ConstIterator it = statics.varMap.constFind(var);
539 return (it != statics.varMap.constEnd()) ? it.value() : var;
540}
541
542
543ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option,
544 ProFileParser *parser, ProFileEvaluatorHandler *handler)
545 : q(q_), m_option(option), m_parser(parser), m_handler(handler)
546{
547 // So that single-threaded apps don't have to call initialize() for now.
548 initStatics();
549
550 // Configuration, more or less
551#ifdef PROEVALUATOR_CUMULATIVE
552 m_cumulative = true;
553#endif
554
555 // Evaluator state
556 m_skipLevel = 0;
557 m_loopLevel = 0;
558 m_listCount = 0;
559 m_valuemapStack.push(QHash<ProString, ProStringList>());
560}
561
562ProFileEvaluator::Private::~Private()
563{
564}
565
566//////// Evaluator tools /////////
567
568uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr)
569{
570 uint len = *tokPtr++;
571 len |= (uint)*tokPtr++ << 16;
572 return len;
573}
574
575ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr)
576{
577 uint len = *tokPtr++;
578 ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, NoHash);
579 ret.setSource(m_current.pro);
580 tokPtr += len;
581 return ret;
582}
583
584ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr)
585{
586 uint hash = getBlockLen(tokPtr);
587 uint len = *tokPtr++;
588 ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
589 tokPtr += len;
590 return ret;
591}
592
593void ProFileEvaluator::Private::skipStr(const ushort *&tokPtr)
594{
595 uint len = *tokPtr++;
596 tokPtr += len;
597}
598
599void ProFileEvaluator::Private::skipHashStr(const ushort *&tokPtr)
600{
601 tokPtr += 2;
602 uint len = *tokPtr++;
603 tokPtr += len;
604}
605
606// FIXME: this should not build new strings for direct sections.
607// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
608ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals, const ProFile *source)
609{
610 QString build;
611 ProStringList ret;
612 QStack<char> quote;
613
614 const ushort SPACE = ' ';
615 const ushort LPAREN = '(';
616 const ushort RPAREN = ')';
617 const ushort SINGLEQUOTE = '\'';
618 const ushort DOUBLEQUOTE = '"';
619 const ushort BACKSLASH = '\\';
620
621 if (!source)
622 source = currentProFile();
623
624 ushort unicode;
625 const QChar *vals_data = vals.data();
626 const int vals_len = vals.length();
627 for (int x = 0, parens = 0; x < vals_len; x++) {
628 unicode = vals_data[x].unicode();
629 if (x != (int)vals_len-1 && unicode == BACKSLASH &&
630 (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
631 build += vals_data[x++]; //get that 'escape'
632 } else if (!quote.isEmpty() && unicode == quote.top()) {
633 quote.pop();
634 } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
635 quote.push(unicode);
636 } else if (unicode == RPAREN) {
637 --parens;
638 } else if (unicode == LPAREN) {
639 ++parens;
640 }
641
642 if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
643 ret << ProString(build, NoHash).setSource(source);
644 build.clear();
645 } else {
646 build += vals_data[x];
647 }
648 }
649 if (!build.isEmpty())
650 ret << ProString(build, NoHash).setSource(source);
651 return ret;
652}
653
654static void zipEmpty(ProStringList *value)
655{
656 for (int i = value->size(); --i >= 0;)
657 if (value->at(i).isEmpty())
658 value->remove(i);
659}
660
661static void insertUnique(ProStringList *varlist, const ProStringList &value)
662{
663 foreach (const ProString &str, value)
664 if (!str.isEmpty() && !varlist->contains(str))
665 varlist->append(str);
666}
667
668static void removeAll(ProStringList *varlist, const ProString &value)
669{
670 for (int i = varlist->size(); --i >= 0; )
671 if (varlist->at(i) == value)
672 varlist->remove(i);
673}
674
675static void removeEach(ProStringList *varlist, const ProStringList &value)
676{
677 foreach (const ProString &str, value)
678 if (!str.isEmpty())
679 removeAll(varlist, str);
680}
681
682static void replaceInList(ProStringList *varlist,
683 const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
684{
685 for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
686 QString val = varit->toQString(tmp);
687 QString copy = val; // Force detach and have a reference value
688 val.replace(regexp, replace);
689 if (!val.isSharedWith(copy)) {
690 if (val.isEmpty()) {
691 varit = varlist->erase(varit);
692 } else {
693 (*varit).setValue(val, NoHash);
694 ++varit;
695 }
696 if (!global)
697 break;
698 } else {
699 ++varit;
700 }
701 }
702}
703
704QString ProFileEvaluator::Private::expandEnvVars(const QString &str) const
705{
706 QString string = str;
707 int rep;
708 QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
709 while ((rep = reg_variableName.indexIn(string)) != -1)
710 string.replace(rep, reg_variableName.matchedLength(),
711 m_option->getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3)));
712 return string;
713}
714
715// This is braindead, but we want qmake compat
716QString ProFileEvaluator::Private::fixPathToLocalOS(const QString &str) const
717{
718 QString string = expandEnvVars(str);
719
720 if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
721 string[0] = string[0].toLower();
722
723#if defined(Q_OS_WIN32)
724 string.replace(QLatin1Char('/'), QLatin1Char('\\'));
725#else
726 string.replace(QLatin1Char('\\'), QLatin1Char('/'));
727#endif
728 return string;
729}
730
731static bool isTrue(const ProString &_str, QString &tmp)
732{
733 const QString &str = _str.toQString(tmp);
734 return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
735}
736
737//////// Evaluator /////////
738
739static ALWAYS_INLINE void addStr(
740 const ProString &str, ProStringList *ret, bool &pending, bool joined)
741{
742 if (joined) {
743 ret->last().append(str, &pending);
744 } else {
745 if (!pending) {
746 pending = true;
747 *ret << str;
748 } else {
749 ret->last().append(str);
750 }
751 }
752}
753
754static ALWAYS_INLINE void addStrList(
755 const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
756{
757 if (!list.isEmpty()) {
758 if (joined) {
759 ret->last().append(list, &pending, !(tok & TokQuoted));
760 } else {
761 if (tok & TokQuoted) {
762 if (!pending) {
763 pending = true;
764 *ret << ProString();
765 }
766 ret->last().append(list);
767 } else {
768 if (!pending) {
769 // Another qmake bizzarity: if nothing is pending and the
770 // first element is empty, it will be eaten
771 if (!list.at(0).isEmpty()) {
772 // The common case
773 pending = true;
774 *ret += list;
775 return;
776 }
777 } else {
778 ret->last().append(list.at(0));
779 }
780 // This is somewhat slow, but a corner case
781 for (int j = 1; j < list.size(); ++j) {
782 pending = true;
783 *ret << list.at(j);
784 }
785 }
786 }
787 }
788}
789
790void ProFileEvaluator::Private::evaluateExpression(
791 const ushort *&tokPtr, ProStringList *ret, bool joined)
792{
793 if (joined)
794 *ret << ProString();
795 bool pending = false;
796 forever {
797 ushort tok = *tokPtr++;
798 if (tok & TokNewStr)
799 pending = false;
800 ushort maskedTok = tok & TokMask;
801 switch (maskedTok) {
802 case TokLine:
803 m_current.line = *tokPtr++;
804 break;
805 case TokLiteral:
806 addStr(getStr(tokPtr), ret, pending, joined);
807 break;
808 case TokHashLiteral:
809 addStr(getHashStr(tokPtr), ret, pending, joined);
810 break;
811 case TokVariable:
812 addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined);
813 break;
814 case TokProperty:
815 addStr(ProString(propertyValue(
816 getStr(tokPtr).toQString(m_tmp1), true), NoHash).setSource(currentProFile()),
817 ret, pending, joined);
818 break;
819 case TokEnvVar:
820 addStrList(split_value_list(m_option->getEnv(getStr(tokPtr).toQString(m_tmp1))),
821 tok, ret, pending, joined);
822 break;
823 case TokFuncName: {
824 ProString func = getHashStr(tokPtr);
825 addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
826 break; }
827 default:
828 tokPtr--;
829 return;
830 }
831 }
832}
833
834void ProFileEvaluator::Private::skipExpression(const ushort *&pTokPtr)
835{
836 const ushort *tokPtr = pTokPtr;
837 forever {
838 ushort tok = *tokPtr++;
839 switch (tok) {
840 case TokLine:
841 m_current.line = *tokPtr++;
842 break;
843 case TokValueTerminator:
844 case TokFuncTerminator:
845 pTokPtr = tokPtr;
846 return;
847 case TokArgSeparator:
848 break;
849 default:
850 switch (tok & TokMask) {
851 case TokLiteral:
852 case TokProperty:
853 case TokEnvVar:
854 skipStr(tokPtr);
855 break;
856 case TokHashLiteral:
857 case TokVariable:
858 skipHashStr(tokPtr);
859 break;
860 case TokFuncName:
861 skipHashStr(tokPtr);
862 pTokPtr = tokPtr;
863 skipExpression(pTokPtr);
864 tokPtr = pTokPtr;
865 break;
866 default:
867 Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
868 break;
869 }
870 }
871 }
872}
873
874ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
875 ProFile *pro, const ushort *tokPtr)
876{
877 m_current.pro = pro;
878 m_current.line = 0;
879 return visitProBlock(tokPtr);
880}
881
882ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
883 const ushort *tokPtr)
884{
885 ProStringList curr;
886 bool okey = true, or_op = false, invert = false;
887 uint blockLen;
888 VisitReturn ret = ReturnTrue;
889 while (ushort tok = *tokPtr++) {
890 switch (tok) {
891 case TokLine:
892 m_current.line = *tokPtr++;
893 continue;
894 case TokAssign:
895 case TokAppend:
896 case TokAppendUnique:
897 case TokRemove:
898 case TokReplace:
899 visitProVariable(tok, curr, tokPtr);
900 curr.clear();
901 continue;
902 case TokBranch:
903 blockLen = getBlockLen(tokPtr);
904 if (m_cumulative) {
905 if (!okey)
906 m_skipLevel++;
907 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
908 tokPtr += blockLen;
909 blockLen = getBlockLen(tokPtr);
910 if (!okey)
911 m_skipLevel--;
912 else
913 m_skipLevel++;
914 if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
915 ret = visitProBlock(tokPtr);
916 if (okey)
917 m_skipLevel--;
918 } else {
919 if (okey)
920 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
921 tokPtr += blockLen;
922 blockLen = getBlockLen(tokPtr);
923 if (!okey)
924 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
925 }
926 tokPtr += blockLen;
927 okey = true, or_op = false; // force next evaluation
928 break;
929 case TokForLoop:
930 if (m_cumulative || okey != or_op) {
931 const ProString &variable = getHashStr(tokPtr);
932 uint exprLen = getBlockLen(tokPtr);
933 const ushort *exprPtr = tokPtr;
934 tokPtr += exprLen;
935 blockLen = getBlockLen(tokPtr);
936 ret = visitProLoop(variable, exprPtr, tokPtr);
937 } else {
938 skipHashStr(tokPtr);
939 uint exprLen = getBlockLen(tokPtr);
940 tokPtr += exprLen;
941 blockLen = getBlockLen(tokPtr);
942 ret = ReturnTrue;
943 }
944 tokPtr += blockLen;
945 okey = true, or_op = false; // force next evaluation
946 break;
947 case TokTestDef:
948 case TokReplaceDef:
949 if (m_cumulative || okey != or_op) {
950 const ProString &name = getHashStr(tokPtr);
951 blockLen = getBlockLen(tokPtr);
952 visitProFunctionDef(tok, name, tokPtr);
953 } else {
954 skipHashStr(tokPtr);
955 blockLen = getBlockLen(tokPtr);
956 }
957 tokPtr += blockLen;
958 okey = true, or_op = false; // force next evaluation
959 continue;
960 case TokNot:
961 invert ^= true;
962 continue;
963 case TokAnd:
964 or_op = false;
965 continue;
966 case TokOr:
967 or_op = true;
968 continue;
969 case TokCondition:
970 if (!m_skipLevel && okey != or_op) {
971 if (curr.size() != 1) {
972 if (!m_cumulative || !curr.isEmpty())
973 evalError(fL1S("Conditional must expand to exactly one word."));
974 okey = false;
975 } else {
976 okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert;
977 }
978 }
979 or_op = !okey; // tentatively force next evaluation
980 invert = false;
981 curr.clear();
982 continue;
983 case TokTestCall:
984 if (!m_skipLevel && okey != or_op) {
985 if (curr.size() != 1) {
986 if (!m_cumulative || !curr.isEmpty())
987 evalError(fL1S("Test name must expand to exactly one word."));
988 skipExpression(tokPtr);
989 okey = false;
990 } else {
991 ret = evaluateConditionalFunction(curr.at(0), tokPtr);
992 switch (ret) {
993 case ReturnTrue: okey = true; break;
994 case ReturnFalse: okey = false; break;
995 default: return ret;
996 }
997 okey ^= invert;
998 }
999 } else if (m_cumulative) {
1000 m_skipLevel++;
1001 if (curr.size() != 1)
1002 skipExpression(tokPtr);
1003 else
1004 evaluateConditionalFunction(curr.at(0), tokPtr);
1005 m_skipLevel--;
1006 } else {
1007 skipExpression(tokPtr);
1008 }
1009 or_op = !okey; // tentatively force next evaluation
1010 invert = false;
1011 curr.clear();
1012 continue;
1013 default: {
1014 const ushort *oTokPtr = --tokPtr;
1015 evaluateExpression(tokPtr, &curr, false);
1016 if (tokPtr != oTokPtr)
1017 continue;
1018 }
1019 Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
1020 }
1021 if (ret != ReturnTrue && ret != ReturnFalse)
1022 break;
1023 }
1024 return ret;
1025}
1026
1027
1028void ProFileEvaluator::Private::visitProFunctionDef(
1029 ushort tok, const ProString &name, const ushort *tokPtr)
1030{
1031 QHash<ProString, FunctionDef> *hash =
1032 (tok == TokTestDef
1033 ? &m_functionDefs.testFunctions
1034 : &m_functionDefs.replaceFunctions);
1035 hash->insert(name, FunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
1036}
1037
1038ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(
1039 const ProString &_variable, const ushort *exprPtr, const ushort *tokPtr)
1040{
1041 VisitReturn ret = ReturnTrue;
1042 bool infinite = false;
1043 int index = 0;
1044 ProString variable;
1045 ProStringList oldVarVal;
1046 ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
1047 if (_variable.isEmpty()) {
1048 if (it_list != statics.strever) {
1049 evalError(fL1S("Invalid loop expression."));
1050 return ReturnFalse;
1051 }
1052 it_list = ProString(statics.strforever);
1053 } else {
1054 variable = map(_variable);
1055 oldVarVal = valuesDirect(variable);
1056 }
1057 ProStringList list = valuesDirect(it_list);
1058 if (list.isEmpty()) {
1059 if (it_list == statics.strforever) {
1060 if (m_cumulative) {
1061 // The termination conditions wouldn't be evaluated, so we must skip it.
1062 return ReturnFalse;
1063 }
1064 infinite = true;
1065 } else {
1066 const QString &itl = it_list.toQString(m_tmp1);
1067 int dotdot = itl.indexOf(statics.strDotDot);
1068 if (dotdot != -1) {
1069 bool ok;
1070 int start = itl.left(dotdot).toInt(&ok);
1071 if (ok) {
1072 int end = itl.mid(dotdot+2).toInt(&ok);
1073 if (ok) {
1074 if (m_cumulative && qAbs(end - start) > 100) {
1075 // Such a loop is unlikely to contribute something useful to the
1076 // file collection, and may cause considerable delay.
1077 return ReturnFalse;
1078 }
1079 if (start < end) {
1080 for (int i = start; i <= end; i++)
1081 list << ProString(QString::number(i), NoHash);
1082 } else {
1083 for (int i = start; i >= end; i--)
1084 list << ProString(QString::number(i), NoHash);
1085 }
1086 }
1087 }
1088 }
1089 }
1090 }
1091
1092 m_loopLevel++;
1093 forever {
1094 if (infinite) {
1095 if (!variable.isEmpty())
1096 m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++), NoHash));
1097 if (index > 1000) {
1098 evalError(fL1S("ran into infinite loop (> 1000 iterations)."));
1099 break;
1100 }
1101 } else {
1102 ProString val;
1103 do {
1104 if (index >= list.count())
1105 goto do_break;
1106 val = list.at(index++);
1107 } while (val.isEmpty()); // stupid, but qmake is like that
1108 m_valuemapStack.top()[variable] = ProStringList(val);
1109 }
1110
1111 ret = visitProBlock(tokPtr);
1112 switch (ret) {
1113 case ReturnTrue:
1114 case ReturnFalse:
1115 break;
1116 case ReturnNext:
1117 ret = ReturnTrue;
1118 break;
1119 case ReturnBreak:
1120 ret = ReturnTrue;
1121 goto do_break;
1122 default:
1123 goto do_break;
1124 }
1125 }
1126 do_break:
1127 m_loopLevel--;
1128
1129 if (!variable.isEmpty())
1130 m_valuemapStack.top()[variable] = oldVarVal;
1131 return ret;
1132}
1133
1134void ProFileEvaluator::Private::visitProVariable(
1135 ushort tok, const ProStringList &curr, const ushort *&tokPtr)
1136{
1137 int sizeHint = *tokPtr++;
1138
1139 if (curr.size() != 1) {
1140 skipExpression(tokPtr);
1141 if (!m_cumulative || !curr.isEmpty())
1142 evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
1143 return;
1144 }
1145 const ProString &varName = map(curr.first());
1146
1147 if (tok == TokReplace) { // ~=
1148 // DEFINES ~= s/a/b/?[gqi]
1149
1150 const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
1151 const QString &val = varVal.at(0).toQString(m_tmp1);
1152 if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
1153 evalError(fL1S("the ~= operator can handle only the s/// function."));
1154 return;
1155 }
1156 QChar sep = val.at(1);
1157 QStringList func = val.split(sep);
1158 if (func.count() < 3 || func.count() > 4) {
1159 evalError(fL1S("the s/// function expects 3 or 4 arguments."));
1160 return;
1161 }
1162
1163 bool global = false, quote = false, case_sense = false;
1164 if (func.count() == 4) {
1165 global = func[3].indexOf(QLatin1Char('g')) != -1;
1166 case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
1167 quote = func[3].indexOf(QLatin1Char('q')) != -1;
1168 }
1169 QString pattern = func[1];
1170 QString replace = func[2];
1171 if (quote)
1172 pattern = QRegExp::escape(pattern);
1173
1174 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
1175
1176 if (!m_skipLevel || m_cumulative) {
1177 // We could make a union of modified and unmodified values,
1178 // but this will break just as much as it fixes, so leave it as is.
1179 replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
1180 }
1181 } else {
1182 ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
1183 switch (tok) {
1184 default: // whatever - cannot happen
1185 case TokAssign: // =
1186 if (!m_cumulative) {
1187 if (!m_skipLevel) {
1188 zipEmpty(&varVal);
1189 m_valuemapStack.top()[varName] = varVal;
1190 }
1191 } else {
1192 zipEmpty(&varVal);
1193 if (!varVal.isEmpty()) {
1194 // We are greedy for values. But avoid exponential growth.
1195 ProStringList &v = valuesRef(varName);
1196 if (v.isEmpty()) {
1197 v = varVal;
1198 } else {
1199 ProStringList old = v;
1200 v = varVal;
1201 QSet<ProString> has;
1202 has.reserve(v.size());
1203 foreach (const ProString &s, v)
1204 has.insert(s);
1205 v.reserve(v.size() + old.size());
1206 foreach (const ProString &s, old)
1207 if (!has.contains(s))
1208 v << s;
1209 }
1210 }
1211 }
1212 break;
1213 case TokAppendUnique: // *=
1214 if (!m_skipLevel || m_cumulative)
1215 insertUnique(&valuesRef(varName), varVal);
1216 break;
1217 case TokAppend: // +=
1218 if (!m_skipLevel || m_cumulative) {
1219 zipEmpty(&varVal);
1220 valuesRef(varName) += varVal;
1221 }
1222 break;
1223 case TokRemove: // -=
1224 if (!m_cumulative) {
1225 if (!m_skipLevel)
1226 removeEach(&valuesRef(varName), varVal);
1227 } else {
1228 // We are stingy with our values, too.
1229 }
1230 break;
1231 }
1232 }
1233}
1234
1235void ProFileEvaluator::Private::visitCmdLine(const QString &cmds)
1236{
1237 if (!cmds.isEmpty()) {
1238 if (ProFile *pro = m_parser->parsedProBlock(fL1S("(command line)"), cmds)) {
1239 m_locationStack.push(m_current);
1240 visitProBlock(pro, pro->tokPtr());
1241 m_current = m_locationStack.pop();
1242 pro->deref();
1243 }
1244 }
1245}
1246
1247ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(
1248 ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
1249 ProFileEvaluator::LoadFlags flags)
1250{
1251 if (!m_cumulative && !pro->isOk())
1252 return ReturnFalse;
1253
1254 m_handler->aboutToEval(currentProFile(), pro, type);
1255 m_profileStack.push(pro);
1256 if (flags & LoadPreFiles) {
1257#ifdef PROEVALUATOR_THREAD_SAFE
1258 {
1259 QMutexLocker locker(&m_option->mutex);
1260 if (m_option->base_inProgress) {
1261 QThreadPool::globalInstance()->releaseThread();
1262 m_option->cond.wait(&m_option->mutex);
1263 QThreadPool::globalInstance()->reserveThread();
1264 } else
1265#endif
1266 if (m_option->base_valuemap.isEmpty()) {
1267#ifdef PROEVALUATOR_THREAD_SAFE
1268 m_option->base_inProgress = true;
1269 locker.unlock();
1270#endif
1271
1272#ifdef PROEVALUATOR_CUMULATIVE
1273 bool cumulative = m_cumulative;
1274 m_cumulative = false;
1275#endif
1276
1277 // ### init QMAKE_QMAKE, QMAKE_SH
1278 // ### init QMAKE_EXT_{C,H,CPP,OBJ}
1279 // ### init TEMPLATE_PREFIX
1280
1281 QString qmake_cache = m_option->cachefile;
1282 if (qmake_cache.isEmpty() && !m_outputDir.isEmpty()) { //find it as it has not been specified
1283 QDir dir(m_outputDir);
1284 forever {
1285 qmake_cache = dir.path() + QLatin1String("/.qmake.cache");
1286 if (IoUtils::exists(qmake_cache))
1287 break;
1288 if (!dir.cdUp() || dir.isRoot()) {
1289 qmake_cache.clear();
1290 break;
1291 }
1292 }
1293 }
1294 if (!qmake_cache.isEmpty()) {
1295 qmake_cache = resolvePath(qmake_cache);
1296 QHash<ProString, ProStringList> cache_valuemap;
1297 if (evaluateFileInto(qmake_cache, ProFileEvaluatorHandler::EvalConfigFile,
1298 &cache_valuemap, 0, EvalProOnly)) {
1299 if (m_option->qmakespec.isEmpty()) {
1300 const ProStringList &vals = cache_valuemap.value(ProString("QMAKESPEC"));
1301 if (!vals.isEmpty())
1302 m_option->qmakespec = vals.first().toQString();
1303 }
1304 } else {
1305 qmake_cache.clear();
1306 }
1307 }
1308 m_option->cachefile = qmake_cache;
1309
1310 QStringList mkspec_roots = qmakeMkspecPaths();
1311
1312 QString qmakespec = expandEnvVars(m_option->qmakespec);
1313 if (qmakespec.isEmpty()) {
1314 foreach (const QString &root, mkspec_roots) {
1315 QString mkspec = root + QLatin1String("/default");
1316 if (IoUtils::fileType(mkspec) == IoUtils::FileIsDir) {
1317 qmakespec = mkspec;
1318 break;
1319 }
1320 }
1321 if (qmakespec.isEmpty()) {
1322 m_handler->configError(fL1S("Could not find qmake configuration directory"));
1323 // Unlike in qmake, not finding the spec is not critical ...
1324 }
1325 }
1326
1327 if (IoUtils::isRelativePath(qmakespec)) {
1328 if (IoUtils::exists(currentDirectory() + QLatin1Char('/') + qmakespec
1329 + QLatin1String("/qmake.conf"))) {
1330 qmakespec = currentDirectory() + QLatin1Char('/') + qmakespec;
1331 } else if (!m_outputDir.isEmpty()
1332 && IoUtils::exists(m_outputDir + QLatin1Char('/') + qmakespec
1333 + QLatin1String("/qmake.conf"))) {
1334 qmakespec = m_outputDir + QLatin1Char('/') + qmakespec;
1335 } else {
1336 foreach (const QString &root, mkspec_roots) {
1337 QString mkspec = root + QLatin1Char('/') + qmakespec;
1338 if (IoUtils::exists(mkspec)) {
1339 qmakespec = mkspec;
1340 goto cool;
1341 }
1342 }
1343 m_handler->configError(fL1S("Could not find qmake configuration file"));
1344 // Unlike in qmake, a missing config is not critical ...
1345 qmakespec.clear();
1346 cool: ;
1347 }
1348 }
1349
1350 if (!qmakespec.isEmpty()) {
1351 m_option->qmakespec = QDir::cleanPath(qmakespec);
1352
1353 QString spec = m_option->qmakespec + QLatin1String("/qmake.conf");
1354 if (!evaluateFileDirect(spec, ProFileEvaluatorHandler::EvalConfigFile,
1355 ProFileEvaluator::LoadProOnly)) {
1356 m_handler->configError(
1357 fL1S("Could not read qmake configuration file %1").arg(spec));
1358 } else if (!m_option->cachefile.isEmpty()) {
1359 evaluateFileDirect(m_option->cachefile,
1360 ProFileEvaluatorHandler::EvalConfigFile,
1361 ProFileEvaluator::LoadProOnly);
1362 }
1363 m_option->qmakespec_name = IoUtils::fileName(m_option->qmakespec).toString();
1364 if (m_option->qmakespec_name == QLatin1String("default")) {
1365#ifdef Q_OS_UNIX
1366 char buffer[1024];
1367 int l = ::readlink(m_option->qmakespec.toLocal8Bit().constData(), buffer, 1024);
1368 if (l != -1)
1369 m_option->qmakespec_name =
1370 IoUtils::fileName(QString::fromLocal8Bit(buffer, l)).toString();
1371#else
1372 // We can't resolve symlinks as they do on Unix, so configure.exe puts
1373 // the source of the qmake.conf at the end of the default/qmake.conf in
1374 // the QMAKESPEC_ORG variable.
1375 const ProStringList &spec_org =
1376 m_option->base_valuemap.value(ProString("QMAKESPEC_ORIGINAL"));
1377 if (!spec_org.isEmpty())
1378 m_option->qmakespec_name =
1379 IoUtils::fileName(spec_org.first().toQString()).toString();
1380#endif
1381 }
1382 }
1383
1384 evaluateFeatureFile(QLatin1String("default_pre.prf"));
1385
1386 m_option->base_valuemap = m_valuemapStack.top();
1387 m_option->base_functions = m_functionDefs;
1388
1389#ifdef PROEVALUATOR_CUMULATIVE
1390 m_cumulative = cumulative;
1391#endif
1392
1393#ifdef PROEVALUATOR_THREAD_SAFE
1394 locker.relock();
1395 m_option->base_inProgress = false;
1396 m_option->cond.wakeAll();
1397#endif
1398 goto fresh;
1399 }
1400#ifdef PROEVALUATOR_THREAD_SAFE
1401 }
1402#endif
1403
1404 m_valuemapStack.top() = m_option->base_valuemap;
1405 m_functionDefs = m_option->base_functions;
1406
1407 fresh:
1408 ProStringList &tgt = m_valuemapStack.top()[ProString("TARGET")];
1409 if (tgt.isEmpty())
1410 tgt.append(ProString(QFileInfo(pro->fileName()).baseName(), NoHash));
1411
1412 visitCmdLine(m_option->precmds);
1413 }
1414
1415 visitProBlock(pro, pro->tokPtr());
1416
1417 if (flags & LoadPostFiles) {
1418 visitCmdLine(m_option->postcmds);
1419
1420 evaluateFeatureFile(QLatin1String("default_post.prf"));
1421
1422 QSet<QString> processed;
1423 forever {
1424 bool finished = true;
1425 ProStringList configs = valuesDirect(statics.strCONFIG);
1426 for (int i = configs.size() - 1; i >= 0; --i) {
1427 QString config = configs.at(i).toQString(m_tmp1).toLower();
1428 if (!processed.contains(config)) {
1429 config.detach();
1430 processed.insert(config);
1431 if (evaluateFeatureFile(config)) {
1432 finished = false;
1433 break;
1434 }
1435 }
1436 }
1437 if (finished)
1438 break;
1439 }
1440 }
1441 m_profileStack.pop();
1442 m_handler->doneWithEval(currentProFile());
1443
1444 return ReturnTrue;
1445}
1446
1447
1448QStringList ProFileEvaluator::Private::qmakeMkspecPaths() const
1449{
1450 QStringList ret;
1451 const QString concat = QLatin1String("/mkspecs");
1452
1453 QString qmakepath = m_option->getEnv(QLatin1String("QMAKEPATH"));
1454 if (!qmakepath.isEmpty())
1455 foreach (const QString &it, qmakepath.split(m_option->dirlist_sep))
1456 ret << QDir::cleanPath(it) + concat;
1457
1458 QString builtIn = propertyValue(QLatin1String("QT_INSTALL_DATA"), false) + concat;
1459 if (!ret.contains(builtIn))
1460 ret << builtIn;
1461
1462 return ret;
1463}
1464
1465QStringList ProFileEvaluator::Private::qmakeFeaturePaths() const
1466{
1467 QString mkspecs_concat = QLatin1String("/mkspecs");
1468 QString features_concat = QLatin1String("/features");
1469 QStringList concat;
1470
1471 validateModes();
1472 switch (m_option->target_mode) {
1473 case ProFileOption::TARG_MACX_MODE:
1474 concat << QLatin1String("/features/mac");
1475 concat << QLatin1String("/features/macx");
1476 concat << QLatin1String("/features/unix");
1477 break;
1478 default: // Can't happen, just make the compiler shut up
1479 case ProFileOption::TARG_UNIX_MODE:
1480 concat << QLatin1String("/features/unix");
1481 break;
1482 case ProFileOption::TARG_WIN_MODE:
1483 concat << QLatin1String("/features/win32");
1484 break;
1485 case ProFileOption::TARG_SYMBIAN_MODE:
1486 concat << QLatin1String("/features/symbian");
1487 break;
1488 }
1489 concat << features_concat;
1490
1491 QStringList feature_roots;
1492
1493 QString mkspec_path = m_option->getEnv(QLatin1String("QMAKEFEATURES"));
1494 if (!mkspec_path.isEmpty())
1495 foreach (const QString &f, mkspec_path.split(m_option->dirlist_sep))
1496 feature_roots += resolvePath(f);
1497
1498 feature_roots += propertyValue(QLatin1String("QMAKEFEATURES"), false).split(
1499 m_option->dirlist_sep, QString::SkipEmptyParts);
1500
1501 if (!m_option->cachefile.isEmpty()) {
1502 QString path = m_option->cachefile.left(m_option->cachefile.lastIndexOf((ushort)'/'));
1503 foreach (const QString &concat_it, concat)
1504 feature_roots << (path + concat_it);
1505 }
1506
1507 QString qmakepath = m_option->getEnv(QLatin1String("QMAKEPATH"));
1508 if (!qmakepath.isNull()) {
1509 const QStringList lst = qmakepath.split(m_option->dirlist_sep);
1510 foreach (const QString &item, lst) {
1511 QString citem = resolvePath(item);
1512 foreach (const QString &concat_it, concat)
1513 feature_roots << (citem + mkspecs_concat + concat_it);
1514 }
1515 }
1516
1517 if (!m_option->qmakespec.isEmpty()) {
1518 QString qmakespec = resolvePath(m_option->qmakespec);
1519 feature_roots << (qmakespec + features_concat);
1520
1521 QDir specdir(qmakespec);
1522 while (!specdir.isRoot()) {
1523 if (!specdir.cdUp() || specdir.isRoot())
1524 break;
1525 if (IoUtils::exists(specdir.path() + features_concat)) {
1526 foreach (const QString &concat_it, concat)
1527 feature_roots << (specdir.path() + concat_it);
1528 break;
1529 }
1530 }
1531 }
1532
1533 foreach (const QString &concat_it, concat)
1534 feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX"), false) +
1535 mkspecs_concat + concat_it);
1536 foreach (const QString &concat_it, concat)
1537 feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA"), false) +
1538 mkspecs_concat + concat_it);
1539
1540 for (int i = 0; i < feature_roots.count(); ++i)
1541 if (!feature_roots.at(i).endsWith((ushort)'/'))
1542 feature_roots[i].append((ushort)'/');
1543
1544 feature_roots.removeDuplicates();
1545
1546 return feature_roots;
1547}
1548
1549QString ProFileEvaluator::Private::propertyValue(const QString &name, bool complain) const
1550{
1551 if (m_option->properties.contains(name))
1552 return m_option->properties.value(name);
1553 if (name == QLatin1String("QMAKE_MKSPECS"))
1554 return qmakeMkspecPaths().join(m_option->dirlist_sep);
1555 if (name == QLatin1String("QMAKE_VERSION"))
1556 return QLatin1String("1.0"); //### FIXME
1557 if (complain)
1558 evalError(fL1S("Querying unknown property %1").arg(name));
1559 return QString();
1560}
1561
1562ProFile *ProFileEvaluator::Private::currentProFile() const
1563{
1564 if (m_profileStack.count() > 0)
1565 return m_profileStack.top();
1566 return 0;
1567}
1568
1569QString ProFileEvaluator::Private::currentFileName() const
1570{
1571 ProFile *pro = currentProFile();
1572 if (pro)
1573 return pro->fileName();
1574 return QString();
1575}
1576
1577QString ProFileEvaluator::Private::currentDirectory() const
1578{
1579 ProFile *cur = m_profileStack.top();
1580 return cur->directoryName();
1581}
1582
1583QString ProFileEvaluator::Private::sysrootify(const QString &path, const QString &baseDir) const
1584{
1585 const bool isHostSystemPath = m_option->sysroot.isEmpty() || path.startsWith(m_option->sysroot)
1586 || path.startsWith(baseDir) || path.startsWith(m_outputDir);
1587 return isHostSystemPath ? path : m_option->sysroot + path;
1588}
1589
1590#ifndef QT_BOOTSTRAPPED
1591void ProFileEvaluator::Private::runProcess(QProcess *proc, const QString &command,
1592 QProcess::ProcessChannel chan) const
1593{
1594 proc->setWorkingDirectory(currentDirectory());
1595 if (!m_option->environment.isEmpty())
1596 proc->setProcessEnvironment(m_option->environment);
1597# ifdef Q_OS_WIN
1598 proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
1599 proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
1600# else
1601 proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
1602# endif
1603 proc->waitForFinished(-1);
1604 proc->setReadChannel(chan);
1605 QByteArray errout = proc->readAll();
1606 if (errout.endsWith('\n'))
1607 errout.chop(1);
1608 m_handler->evalError(QString(), 0, QString::fromLocal8Bit(errout));
1609}
1610#endif
1611
1612// The (QChar*)current->constData() constructs below avoid pointless detach() calls
1613// FIXME: This is inefficient. Should not make new string if it is a straight subsegment
1614static ALWAYS_INLINE void appendChar(ushort unicode,
1615 QString *current, QChar **ptr, ProString *pending)
1616{
1617 if (!pending->isEmpty()) {
1618 int len = pending->size();
1619 current->resize(current->size() + len);
1620 ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
1621 pending->clear();
1622 *ptr = (QChar*)current->constData() + len;
1623 }
1624 *(*ptr)++ = QChar(unicode);
1625}
1626
1627static void appendString(const ProString &string,
1628 QString *current, QChar **ptr, ProString *pending)
1629{
1630 if (string.isEmpty())
1631 return;
1632 QChar *uc = (QChar*)current->constData();
1633 int len;
1634 if (*ptr != uc) {
1635 len = *ptr - uc;
1636 current->resize(current->size() + string.size());
1637 } else if (!pending->isEmpty()) {
1638 len = pending->size();
1639 current->resize(current->size() + len + string.size());
1640 ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
1641 pending->clear();
1642 } else {
1643 *pending = string;
1644 return;
1645 }
1646 *ptr = (QChar*)current->constData() + len;
1647 ::memcpy(*ptr, string.constData(), string.size() * 2);
1648 *ptr += string.size();
1649}
1650
1651static void flushCurrent(ProStringList *ret,
1652 QString *current, QChar **ptr, ProString *pending, bool joined)
1653{
1654 QChar *uc = (QChar*)current->constData();
1655 int len = *ptr - uc;
1656 if (len) {
1657 ret->append(ProString(QString(uc, len), NoHash));
1658 *ptr = uc;
1659 } else if (!pending->isEmpty()) {
1660 ret->append(*pending);
1661 pending->clear();
1662 } else if (joined) {
1663 ret->append(ProString());
1664 }
1665}
1666
1667static inline void flushFinal(ProStringList *ret,
1668 const QString &current, const QChar *ptr, const ProString &pending,
1669 const ProString &str, bool replaced, bool joined)
1670{
1671 int len = ptr - current.data();
1672 if (len) {
1673 if (!replaced && len == str.size())
1674 ret->append(str);
1675 else
1676 ret->append(ProString(QString(current.data(), len), NoHash));
1677 } else if (!pending.isEmpty()) {
1678 ret->append(pending);
1679 } else if (joined) {
1680 ret->append(ProString());
1681 }
1682}
1683
1684ProStringList ProFileEvaluator::Private::expandVariableReferences(
1685 const ProString &str, int *pos, bool joined)
1686{
1687 ProStringList ret;
1688// if (ok)
1689// *ok = true;
1690 if (str.isEmpty() && !pos)
1691 return ret;
1692
1693 const ushort LSQUARE = '[';
1694 const ushort RSQUARE = ']';
1695 const ushort LCURLY = '{';
1696 const ushort RCURLY = '}';
1697 const ushort LPAREN = '(';
1698 const ushort RPAREN = ')';
1699 const ushort DOLLAR = '$';
1700 const ushort BACKSLASH = '\\';
1701 const ushort UNDERSCORE = '_';
1702 const ushort DOT = '.';
1703 const ushort SPACE = ' ';
1704 const ushort TAB = '\t';
1705 const ushort COMMA = ',';
1706 const ushort SINGLEQUOTE = '\'';
1707 const ushort DOUBLEQUOTE = '"';
1708
1709 ushort unicode, quote = 0, parens = 0;
1710 const ushort *str_data = (const ushort *)str.constData();
1711 const int str_len = str.size();
1712
1713 ProString var, args;
1714
1715 bool replaced = false;
1716 bool putSpace = false;
1717 QString current; // Buffer for successively assembled string segments
1718 current.resize(str.size());
1719 QChar *ptr = current.data();
1720 ProString pending; // Buffer for string segments from variables
1721 // Only one of the above buffers can be filled at a given time.
1722 for (int i = pos ? *pos : 0; i < str_len; ++i) {
1723 unicode = str_data[i];
1724 if (unicode == DOLLAR) {
1725 if (str_len > i+2 && str_data[i+1] == DOLLAR) {
1726 ++i;
1727 ushort term = 0;
1728 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
1729 unicode = str_data[++i];
1730 if (unicode == LSQUARE) {
1731 unicode = str_data[++i];
1732 term = RSQUARE;
1733 var_type = PROPERTY;
1734 } else if (unicode == LCURLY) {
1735 unicode = str_data[++i];
1736 var_type = VAR;
1737 term = RCURLY;
1738 } else if (unicode == LPAREN) {
1739 unicode = str_data[++i];
1740 var_type = ENVIRON;
1741 term = RPAREN;
1742 }
1743 int name_start = i;
1744 forever {
1745 if (!(unicode & (0xFF<<8)) &&
1746 unicode != DOT && unicode != UNDERSCORE &&
1747 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
1748 (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
1749 (unicode < '0' || unicode > '9'))
1750 break;
1751 if (++i == str_len)
1752 break;
1753 unicode = str_data[i];
1754 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
1755 }
1756 var = str.mid(name_start, i - name_start);
1757 if (var_type == VAR && unicode == LPAREN) {
1758 var_type = FUNCTION;
1759 name_start = i + 1;
1760 int depth = 0;
1761 forever {
1762 if (++i == str_len)
1763 break;
1764 unicode = str_data[i];
1765 if (unicode == LPAREN) {
1766 depth++;
1767 } else if (unicode == RPAREN) {
1768 if (!depth)
1769 break;
1770 --depth;
1771 }
1772 }
1773 args = str.mid(name_start, i - name_start);
1774 if (++i < str_len)
1775 unicode = str_data[i];
1776 else
1777 unicode = 0;
1778 // at this point i is pointing to the 'next' character (which is in unicode)
1779 // this might actually be a term character since you can do $${func()}
1780 }
1781 if (term) {
1782 if (unicode != term) {
1783 evalError(fL1S("Missing %1 terminator [found %2]")
1784 .arg(QChar(term))
1785 .arg(unicode ? QString(unicode) : fL1S("end-of-line")));
1786// if (ok)
1787// *ok = false;
1788 if (pos)
1789 *pos = str_len;
1790 return ProStringList();
1791 }
1792 } else {
1793 // move the 'cursor' back to the last char of the thing we were looking at
1794 --i;
1795 }
1796
1797 ProStringList replacement;
1798 if (var_type == ENVIRON) {
1799 replacement = split_value_list(m_option->getEnv(var.toQString(m_tmp1)));
1800 } else if (var_type == PROPERTY) {
1801 replacement << ProString(propertyValue(var.toQString(m_tmp1), true), NoHash);
1802 } else if (var_type == FUNCTION) {
1803 replacement += evaluateExpandFunction(var, args);
1804 } else if (var_type == VAR) {
1805 replacement = values(map(var));
1806 }
1807 if (!replacement.isEmpty()) {
1808 if (quote || joined) {
1809 if (putSpace) {
1810 putSpace = false;
1811 if (!replacement.at(0).isEmpty()) // Bizarre, indeed
1812 appendChar(' ', &current, &ptr, &pending);
1813 }
1814 appendString(ProString(replacement.join(statics.field_sep), NoHash),
1815 &current, &ptr, &pending);
1816 } else {
1817 appendString(replacement.at(0), &current, &ptr, &pending);
1818 if (replacement.size() > 1) {
1819 flushCurrent(&ret, &current, &ptr, &pending, false);
1820 int j = 1;
1821 if (replacement.size() > 2) {
1822 // FIXME: ret.reserve(ret.size() + replacement.size() - 2);
1823 for (; j < replacement.size() - 1; ++j)
1824 ret << replacement.at(j);
1825 }
1826 pending = replacement.at(j);
1827 }
1828 }
1829 replaced = true;
1830 }
1831 continue;
1832 }
1833 } else if (unicode == BACKSLASH) {
1834 static const char symbols[] = "[]{}()$\\'\"";
1835 ushort unicode2 = str_data[i+1];
1836 if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
1837 unicode = unicode2;
1838 ++i;
1839 }
1840 } else if (quote) {
1841 if (unicode == quote) {
1842 quote = 0;
1843 continue;
1844 }
1845 } else {
1846 if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
1847 quote = unicode;
1848 continue;
1849 } else if (unicode == SPACE || unicode == TAB) {
1850 if (!joined)
1851 flushCurrent(&ret, &current, &ptr, &pending, false);
1852 else if ((ptr - (QChar*)current.constData()) || !pending.isEmpty())
1853 putSpace = true;
1854 continue;
1855 } else if (pos) {
1856 if (unicode == LPAREN) {
1857 ++parens;
1858 } else if (unicode == RPAREN) {
1859 --parens;
1860 } else if (!parens && unicode == COMMA) {
1861 if (!joined) {
1862 *pos = i + 1;
1863 flushFinal(&ret, current, ptr, pending, str, replaced, false);
1864 return ret;
1865 }
1866 flushCurrent(&ret, &current, &ptr, &pending, true);
1867 putSpace = false;
1868 continue;
1869 }
1870 }
1871 }
1872 if (putSpace) {
1873 putSpace = false;
1874 appendChar(' ', &current, &ptr, &pending);
1875 }
1876 appendChar(unicode, &current, &ptr, &pending);
1877 }
1878 if (pos)
1879 *pos = str_len;
1880 flushFinal(&ret, current, ptr, pending, str, replaced, joined);
1881 return ret;
1882}
1883
1884bool ProFileEvaluator::Private::modesForGenerator(const QString &gen,
1885 ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const
1886{
1887 if (gen == fL1S("UNIX")) {
1888#ifdef Q_OS_MAC
1889 *host_mode = ProFileOption::HOST_MACX_MODE;
1890 *target_mode = ProFileOption::TARG_MACX_MODE;
1891#else
1892 *host_mode = ProFileOption::HOST_UNIX_MODE;
1893 *target_mode = ProFileOption::TARG_UNIX_MODE;
1894#endif
1895 } else if (gen == fL1S("MSVC.NET") || gen == fL1S("BMAKE") || gen == fL1S("MSBUILD")) {
1896 *host_mode = ProFileOption::HOST_WIN_MODE;
1897 *target_mode = ProFileOption::TARG_WIN_MODE;
1898 } else if (gen == fL1S("MINGW")) {
1899#if defined(Q_OS_MAC)
1900 *host_mode = ProFileOption::HOST_MACX_MODE;
1901#elif defined(Q_OS_UNIX)
1902 *host_mode = ProFileOption::HOST_UNIX_MODE;
1903#else
1904 *host_mode = ProFileOption::HOST_WIN_MODE;
1905#endif
1906 *target_mode = ProFileOption::TARG_WIN_MODE;
1907 } else if (gen == fL1S("PROJECTBUILDER") || gen == fL1S("XCODE")) {
1908 *host_mode = ProFileOption::HOST_MACX_MODE;
1909 *target_mode = ProFileOption::TARG_MACX_MODE;
1910 } else if (gen == fL1S("SYMBIAN_ABLD") || gen == fL1S("SYMBIAN_SBSV2")
1911 || gen == fL1S("SYMBIAN_UNIX") || gen == fL1S("SYMBIAN_MINGW")) {
1912#if defined(Q_OS_MAC)
1913 *host_mode = ProFileOption::HOST_MACX_MODE;
1914#elif defined(Q_OS_UNIX)
1915 *host_mode = ProFileOption::HOST_UNIX_MODE;
1916#else
1917 *host_mode = ProFileOption::HOST_WIN_MODE;
1918#endif
1919 *target_mode = ProFileOption::TARG_SYMBIAN_MODE;
1920 } else {
1921 evalError(fL1S("Unknown generator specified: %1").arg(gen));
1922 return false;
1923 }
1924 return true;
1925}
1926
1927void ProFileEvaluator::Private::validateModes() const
1928{
1929 if (m_option->host_mode == ProFileOption::HOST_UNKNOWN_MODE
1930 || m_option->target_mode == ProFileOption::TARG_UNKNOWN_MODE) {
1931 const QHash<ProString, ProStringList> &vals =
1932 m_option->base_valuemap.isEmpty() ? m_valuemapStack[0] : m_option->base_valuemap;
1933 ProFileOption::HOST_MODE host_mode;
1934 ProFileOption::TARG_MODE target_mode;
1935 const ProStringList &gen = vals.value(ProString("MAKEFILE_GENERATOR"));
1936 if (gen.isEmpty()) {
1937 evalError(fL1S("Using OS scope before setting MAKEFILE_GENERATOR"));
1938 } else if (modesForGenerator(gen.at(0).toQString(), &host_mode, &target_mode)) {
1939 if (m_option->host_mode == ProFileOption::HOST_UNKNOWN_MODE) {
1940 m_option->host_mode = host_mode;
1941 m_option->applyHostMode();
1942 }
1943
1944 if (m_option->target_mode == ProFileOption::TARG_UNKNOWN_MODE) {
1945 const ProStringList &tgt = vals.value(ProString("TARGET_PLATFORM"));
1946 if (!tgt.isEmpty()) {
1947 const QString &os = tgt.at(0).toQString();
1948 if (os == statics.strunix)
1949 m_option->target_mode = ProFileOption::TARG_UNIX_MODE;
1950 else if (os == statics.strmacx)
1951 m_option->target_mode = ProFileOption::TARG_MACX_MODE;
1952 else if (os == statics.strsymbian)
1953 m_option->target_mode = ProFileOption::TARG_SYMBIAN_MODE;
1954 else if (os == statics.strwin32)
1955 m_option->target_mode = ProFileOption::TARG_WIN_MODE;
1956 else
1957 evalError(fL1S("Unknown target platform specified: %1").arg(os));
1958 } else {
1959 m_option->target_mode = target_mode;
1960 }
1961 }
1962 }
1963 }
1964}
1965
1966bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
1967{
1968 // magic types for easy flipping
1969 if (config == statics.strtrue)
1970 return true;
1971 if (config == statics.strfalse)
1972 return false;
1973
1974 if (config == statics.strunix) {
1975 validateModes();
1976 return m_option->target_mode == ProFileOption::TARG_UNIX_MODE
1977 || m_option->target_mode == ProFileOption::TARG_MACX_MODE
1978 || m_option->target_mode == ProFileOption::TARG_SYMBIAN_MODE;
1979 } else if (config == statics.strmacx || config == statics.strmac) {
1980 validateModes();
1981 return m_option->target_mode == ProFileOption::TARG_MACX_MODE;
1982 } else if (config == statics.strsymbian) {
1983 validateModes();
1984 return m_option->target_mode == ProFileOption::TARG_SYMBIAN_MODE;
1985 } else if (config == statics.strwin32) {
1986 validateModes();
1987 return m_option->target_mode == ProFileOption::TARG_WIN_MODE;
1988 }
1989
1990 if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
1991 QString cfg = config;
1992 cfg.detach(); // Keep m_tmp out of QRegExp's cache
1993 QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
1994
1995 // mkspecs
1996 if (re.exactMatch(m_option->qmakespec_name))
1997 return true;
1998
1999 // CONFIG variable
2000 int t = 0;
2001 foreach (const ProString &configValue, valuesDirect(statics.strCONFIG)) {
2002 if (re.exactMatch(configValue.toQString(m_tmp[t])))
2003 return true;
2004 t ^= 1;
2005 }
2006 } else {
2007 // mkspecs
2008 if (m_option->qmakespec_name == config)
2009 return true;
2010
2011 // CONFIG variable
2012 if (valuesDirect(statics.strCONFIG).contains(ProString(config, NoHash)))
2013 return true;
2014 }
2015
2016 return false;
2017}
2018
2019ProStringList ProFileEvaluator::Private::expandVariableReferences(
2020 const ushort *&tokPtr, int sizeHint, bool joined)
2021{
2022 ProStringList ret;
2023 ret.reserve(sizeHint);
2024 forever {
2025 evaluateExpression(tokPtr, &ret, joined);
2026 switch (*tokPtr) {
2027 case TokValueTerminator:
2028 case TokFuncTerminator:
2029 tokPtr++;
2030 return ret;
2031 case TokArgSeparator:
2032 if (joined) {
2033 tokPtr++;
2034 continue;
2035 }
2036 // fallthrough
2037 default:
2038 Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
2039 break;
2040 }
2041 }
2042}
2043
2044QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ushort *&tokPtr)
2045{
2046 QList<ProStringList> args_list;
2047 if (*tokPtr != TokFuncTerminator) {
2048 for (;; tokPtr++) {
2049 ProStringList arg;
2050 evaluateExpression(tokPtr, &arg, false);
2051 args_list << arg;
2052 if (*tokPtr == TokFuncTerminator)
2053 break;
2054 Q_ASSERT(*tokPtr == TokArgSeparator);
2055 }
2056 }
2057 tokPtr++;
2058 return args_list;
2059}
2060
2061QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ProString &arguments)
2062{
2063 QList<ProStringList> args_list;
2064 for (int pos = 0; pos < arguments.size(); )
2065 args_list << expandVariableReferences(arguments, &pos);
2066 return args_list;
2067}
2068
2069ProStringList ProFileEvaluator::Private::evaluateFunction(
2070 const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
2071{
2072 bool oki;
2073 ProStringList ret;
2074
2075 if (m_valuemapStack.count() >= 100) {
2076 evalError(fL1S("ran into infinite recursion (depth > 100)."));
2077 oki = false;
2078 } else {
2079 m_valuemapStack.push(QHash<ProString, ProStringList>());
2080 m_locationStack.push(m_current);
2081 int loopLevel = m_loopLevel;
2082 m_loopLevel = 0;
2083
2084 ProStringList args;
2085 for (int i = 0; i < argumentsList.count(); ++i) {
2086 args += argumentsList[i];
2087 m_valuemapStack.top()[ProString(QString::number(i+1))] = argumentsList[i];
2088 }
2089 m_valuemapStack.top()[statics.strARGS] = args;
2090 oki = (visitProBlock(func.pro(), func.tokPtr()) != ReturnFalse); // True || Return
2091 ret = m_returnValue;
2092 m_returnValue.clear();
2093
2094 m_loopLevel = loopLevel;
2095 m_current = m_locationStack.pop();
2096 m_valuemapStack.pop();
2097 }
2098 if (ok)
2099 *ok = oki;
2100 if (oki)
2101 return ret;
2102 return ProStringList();
2103}
2104
2105ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateBoolFunction(
2106 const FunctionDef &func, const QList<ProStringList> &argumentsList,
2107 const ProString &function)
2108{
2109 bool ok;
2110 ProStringList ret = evaluateFunction(func, argumentsList, &ok);
2111 if (ok) {
2112 if (ret.isEmpty())
2113 return ReturnTrue;
2114 if (ret.at(0) != statics.strfalse) {
2115 if (ret.at(0) == statics.strtrue)
2116 return ReturnTrue;
2117 int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
2118 if (ok) {
2119 if (val)
2120 return ReturnTrue;
2121 } else {
2122 evalError(fL1S("Unexpected return value from test '%1': %2")
2123 .arg(function.toQString(m_tmp1))
2124 .arg(ret.join(QLatin1String(" :: "))));
2125 }
2126 }
2127 }
2128 return ReturnFalse;
2129}
2130
2131ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
2132 const ProString &func, const ushort *&tokPtr)
2133{
2134 QHash<ProString, FunctionDef>::ConstIterator it =
2135 m_functionDefs.replaceFunctions.constFind(func);
2136 if (it != m_functionDefs.replaceFunctions.constEnd())
2137 return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0);
2138
2139 //why don't the builtin functions just use args_list? --Sam
2140 return evaluateExpandFunction(func, expandVariableReferences(tokPtr, 5, true));
2141}
2142
2143ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
2144 const ProString &func, const ProString &arguments)
2145{
2146 QHash<ProString, FunctionDef>::ConstIterator it =
2147 m_functionDefs.replaceFunctions.constFind(func);
2148 if (it != m_functionDefs.replaceFunctions.constEnd())
2149 return evaluateFunction(*it, prepareFunctionArgs(arguments), 0);
2150
2151 //why don't the builtin functions just use args_list? --Sam
2152 int pos = 0;
2153 return evaluateExpandFunction(func, expandVariableReferences(arguments, &pos, true));
2154}
2155
2156ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
2157 const ProString &func, const ProStringList &args)
2158{
2159 ExpandFunc func_t = ExpandFunc(statics.expands.value(func));
2160 if (func_t == 0) {
2161 const QString &fn = func.toQString(m_tmp1);
2162 const QString &lfn = fn.toLower();
2163 if (!fn.isSharedWith(lfn))
2164 func_t = ExpandFunc(statics.expands.value(ProString(lfn)));
2165 }
2166
2167 ProStringList ret;
2168
2169 switch (func_t) {
2170 case E_BASENAME:
2171 case E_DIRNAME:
2172 case E_SECTION: {
2173 bool regexp = false;
2174 QString sep;
2175 ProString var;
2176 int beg = 0;
2177 int end = -1;
2178 if (func_t == E_SECTION) {
2179 if (args.count() != 3 && args.count() != 4) {
2180 evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
2181 " three or four arguments.").arg(func.toQString(m_tmp1)));
2182 } else {
2183 var = args[0];
2184 sep = args.at(1).toQString();
2185 beg = args.at(2).toQString(m_tmp2).toInt();
2186 if (args.count() == 4)
2187 end = args.at(3).toQString(m_tmp2).toInt();
2188 }
2189 } else {
2190 if (args.count() != 1) {
2191 evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
2192 } else {
2193 var = args[0];
2194 regexp = true;
2195 sep = QLatin1String("[\\\\/]");
2196 if (func_t == E_DIRNAME)
2197 end = -2;
2198 else
2199 beg = -1;
2200 }
2201 }
2202 if (!var.isEmpty()) {
2203 if (regexp) {
2204 QRegExp sepRx(sep);
2205 foreach (const ProString &str, values(map(var))) {
2206 const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
2207 ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash).setSource(str));
2208 }
2209 } else {
2210 foreach (const ProString &str, values(map(var))) {
2211 const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
2212 ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash).setSource(str));
2213 }
2214 }
2215 }
2216 break;
2217 }
2218 case E_SPRINTF:
2219 if(args.count() < 1) {
2220 evalError(fL1S("sprintf(format, ...) requires at least one argument"));
2221 } else {
2222 QString tmp = args.at(0).toQString(m_tmp1);
2223 for (int i = 1; i < args.count(); ++i)
2224 tmp = tmp.arg(args.at(i).toQString(m_tmp2));
2225 // Note: this depends on split_value_list() making a deep copy
2226 ret = split_value_list(tmp);
2227 }
2228 break;
2229 case E_JOIN: {
2230 if (args.count() < 1 || args.count() > 4) {
2231 evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
2232 } else {
2233 QString glue;
2234 ProString before, after;
2235 if (args.count() >= 2)
2236 glue = args.at(1).toQString(m_tmp1);
2237 if (args.count() >= 3)
2238 before = args[2];
2239 if (args.count() == 4)
2240 after = args[3];
2241 const ProStringList &var = values(map(args.at(0)));
2242 if (!var.isEmpty()) {
2243 const ProFile *src = currentProFile();
2244 foreach (const ProString &v, var)
2245 if (const ProFile *s = v.sourceFile()) {
2246 src = s;
2247 break;
2248 }
2249 ret.append(ProString(before + var.join(glue) + after, NoHash).setSource(src));
2250 }
2251 }
2252 break;
2253 }
2254 case E_SPLIT:
2255 if (args.count() != 2) {
2256 evalError(fL1S("split(var, sep) requires one or two arguments"));
2257 } else {
2258 const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
2259 foreach (const ProString &var, values(map(args.at(0))))
2260 foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
2261 ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt, NoHash).setSource(var));
2262 }
2263 break;
2264 case E_MEMBER:
2265 if (args.count() < 1 || args.count() > 3) {
2266 evalError(fL1S("member(var, start, end) requires one to three arguments."));
2267 } else {
2268 bool ok = true;
2269 const ProStringList &var = values(map(args.at(0)));
2270 int start = 0, end = 0;
2271 if (args.count() >= 2) {
2272 const QString &start_str = args.at(1).toQString(m_tmp1);
2273 start = start_str.toInt(&ok);
2274 if (!ok) {
2275 if (args.count() == 2) {
2276 int dotdot = start_str.indexOf(statics.strDotDot);
2277 if (dotdot != -1) {
2278 start = start_str.left(dotdot).toInt(&ok);
2279 if (ok)
2280 end = start_str.mid(dotdot+2).toInt(&ok);
2281 }
2282 }
2283 if (!ok)
2284 evalError(fL1S("member() argument 2 (start) '%2' invalid.")
2285 .arg(start_str));
2286 } else {
2287 end = start;
2288 if (args.count() == 3)
2289 end = args.at(2).toQString(m_tmp1).toInt(&ok);
2290 if (!ok)
2291 evalError(fL1S("member() argument 3 (end) '%2' invalid.\n")
2292 .arg(args.at(2).toQString(m_tmp1)));
2293 }
2294 }
2295 if (ok) {
2296 if (start < 0)
2297 start += var.count();
2298 if (end < 0)
2299 end += var.count();
2300 if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
2301 //nothing
2302 } else if (start < end) {
2303 for (int i = start; i <= end && var.count() >= i; i++)
2304 ret.append(var[i]);
2305 } else {
2306 for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
2307 ret += var[i];
2308 }
2309 }
2310 }
2311 break;
2312 case E_FIRST:
2313 case E_LAST:
2314 if (args.count() != 1) {
2315 evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
2316 } else {
2317 const ProStringList &var = values(map(args.at(0)));
2318 if (!var.isEmpty()) {
2319 if (func_t == E_FIRST)
2320 ret.append(var[0]);
2321 else
2322 ret.append(var.last());
2323 }
2324 }
2325 break;
2326 case E_SIZE:
2327 if (args.count() != 1)
2328 evalError(fL1S("size(var) requires one argument."));
2329 else
2330 ret.append(ProString(QString::number(values(map(args.at(0))).size()), NoHash));
2331 break;
2332 case E_CAT:
2333 if (args.count() < 1 || args.count() > 2) {
2334 evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
2335 } else {
2336 const QString &file = args.at(0).toQString(m_tmp1);
2337
2338 bool singleLine = true;
2339 if (args.count() > 1)
2340 singleLine = isTrue(args.at(1), m_tmp2);
2341
2342 QFile qfile(resolvePath(expandEnvVars(file)));
2343 if (qfile.open(QIODevice::ReadOnly)) {
2344 QTextStream stream(&qfile);
2345 while (!stream.atEnd()) {
2346 ret += split_value_list(stream.readLine().trimmed());
2347 if (!singleLine)
2348 ret += ProString("\n", NoHash);
2349 }
2350 qfile.close();
2351 }
2352 }
2353 break;
2354 case E_FROMFILE:
2355 if (args.count() != 2) {
2356 evalError(fL1S("fromfile(file, variable) requires two arguments."));
2357 } else {
2358 QHash<ProString, ProStringList> vars;
2359 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2360 fn.detach();
2361 if (evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2362 &vars, &m_functionDefs, EvalWithDefaults))
2363 ret = vars.value(map(args.at(1)));
2364 }
2365 break;
2366 case E_EVAL:
2367 if (args.count() != 1) {
2368 evalError(fL1S("eval(variable) requires one argument"));
2369 } else {
2370 ret += values(map(args.at(0)));
2371 }
2372 break;
2373 case E_LIST: {
2374 QString tmp;
2375 tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
2376 ret = ProStringList(ProString(tmp, NoHash));
2377 ProStringList lst;
2378 foreach (const ProString &arg, args)
2379 lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy
2380 m_valuemapStack.top()[ret.at(0)] = lst;
2381 break; }
2382 case E_FIND:
2383 if (args.count() != 2) {
2384 evalError(fL1S("find(var, str) requires two arguments."));
2385 } else {
2386 QRegExp regx(args.at(1).toQString());
2387 int t = 0;
2388 foreach (const ProString &val, values(map(args.at(0)))) {
2389 if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
2390 ret += val;
2391 t ^= 1;
2392 }
2393 }
2394 break;
2395 case E_SYSTEM:
2396 if (!m_skipLevel) {
2397 if (args.count() < 1 || args.count() > 2) {
2398 evalError(fL1S("system(execute) requires one or two arguments."));
2399 } else {
2400 bool singleLine = true;
2401 if (args.count() > 1)
2402 singleLine = isTrue(args.at(1), m_tmp2);
2403 QByteArray output;
2404#ifndef QT_BOOTSTRAPPED
2405 QProcess proc;
2406 runProcess(&proc, args.at(0).toQString(m_tmp2), QProcess::StandardError);
2407 output = proc.readAllStandardOutput();
2408 output.replace('\t', ' ');
2409 if (singleLine)
2410 output.replace('\n', ' ');
2411#else
2412 char buff[256];
2413 FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
2414 + IoUtils::shellQuote(currentDirectory())
2415 + QLatin1String(" && ") + args[0]).toLocal8Bit(), "r");
2416 while (proc && !feof(proc)) {
2417 int read_in = int(fread(buff, 1, 255, proc));
2418 if (!read_in)
2419 break;
2420 for (int i = 0; i < read_in; i++) {
2421 if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
2422 buff[i] = ' ';
2423 }
2424 output.append(buff, read_in);
2425 }
2426 if (proc)
2427 QT_PCLOSE(proc);
2428#endif
2429 ret += split_value_list(QString::fromLocal8Bit(output));
2430 }
2431 }
2432 break;
2433 case E_UNIQUE:
2434 if(args.count() != 1) {
2435 evalError(fL1S("unique(var) requires one argument."));
2436 } else {
2437 ret = values(map(args.at(0)));
2438 ret.removeDuplicates();
2439 }
2440 break;
2441 case E_QUOTE:
2442 ret += args;
2443 break;
2444 case E_ESCAPE_EXPAND:
2445 for (int i = 0; i < args.size(); ++i) {
2446 QString str = args.at(i).toQString();
2447 QChar *i_data = str.data();
2448 int i_len = str.length();
2449 for (int x = 0; x < i_len; ++x) {
2450 if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
2451 if (*(i_data+x+1) == QLatin1Char('\\')) {
2452 ++x;
2453 } else {
2454 struct {
2455 char in, out;
2456 } mapped_quotes[] = {
2457 { 'n', '\n' },
2458 { 't', '\t' },
2459 { 'r', '\r' },
2460 { 0, 0 }
2461 };
2462 for (int i = 0; mapped_quotes[i].in; ++i) {
2463 if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
2464 *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
2465 if (x < i_len-2)
2466 memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
2467 --i_len;
2468 break;
2469 }
2470 }
2471 }
2472 }
2473 }
2474 ret.append(ProString(QString(i_data, i_len), NoHash).setSource(args.at(i)));
2475 }
2476 break;
2477 case E_RE_ESCAPE:
2478 for (int i = 0; i < args.size(); ++i) {
2479 const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
2480 ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash).setSource(args.at(i)));
2481 }
2482 break;
2483 case E_UPPER:
2484 case E_LOWER:
2485 for (int i = 0; i < args.count(); ++i) {
2486 QString rstr = args.at(i).toQString(m_tmp1);
2487 rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
2488 ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash).setSource(args.at(i)));
2489 }
2490 break;
2491 case E_FILES:
2492 if (args.count() != 1 && args.count() != 2) {
2493 evalError(fL1S("files(pattern, recursive=false) requires one or two arguments"));
2494 } else {
2495 bool recursive = false;
2496 if (args.count() == 2)
2497 recursive = isTrue(args.at(1), m_tmp2);
2498 QStringList dirs;
2499 QString r = fixPathToLocalOS(args.at(0).toQString(m_tmp1));
2500 QString pfx;
2501 if (IoUtils::isRelativePath(r)) {
2502 pfx = currentDirectory();
2503 if (!pfx.endsWith(QLatin1Char('/')))
2504 pfx += QLatin1Char('/');
2505 }
2506 int slash = r.lastIndexOf(QDir::separator());
2507 if (slash != -1) {
2508 dirs.append(r.left(slash+1));
2509 r = r.mid(slash+1);
2510 } else {
2511 dirs.append(QString());
2512 }
2513
2514 r.detach(); // Keep m_tmp out of QRegExp's cache
2515 const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
2516 for (int d = 0; d < dirs.count(); d++) {
2517 QString dir = dirs[d];
2518 QDir qdir(pfx + dir);
2519 for (int i = 0; i < (int)qdir.count(); ++i) {
2520 if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
2521 continue;
2522 QString fname = dir + qdir[i];
2523 if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
2524 if (recursive)
2525 dirs.append(fname + QDir::separator());
2526 }
2527 if (regex.exactMatch(qdir[i]))
2528 ret += ProString(fname, NoHash).setSource(currentProFile());
2529 }
2530 }
2531 }
2532 break;
2533 case E_REPLACE:
2534 if(args.count() != 3 ) {
2535 evalError(fL1S("replace(var, before, after) requires three arguments"));
2536 } else {
2537 const QRegExp before(args.at(1).toQString());
2538 const QString &after(args.at(2).toQString(m_tmp2));
2539 foreach (const ProString &val, values(map(args.at(0)))) {
2540 QString rstr = val.toQString(m_tmp1);
2541 QString copy = rstr; // Force a detach on modify
2542 rstr.replace(before, after);
2543 ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr, NoHash).setSource(val));
2544 }
2545 }
2546 break;
2547 case E_INVALID:
2548 evalError(fL1S("'%1' is not a recognized replace function")
2549 .arg(func.toQString(m_tmp1)));
2550 break;
2551 default:
2552 evalError(fL1S("Function '%1' is not implemented").arg(func.toQString(m_tmp1)));
2553 break;
2554 }
2555
2556 return ret;
2557}
2558
2559ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2560 const ProString &function, const ProString &arguments)
2561{
2562 QHash<ProString, FunctionDef>::ConstIterator it =
2563 m_functionDefs.testFunctions.constFind(function);
2564 if (it != m_functionDefs.testFunctions.constEnd())
2565 return evaluateBoolFunction(*it, prepareFunctionArgs(arguments), function);
2566
2567 //why don't the builtin functions just use args_list? --Sam
2568 int pos = 0;
2569 return evaluateConditionalFunction(function, expandVariableReferences(arguments, &pos, true));
2570}
2571
2572ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2573 const ProString &function, const ushort *&tokPtr)
2574{
2575 QHash<ProString, FunctionDef>::ConstIterator it =
2576 m_functionDefs.testFunctions.constFind(function);
2577 if (it != m_functionDefs.testFunctions.constEnd())
2578 return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function);
2579
2580 //why don't the builtin functions just use args_list? --Sam
2581 return evaluateConditionalFunction(function, expandVariableReferences(tokPtr, 5, true));
2582}
2583
2584ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2585 const ProString &function, const ProStringList &args)
2586{
2587 TestFunc func_t = (TestFunc)statics.functions.value(function);
2588
2589 switch (func_t) {
2590 case T_DEFINED:
2591 if (args.count() < 1 || args.count() > 2) {
2592 evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
2593 " requires one or two arguments."));
2594 return ReturnFalse;
2595 }
2596 if (args.count() > 1) {
2597 if (args[1] == QLatin1String("test"))
2598 return returnBool(m_functionDefs.testFunctions.contains(args[0]));
2599 else if (args[1] == QLatin1String("replace"))
2600 return returnBool(m_functionDefs.replaceFunctions.contains(args[0]));
2601 evalError(fL1S("defined(function, type): unexpected type [%1].\n")
2602 .arg(args.at(1).toQString(m_tmp1)));
2603 return ReturnFalse;
2604 }
2605 return returnBool(m_functionDefs.replaceFunctions.contains(args[0])
2606 || m_functionDefs.testFunctions.contains(args[0]));
2607 case T_RETURN:
2608 m_returnValue = args;
2609 // It is "safe" to ignore returns - due to qmake brokeness
2610 // they cannot be used to terminate loops anyway.
2611 if (m_skipLevel || m_cumulative)
2612 return ReturnTrue;
2613 if (m_valuemapStack.isEmpty()) {
2614 evalError(fL1S("unexpected return()."));
2615 return ReturnFalse;
2616 }
2617 return ReturnReturn;
2618 case T_EXPORT: {
2619 if (m_skipLevel && !m_cumulative)
2620 return ReturnTrue;
2621 if (args.count() != 1) {
2622 evalError(fL1S("export(variable) requires one argument."));
2623 return ReturnFalse;
2624 }
2625 const ProString &var = map(args.at(0));
2626 for (int i = m_valuemapStack.size(); --i > 0; ) {
2627 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(var);
2628 if (it != m_valuemapStack.at(i).end()) {
2629 if (it->constBegin() == statics.fakeValue.constBegin()) {
2630 // This is stupid, but qmake doesn't propagate deletions
2631 m_valuemapStack[0][var] = ProStringList();
2632 } else {
2633 m_valuemapStack[0][var] = *it;
2634 }
2635 m_valuemapStack[i].erase(it);
2636 while (--i)
2637 m_valuemapStack[i].remove(var);
2638 break;
2639 }
2640 }
2641 return ReturnTrue;
2642 }
2643 case T_INFILE:
2644 if (args.count() < 2 || args.count() > 3) {
2645 evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
2646 } else {
2647 QHash<ProString, ProStringList> vars;
2648 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2649 fn.detach();
2650 if (!evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2651 &vars, &m_functionDefs, EvalWithDefaults))
2652 return ReturnFalse;
2653 if (args.count() == 2)
2654 return returnBool(vars.contains(args.at(1)));
2655 QRegExp regx;
2656 const QString &qry = args.at(2).toQString(m_tmp1);
2657 if (qry != QRegExp::escape(qry)) {
2658 QString copy = qry;
2659 copy.detach();
2660 regx.setPattern(copy);
2661 }
2662 int t = 0;
2663 foreach (const ProString &s, vars.value(map(args.at(1)))) {
2664 if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
2665 return ReturnTrue;
2666 t ^= 1;
2667 }
2668 }
2669 return ReturnFalse;
2670#if 0
2671 case T_REQUIRES:
2672#endif
2673 case T_EVAL: {
2674 ProFile *pro = m_parser->parsedProBlock(fL1S("(eval)"),
2675 args.join(statics.field_sep));
2676 if (!pro)
2677 return ReturnFalse;
2678 m_locationStack.push(m_current);
2679 VisitReturn ret = visitProBlock(pro, pro->tokPtr());
2680 m_current = m_locationStack.pop();
2681 pro->deref();
2682 return ret;
2683 }
2684 case T_BREAK:
2685 if (m_skipLevel)
2686 return ReturnFalse;
2687 if (m_loopLevel)
2688 return ReturnBreak;
2689 evalError(fL1S("unexpected break()."));
2690 return ReturnFalse;
2691 case T_NEXT:
2692 if (m_skipLevel)
2693 return ReturnFalse;
2694 if (m_loopLevel)
2695 return ReturnNext;
2696 evalError(fL1S("unexpected next()."));
2697 return ReturnFalse;
2698 case T_IF: {
2699 if (m_skipLevel && !m_cumulative)
2700 return ReturnFalse;
2701 if (args.count() != 1) {
2702 evalError(fL1S("if(condition) requires one argument."));
2703 return ReturnFalse;
2704 }
2705 const ProString &cond = args.at(0);
2706 bool quoted = false;
2707 bool ret = true;
2708 bool orOp = false;
2709 bool invert = false;
2710 bool isFunc = false;
2711 int parens = 0;
2712 QString test;
2713 test.reserve(20);
2714 QString argsString;
2715 argsString.reserve(50);
2716 const QChar *d = cond.constData();
2717 const QChar *ed = d + cond.size();
2718 while (d < ed) {
2719 ushort c = (d++)->unicode();
2720 bool isOp = false;
2721 if (quoted) {
2722 if (c == '"')
2723 quoted = false;
2724 else if (c == '!' && test.isEmpty())
2725 invert = true;
2726 else
2727 test += c;
2728 } else if (c == '(') {
2729 isFunc = true;
2730 if (parens)
2731 argsString += c;
2732 ++parens;
2733 } else if (c == ')') {
2734 --parens;
2735 if (parens)
2736 argsString += c;
2737 } else if (!parens) {
2738 if (c == '"')
2739 quoted = true;
2740 else if (c == ':' || c == '|')
2741 isOp = true;
2742 else if (c == '!' && test.isEmpty())
2743 invert = true;
2744 else
2745 test += c;
2746 } else {
2747 argsString += c;
2748 }
2749 if (!quoted && !parens && (isOp || d == ed)) {
2750 if (m_cumulative || (orOp != ret)) {
2751 test = test.trimmed();
2752 if (isFunc)
2753 ret = evaluateConditionalFunction(ProString(test), ProString(argsString, NoHash));
2754 else
2755 ret = isActiveConfig(test, true);
2756 ret ^= invert;
2757 }
2758 orOp = (c == '|');
2759 invert = false;
2760 isFunc = false;
2761 test.clear();
2762 argsString.clear();
2763 }
2764 }
2765 return returnBool(ret);
2766 }
2767 case T_CONFIG: {
2768 if (args.count() < 1 || args.count() > 2) {
2769 evalError(fL1S("CONFIG(config) requires one or two arguments."));
2770 return ReturnFalse;
2771 }
2772 if (args.count() == 1)
2773 return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
2774 const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
2775 const ProStringList &configs = valuesDirect(statics.strCONFIG);
2776
2777 for (int i = configs.size() - 1; i >= 0; i--) {
2778 for (int mut = 0; mut < mutuals.count(); mut++) {
2779 if (configs[i] == mutuals[mut].trimmed()) {
2780 return returnBool(configs[i] == args[0]);
2781 }
2782 }
2783 }
2784 return ReturnFalse;
2785 }
2786 case T_CONTAINS: {
2787 if (args.count() < 2 || args.count() > 3) {
2788 evalError(fL1S("contains(var, val) requires two or three arguments."));
2789 return ReturnFalse;
2790 }
2791
2792 const QString &qry = args.at(1).toQString(m_tmp1);
2793 QRegExp regx;
2794 if (qry != QRegExp::escape(qry)) {
2795 QString copy = qry;
2796 copy.detach();
2797 regx.setPattern(copy);
2798 }
2799 const ProStringList &l = values(map(args.at(0)));
2800 if (args.count() == 2) {
2801 int t = 0;
2802 for (int i = 0; i < l.size(); ++i) {
2803 const ProString &val = l[i];
2804 if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
2805 return ReturnTrue;
2806 t ^= 1;
2807 }
2808 } else {
2809 const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
2810 for (int i = l.size() - 1; i >= 0; i--) {
2811 const ProString val = l[i];
2812 for (int mut = 0; mut < mutuals.count(); mut++) {
2813 if (val == mutuals[mut].trimmed()) {
2814 return returnBool((!regx.isEmpty()
2815 && regx.exactMatch(val.toQString(m_tmp2)))
2816 || val == qry);
2817 }
2818 }
2819 }
2820 }
2821 return ReturnFalse;
2822 }
2823 case T_COUNT: {
2824 if (args.count() != 2 && args.count() != 3) {
2825 evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
2826 return ReturnFalse;
2827 }
2828 int cnt = values(map(args.at(0))).count();
2829 if (args.count() == 3) {
2830 const ProString &comp = args.at(2);
2831 const int val = args.at(1).toQString(m_tmp1).toInt();
2832 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
2833 return returnBool(cnt > val);
2834 } else if (comp == QLatin1String(">=")) {
2835 return returnBool(cnt >= val);
2836 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
2837 return returnBool(cnt < val);
2838 } else if (comp == QLatin1String("<=")) {
2839 return returnBool(cnt <= val);
2840 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
2841 || comp == QLatin1String("=") || comp == QLatin1String("==")) {
2842 return returnBool(cnt == val);
2843 } else {
2844 evalError(fL1S("unexpected modifier to count(%2)").arg(comp.toQString(m_tmp1)));
2845 return ReturnFalse;
2846 }
2847 }
2848 return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
2849 }
2850 case T_GREATERTHAN:
2851 case T_LESSTHAN: {
2852 if (args.count() != 2) {
2853 evalError(fL1S("%1(variable, value) requires two arguments.")
2854 .arg(function.toQString(m_tmp1)));
2855 return ReturnFalse;
2856 }
2857 const QString &rhs(args.at(1).toQString(m_tmp1)),
2858 &lhs(values(map(args.at(0))).join(statics.field_sep));
2859 bool ok;
2860 int rhs_int = rhs.toInt(&ok);
2861 if (ok) { // do integer compare
2862 int lhs_int = lhs.toInt(&ok);
2863 if (ok) {
2864 if (func_t == T_GREATERTHAN)
2865 return returnBool(lhs_int > rhs_int);
2866 return returnBool(lhs_int < rhs_int);
2867 }
2868 }
2869 if (func_t == T_GREATERTHAN)
2870 return returnBool(lhs > rhs);
2871 return returnBool(lhs < rhs);
2872 }
2873 case T_EQUALS:
2874 if (args.count() != 2) {
2875 evalError(fL1S("%1(variable, value) requires two arguments.")
2876 .arg(function.toQString(m_tmp1)));
2877 return ReturnFalse;
2878 }
2879 return returnBool(values(map(args.at(0))).join(statics.field_sep)
2880 == args.at(1).toQString(m_tmp1));
2881 case T_CLEAR: {
2882 if (m_skipLevel && !m_cumulative)
2883 return ReturnFalse;
2884 if (args.count() != 1) {
2885 evalError(fL1S("%1(variable) requires one argument.")
2886 .arg(function.toQString(m_tmp1)));
2887 return ReturnFalse;
2888 }
2889 QHash<ProString, ProStringList> *hsh;
2890 QHash<ProString, ProStringList>::Iterator it;
2891 const ProString &var = map(args.at(0));
2892 if (!(hsh = findValues(var, &it)))
2893 return ReturnFalse;
2894 if (hsh == &m_valuemapStack.top())
2895 it->clear();
2896 else
2897 m_valuemapStack.top()[var].clear();
2898 return ReturnTrue;
2899 }
2900 case T_UNSET: {
2901 if (m_skipLevel && !m_cumulative)
2902 return ReturnFalse;
2903 if (args.count() != 1) {
2904 evalError(fL1S("%1(variable) requires one argument.")
2905 .arg(function.toQString(m_tmp1)));
2906 return ReturnFalse;
2907 }
2908 QHash<ProString, ProStringList> *hsh;
2909 QHash<ProString, ProStringList>::Iterator it;
2910 const ProString &var = map(args.at(0));
2911 if (!(hsh = findValues(var, &it)))
2912 return ReturnFalse;
2913 if (m_valuemapStack.size() == 1)
2914 hsh->erase(it);
2915 else if (hsh == &m_valuemapStack.top())
2916 *it = statics.fakeValue;
2917 else
2918 m_valuemapStack.top()[var] = statics.fakeValue;
2919 return ReturnTrue;
2920 }
2921 case T_INCLUDE: {
2922 if (m_skipLevel && !m_cumulative)
2923 return ReturnFalse;
2924 QString parseInto;
2925 // the third optional argument to include() controls warnings
2926 // and is not used here
2927 if ((args.count() == 2) || (args.count() == 3) ) {
2928 parseInto = args.at(1).toQString(m_tmp2);
2929 } else if (args.count() != 1) {
2930 evalError(fL1S("include(file, into, silent) requires one, two or three arguments."));
2931 return ReturnFalse;
2932 }
2933 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2934 fn.detach();
2935 bool ok;
2936 if (parseInto.isEmpty()) {
2937 ok = evaluateFile(fn, ProFileEvaluatorHandler::EvalIncludeFile,
2938 ProFileEvaluator::LoadProOnly);
2939 } else {
2940 QHash<ProString, ProStringList> symbols;
2941 if ((ok = evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2942 &symbols, 0, EvalWithSetup))) {
2943 QHash<ProString, ProStringList> newMap;
2944 for (QHash<ProString, ProStringList>::ConstIterator
2945 it = m_valuemapStack.top().constBegin(),
2946 end = m_valuemapStack.top().constEnd();
2947 it != end; ++it) {
2948 const QString &ky = it.key().toQString(m_tmp1);
2949 if (!(ky.startsWith(parseInto) &&
2950 (ky.length() == parseInto.length()
2951 || ky.at(parseInto.length()) == QLatin1Char('.'))))
2952 newMap[it.key()] = it.value();
2953 }
2954 for (QHash<ProString, ProStringList>::ConstIterator it = symbols.constBegin();
2955 it != symbols.constEnd(); ++it) {
2956 const QString &ky = it.key().toQString(m_tmp1);
2957 if (!ky.startsWith(QLatin1Char('.')))
2958 newMap.insert(ProString(parseInto + QLatin1Char('.') + ky), it.value());
2959 }
2960 m_valuemapStack.top() = newMap;
2961 }
2962 }
2963 return returnBool(ok);
2964 }
2965 case T_LOAD: {
2966 if (m_skipLevel && !m_cumulative)
2967 return ReturnFalse;
2968 bool ignore_error = false;
2969 if (args.count() == 2) {
2970 ignore_error = isTrue(args.at(1), m_tmp2);
2971 } else if (args.count() != 1) {
2972 evalError(fL1S("load(feature) requires one or two arguments."));
2973 return ReturnFalse;
2974 }
2975 // XXX ignore_error unused
2976 return returnBool(evaluateFeatureFile(expandEnvVars(args.at(0).toQString())));
2977 }
2978 case T_DEBUG:
2979 // Yup - do nothing. Nothing is going to enable debug output anyway.
2980 return ReturnFalse;
2981 case T_MESSAGE: {
2982 if (args.count() != 1) {
2983 evalError(fL1S("%1(message) requires one argument.")
2984 .arg(function.toQString(m_tmp1)));
2985 return ReturnFalse;
2986 }
2987 const QString &msg = expandEnvVars(args.at(0).toQString(m_tmp2));
2988 if (!m_skipLevel)
2989 m_handler->fileMessage(fL1S("Project %1: %2")
2990 .arg(function.toQString(m_tmp1).toUpper(), msg));
2991 // ### Consider real termination in non-cumulative mode
2992 return returnBool(function != QLatin1String("error"));
2993 }
2994#if 0 // Way too dangerous to enable.
2995 case T_SYSTEM: {
2996 if (args.count() != 1) {
2997 evalError(fL1S("system(exec) requires one argument."));
2998 return ReturnFalse;
2999 }
3000#ifndef QT_BOOTSTRAPPED
3001 QProcess proc;
3002 proc.setProcessChannelMode(QProcess::MergedChannels);
3003 runProcess(&proc, args.at(0).toQString(m_tmp2), QProcess::StandardOutput);
3004 return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
3005#else
3006 return returnBool(system((QLatin1String("cd ")
3007 + IoUtils::shellQuote(currentDirectory())
3008 + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
3009#endif
3010 }
3011#endif
3012 case T_ISEMPTY: {
3013 if (args.count() != 1) {
3014 evalError(fL1S("isEmpty(var) requires one argument."));
3015 return ReturnFalse;
3016 }
3017 const ProStringList &sl = values(map(args.at(0)));
3018 if (sl.count() == 0) {
3019 return ReturnTrue;
3020 } else if (sl.count() > 0) {
3021 const ProString &var = sl.first();
3022 if (var.isEmpty())
3023 return ReturnTrue;
3024 }
3025 return ReturnFalse;
3026 }
3027 case T_EXISTS: {
3028 if (args.count() != 1) {
3029 evalError(fL1S("exists(file) requires one argument."));
3030 return ReturnFalse;
3031 }
3032 const QString &file = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
3033
3034 if (IoUtils::exists(file)) {
3035 return ReturnTrue;
3036 }
3037 int slsh = file.lastIndexOf(QLatin1Char('/'));
3038 QString fn = file.mid(slsh+1);
3039 if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
3040 QString dirstr = file.left(slsh+1);
3041 if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
3042 return ReturnTrue;
3043 }
3044
3045 return ReturnFalse;
3046 }
3047 case T_INVALID:
3048 evalError(fL1S("'%1' is not a recognized test function")
3049 .arg(function.toQString(m_tmp1)));
3050 return ReturnFalse;
3051 default:
3052 evalError(fL1S("Function '%1' is not implemented").arg(function.toQString(m_tmp1)));
3053 return ReturnFalse;
3054 }
3055}
3056
3057QHash<ProString, ProStringList> *ProFileEvaluator::Private::findValues(
3058 const ProString &variableName, QHash<ProString, ProStringList>::Iterator *rit)
3059{
3060 for (int i = m_valuemapStack.size(); --i >= 0; ) {
3061 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(variableName);
3062 if (it != m_valuemapStack[i].end()) {
3063 if (it->constBegin() == statics.fakeValue.constBegin())
3064 return 0;
3065 *rit = it;
3066 return &m_valuemapStack[i];
3067 }
3068 }
3069 return 0;
3070}
3071
3072ProStringList &ProFileEvaluator::Private::valuesRef(const ProString &variableName)
3073{
3074 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack.top().find(variableName);
3075 if (it != m_valuemapStack.top().end())
3076 return *it;
3077 for (int i = m_valuemapStack.size() - 1; --i >= 0; ) {
3078 QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
3079 if (it != m_valuemapStack.at(i).constEnd()) {
3080 ProStringList &ret = m_valuemapStack.top()[variableName];
3081 ret = *it;
3082 return ret;
3083 }
3084 }
3085 return m_valuemapStack.top()[variableName];
3086}
3087
3088ProStringList ProFileEvaluator::Private::valuesDirect(const ProString &variableName) const
3089{
3090 for (int i = m_valuemapStack.size(); --i >= 0; ) {
3091 QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
3092 if (it != m_valuemapStack.at(i).constEnd()) {
3093 if (it->constBegin() == statics.fakeValue.constBegin())
3094 break;
3095 return *it;
3096 }
3097 }
3098 return ProStringList();
3099}
3100
3101ProStringList ProFileEvaluator::Private::values(const ProString &variableName) const
3102{
3103 QHash<ProString, int>::ConstIterator vli = statics.varList.find(variableName);
3104 if (vli != statics.varList.constEnd()) {
3105 int vlidx = *vli;
3106 QString ret;
3107 switch ((VarName)vlidx) {
3108 case V_LITERAL_WHITESPACE: ret = QLatin1String("\t"); break;
3109 case V_LITERAL_DOLLAR: ret = QLatin1String("$"); break;
3110 case V_LITERAL_HASH: ret = QLatin1String("#"); break;
3111 case V_OUT_PWD: // the outgoing dir (shadow of _PRO_FILE_PWD_)
3112 ret = m_outputDir;
3113 break;
3114 case V_PWD: // containing directory of most nested project/include file
3115 case V_IN_PWD:
3116 ret = currentDirectory();
3117 break;
3118 case V_DIR_SEPARATOR:
3119 validateModes();
3120 ret = m_option->dir_sep;
3121 break;
3122 case V_DIRLIST_SEPARATOR:
3123 ret = m_option->dirlist_sep;
3124 break;
3125 case V__LINE_: // currently executed line number
3126 ret = QString::number(m_current.line);
3127 break;
3128 case V__FILE_: // currently executed file
3129 ret = m_current.pro->fileName();
3130 break;
3131 case V__DATE_: //current date/time
3132 ret = QDateTime::currentDateTime().toString();
3133 break;
3134 case V__PRO_FILE_:
3135 ret = m_profileStack.first()->fileName();
3136 break;
3137 case V__PRO_FILE_PWD_:
3138 ret = m_profileStack.first()->directoryName();
3139 break;
3140 case V__QMAKE_CACHE_:
3141 ret = m_option->cachefile;
3142 break;
3143#if defined(Q_OS_WIN32)
3144 case V_QMAKE_HOST_os: ret = QLatin1String("Windows"); break;
3145 case V_QMAKE_HOST_name: {
3146 DWORD name_length = 1024;
3147 TCHAR name[1024];
3148 if (GetComputerName(name, &name_length))
3149 ret = QString::fromUtf16((ushort*)name, name_length);
3150 break;
3151 }
3152 case V_QMAKE_HOST_version:
3153 ret = QString::number(QSysInfo::WindowsVersion);
3154 break;
3155 case V_QMAKE_HOST_version_string:
3156 switch (QSysInfo::WindowsVersion) {
3157 case QSysInfo::WV_Me: ret = QLatin1String("WinMe"); break;
3158 case QSysInfo::WV_95: ret = QLatin1String("Win95"); break;
3159 case QSysInfo::WV_98: ret = QLatin1String("Win98"); break;
3160 case QSysInfo::WV_NT: ret = QLatin1String("WinNT"); break;
3161 case QSysInfo::WV_2000: ret = QLatin1String("Win2000"); break;
3162 case QSysInfo::WV_2003: ret = QLatin1String("Win2003"); break;
3163 case QSysInfo::WV_XP: ret = QLatin1String("WinXP"); break;
3164 case QSysInfo::WV_VISTA: ret = QLatin1String("WinVista"); break;
3165 default: ret = QLatin1String("Unknown"); break;
3166 }
3167 break;
3168 case V_QMAKE_HOST_arch:
3169 SYSTEM_INFO info;
3170 GetSystemInfo(&info);
3171 switch(info.wProcessorArchitecture) {
3172#ifdef PROCESSOR_ARCHITECTURE_AMD64
3173 case PROCESSOR_ARCHITECTURE_AMD64:
3174 ret = QLatin1String("x86_64");
3175 break;
3176#endif
3177 case PROCESSOR_ARCHITECTURE_INTEL:
3178 ret = QLatin1String("x86");
3179 break;
3180 case PROCESSOR_ARCHITECTURE_IA64:
3181#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
3182 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
3183#endif
3184 ret = QLatin1String("IA64");
3185 break;
3186 default:
3187 ret = QLatin1String("Unknown");
3188 break;
3189 }
3190 break;
3191#elif defined(Q_OS_UNIX)
3192 case V_QMAKE_HOST_os:
3193 case V_QMAKE_HOST_name:
3194 case V_QMAKE_HOST_version:
3195 case V_QMAKE_HOST_version_string:
3196 case V_QMAKE_HOST_arch:
3197 {
3198 struct utsname name;
3199 const char *what;
3200 if (!uname(&name)) {
3201 switch (vlidx) {
3202 case V_QMAKE_HOST_os: what = name.sysname; break;
3203 case V_QMAKE_HOST_name: what = name.nodename; break;
3204 case V_QMAKE_HOST_version: what = name.release; break;
3205 case V_QMAKE_HOST_version_string: what = name.version; break;
3206 case V_QMAKE_HOST_arch: what = name.machine; break;
3207 }
3208 ret = QString::fromLocal8Bit(what);
3209 }
3210 }
3211#endif
3212 }
3213 return ProStringList(ProString(ret, NoHash));
3214 }
3215
3216 ProStringList result = valuesDirect(variableName);
3217 if (result.isEmpty()) {
3218 if (variableName == statics.strTEMPLATE) {
3219 result.append(ProString("app", NoHash));
3220 } else if (variableName == statics.strQMAKE_DIR_SEP) {
3221 result.append(ProString(m_option->dirlist_sep, NoHash));
3222 }
3223 }
3224 return result;
3225}
3226
3227bool ProFileEvaluator::Private::evaluateFileDirect(
3228 const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
3229 ProFileEvaluator::LoadFlags flags)
3230{
3231 if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
3232 m_locationStack.push(m_current);
3233 bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
3234 m_current = m_locationStack.pop();
3235 pro->deref();
3236 return ok;
3237 } else {
3238 return false;
3239 }
3240}
3241
3242bool ProFileEvaluator::Private::evaluateFile(
3243 const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
3244 ProFileEvaluator::LoadFlags flags)
3245{
3246 if (fileName.isEmpty())
3247 return false;
3248 foreach (const ProFile *pf, m_profileStack)
3249 if (pf->fileName() == fileName) {
3250 evalError(fL1S("circular inclusion of %1").arg(fileName));
3251 return false;
3252 }
3253 return evaluateFileDirect(fileName, type, flags);
3254}
3255
3256bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
3257{
3258 QString fn = fileName;
3259 if (!fn.endsWith(QLatin1String(".prf")))
3260 fn += QLatin1String(".prf");
3261
3262 if ((!fileName.contains((ushort)'/') && !fileName.contains((ushort)'\\'))
3263 || !IoUtils::exists(resolvePath(fn))) {
3264 if (m_option->feature_roots.isEmpty())
3265 m_option->feature_roots = qmakeFeaturePaths();
3266 int start_root = 0;
3267 QString currFn = currentFileName();
3268 if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
3269 for (int root = 0; root < m_option->feature_roots.size(); ++root)
3270 if (currFn == m_option->feature_roots.at(root) + fn) {
3271 start_root = root + 1;
3272 break;
3273 }
3274 }
3275 for (int root = start_root; root < m_option->feature_roots.size(); ++root) {
3276 QString fname = m_option->feature_roots.at(root) + fn;
3277 if (IoUtils::exists(fname)) {
3278 fn = fname;
3279 goto cool;
3280 }
3281 }
3282 return false;
3283
3284 cool:
3285 // It's beyond me why qmake has this inside this if ...
3286 ProStringList &already = valuesRef(ProString("QMAKE_INTERNAL_INCLUDED_FEATURES"));
3287 ProString afn(fn, NoHash);
3288 if (already.contains(afn))
3289 return true;
3290 already.append(afn);
3291 } else {
3292 fn = resolvePath(fn);
3293 }
3294
3295#ifdef PROEVALUATOR_CUMULATIVE
3296 bool cumulative = m_cumulative;
3297 m_cumulative = false;
3298#endif
3299
3300 // The path is fully normalized already.
3301 bool ok = evaluateFileDirect(fn, ProFileEvaluatorHandler::EvalFeatureFile,
3302 ProFileEvaluator::LoadProOnly);
3303
3304#ifdef PROEVALUATOR_CUMULATIVE
3305 m_cumulative = cumulative;
3306#endif
3307 return ok;
3308}
3309
3310bool ProFileEvaluator::Private::evaluateFileInto(
3311 const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
3312 QHash<ProString, ProStringList> *values, FunctionDefs *funcs, EvalIntoMode mode)
3313{
3314 ProFileEvaluator visitor(m_option, m_parser, m_handler);
3315#ifdef PROEVALUATOR_CUMULATIVE
3316 visitor.d->m_cumulative = false;
3317#endif
3318 visitor.d->m_outputDir = m_outputDir;
3319// visitor.d->m_valuemapStack.top() = *values;
3320 if (funcs)
3321 visitor.d->m_functionDefs = *funcs;
3322 if (mode == EvalWithDefaults)
3323 visitor.d->evaluateFeatureFile(QLatin1String("default_pre.prf"));
3324 if (!visitor.d->evaluateFile(fileName, type,
3325 (mode == EvalWithSetup) ? ProFileEvaluator::LoadAll : ProFileEvaluator::LoadProOnly))
3326 return false;
3327 *values = visitor.d->m_valuemapStack.top();
3328// if (funcs)
3329// *funcs = visitor.d->m_functionDefs;
3330 return true;
3331}
3332
3333void ProFileEvaluator::Private::evalError(const QString &message) const
3334{
3335 if (!m_skipLevel)
3336 m_handler->evalError(m_current.line ? m_current.pro->fileName() : QString(),
3337 m_current.line, message);
3338}
3339
3340
3341///////////////////////////////////////////////////////////////////////
3342//
3343// ProFileEvaluator
3344//
3345///////////////////////////////////////////////////////////////////////
3346
3347void ProFileEvaluator::initialize()
3348{
3349 Private::initStatics();
3350}
3351
3352ProFileEvaluator::ProFileEvaluator(ProFileOption *option, ProFileParser *parser,
3353 ProFileEvaluatorHandler *handler)
3354 : d(new Private(this, option, parser, handler))
3355{
3356}
3357
3358ProFileEvaluator::~ProFileEvaluator()
3359{
3360 delete d;
3361}
3362
3363bool ProFileEvaluator::contains(const QString &variableName) const
3364{
3365 return d->m_valuemapStack.top().contains(ProString(variableName));
3366}
3367
3368QString ProFileEvaluator::value(const QString &variable) const
3369{
3370 const QStringList &vals = values(variable);
3371 if (!vals.isEmpty())
3372 return vals.first();
3373
3374 return QString();
3375}
3376
3377QStringList ProFileEvaluator::values(const QString &variableName) const
3378{
3379 const ProStringList &values = d->values(ProString(variableName));
3380 QStringList ret;
3381 ret.reserve(values.size());
3382 foreach (const ProString &str, values)
3383 ret << d->expandEnvVars(str.toQString());
3384 return ret;
3385}
3386
3387QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
3388{
3389 // It makes no sense to put any kind of magic into expanding these
3390 const ProStringList &values = d->m_valuemapStack.at(0).value(ProString(variableName));
3391 QStringList ret;
3392 ret.reserve(values.size());
3393 foreach (const ProString &str, values)
3394 if (str.sourceFile() == pro)
3395 ret << d->expandEnvVars(str.toQString());
3396 return ret;
3397}
3398
3399QStringList ProFileEvaluator::absolutePathValues(
3400 const QString &variable, const QString &baseDirectory) const
3401{
3402 QStringList result;
3403 foreach (const QString &el, values(variable)) {
3404 QString absEl = IoUtils::isAbsolutePath(el)
3405 ? d->sysrootify(el, baseDirectory) : IoUtils::resolvePath(baseDirectory, el);
3406 if (IoUtils::fileType(absEl) == IoUtils::FileIsDir)
3407 result << QDir::cleanPath(absEl);
3408 }
3409 return result;
3410}
3411
3412QStringList ProFileEvaluator::absoluteFileValues(
3413 const QString &variable, const QString &baseDirectory, const QStringList &searchDirs,
3414 const ProFile *pro) const
3415{
3416 QStringList result;
3417 foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
3418 QString absEl;
3419 if (IoUtils::isAbsolutePath(el)) {
3420 const QString elWithSysroot = d->sysrootify(el, baseDirectory);
3421 if (IoUtils::exists(elWithSysroot)) {
3422 result << QDir::cleanPath(elWithSysroot);
3423 goto next;
3424 }
3425 absEl = elWithSysroot;
3426 } else {
3427 foreach (const QString &dir, searchDirs) {
3428 QString fn = dir + QLatin1Char('/') + el;
3429 if (IoUtils::exists(fn)) {
3430 result << QDir::cleanPath(fn);
3431 goto next;
3432 }
3433 }
3434 if (baseDirectory.isEmpty())
3435 goto next;
3436 absEl = baseDirectory + QLatin1Char('/') + el;
3437 }
3438 {
3439 absEl = QDir::cleanPath(absEl);
3440 int nameOff = absEl.lastIndexOf(QLatin1Char('/'));
3441 QString absDir = d->m_tmp1.setRawData(absEl.constData(), nameOff);
3442 if (IoUtils::exists(absDir)) {
3443 QString wildcard = d->m_tmp2.setRawData(absEl.constData() + nameOff + 1,
3444 absEl.length() - nameOff - 1);
3445 if (wildcard.contains(QLatin1Char('*')) || wildcard.contains(QLatin1Char('?'))) {
3446 wildcard.detach(); // Keep m_tmp out of QRegExp's cache
3447 QDir theDir(absDir);
3448 foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
3449 if (fn != statics.strDot && fn != statics.strDotDot)
3450 result << absDir + QLatin1Char('/') + fn;
3451 } // else if (acceptMissing)
3452 }
3453 }
3454 next: ;
3455 }
3456 return result;
3457}
3458
3459ProFileEvaluator::TemplateType ProFileEvaluator::templateType() const
3460{
3461 const ProStringList &templ = d->values(statics.strTEMPLATE);
3462 if (templ.count() >= 1) {
3463 const QString &t = templ.at(0).toQString();
3464 if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
3465 return TT_Application;
3466 if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
3467 return TT_Library;
3468 if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
3469 return TT_Script;
3470 if (!t.compare(QLatin1String("aux"), Qt::CaseInsensitive))
3471 return TT_Aux;
3472 if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
3473 return TT_Subdirs;
3474 }
3475 return TT_Unknown;
3476}
3477
3478bool ProFileEvaluator::accept(ProFile *pro, LoadFlags flags)
3479{
3480 return d->visitProFile(pro, ProFileEvaluatorHandler::EvalProjectFile, flags) == Private::ReturnTrue;
3481}
3482
3483QString ProFileEvaluator::propertyValue(const QString &name) const
3484{
3485 return d->propertyValue(name, false);
3486}
3487
3488#ifdef PROEVALUATOR_CUMULATIVE
3489void ProFileEvaluator::setCumulative(bool on)
3490{
3491 d->m_cumulative = on;
3492}
3493#endif
3494
3495void ProFileEvaluator::setOutputDir(const QString &dir)
3496{
3497 d->m_outputDir = dir;
3498}
3499
3500QT_END_NAMESPACE
3501