1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the qmake application of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qmakeevaluator.h"
30#include "qmakeevaluator_p.h"
31
32#include "qmakeglobals.h"
33#include "qmakeparser.h"
34#include "qmakevfs.h"
35#include "ioutils.h"
36
37#include <qbytearray.h>
38#include <qdatetime.h>
39#include <qdebug.h>
40#include <qdir.h>
41#include <qfile.h>
42#include <qfileinfo.h>
43#include <qlist.h>
44#include <qregexp.h>
45#include <qset.h>
46#include <qstack.h>
47#include <qstring.h>
48#include <qstringlist.h>
49#ifdef PROEVALUATOR_THREAD_SAFE
50# include <qthreadpool.h>
51#endif
52
53#ifdef Q_OS_UNIX
54#include <unistd.h>
55#include <sys/utsname.h>
56# ifdef Q_OS_BSD4
57# include <sys/sysctl.h>
58# endif
59#else
60#include <windows.h>
61#endif
62#include <stdio.h>
63#include <stdlib.h>
64
65using namespace QMakeInternal;
66
67QT_BEGIN_NAMESPACE
68
69#define fL1S(s) QString::fromLatin1(s)
70
71// we can't use QThread in qmake
72// this function is a merger of QThread::idealThreadCount from qthread_win.cpp and qthread_unix.cpp
73static int idealThreadCount()
74{
75#ifdef PROEVALUATOR_THREAD_SAFE
76 return QThread::idealThreadCount();
77#elif defined(Q_OS_WIN)
78 SYSTEM_INFO sysinfo;
79 GetSystemInfo(&sysinfo);
80 return sysinfo.dwNumberOfProcessors;
81#else
82 // there are a couple more definitions in the Unix QThread::idealThreadCount, but
83 // we don't need them all here
84 int cores = 1;
85# if defined(Q_OS_BSD4)
86 // FreeBSD, OpenBSD, NetBSD, BSD/OS, OS X
87 size_t len = sizeof(cores);
88 int mib[2];
89 mib[0] = CTL_HW;
90 mib[1] = HW_NCPU;
91 if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) {
92 perror("sysctl");
93 }
94# elif defined(_SC_NPROCESSORS_ONLN)
95 // the rest: Linux, Solaris, AIX, Tru64
96 cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
97 if (cores == -1)
98 return 1;
99# endif
100 return cores;
101#endif
102}
103
104
105QMakeBaseKey::QMakeBaseKey(const QString &_root, const QString &_stash, bool _hostBuild)
106 : root(_root), stash(_stash), hostBuild(_hostBuild)
107{
108}
109
110uint qHash(const QMakeBaseKey &key)
111{
112 return qHash(key: key.root) ^ qHash(key: key.stash) ^ (uint)key.hostBuild;
113}
114
115bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
116{
117 return one.root == two.root && one.stash == two.stash && one.hostBuild == two.hostBuild;
118}
119
120QMakeBaseEnv::QMakeBaseEnv()
121 : evaluator(nullptr)
122{
123#ifdef PROEVALUATOR_THREAD_SAFE
124 inProgress = false;
125#endif
126}
127
128QMakeBaseEnv::~QMakeBaseEnv()
129{
130 delete evaluator;
131}
132
133namespace QMakeInternal {
134QMakeStatics statics;
135}
136
137void QMakeEvaluator::initStatics()
138{
139 if (!statics.field_sep.isNull())
140 return;
141
142 statics.field_sep = QLatin1String(" ");
143 statics.strtrue = QLatin1String("true");
144 statics.strfalse = QLatin1String("false");
145 statics.strCONFIG = ProKey("CONFIG");
146 statics.strARGS = ProKey("ARGS");
147 statics.strARGC = ProKey("ARGC");
148 statics.strDot = QLatin1String(".");
149 statics.strDotDot = QLatin1String("..");
150 statics.strever = QLatin1String("ever");
151 statics.strforever = QLatin1String("forever");
152 statics.strhost_build = QLatin1String("host_build");
153 statics.strTEMPLATE = ProKey("TEMPLATE");
154 statics.strQMAKE_PLATFORM = ProKey("QMAKE_PLATFORM");
155 statics.strQMAKE_DIR_SEP = ProKey("QMAKE_DIR_SEP");
156 statics.strQMAKESPEC = ProKey("QMAKESPEC");
157#ifdef PROEVALUATOR_FULL
158 statics.strREQUIRES = ProKey("REQUIRES");
159#endif
160
161 statics.fakeValue = ProStringList(ProString("_FAKE_")); // It has to have a unique begin() value
162
163 initFunctionStatics();
164
165 static const struct {
166 const char * const oldname, * const newname;
167 } mapInits[] = {
168 { .oldname: "INTERFACES", .newname: "FORMS" },
169 { .oldname: "QMAKE_POST_BUILD", .newname: "QMAKE_POST_LINK" },
170 { .oldname: "TARGETDEPS", .newname: "POST_TARGETDEPS" },
171 { .oldname: "LIBPATH", .newname: "QMAKE_LIBDIR" },
172 { .oldname: "QMAKE_EXT_MOC", .newname: "QMAKE_EXT_CPP_MOC" },
173 { .oldname: "QMAKE_MOD_MOC", .newname: "QMAKE_H_MOD_MOC" },
174 { .oldname: "QMAKE_LFLAGS_SHAPP", .newname: "QMAKE_LFLAGS_APP" },
175 { .oldname: "PRECOMPH", .newname: "PRECOMPILED_HEADER" },
176 { .oldname: "PRECOMPCPP", .newname: "PRECOMPILED_SOURCE" },
177 { .oldname: "INCPATH", .newname: "INCLUDEPATH" },
178 { .oldname: "QMAKE_EXTRA_WIN_COMPILERS", .newname: "QMAKE_EXTRA_COMPILERS" },
179 { .oldname: "QMAKE_EXTRA_UNIX_COMPILERS", .newname: "QMAKE_EXTRA_COMPILERS" },
180 { .oldname: "QMAKE_EXTRA_WIN_TARGETS", .newname: "QMAKE_EXTRA_TARGETS" },
181 { .oldname: "QMAKE_EXTRA_UNIX_TARGETS", .newname: "QMAKE_EXTRA_TARGETS" },
182 { .oldname: "QMAKE_EXTRA_UNIX_INCLUDES", .newname: "QMAKE_EXTRA_INCLUDES" },
183 { .oldname: "QMAKE_EXTRA_UNIX_VARIABLES", .newname: "QMAKE_EXTRA_VARIABLES" },
184 { .oldname: "QMAKE_RPATH", .newname: "QMAKE_LFLAGS_RPATH" },
185 { .oldname: "QMAKE_FRAMEWORKDIR", .newname: "QMAKE_FRAMEWORKPATH" },
186 { .oldname: "QMAKE_FRAMEWORKDIR_FLAGS", .newname: "QMAKE_FRAMEWORKPATH_FLAGS" },
187 { .oldname: "IN_PWD", .newname: "PWD" },
188 { .oldname: "DEPLOYMENT", .newname: "INSTALLS" }
189 };
190 statics.varMap.reserve(asize: (int)(sizeof(mapInits)/sizeof(mapInits[0])));
191 for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
192 statics.varMap.insert(akey: ProKey(mapInits[i].oldname), avalue: ProKey(mapInits[i].newname));
193}
194
195const ProKey &QMakeEvaluator::map(const ProKey &var)
196{
197 QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(akey: var);
198 if (it == statics.varMap.constEnd())
199 return var;
200 deprecationWarning(fL1S("Variable %1 is deprecated; use %2 instead.")
201 .arg(args: var.toQString(), args: it.value().toQString()));
202 return it.value();
203}
204
205
206QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
207 QMakeHandler *handler)
208 :
209#ifdef PROEVALUATOR_DEBUG
210 m_debugLevel(option->debugLevel),
211#endif
212 m_option(option), m_parser(parser), m_handler(handler), m_vfs(vfs)
213{
214 // So that single-threaded apps don't have to call initialize() for now.
215 initStatics();
216
217 // Configuration, more or less
218 m_caller = nullptr;
219#ifdef PROEVALUATOR_CUMULATIVE
220 m_cumulative = false;
221#endif
222 m_hostBuild = false;
223
224 // Evaluator state
225#ifdef PROEVALUATOR_CUMULATIVE
226 m_skipLevel = 0;
227#endif
228 m_listCount = 0;
229 m_toggle = 0;
230 m_valuemapStack.push(t: ProValueMap());
231 m_valuemapInited = false;
232}
233
234QMakeEvaluator::~QMakeEvaluator()
235{
236}
237
238void QMakeEvaluator::initFrom(const QMakeEvaluator *other)
239{
240 Q_ASSERT_X(other, "QMakeEvaluator::visitProFile", "Project not prepared");
241 m_functionDefs = other->m_functionDefs;
242 m_valuemapStack = other->m_valuemapStack;
243 m_valuemapInited = true;
244 m_qmakespec = other->m_qmakespec;
245 m_qmakespecName = other->m_qmakespecName;
246 m_mkspecPaths = other->m_mkspecPaths;
247 m_featureRoots = other->m_featureRoots;
248 m_dirSep = other->m_dirSep;
249}
250
251//////// Evaluator tools /////////
252
253uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr)
254{
255 uint len = *tokPtr++;
256 len |= (uint)*tokPtr++ << 16;
257 return len;
258}
259
260void QMakeEvaluator::skipStr(const ushort *&tokPtr)
261{
262 uint len = *tokPtr++;
263 tokPtr += len;
264}
265
266void QMakeEvaluator::skipHashStr(const ushort *&tokPtr)
267{
268 tokPtr += 2;
269 uint len = *tokPtr++;
270 tokPtr += len;
271}
272
273// FIXME: this should not build new strings for direct sections.
274// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
275ProStringList QMakeEvaluator::split_value_list(const QStringRef &vals, int source)
276{
277 QString build;
278 ProStringList ret;
279
280 if (!source)
281 source = currentFileId();
282
283 const QChar *vals_data = vals.data();
284 const int vals_len = vals.length();
285 ushort quote = 0;
286 bool hadWord = false;
287 for (int x = 0; x < vals_len; x++) {
288 ushort unicode = vals_data[x].unicode();
289 if (unicode == quote) {
290 quote = 0;
291 hadWord = true;
292 build += QChar(unicode);
293 continue;
294 }
295 switch (unicode) {
296 case '"':
297 case '\'':
298 if (!quote)
299 quote = unicode;
300 // FIXME: this is inconsistent with the "there are no empty strings" dogma.
301 hadWord = true;
302 break;
303 case ' ':
304 case '\t':
305 if (!quote) {
306 if (hadWord) {
307 ret << ProString(build).setSource(source);
308 build.clear();
309 hadWord = false;
310 }
311 continue;
312 }
313 break;
314 case '\\':
315 if (x + 1 != vals_len) {
316 ushort next = vals_data[++x].unicode();
317 if (next == '\'' || next == '"' || next == '\\') {
318 build += QChar(unicode);
319 unicode = next;
320 } else {
321 --x;
322 }
323 }
324 Q_FALLTHROUGH();
325 default:
326 hadWord = true;
327 break;
328 }
329 build += QChar(unicode);
330 }
331 if (hadWord)
332 ret << ProString(build).setSource(source);
333 return ret;
334}
335
336static void replaceInList(ProStringList *varlist,
337 const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
338{
339 for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
340 ProStringRoUser u1(*varit, tmp);
341 QString val = u1.str();
342 QString copy = val; // Force detach and have a reference value
343 val.replace(rx: regexp, after: replace);
344 if (!val.isSharedWith(other: copy) && val != copy) {
345 if (val.isEmpty()) {
346 varit = varlist->erase(pos: varit);
347 } else {
348 (*varit).setValue(val);
349 ++varit;
350 }
351 if (!global)
352 break;
353 } else {
354 ++varit;
355 }
356 }
357}
358
359//////// Evaluator /////////
360
361static ALWAYS_INLINE void addStr(
362 const ProString &str, ProStringList *ret, bool &pending, bool joined)
363{
364 if (joined) {
365 ret->last().append(other: str, pending: &pending);
366 } else {
367 if (!pending) {
368 pending = true;
369 *ret << str;
370 } else {
371 ret->last().append(other: str);
372 }
373 }
374}
375
376static ALWAYS_INLINE void addStrList(
377 const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
378{
379 if (!list.isEmpty()) {
380 if (joined) {
381 ret->last().append(other: list, pending: &pending, skipEmpty1st: !(tok & TokQuoted));
382 } else {
383 if (tok & TokQuoted) {
384 if (!pending) {
385 pending = true;
386 *ret << ProString();
387 }
388 ret->last().append(other: list);
389 } else {
390 if (!pending) {
391 // Another qmake bizzarity: if nothing is pending and the
392 // first element is empty, it will be eaten
393 if (!list.at(i: 0).isEmpty()) {
394 // The common case
395 pending = true;
396 *ret += list;
397 return;
398 }
399 } else {
400 ret->last().append(other: list.at(i: 0));
401 }
402 // This is somewhat slow, but a corner case
403 for (int j = 1; j < list.size(); ++j) {
404 pending = true;
405 *ret << list.at(i: j);
406 }
407 }
408 }
409 }
410}
411
412QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpression(
413 const ushort *&tokPtr, ProStringList *ret, bool joined)
414{
415 debugMsg(level: 2, fmt: joined ? "evaluating joined expression" : "evaluating expression");
416 ProFile *pro = m_current.pro;
417 if (joined)
418 *ret << ProString();
419 bool pending = false;
420 forever {
421 ushort tok = *tokPtr++;
422 if (tok & TokNewStr) {
423 debugMsg(level: 2, fmt: "new string");
424 pending = false;
425 }
426 ushort maskedTok = tok & TokMask;
427 switch (maskedTok) {
428 case TokLine:
429 m_current.line = *tokPtr++;
430 break;
431 case TokLiteral: {
432 const ProString &val = pro->getStr(tPtr&: tokPtr);
433 debugMsg(level: 2, fmt: "literal %s", dbgStr(val));
434 addStr(str: val, ret, pending, joined);
435 break; }
436 case TokHashLiteral: {
437 const ProKey &val = pro->getHashStr(tPtr&: tokPtr);
438 debugMsg(level: 2, fmt: "hashed literal %s", dbgStr(val.toString()));
439 addStr(str: val, ret, pending, joined);
440 break; }
441 case TokVariable: {
442 const ProKey &var = pro->getHashStr(tPtr&: tokPtr);
443 const ProStringList &val = values(variableName: map(var));
444 debugMsg(level: 2, fmt: "variable %s => %s", dbgKey(var), dbgStrList(val));
445 addStrList(list: val, tok, ret, pending, joined);
446 break; }
447 case TokProperty: {
448 const ProKey &var = pro->getHashStr(tPtr&: tokPtr);
449 const ProString &val = propertyValue(val: var);
450 debugMsg(level: 2, fmt: "property %s => %s", dbgKey(var), dbgStr(val));
451 addStr(str: val, ret, pending, joined);
452 break; }
453 case TokEnvVar: {
454 const ProString &var = pro->getStr(tPtr&: tokPtr);
455 const ProString &val = ProString(m_option->getEnv(var.toQString()));
456 debugMsg(level: 2, fmt: "env var %s => %s", dbgStr(var), dbgStr(val));
457 addStr(str: val, ret, pending, joined);
458 break; }
459 case TokFuncName: {
460 const ProKey &func = pro->getHashStr(tPtr&: tokPtr);
461 debugMsg(level: 2, fmt: "function %s", dbgKey(func));
462 ProStringList val;
463 if (evaluateExpandFunction(function: func, tokPtr, ret: &val) == ReturnError)
464 return ReturnError;
465 addStrList(list: val, tok, ret, pending, joined);
466 break; }
467 default:
468 debugMsg(level: 2, fmt: "evaluated expression => %s", dbgStrList(*ret));
469 tokPtr--;
470 return ReturnTrue;
471 }
472 }
473}
474
475void QMakeEvaluator::skipExpression(const ushort *&pTokPtr)
476{
477 const ushort *tokPtr = pTokPtr;
478 forever {
479 ushort tok = *tokPtr++;
480 switch (tok) {
481 case TokLine:
482 m_current.line = *tokPtr++;
483 break;
484 case TokValueTerminator:
485 case TokFuncTerminator:
486 pTokPtr = tokPtr;
487 return;
488 case TokArgSeparator:
489 break;
490 default:
491 switch (tok & TokMask) {
492 case TokLiteral:
493 case TokEnvVar:
494 skipStr(tokPtr);
495 break;
496 case TokHashLiteral:
497 case TokVariable:
498 case TokProperty:
499 skipHashStr(tokPtr);
500 break;
501 case TokFuncName:
502 skipHashStr(tokPtr);
503 pTokPtr = tokPtr;
504 skipExpression(pTokPtr);
505 tokPtr = pTokPtr;
506 break;
507 default:
508 Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
509 break;
510 }
511 }
512 }
513}
514
515QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
516 ProFile *pro, const ushort *tokPtr)
517{
518 m_current.pro = pro;
519 m_current.line = 0;
520 return visitProBlock(tokPtr);
521}
522
523QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
524 const ushort *tokPtr)
525{
526 traceMsg(fmt: "entering block");
527 ProStringList curr;
528 ProFile *pro = m_current.pro;
529 bool okey = true, or_op = false, invert = false;
530 uint blockLen;
531 while (ushort tok = *tokPtr++) {
532 VisitReturn ret;
533 switch (tok) {
534 case TokLine:
535 m_current.line = *tokPtr++;
536 continue;
537 case TokAssign:
538 case TokAppend:
539 case TokAppendUnique:
540 case TokRemove:
541 case TokReplace:
542 ret = visitProVariable(tok, curr, tokPtr);
543 if (ret == ReturnError)
544 break;
545 curr.clear();
546 continue;
547 case TokBranch:
548 blockLen = getBlockLen(tokPtr);
549 if (m_cumulative) {
550#ifdef PROEVALUATOR_CUMULATIVE
551 if (!okey)
552 m_skipLevel++;
553 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
554 tokPtr += blockLen;
555 blockLen = getBlockLen(tokPtr);
556 if (!okey)
557 m_skipLevel--;
558 else
559 m_skipLevel++;
560 if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
561 ret = visitProBlock(tokPtr);
562 if (okey)
563 m_skipLevel--;
564#endif
565 } else {
566 if (okey) {
567 traceMsg(fmt: "taking 'then' branch");
568 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
569 traceMsg(fmt: "finished 'then' branch");
570 }
571 tokPtr += blockLen;
572 blockLen = getBlockLen(tokPtr);
573 if (!okey) {
574 traceMsg(fmt: "taking 'else' branch");
575 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
576 traceMsg(fmt: "finished 'else' branch");
577 }
578 }
579 tokPtr += blockLen;
580 okey = true, or_op = false; // force next evaluation
581 break;
582 case TokForLoop:
583 if (m_cumulative || okey != or_op) {
584 const ProKey &variable = pro->getHashStr(tPtr&: tokPtr);
585 uint exprLen = getBlockLen(tokPtr);
586 const ushort *exprPtr = tokPtr;
587 tokPtr += exprLen;
588 blockLen = getBlockLen(tokPtr);
589 ret = visitProLoop(variable, exprPtr, tokPtr);
590 } else {
591 skipHashStr(tokPtr);
592 uint exprLen = getBlockLen(tokPtr);
593 tokPtr += exprLen;
594 blockLen = getBlockLen(tokPtr);
595 traceMsg(fmt: "skipped loop");
596 ret = ReturnTrue;
597 }
598 tokPtr += blockLen;
599 okey = true, or_op = false; // force next evaluation
600 break;
601 case TokBypassNesting:
602 blockLen = getBlockLen(tokPtr);
603 if ((m_cumulative || okey != or_op) && blockLen) {
604 ProValueMapStack savedValuemapStack = std::move(m_valuemapStack);
605 m_valuemapStack.clear();
606 m_valuemapStack.splice(position: m_valuemapStack.end(),
607 x&: savedValuemapStack, i: savedValuemapStack.begin());
608 traceMsg(fmt: "visiting nesting-bypassing block");
609 ret = visitProBlock(tokPtr);
610 traceMsg(fmt: "visited nesting-bypassing block");
611 savedValuemapStack.splice(position: savedValuemapStack.begin(),
612 x&: m_valuemapStack, i: m_valuemapStack.begin());
613 m_valuemapStack = std::move(savedValuemapStack);
614 } else {
615 traceMsg(fmt: "skipped nesting-bypassing block");
616 ret = ReturnTrue;
617 }
618 tokPtr += blockLen;
619 okey = true, or_op = false; // force next evaluation
620 break;
621 case TokTestDef:
622 case TokReplaceDef:
623 if (m_cumulative || okey != or_op) {
624 const ProKey &name = pro->getHashStr(tPtr&: tokPtr);
625 blockLen = getBlockLen(tokPtr);
626 visitProFunctionDef(tok, name, tokPtr);
627 traceMsg(fmt: "defined %s function %s",
628 tok == TokTestDef ? "test" : "replace", dbgKey(name));
629 } else {
630 traceMsg(fmt: "skipped function definition");
631 skipHashStr(tokPtr);
632 blockLen = getBlockLen(tokPtr);
633 }
634 tokPtr += blockLen;
635 okey = true, or_op = false; // force next evaluation
636 continue;
637 case TokNot:
638 traceMsg(fmt: "NOT");
639 invert ^= true;
640 continue;
641 case TokAnd:
642 traceMsg(fmt: "AND");
643 or_op = false;
644 continue;
645 case TokOr:
646 traceMsg(fmt: "OR");
647 or_op = true;
648 continue;
649 case TokCondition:
650 if (!m_skipLevel && okey != or_op) {
651 if (curr.size() != 1) {
652 if (!m_cumulative || !curr.isEmpty())
653 evalError(fL1S("Conditional must expand to exactly one word."));
654 okey = false;
655 } else {
656 okey = isActiveConfig(config: curr.at(i: 0).toQStringRef(), regex: true);
657 traceMsg(fmt: "condition %s is %s", dbgStr(curr.at(0)), dbgBool(okey));
658 okey ^= invert;
659 }
660 } else {
661 traceMsg(fmt: "skipped condition %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>");
662 }
663 or_op = !okey; // tentatively force next evaluation
664 invert = false;
665 curr.clear();
666 continue;
667 case TokTestCall:
668 if (!m_skipLevel && okey != or_op) {
669 if (curr.size() != 1) {
670 if (!m_cumulative || !curr.isEmpty())
671 evalError(fL1S("Test name must expand to exactly one word."));
672 skipExpression(pTokPtr&: tokPtr);
673 okey = false;
674 } else {
675 traceMsg(fmt: "evaluating test function %s", dbgStr(curr.at(0)));
676 ret = evaluateConditionalFunction(function: curr.at(i: 0).toKey(), tokPtr);
677 switch (ret) {
678 case ReturnTrue: okey = true; break;
679 case ReturnFalse: okey = false; break;
680 default:
681 traceMsg(fmt: "aborting block, function status: %s", dbgReturn(ret));
682 return ret;
683 }
684 traceMsg(fmt: "test function returned %s", dbgBool(okey));
685 okey ^= invert;
686 }
687 } else if (m_cumulative) {
688#ifdef PROEVALUATOR_CUMULATIVE
689 m_skipLevel++;
690 if (curr.size() != 1)
691 skipExpression(tokPtr);
692 else
693 evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
694 m_skipLevel--;
695#endif
696 } else {
697 skipExpression(pTokPtr&: tokPtr);
698 traceMsg(fmt: "skipped test function %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>");
699 }
700 or_op = !okey; // tentatively force next evaluation
701 invert = false;
702 curr.clear();
703 continue;
704 case TokReturn:
705 m_returnValue = curr;
706 curr.clear();
707 ret = ReturnReturn;
708 goto ctrlstm;
709 case TokBreak:
710 ret = ReturnBreak;
711 goto ctrlstm;
712 case TokNext:
713 ret = ReturnNext;
714 ctrlstm:
715 if (!m_skipLevel && okey != or_op) {
716 traceMsg(fmt: "flow control statement '%s', aborting block", dbgReturn(ret));
717 return ret;
718 }
719 traceMsg(fmt: "skipped flow control statement '%s'", dbgReturn(ret));
720 okey = false, or_op = true; // force next evaluation
721 continue;
722 default: {
723 const ushort *oTokPtr = --tokPtr;
724 ret = evaluateExpression(tokPtr, ret: &curr, joined: false);
725 if (ret == ReturnError || tokPtr != oTokPtr)
726 break;
727 }
728 Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
729 continue;
730 }
731 if (ret != ReturnTrue && ret != ReturnFalse) {
732 traceMsg(fmt: "aborting block, status: %s", dbgReturn(ret));
733 return ret;
734 }
735 }
736 traceMsg(fmt: "leaving block, okey=%s", dbgBool(okey));
737 return returnBool(b: okey);
738}
739
740
741void QMakeEvaluator::visitProFunctionDef(
742 ushort tok, const ProKey &name, const ushort *tokPtr)
743{
744 QHash<ProKey, ProFunctionDef> *hash =
745 (tok == TokTestDef
746 ? &m_functionDefs.testFunctions
747 : &m_functionDefs.replaceFunctions);
748 hash->insert(akey: name, avalue: ProFunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
749}
750
751QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
752 const ProKey &_variable, const ushort *exprPtr, const ushort *tokPtr)
753{
754 VisitReturn ret = ReturnTrue;
755 bool infinite = false;
756 int index = 0;
757 ProKey variable;
758 ProStringList oldVarVal;
759 ProStringList it_list_out;
760 if (expandVariableReferences(tokPtr&: exprPtr, sizeHint: 0, ret: &it_list_out, joined: true) == ReturnError)
761 return ReturnError;
762 ProString it_list = it_list_out.at(i: 0);
763 if (_variable.isEmpty()) {
764 if (it_list != statics.strever) {
765 evalError(fL1S("Invalid loop expression."));
766 return ReturnFalse;
767 }
768 it_list = ProString(statics.strforever);
769 } else {
770 variable = map(var: _variable);
771 oldVarVal = values(variableName: variable);
772 }
773 ProStringList list = values(variableName: it_list.toKey());
774 if (list.isEmpty()) {
775 if (it_list == statics.strforever) {
776 if (m_cumulative) {
777 // The termination conditions wouldn't be evaluated, so we must skip it.
778 traceMsg(fmt: "skipping forever loop in cumulative mode");
779 return ReturnFalse;
780 }
781 infinite = true;
782 } else {
783 const QStringRef &itl = it_list.toQStringRef();
784 int dotdot = itl.indexOf(str: statics.strDotDot);
785 if (dotdot != -1) {
786 bool ok;
787 int start = itl.left(n: dotdot).toInt(ok: &ok);
788 if (ok) {
789 int end = itl.mid(pos: dotdot+2).toInt(ok: &ok);
790 if (ok) {
791 const int absDiff = qAbs(t: end - start);
792 if (m_cumulative && absDiff > 100) {
793 // Such a loop is unlikely to contribute something useful to the
794 // file collection, and may cause considerable delay.
795 traceMsg(fmt: "skipping excessive loop in cumulative mode");
796 return ReturnFalse;
797 }
798 list.reserve(asize: absDiff + 1);
799 if (start < end) {
800 for (int i = start; i <= end; i++)
801 list << ProString(QString::number(i));
802 } else {
803 for (int i = start; i >= end; i--)
804 list << ProString(QString::number(i));
805 }
806 }
807 }
808 }
809 }
810 }
811
812 if (infinite)
813 traceMsg(fmt: "entering infinite loop for %s", dbgKey(variable));
814 else
815 traceMsg(fmt: "entering loop for %s over %s", dbgKey(variable), dbgStrList(list));
816
817 forever {
818 if (infinite) {
819 if (!variable.isEmpty())
820 m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index)));
821 if (++index > 1000) {
822 evalError(fL1S("Ran into infinite loop (> 1000 iterations)."));
823 break;
824 }
825 traceMsg(fmt: "loop iteration %d", index);
826 } else {
827 ProString val;
828 do {
829 if (index >= list.count())
830 goto do_break;
831 val = list.at(i: index++);
832 } while (val.isEmpty()); // stupid, but qmake is like that
833 traceMsg(fmt: "loop iteration %s", dbgStr(val));
834 m_valuemapStack.top()[variable] = ProStringList(val);
835 }
836
837 ret = visitProBlock(tokPtr);
838 switch (ret) {
839 case ReturnTrue:
840 case ReturnFalse:
841 break;
842 case ReturnNext:
843 ret = ReturnTrue;
844 break;
845 case ReturnBreak:
846 ret = ReturnTrue;
847 goto do_break;
848 default:
849 goto do_break;
850 }
851 }
852 do_break:
853
854 traceMsg(fmt: "done looping");
855
856 if (!variable.isEmpty())
857 m_valuemapStack.top()[variable] = oldVarVal;
858 return ret;
859}
860
861QMakeEvaluator::VisitReturn QMakeEvaluator::visitProVariable(
862 ushort tok, const ProStringList &curr, const ushort *&tokPtr)
863{
864 int sizeHint = *tokPtr++;
865
866 if (curr.size() != 1) {
867 skipExpression(pTokPtr&: tokPtr);
868 if (!m_cumulative || !curr.isEmpty())
869 evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
870 return ReturnTrue;
871 }
872 const ProKey &varName = map(var: curr.first());
873
874 if (tok == TokReplace) { // ~=
875 // DEFINES ~= s/a/b/?[gqi]
876
877 ProStringList varVal;
878 if (expandVariableReferences(tokPtr, sizeHint, ret: &varVal, joined: true) == ReturnError)
879 return ReturnError;
880 const QStringRef &val = varVal.at(i: 0).toQStringRef();
881 if (val.length() < 4 || val.at(i: 0) != QLatin1Char('s')) {
882 evalError(fL1S("The ~= operator can handle only the s/// function."));
883 return ReturnTrue;
884 }
885 QChar sep = val.at(i: 1);
886 auto func = val.split(sep, behavior: Qt::KeepEmptyParts);
887 if (func.count() < 3 || func.count() > 4) {
888 evalError(fL1S("The s/// function expects 3 or 4 arguments."));
889 return ReturnTrue;
890 }
891
892 bool global = false, quote = false, case_sense = false;
893 if (func.count() == 4) {
894 global = func[3].indexOf(ch: QLatin1Char('g')) != -1;
895 case_sense = func[3].indexOf(ch: QLatin1Char('i')) == -1;
896 quote = func[3].indexOf(ch: QLatin1Char('q')) != -1;
897 }
898 QString pattern = func[1].toString();
899 QString replace = func[2].toString();
900 if (quote)
901 pattern = QRegExp::escape(str: pattern);
902
903 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
904
905 // We could make a union of modified and unmodified values,
906 // but this will break just as much as it fixes, so leave it as is.
907 replaceInList(varlist: &valuesRef(variableName: varName), regexp, replace, global, tmp&: m_tmp2);
908 debugMsg(level: 2, fmt: "replaced %s with %s", dbgQStr(pattern), dbgQStr(replace));
909 } else {
910 ProStringList varVal;
911 if (expandVariableReferences(tokPtr, sizeHint, ret: &varVal, joined: false) == ReturnError)
912 return ReturnError;
913 switch (tok) {
914 default: // whatever - cannot happen
915 case TokAssign: // =
916 varVal.removeEmpty();
917 // FIXME: add check+warning about accidental value removal.
918 // This may be a bit too noisy, though.
919 m_valuemapStack.top()[varName] = varVal;
920 debugMsg(level: 2, fmt: "assigning");
921 break;
922 case TokAppendUnique: // *=
923 valuesRef(variableName: varName).insertUnique(value: varVal);
924 debugMsg(level: 2, fmt: "appending unique");
925 break;
926 case TokAppend: // +=
927 varVal.removeEmpty();
928 valuesRef(variableName: varName) += varVal;
929 debugMsg(level: 2, fmt: "appending");
930 break;
931 case TokRemove: // -=
932 if (!m_cumulative) {
933 valuesRef(variableName: varName).removeEach(value: varVal);
934 } else {
935 // We are stingy with our values.
936 }
937 debugMsg(level: 2, fmt: "removing");
938 break;
939 }
940 }
941 traceMsg(fmt: "%s := %s", dbgKey(varName), dbgStrList(values(varName)));
942
943 if (varName == statics.strTEMPLATE)
944 setTemplate();
945 else if (varName == statics.strQMAKE_PLATFORM)
946 m_featureRoots = nullptr;
947 else if (varName == statics.strQMAKE_DIR_SEP)
948 m_dirSep = first(variableName: varName);
949 else if (varName == statics.strQMAKESPEC) {
950 if (!values(variableName: varName).isEmpty()) {
951 QString spec = values(variableName: varName).first().toQString();
952 if (IoUtils::isAbsolutePath(fileName: spec)) {
953 m_qmakespec = spec;
954 m_qmakespecName = IoUtils::fileName(fileName: m_qmakespec).toString();
955 m_featureRoots = nullptr;
956 }
957 }
958 }
959#ifdef PROEVALUATOR_FULL
960 else if (varName == statics.strREQUIRES)
961 return checkRequirements(deps: values(variableName: varName));
962#endif
963
964 return ReturnTrue;
965}
966
967void QMakeEvaluator::setTemplate()
968{
969 ProStringList &values = valuesRef(variableName: statics.strTEMPLATE);
970 if (!m_option->user_template.isEmpty()) {
971 // Don't allow override
972 values = ProStringList(ProString(m_option->user_template));
973 } else {
974 if (values.isEmpty())
975 values.append(t: ProString("app"));
976 else
977 values.erase(abegin: values.begin() + 1, aend: values.end());
978 }
979 if (!m_option->user_template_prefix.isEmpty()) {
980 ProString val = values.first();
981 if (!val.startsWith(sub: m_option->user_template_prefix))
982 values = ProStringList(ProString(m_option->user_template_prefix + val));
983 }
984}
985
986#if defined(Q_CC_MSVC)
987static ProString msvcBinDirToQMakeArch(QString subdir)
988{
989 int idx = subdir.indexOf(QLatin1Char('\\'));
990 if (idx == -1)
991 return ProString("x86");
992 subdir.remove(0, idx + 1);
993 idx = subdir.indexOf(QLatin1Char('_'));
994 if (idx >= 0)
995 subdir.remove(0, idx + 1);
996 subdir = subdir.toLower();
997 if (subdir == QLatin1String("amd64"))
998 return ProString("x86_64");
999 // Since 2017 the folder structure from here is HostX64|X86/x64|x86
1000 idx = subdir.indexOf(QLatin1Char('\\'));
1001 if (idx == -1)
1002 return ProString("x86");
1003 subdir.remove(0, idx + 1);
1004 if (subdir == QLatin1String("x64"))
1005 return ProString("x86_64");
1006 return ProString(subdir);
1007}
1008
1009static ProString defaultMsvcArchitecture()
1010{
1011#if defined(Q_OS_WIN64)
1012 return ProString("x86_64");
1013#else
1014 return ProString("x86");
1015#endif
1016}
1017
1018static ProString msvcArchitecture(const QString &vcInstallDir, const QString &pathVar)
1019{
1020 if (vcInstallDir.isEmpty())
1021 return defaultMsvcArchitecture();
1022 QString vcBinDir = vcInstallDir;
1023 if (vcBinDir.endsWith(QLatin1Char('\\')))
1024 vcBinDir.chop(1);
1025 const auto dirs = pathVar.split(QLatin1Char(';'), Qt::SkipEmptyParts);
1026 for (const QString &dir : dirs) {
1027 if (!dir.startsWith(vcBinDir, Qt::CaseInsensitive))
1028 continue;
1029 const ProString arch = msvcBinDirToQMakeArch(dir.mid(vcBinDir.length() + 1));
1030 if (!arch.isEmpty())
1031 return arch;
1032 }
1033 return defaultMsvcArchitecture();
1034}
1035#endif // defined(Q_CC_MSVC)
1036
1037void QMakeEvaluator::loadDefaults()
1038{
1039 ProValueMap &vars = m_valuemapStack.top();
1040
1041 vars[ProKey("DIR_SEPARATOR")] << ProString(m_option->dir_sep);
1042 vars[ProKey("DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep);
1043 vars[ProKey("_DATE_")] << ProString(QDateTime::currentDateTime().toString());
1044 if (!m_option->qmake_abslocation.isEmpty())
1045 vars[ProKey("QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation);
1046 if (!m_option->qmake_args.isEmpty())
1047 vars[ProKey("QMAKE_ARGS")] = ProStringList(m_option->qmake_args);
1048 if (!m_option->qtconf.isEmpty())
1049 vars[ProKey("QMAKE_QTCONF")] = ProString(m_option->qtconf);
1050 vars[ProKey("QMAKE_HOST.cpu_count")] = ProString(QString::number(idealThreadCount()));
1051#if defined(Q_OS_WIN32)
1052 vars[ProKey("QMAKE_HOST.os")] << ProString("Windows");
1053
1054 DWORD name_length = 1024;
1055 wchar_t name[1024];
1056 if (GetComputerName(name, &name_length))
1057 vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name));
1058
1059 vars[ProKey("QMAKE_HOST.version")] << ProString(QSysInfo::kernelVersion());
1060 vars[ProKey("QMAKE_HOST.version_string")] << ProString(QSysInfo::productVersion());
1061
1062 SYSTEM_INFO info;
1063 GetSystemInfo(&info);
1064 ProString archStr;
1065 switch (info.wProcessorArchitecture) {
1066# ifdef PROCESSOR_ARCHITECTURE_AMD64
1067 case PROCESSOR_ARCHITECTURE_AMD64:
1068 archStr = ProString("x86_64");
1069 break;
1070# endif
1071 case PROCESSOR_ARCHITECTURE_INTEL:
1072 archStr = ProString("x86");
1073 break;
1074 case PROCESSOR_ARCHITECTURE_IA64:
1075# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
1076 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
1077# endif
1078 archStr = ProString("IA64");
1079 break;
1080 default:
1081 archStr = ProString("Unknown");
1082 break;
1083 }
1084 vars[ProKey("QMAKE_HOST.arch")] << archStr;
1085
1086# if defined(Q_CC_MSVC) // ### bogus condition, but nobody x-builds for msvc with a different qmake
1087 // Since VS 2017 we need VCToolsInstallDir instead of VCINSTALLDIR
1088 QString vcInstallDir = m_option->getEnv(QLatin1String("VCToolsInstallDir"));
1089 if (vcInstallDir.isEmpty())
1090 vcInstallDir = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
1091 vars[ProKey("QMAKE_TARGET.arch")] = msvcArchitecture(
1092 vcInstallDir,
1093 m_option->getEnv(QLatin1String("PATH")));
1094# endif
1095#elif defined(Q_OS_UNIX)
1096 struct utsname name;
1097 if (uname(name: &name) != -1) {
1098 vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
1099 vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(str: name.nodename));
1100 vars[ProKey("QMAKE_HOST.version")] << ProString(name.release);
1101 vars[ProKey("QMAKE_HOST.version_string")] << ProString(name.version);
1102 vars[ProKey("QMAKE_HOST.arch")] << ProString(name.machine);
1103 }
1104#endif
1105
1106 m_valuemapInited = true;
1107}
1108
1109bool QMakeEvaluator::prepareProject(const QString &inDir)
1110{
1111 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1112 QString superdir;
1113 if (m_option->do_cache) {
1114 QString conffile;
1115 QString cachefile = m_option->cachefile;
1116 if (cachefile.isEmpty()) { //find it as it has not been specified
1117 if (m_outputDir.isEmpty())
1118 goto no_cache;
1119 superdir = m_outputDir;
1120 forever {
1121 QString superfile = superdir + QLatin1String("/.qmake.super");
1122 if (m_vfs->exists(fn: superfile, flags)) {
1123 m_superfile = QDir::cleanPath(path: superfile);
1124 break;
1125 }
1126 QFileInfo qdfi(superdir);
1127 if (qdfi.isRoot()) {
1128 superdir.clear();
1129 break;
1130 }
1131 superdir = qdfi.path();
1132 }
1133 QString sdir = inDir;
1134 QString dir = m_outputDir;
1135 forever {
1136 conffile = sdir + QLatin1String("/.qmake.conf");
1137 if (!m_vfs->exists(fn: conffile, flags))
1138 conffile.clear();
1139 cachefile = dir + QLatin1String("/.qmake.cache");
1140 if (!m_vfs->exists(fn: cachefile, flags))
1141 cachefile.clear();
1142 if (!conffile.isEmpty() || !cachefile.isEmpty()) {
1143 if (dir != sdir)
1144 m_sourceRoot = sdir;
1145 m_buildRoot = dir;
1146 break;
1147 }
1148 if (dir == superdir)
1149 goto no_cache;
1150 QFileInfo qsdfi(sdir);
1151 QFileInfo qdfi(dir);
1152 if (qsdfi.isRoot() || qdfi.isRoot())
1153 goto no_cache;
1154 sdir = qsdfi.path();
1155 dir = qdfi.path();
1156 }
1157 } else {
1158 m_buildRoot = QFileInfo(cachefile).path();
1159 }
1160 m_conffile = QDir::cleanPath(path: conffile);
1161 m_cachefile = QDir::cleanPath(path: cachefile);
1162 }
1163 no_cache:
1164
1165 QString dir = m_outputDir;
1166 forever {
1167 QString stashfile = dir + QLatin1String("/.qmake.stash");
1168 if (dir == (!superdir.isEmpty() ? superdir : m_buildRoot) || m_vfs->exists(fn: stashfile, flags)) {
1169 m_stashfile = QDir::cleanPath(path: stashfile);
1170 break;
1171 }
1172 QFileInfo qdfi(dir);
1173 if (qdfi.isRoot())
1174 break;
1175 dir = qdfi.path();
1176 }
1177
1178 return true;
1179}
1180
1181bool QMakeEvaluator::loadSpecInternal()
1182{
1183 if (evaluateFeatureFile(fileName: QLatin1String("spec_pre.prf")) != ReturnTrue)
1184 return false;
1185 QString spec = m_qmakespec + QLatin1String("/qmake.conf");
1186 if (evaluateFile(fileName: spec, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly) != ReturnTrue) {
1187 evalError(fL1S("Could not read qmake configuration file %1.").arg(a: spec));
1188 return false;
1189 }
1190#ifndef QT_BUILD_QMAKE
1191 // Legacy support for Qt4 default specs
1192# ifdef Q_OS_UNIX
1193 if (m_qmakespec.endsWith(s: QLatin1String("/default-host"))
1194 || m_qmakespec.endsWith(s: QLatin1String("/default"))) {
1195 QString rspec = QFileInfo(m_qmakespec).symLinkTarget();
1196 if (!rspec.isEmpty())
1197 m_qmakespec = QDir::cleanPath(path: QDir(m_qmakespec).absoluteFilePath(fileName: rspec));
1198 }
1199# else
1200 // We can't resolve symlinks as they do on Unix, so configure.exe puts
1201 // the source of the qmake.conf at the end of the default/qmake.conf in
1202 // the QMAKESPEC_ORIGINAL variable.
1203 const ProString &orig_spec = first(ProKey("QMAKESPEC_ORIGINAL"));
1204 if (!orig_spec.isEmpty()) {
1205 QString spec = orig_spec.toQString();
1206 if (IoUtils::isAbsolutePath(spec))
1207 m_qmakespec = spec;
1208 }
1209# endif
1210#endif
1211 valuesRef(variableName: ProKey("QMAKESPEC")) = ProString(m_qmakespec);
1212 m_qmakespecName = IoUtils::fileName(fileName: m_qmakespec).toString();
1213 // This also ensures that m_featureRoots is valid.
1214 if (evaluateFeatureFile(fileName: QLatin1String("spec_post.prf")) != ReturnTrue)
1215 return false;
1216 return true;
1217}
1218
1219bool QMakeEvaluator::loadSpec()
1220{
1221 QString qmakespec = m_option->expandEnvVars(
1222 str: m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
1223
1224 {
1225 QMakeEvaluator evaluator(m_option, m_parser, m_vfs, m_handler);
1226 evaluator.m_sourceRoot = m_sourceRoot;
1227 evaluator.m_buildRoot = m_buildRoot;
1228
1229 if (!m_superfile.isEmpty() && evaluator.evaluateFile(
1230 fileName: m_superfile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly|LoadHidden) != ReturnTrue) {
1231 return false;
1232 }
1233 if (!m_conffile.isEmpty() && evaluator.evaluateFile(
1234 fileName: m_conffile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly|LoadHidden) != ReturnTrue) {
1235 return false;
1236 }
1237 if (!m_cachefile.isEmpty() && evaluator.evaluateFile(
1238 fileName: m_cachefile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly|LoadHidden) != ReturnTrue) {
1239 return false;
1240 }
1241 if (qmakespec.isEmpty()) {
1242 if (!m_hostBuild)
1243 qmakespec = evaluator.first(variableName: ProKey("XQMAKESPEC")).toQString();
1244 if (qmakespec.isEmpty())
1245 qmakespec = evaluator.first(variableName: ProKey("QMAKESPEC")).toQString();
1246 }
1247 m_qmakepath = evaluator.values(variableName: ProKey("QMAKEPATH")).toQStringList();
1248 m_qmakefeatures = evaluator.values(variableName: ProKey("QMAKEFEATURES")).toQStringList();
1249 }
1250
1251 updateMkspecPaths();
1252 if (qmakespec.isEmpty())
1253 qmakespec = propertyValue(val: ProKey(m_hostBuild ? "QMAKE_SPEC" : "QMAKE_XSPEC")).toQString();
1254#ifndef QT_BUILD_QMAKE
1255 // Legacy support for Qt4 qmake in Qt Creator, etc.
1256 if (qmakespec.isEmpty())
1257 qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
1258#endif
1259 if (IoUtils::isRelativePath(fileName: qmakespec)) {
1260 for (const QString &root : qAsConst(t&: m_mkspecPaths)) {
1261 QString mkspec = root + QLatin1Char('/') + qmakespec;
1262 if (IoUtils::exists(fileName: mkspec)) {
1263 qmakespec = mkspec;
1264 goto cool;
1265 }
1266 }
1267 evalError(fL1S("Could not find qmake spec '%1'.").arg(a: qmakespec));
1268 return false;
1269 }
1270 cool:
1271 m_qmakespec = QDir::cleanPath(path: qmakespec);
1272
1273 if (!m_superfile.isEmpty()) {
1274 valuesRef(variableName: ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
1275 if (evaluateFile(
1276 fileName: m_superfile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly|LoadHidden) != ReturnTrue)
1277 return false;
1278 }
1279 if (!loadSpecInternal())
1280 return false;
1281 if (!m_conffile.isEmpty()) {
1282 valuesRef(variableName: ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
1283 if (evaluateFile(
1284 fileName: m_conffile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly) != ReturnTrue)
1285 return false;
1286 }
1287 if (!m_cachefile.isEmpty()) {
1288 valuesRef(variableName: ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
1289 if (evaluateFile(
1290 fileName: m_cachefile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly) != ReturnTrue)
1291 return false;
1292 }
1293 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1294 if (!m_stashfile.isEmpty() && m_vfs->exists(fn: m_stashfile, flags)) {
1295 valuesRef(variableName: ProKey("_QMAKE_STASH_")) << ProString(m_stashfile);
1296 if (evaluateFile(
1297 fileName: m_stashfile, type: QMakeHandler::EvalConfigFile, flags: LoadProOnly) != ReturnTrue)
1298 return false;
1299 }
1300 return true;
1301}
1302
1303void QMakeEvaluator::setupProject()
1304{
1305 setTemplate();
1306 ProValueMap &vars = m_valuemapStack.top();
1307 int proFile = currentFileId();
1308 vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName()).setSource(proFile);
1309 vars[ProKey("_PRO_FILE_")] << ProString(currentFileName()).setSource(proFile);
1310 vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory()).setSource(proFile);
1311 vars[ProKey("OUT_PWD")] << ProString(m_outputDir).setSource(proFile);
1312}
1313
1314void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where)
1315{
1316 if (!cmds.isEmpty()) {
1317 ProFile *pro = m_parser->parsedProBlock(contents: QStringRef(&cmds), id: 0, name: where, line: -1);
1318 if (pro->isOk()) {
1319 m_locationStack.push(t: m_current);
1320 visitProBlock(pro, tokPtr: pro->tokPtr());
1321 m_current = m_locationStack.pop();
1322 }
1323 pro->deref();
1324 }
1325}
1326
1327void QMakeEvaluator::applyExtraConfigs()
1328{
1329 if (m_extraConfigs.isEmpty())
1330 return;
1331
1332 evaluateCommand(fL1S("CONFIG += ") + m_extraConfigs.join(sep: QLatin1Char(' ')), fL1S("(extra configs)"));
1333}
1334
1335QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConfigFeatures()
1336{
1337 QSet<QString> processed;
1338 forever {
1339 bool finished = true;
1340 ProStringList configs = values(variableName: statics.strCONFIG);
1341 for (int i = configs.size() - 1; i >= 0; --i) {
1342 ProStringRoUser u1(configs.at(i), m_tmp1);
1343 QString config = u1.str().toLower();
1344 if (!processed.contains(value: config)) {
1345 config.detach();
1346 processed.insert(value: config);
1347 VisitReturn vr = evaluateFeatureFile(fileName: config, silent: true);
1348 if (vr == ReturnError && !m_cumulative)
1349 return vr;
1350 if (vr == ReturnTrue) {
1351 finished = false;
1352 break;
1353 }
1354 }
1355 }
1356 if (finished)
1357 break;
1358 }
1359 return ReturnTrue;
1360}
1361
1362QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
1363 ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags)
1364{
1365 if (!m_cumulative && !pro->isOk())
1366 return ReturnFalse;
1367
1368 if (flags & LoadPreFiles) {
1369 if (!prepareProject(inDir: pro->directoryName()))
1370 return ReturnFalse;
1371
1372 m_hostBuild = pro->isHostBuild();
1373
1374#ifdef PROEVALUATOR_THREAD_SAFE
1375 m_option->mutex.lock();
1376#endif
1377 QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_stashfile, m_hostBuild)];
1378 if (!*baseEnvPtr)
1379 *baseEnvPtr = new QMakeBaseEnv;
1380 QMakeBaseEnv *baseEnv = *baseEnvPtr;
1381
1382#ifdef PROEVALUATOR_THREAD_SAFE
1383 QMutexLocker locker(&baseEnv->mutex);
1384 m_option->mutex.unlock();
1385 if (baseEnv->inProgress) {
1386 QThreadPool::globalInstance()->releaseThread();
1387 baseEnv->cond.wait(&baseEnv->mutex);
1388 QThreadPool::globalInstance()->reserveThread();
1389 if (!baseEnv->isOk)
1390 return ReturnFalse;
1391 } else
1392#endif
1393 if (!baseEnv->evaluator) {
1394#ifdef PROEVALUATOR_THREAD_SAFE
1395 baseEnv->inProgress = true;
1396 locker.unlock();
1397#endif
1398
1399 QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_vfs, m_handler);
1400 baseEnv->evaluator = baseEval;
1401 baseEval->m_superfile = m_superfile;
1402 baseEval->m_conffile = m_conffile;
1403 baseEval->m_cachefile = m_cachefile;
1404 baseEval->m_stashfile = m_stashfile;
1405 baseEval->m_sourceRoot = m_sourceRoot;
1406 baseEval->m_buildRoot = m_buildRoot;
1407 baseEval->m_hostBuild = m_hostBuild;
1408 bool ok = baseEval->loadSpec();
1409
1410#ifdef PROEVALUATOR_THREAD_SAFE
1411 locker.relock();
1412 baseEnv->isOk = ok;
1413 baseEnv->inProgress = false;
1414 baseEnv->cond.wakeAll();
1415#endif
1416
1417 if (!ok)
1418 return ReturnFalse;
1419 }
1420#ifdef PROEVALUATOR_THREAD_SAFE
1421 else if (!baseEnv->isOk)
1422 return ReturnFalse;
1423#endif
1424
1425 initFrom(other: baseEnv->evaluator);
1426 } else {
1427 if (!m_valuemapInited)
1428 loadDefaults();
1429 }
1430
1431 VisitReturn vr;
1432
1433 m_handler->aboutToEval(parent: currentProFile(), proFile: pro, type);
1434 m_profileStack.push(t: pro);
1435 valuesRef(variableName: ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1436 if (flags & LoadPreFiles) {
1437 setupProject();
1438
1439 if (!m_option->extra_cmds[QMakeEvalEarly].isEmpty())
1440 evaluateCommand(cmds: m_option->extra_cmds[QMakeEvalEarly], fL1S("(command line -early)"));
1441
1442 for (ProValueMap::ConstIterator it = m_extraVars.constBegin();
1443 it != m_extraVars.constEnd(); ++it)
1444 m_valuemapStack.front().insert(akey: it.key(), avalue: it.value());
1445
1446 // In case default_pre needs to make decisions based on the current
1447 // build pass configuration.
1448 applyExtraConfigs();
1449
1450 if ((vr = evaluateFeatureFile(fileName: QLatin1String("default_pre.prf"))) == ReturnError)
1451 goto failed;
1452
1453 if (!m_option->extra_cmds[QMakeEvalBefore].isEmpty()) {
1454 evaluateCommand(cmds: m_option->extra_cmds[QMakeEvalBefore], fL1S("(command line)"));
1455
1456 // Again, after user configs, to override them
1457 applyExtraConfigs();
1458 }
1459 }
1460
1461 debugMsg(level: 1, fmt: "visiting file %s", qPrintable(pro->fileName()));
1462 if ((vr = visitProBlock(pro, tokPtr: pro->tokPtr())) == ReturnError)
1463 goto failed;
1464 debugMsg(level: 1, fmt: "done visiting file %s", qPrintable(pro->fileName()));
1465
1466 if (flags & LoadPostFiles) {
1467 evaluateCommand(cmds: m_option->extra_cmds[QMakeEvalAfter], fL1S("(command line -after)"));
1468
1469 // Again, to ensure the project does not mess with us.
1470 // Specifically, do not allow a project to override debug/release within a
1471 // debug_and_release build pass - it's too late for that at this point anyway.
1472 applyExtraConfigs();
1473
1474 if ((vr = evaluateFeatureFile(fileName: QLatin1String("default_post.prf"))) == ReturnError)
1475 goto failed;
1476
1477 if (!m_option->extra_cmds[QMakeEvalLate].isEmpty())
1478 evaluateCommand(cmds: m_option->extra_cmds[QMakeEvalLate], fL1S("(command line -late)"));
1479
1480 if ((vr = evaluateConfigFeatures()) == ReturnError)
1481 goto failed;
1482 }
1483 vr = ReturnTrue;
1484 failed:
1485 m_profileStack.pop();
1486 valuesRef(variableName: ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1487 m_handler->doneWithEval(parent: currentProFile());
1488
1489 return vr;
1490}
1491
1492
1493void QMakeEvaluator::updateMkspecPaths()
1494{
1495 QStringList ret;
1496 const QString concat = QLatin1String("/mkspecs");
1497
1498 const auto paths = m_option->getPathListEnv(var: QLatin1String("QMAKEPATH"));
1499 for (const QString &it : paths)
1500 ret << it + concat;
1501
1502 for (const QString &it : qAsConst(t&: m_qmakepath))
1503 ret << it + concat;
1504
1505 if (!m_buildRoot.isEmpty())
1506 ret << m_buildRoot + concat;
1507 if (!m_sourceRoot.isEmpty())
1508 ret << m_sourceRoot + concat;
1509
1510 ret << m_option->propertyValue(name: ProKey("QT_HOST_DATA/get")) + concat;
1511 ret << m_option->propertyValue(name: ProKey("QT_HOST_DATA/src")) + concat;
1512
1513 ret.removeDuplicates();
1514 m_mkspecPaths = ret;
1515}
1516
1517void QMakeEvaluator::updateFeaturePaths()
1518{
1519 QString mkspecs_concat = QLatin1String("/mkspecs");
1520 QString features_concat = QLatin1String("/features/");
1521
1522 QStringList feature_roots;
1523
1524 feature_roots += m_option->getPathListEnv(var: QLatin1String("QMAKEFEATURES"));
1525 feature_roots += m_qmakefeatures;
1526 feature_roots += m_option->splitPathList(
1527 value: m_option->propertyValue(name: ProKey("QMAKEFEATURES")).toQString());
1528
1529 QStringList feature_bases;
1530 if (!m_buildRoot.isEmpty()) {
1531 feature_bases << m_buildRoot + mkspecs_concat;
1532 feature_bases << m_buildRoot;
1533 }
1534 if (!m_sourceRoot.isEmpty()) {
1535 feature_bases << m_sourceRoot + mkspecs_concat;
1536 feature_bases << m_sourceRoot;
1537 }
1538
1539 const auto items = m_option->getPathListEnv(var: QLatin1String("QMAKEPATH"));
1540 for (const QString &item : items)
1541 feature_bases << (item + mkspecs_concat);
1542
1543 for (const QString &item : qAsConst(t&: m_qmakepath))
1544 feature_bases << (item + mkspecs_concat);
1545
1546 if (!m_qmakespec.isEmpty()) {
1547 // The spec is already platform-dependent, so no subdirs here.
1548 feature_roots << (m_qmakespec + features_concat);
1549
1550 // Also check directly under the root directory of the mkspecs collection
1551 QDir specdir(m_qmakespec);
1552 while (!specdir.isRoot() && specdir.cdUp()) {
1553 const QString specpath = specdir.path();
1554 if (specpath.endsWith(s: mkspecs_concat)) {
1555 if (IoUtils::exists(fileName: specpath + features_concat))
1556 feature_bases << specpath;
1557 break;
1558 }
1559 }
1560 }
1561
1562 feature_bases << (m_option->propertyValue(name: ProKey("QT_HOST_DATA/get")) + mkspecs_concat);
1563 feature_bases << (m_option->propertyValue(name: ProKey("QT_HOST_DATA/src")) + mkspecs_concat);
1564
1565 for (const QString &fb : qAsConst(t&: feature_bases)) {
1566 const auto sfxs = values(variableName: ProKey("QMAKE_PLATFORM"));
1567 for (const ProString &sfx : sfxs)
1568 feature_roots << (fb + features_concat + sfx + QLatin1Char('/'));
1569 feature_roots << (fb + features_concat);
1570 }
1571
1572 for (int i = 0; i < feature_roots.count(); ++i)
1573 if (!feature_roots.at(i).endsWith(c: QLatin1Char('/')))
1574 feature_roots[i].append(c: QLatin1Char('/'));
1575
1576 feature_roots.removeDuplicates();
1577
1578 QStringList ret;
1579 for (const QString &root : qAsConst(t&: feature_roots))
1580 if (IoUtils::exists(fileName: root))
1581 ret << root;
1582 m_featureRoots = new QMakeFeatureRoots(ret);
1583}
1584
1585ProString QMakeEvaluator::propertyValue(const ProKey &name) const
1586{
1587 if (name == QLatin1String("QMAKE_MKSPECS"))
1588 return ProString(m_mkspecPaths.join(sep: m_option->dirlist_sep));
1589 ProString ret = m_option->propertyValue(name);
1590// if (ret.isNull())
1591// evalError(fL1S("Querying unknown property %1").arg(name.toQStringView()));
1592 return ret;
1593}
1594
1595ProFile *QMakeEvaluator::currentProFile() const
1596{
1597 if (m_profileStack.count() > 0)
1598 return m_profileStack.top();
1599 return nullptr;
1600}
1601
1602int QMakeEvaluator::currentFileId() const
1603{
1604 ProFile *pro = currentProFile();
1605 if (pro)
1606 return pro->id();
1607 return 0;
1608}
1609
1610QString QMakeEvaluator::currentFileName() const
1611{
1612 ProFile *pro = currentProFile();
1613 if (pro)
1614 return pro->fileName();
1615 return QString();
1616}
1617
1618QString QMakeEvaluator::currentDirectory() const
1619{
1620 ProFile *pro = currentProFile();
1621 if (pro)
1622 return pro->directoryName();
1623 return QString();
1624}
1625
1626bool QMakeEvaluator::isActiveConfig(const QStringRef &config, bool regex)
1627{
1628 // magic types for easy flipping
1629 if (config == statics.strtrue)
1630 return true;
1631 if (config == statics.strfalse)
1632 return false;
1633
1634 if (config == statics.strhost_build)
1635 return m_hostBuild;
1636
1637 if (regex && (config.contains(c: QLatin1Char('*')) || config.contains(c: QLatin1Char('?')))) {
1638 QRegExp re(config.toString(), Qt::CaseSensitive, QRegExp::Wildcard);
1639
1640 // mkspecs
1641 if (re.exactMatch(str: m_qmakespecName))
1642 return true;
1643
1644 // CONFIG variable
1645 const auto configValues = values(variableName: statics.strCONFIG);
1646 for (const ProString &configValue : configValues) {
1647 ProStringRoUser u1(configValue, m_tmp[m_toggle ^= 1]);
1648 if (re.exactMatch(str: u1.str()))
1649 return true;
1650 }
1651 } else {
1652 // mkspecs
1653 if (m_qmakespecName == config)
1654 return true;
1655
1656 // CONFIG variable
1657 if (values(variableName: statics.strCONFIG).contains(str: config))
1658 return true;
1659 }
1660
1661 return false;
1662}
1663
1664QMakeEvaluator::VisitReturn QMakeEvaluator::expandVariableReferences(
1665 const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined)
1666{
1667 ret->reserve(asize: sizeHint);
1668 forever {
1669 if (evaluateExpression(tokPtr, ret, joined) == ReturnError)
1670 return ReturnError;
1671 switch (*tokPtr) {
1672 case TokValueTerminator:
1673 case TokFuncTerminator:
1674 tokPtr++;
1675 return ReturnTrue;
1676 case TokArgSeparator:
1677 if (joined) {
1678 tokPtr++;
1679 continue;
1680 }
1681 Q_FALLTHROUGH();
1682 default:
1683 Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
1684 break;
1685 }
1686 }
1687}
1688
1689QMakeEvaluator::VisitReturn QMakeEvaluator::prepareFunctionArgs(
1690 const ushort *&tokPtr, QList<ProStringList> *ret)
1691{
1692 if (*tokPtr != TokFuncTerminator) {
1693 for (;; tokPtr++) {
1694 ProStringList arg;
1695 if (evaluateExpression(tokPtr, ret: &arg, joined: false) == ReturnError)
1696 return ReturnError;
1697 *ret << arg;
1698 if (*tokPtr == TokFuncTerminator)
1699 break;
1700 Q_ASSERT(*tokPtr == TokArgSeparator);
1701 }
1702 }
1703 tokPtr++;
1704 return ReturnTrue;
1705}
1706
1707QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFunction(
1708 const ProFunctionDef &func, const QList<ProStringList> &argumentsList, ProStringList *ret)
1709{
1710 VisitReturn vr;
1711
1712 if (m_valuemapStack.size() >= 100) {
1713 evalError(fL1S("Ran into infinite recursion (depth > 100)."));
1714 vr = ReturnError;
1715 } else {
1716 m_valuemapStack.push(t: ProValueMap());
1717 m_locationStack.push(t: m_current);
1718
1719 ProStringList args;
1720 for (int i = 0; i < argumentsList.count(); ++i) {
1721 args += argumentsList[i];
1722 m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i];
1723 }
1724 m_valuemapStack.top()[statics.strARGS] = args;
1725 m_valuemapStack.top()[statics.strARGC] = ProStringList(ProString(QString::number(argumentsList.count())));
1726 vr = visitProBlock(pro: func.pro(), tokPtr: func.tokPtr());
1727 if (vr == ReturnReturn)
1728 vr = ReturnTrue;
1729 if (vr == ReturnTrue)
1730 *ret = m_returnValue;
1731 m_returnValue.clear();
1732
1733 m_current = m_locationStack.pop();
1734 m_valuemapStack.pop();
1735 }
1736 return vr;
1737}
1738
1739QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
1740 const ProFunctionDef &func, const QList<ProStringList> &argumentsList,
1741 const ProString &function)
1742{
1743 ProStringList ret;
1744 VisitReturn vr = evaluateFunction(func, argumentsList, ret: &ret);
1745 if (vr == ReturnTrue) {
1746 if (ret.isEmpty())
1747 return ReturnTrue;
1748 if (ret.at(i: 0) != statics.strfalse) {
1749 if (ret.at(i: 0) == statics.strtrue)
1750 return ReturnTrue;
1751 bool ok;
1752 int val = ret.at(i: 0).toInt(ok: &ok);
1753 if (ok) {
1754 if (val)
1755 return ReturnTrue;
1756 } else {
1757 ProStringRoUser u1(function, m_tmp1);
1758 evalError(fL1S("Unexpected return value from test '%1': %2.")
1759 .arg(args&: u1.str(), args: ret.join(sep: QLatin1String(" :: "))));
1760 }
1761 }
1762 return ReturnFalse;
1763 }
1764 return vr;
1765}
1766
1767QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
1768 const ProKey &func, const ushort *&tokPtr)
1769{
1770 auto adef = statics.functions.constFind(akey: func);
1771 if (adef != statics.functions.constEnd()) {
1772 //why don't the builtin functions just use args_list? --Sam
1773 ProStringList args;
1774 if (expandVariableReferences(tokPtr, sizeHint: 5, ret: &args, joined: true) == ReturnError)
1775 return ReturnError;
1776 return evaluateBuiltinConditional(adef: *adef, function: func, args);
1777 }
1778
1779 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1780 m_functionDefs.testFunctions.constFind(akey: func);
1781 if (it != m_functionDefs.testFunctions.constEnd()) {
1782 QList<ProStringList> args;
1783 if (prepareFunctionArgs(tokPtr, ret: &args) == ReturnError)
1784 return ReturnError;
1785 traceMsg(fmt: "calling %s(%s)", dbgKey(func), dbgStrListList(args));
1786 return evaluateBoolFunction(func: *it, argumentsList: args, function: func);
1787 }
1788
1789 skipExpression(pTokPtr&: tokPtr);
1790 evalError(fL1S("'%1' is not a recognized test function.").arg(a: func.toQStringView()));
1791 return ReturnFalse;
1792}
1793
1794QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction(
1795 const ProKey &func, const ushort *&tokPtr, ProStringList *ret)
1796{
1797 auto adef = statics.expands.constFind(akey: func);
1798 if (adef != statics.expands.constEnd()) {
1799 //why don't the builtin functions just use args_list? --Sam
1800 ProStringList args;
1801 if (expandVariableReferences(tokPtr, sizeHint: 5, ret: &args, joined: true) == ReturnError)
1802 return ReturnError;
1803 return evaluateBuiltinExpand(adef: *adef, function: func, args, ret&: *ret);
1804 }
1805
1806 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1807 m_functionDefs.replaceFunctions.constFind(akey: func);
1808 if (it != m_functionDefs.replaceFunctions.constEnd()) {
1809 QList<ProStringList> args;
1810 if (prepareFunctionArgs(tokPtr, ret: &args) == ReturnError)
1811 return ReturnError;
1812 traceMsg(fmt: "calling $$%s(%s)", dbgKey(func), dbgStrListList(args));
1813 return evaluateFunction(func: *it, argumentsList: args, ret);
1814 }
1815
1816 skipExpression(pTokPtr&: tokPtr);
1817 evalError(fL1S("'%1' is not a recognized replace function.").arg(a: func.toQStringView()));
1818 return ReturnFalse;
1819}
1820
1821QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditional(
1822 const QStringRef &cond, const QString &where, int line)
1823{
1824 VisitReturn ret = ReturnFalse;
1825 ProFile *pro = m_parser->parsedProBlock(contents: cond, id: 0, name: where, line, grammar: QMakeParser::TestGrammar);
1826 if (pro->isOk()) {
1827 m_locationStack.push(t: m_current);
1828 ret = visitProBlock(pro, tokPtr: pro->tokPtr());
1829 m_current = m_locationStack.pop();
1830 }
1831 pro->deref();
1832 return ret;
1833}
1834
1835#ifdef PROEVALUATOR_FULL
1836QMakeEvaluator::VisitReturn QMakeEvaluator::checkRequirements(const ProStringList &deps)
1837{
1838 ProStringList &failed = valuesRef(variableName: ProKey("QMAKE_FAILED_REQUIREMENTS"));
1839 for (const ProString &dep : deps) {
1840 VisitReturn vr = evaluateConditional(cond: dep.toQStringRef(), where: m_current.pro->fileName(), line: m_current.line);
1841 if (vr == ReturnError)
1842 return ReturnError;
1843 if (vr != ReturnTrue)
1844 failed << dep;
1845 }
1846 return ReturnTrue;
1847}
1848#endif
1849
1850static bool isFunctParam(const ProKey &variableName)
1851{
1852 const int len = variableName.size();
1853 const QChar *data = variableName.constData();
1854 for (int i = 0; i < len; i++) {
1855 ushort c = data[i].unicode();
1856 if (c < '0' || c > '9')
1857 return false;
1858 }
1859 return true;
1860}
1861
1862ProValueMap *QMakeEvaluator::findValues(const ProKey &variableName, ProValueMap::Iterator *rit)
1863{
1864 ProValueMapStack::iterator vmi = m_valuemapStack.end();
1865 for (bool first = true; ; first = false) {
1866 --vmi;
1867 ProValueMap::Iterator it = (*vmi).find(akey: variableName);
1868 if (it != (*vmi).end()) {
1869 if (it->constBegin() == statics.fakeValue.constBegin())
1870 break;
1871 *rit = it;
1872 return &(*vmi);
1873 }
1874 if (vmi == m_valuemapStack.begin())
1875 break;
1876 if (first && isFunctParam(variableName))
1877 break;
1878 }
1879 return nullptr;
1880}
1881
1882ProStringList &QMakeEvaluator::valuesRef(const ProKey &variableName)
1883{
1884 ProValueMap::Iterator it = m_valuemapStack.top().find(akey: variableName);
1885 if (it != m_valuemapStack.top().end()) {
1886 if (it->constBegin() == statics.fakeValue.constBegin())
1887 it->clear();
1888 return *it;
1889 }
1890 if (!isFunctParam(variableName)) {
1891 ProValueMapStack::iterator vmi = m_valuemapStack.end();
1892 if (--vmi != m_valuemapStack.begin()) {
1893 do {
1894 --vmi;
1895 ProValueMap::ConstIterator it = (*vmi).constFind(akey: variableName);
1896 if (it != (*vmi).constEnd()) {
1897 ProStringList &ret = m_valuemapStack.top()[variableName];
1898 if (it->constBegin() != statics.fakeValue.constBegin())
1899 ret = *it;
1900 return ret;
1901 }
1902 } while (vmi != m_valuemapStack.begin());
1903 }
1904 }
1905 return m_valuemapStack.top()[variableName];
1906}
1907
1908ProStringList QMakeEvaluator::values(const ProKey &variableName) const
1909{
1910 ProValueMapStack::const_iterator vmi = m_valuemapStack.cend();
1911 for (bool first = true; ; first = false) {
1912 --vmi;
1913 ProValueMap::ConstIterator it = (*vmi).constFind(akey: variableName);
1914 if (it != (*vmi).constEnd()) {
1915 if (it->constBegin() == statics.fakeValue.constBegin())
1916 break;
1917 return *it;
1918 }
1919 if (vmi == m_valuemapStack.cbegin())
1920 break;
1921 if (first && isFunctParam(variableName))
1922 break;
1923 }
1924 return ProStringList();
1925}
1926
1927ProString QMakeEvaluator::first(const ProKey &variableName) const
1928{
1929 const ProStringList &vals = values(variableName);
1930 if (!vals.isEmpty())
1931 return vals.first();
1932 return ProString();
1933}
1934
1935QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
1936 const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
1937{
1938 QMakeParser::ParseFlags pflags = QMakeParser::ParseUseCache;
1939 if (!(flags & LoadSilent))
1940 pflags |= QMakeParser::ParseReportMissing;
1941 if (ProFile *pro = m_parser->parsedProFile(fileName, flags: pflags)) {
1942 m_locationStack.push(t: m_current);
1943 VisitReturn ok = visitProFile(pro, type, flags);
1944 m_current = m_locationStack.pop();
1945 pro->deref();
1946 if (ok == ReturnTrue && !(flags & LoadHidden)) {
1947 ProStringList &iif = m_valuemapStack.front()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
1948 ProString ifn(fileName);
1949 if (!iif.contains(str: ifn))
1950 iif << ifn;
1951 }
1952 return ok;
1953 } else {
1954 return ReturnFalse;
1955 }
1956}
1957
1958QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileChecked(
1959 const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
1960{
1961 if (fileName.isEmpty())
1962 return ReturnFalse;
1963 const QMakeEvaluator *ref = this;
1964 do {
1965 for (const ProFile *pf : ref->m_profileStack)
1966 if (pf->fileName() == fileName) {
1967 evalError(fL1S("Circular inclusion of %1.").arg(a: fileName));
1968 return ReturnFalse;
1969 }
1970 } while ((ref = ref->m_caller));
1971 return evaluateFile(fileName, type, flags);
1972}
1973
1974QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
1975 const QString &fileName, bool silent)
1976{
1977 QString fn = fileName;
1978 if (!fn.endsWith(s: QLatin1String(".prf")))
1979 fn += QLatin1String(".prf");
1980
1981 if (!m_featureRoots)
1982 updateFeaturePaths();
1983#ifdef PROEVALUATOR_THREAD_SAFE
1984 m_featureRoots->mutex.lock();
1985#endif
1986 QString currFn = currentFileName();
1987 if (IoUtils::fileName(fileName: currFn) != IoUtils::fileName(fileName: fn))
1988 currFn.clear();
1989 // Null values cannot regularly exist in the hash, so they indicate that the value still
1990 // needs to be determined. Failed lookups are represented via non-null empty strings.
1991 QString *fnp = &m_featureRoots->cache[qMakePair(x: fn, y: currFn)];
1992 if (fnp->isNull()) {
1993#ifdef QMAKE_OVERRIDE_PRFS
1994 {
1995 QString ovrfn(QLatin1String(":/qmake/override_features/") + fn);
1996 if (QFileInfo::exists(ovrfn)) {
1997 fn = ovrfn;
1998 goto cool;
1999 }
2000 }
2001#endif
2002 {
2003 int start_root = 0;
2004 const QStringList &paths = m_featureRoots->paths;
2005 if (!currFn.isEmpty()) {
2006 QStringRef currPath = IoUtils::pathName(fileName: currFn);
2007 for (int root = 0; root < paths.size(); ++root)
2008 if (currPath == paths.at(i: root)) {
2009 start_root = root + 1;
2010 break;
2011 }
2012 }
2013 for (int root = start_root; root < paths.size(); ++root) {
2014 QString fname = paths.at(i: root) + fn;
2015 if (IoUtils::exists(fileName: fname)) {
2016 fn = fname;
2017 goto cool;
2018 }
2019 }
2020 }
2021#ifdef QMAKE_BUILTIN_PRFS
2022 fn.prepend(QLatin1String(":/qmake/features/"));
2023 if (QFileInfo::exists(fn))
2024 goto cool;
2025#endif
2026 fn = QLatin1String(""); // Indicate failed lookup. See comment above.
2027
2028 cool:
2029 *fnp = fn;
2030 } else {
2031 fn = *fnp;
2032 }
2033#ifdef PROEVALUATOR_THREAD_SAFE
2034 m_featureRoots->mutex.unlock();
2035#endif
2036 if (fn.isEmpty()) {
2037 if (!silent)
2038 evalError(fL1S("Cannot find feature %1").arg(a: fileName));
2039 return ReturnFalse;
2040 }
2041 ProStringList &already = valuesRef(variableName: ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
2042 ProString afn(fn);
2043 if (already.contains(str: afn)) {
2044 if (!silent)
2045 languageWarning(fL1S("Feature %1 already included").arg(a: fileName));
2046 return ReturnTrue;
2047 }
2048 already.append(t: afn);
2049
2050#ifdef PROEVALUATOR_CUMULATIVE
2051 bool cumulative = m_cumulative;
2052 m_cumulative = false;
2053#endif
2054
2055 // The path is fully normalized already.
2056 VisitReturn ok = evaluateFile(fileName: fn, type: QMakeHandler::EvalFeatureFile, flags: LoadProOnly);
2057
2058#ifdef PROEVALUATOR_CUMULATIVE
2059 m_cumulative = cumulative;
2060#endif
2061 return ok;
2062}
2063
2064QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto(
2065 const QString &fileName, ProValueMap *values, LoadFlags flags)
2066{
2067 QMakeEvaluator visitor(m_option, m_parser, m_vfs, m_handler);
2068 visitor.m_caller = this;
2069 visitor.m_outputDir = m_outputDir;
2070 visitor.m_featureRoots = m_featureRoots;
2071 VisitReturn ret = visitor.evaluateFileChecked(fileName, type: QMakeHandler::EvalAuxFile, flags);
2072 if (ret != ReturnTrue)
2073 return ret;
2074 *values = visitor.m_valuemapStack.top();
2075 ProKey qiif("QMAKE_INTERNAL_INCLUDED_FILES");
2076 ProStringList &iif = m_valuemapStack.front()[qiif];
2077 const auto ifns = values->value(akey: qiif);
2078 for (const ProString &ifn : ifns)
2079 if (!iif.contains(str: ifn))
2080 iif << ifn;
2081 return ReturnTrue;
2082}
2083
2084void QMakeEvaluator::message(int type, const QString &msg) const
2085{
2086 if (!m_skipLevel)
2087 m_handler->message(type: type | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0), msg,
2088 fileName: m_current.line ? m_current.pro->fileName() : QString(),
2089 lineNo: m_current.line != 0xffff ? m_current.line : -1);
2090}
2091
2092#ifdef PROEVALUATOR_DEBUG
2093void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const
2094{
2095 va_list ap;
2096
2097 if (level <= m_debugLevel) {
2098 fprintf(stderr, format: "DEBUG %d: ", level);
2099 va_start(ap, fmt);
2100 vfprintf(stderr, format: fmt, arg: ap);
2101 va_end(ap);
2102 fputc(c: '\n', stderr);
2103 }
2104}
2105
2106void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const
2107{
2108 va_list ap;
2109
2110 if (!m_current.pro)
2111 fprintf(stderr, format: "DEBUG 1: ");
2112 else if (m_current.line <= 0)
2113 fprintf(stderr, format: "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName()));
2114 else
2115 fprintf(stderr, format: "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line);
2116 va_start(ap, fmt);
2117 vfprintf(stderr, format: fmt, arg: ap);
2118 va_end(ap);
2119 fputc(c: '\n', stderr);
2120}
2121
2122QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote)
2123{
2124 QString ret;
2125 ret.reserve(asize: val.size() + 2);
2126 const QChar *chars = val.constData();
2127 bool quote = forceQuote || val.isEmpty();
2128 for (int i = 0, l = val.size(); i < l; i++) {
2129 QChar c = chars[i];
2130 ushort uc = c.unicode();
2131 if (uc < 32) {
2132 switch (uc) {
2133 case '\r':
2134 ret += QLatin1String("\\r");
2135 break;
2136 case '\n':
2137 ret += QLatin1String("\\n");
2138 break;
2139 case '\t':
2140 ret += QLatin1String("\\t");
2141 break;
2142 default:
2143 ret += QString::fromLatin1(str: "\\x%1").arg(a: uc, fieldWidth: 2, base: 16, fillChar: QLatin1Char('0'));
2144 break;
2145 }
2146 } else {
2147 switch (uc) {
2148 case '\\':
2149 ret += QLatin1String("\\\\");
2150 break;
2151 case '"':
2152 ret += QLatin1String("\\\"");
2153 break;
2154 case '\'':
2155 ret += QLatin1String("\\'");
2156 break;
2157 case 32:
2158 quote = true;
2159 Q_FALLTHROUGH();
2160 default:
2161 ret += c;
2162 break;
2163 }
2164 }
2165 }
2166 if (quote) {
2167 ret.prepend(c: QLatin1Char('"'));
2168 ret.append(c: QLatin1Char('"'));
2169 }
2170 return ret;
2171}
2172
2173QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas)
2174{
2175 QString ret;
2176
2177 for (const ProString &str : vals) {
2178 if (!ret.isEmpty()) {
2179 if (commas)
2180 ret += QLatin1Char(',');
2181 ret += QLatin1Char(' ');
2182 }
2183 ret += formatValue(val: str);
2184 }
2185 return ret;
2186}
2187
2188QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists)
2189{
2190 QString ret;
2191
2192 for (const ProStringList &list : lists) {
2193 if (!ret.isEmpty())
2194 ret += QLatin1String(", ");
2195 ret += formatValueList(vals: list);
2196 }
2197 return ret;
2198}
2199#endif
2200
2201QT_END_NAMESPACE
2202

source code of qtbase/qmake/library/qmakeevaluator.cpp