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 <qregularexpression.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
110size_t qHash(const QMakeBaseKey &key)
111{
112 return qHash(key.root) ^ qHash(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 { "INTERFACES", "FORMS" },
169 { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
170 { "TARGETDEPS", "POST_TARGETDEPS" },
171 { "LIBPATH", "QMAKE_LIBDIR" },
172 { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
173 { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
174 { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
175 { "PRECOMPH", "PRECOMPILED_HEADER" },
176 { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
177 { "INCPATH", "INCLUDEPATH" },
178 { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
179 { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
180 { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
181 { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
182 { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
183 { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
184 { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
185 { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
186 { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" },
187 { "IN_PWD", "PWD" },
188 { "DEPLOYMENT", "INSTALLS" }
189 };
190 statics.varMap.reserve((int)(sizeof(mapInits)/sizeof(mapInits[0])));
191 for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
192 statics.varMap.insert(ProKey(mapInits[i].oldname), ProKey(mapInits[i].newname));
193}
194
195const ProKey &QMakeEvaluator::map(const ProKey &var)
196{
197 QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(var);
198 if (it == statics.varMap.constEnd())
199 return var;
200 deprecationWarning(fL1S("Variable %1 is deprecated; use %2 instead.")
201 .arg(var.toQString(), 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(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(QStringView 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 char16_t quote = 0;
286 bool hadWord = false;
287 for (int x = 0; x < vals_len; x++) {
288 char16_t 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 char16_t 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 QRegularExpression &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(regexp, replace);
344 if (!val.isSharedWith(copy) && val != copy) {
345 if (val.isEmpty()) {
346 varit = varlist->erase(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(str, &pending);
366 } else {
367 if (!pending) {
368 pending = true;
369 *ret << str;
370 } else {
371 ret->last().append(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(list, &pending, !(tok & TokQuoted));
382 } else {
383 if (tok & TokQuoted) {
384 if (!pending) {
385 pending = true;
386 *ret << ProString();
387 }
388 ret->last().append(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(0).isEmpty()) {
394 // The common case
395 pending = true;
396 *ret += list;
397 return;
398 }
399 } else {
400 ret->last().append(list.at(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(j);
406 }
407 }
408 }
409 }
410}
411
412QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpression(
413 const ushort *&tokPtr, ProStringList *ret, bool joined)
414{
415 debugMsg(2, 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(2, "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(tokPtr);
433 debugMsg(2, "literal %s", dbgStr(val));
434 addStr(val, ret, pending, joined);
435 break; }
436 case TokHashLiteral: {
437 const ProKey &val = pro->getHashStr(tokPtr);
438 debugMsg(2, "hashed literal %s", dbgStr(val.toString()));
439 addStr(val, ret, pending, joined);
440 break; }
441 case TokVariable: {
442 const ProKey &var = pro->getHashStr(tokPtr);
443 const ProStringList &val = values(map(var));
444 debugMsg(2, "variable %s => %s", dbgKey(var), dbgStrList(val));
445 addStrList(val, tok, ret, pending, joined);
446 break; }
447 case TokProperty: {
448 const ProKey &var = pro->getHashStr(tokPtr);
449 const ProString &val = propertyValue(var);
450 debugMsg(2, "property %s => %s", dbgKey(var), dbgStr(val));
451 addStr(val, ret, pending, joined);
452 break; }
453 case TokEnvVar: {
454 const ProString &var = pro->getStr(tokPtr);
455 const ProString &val = ProString(m_option->getEnv(var.toQString()));
456 debugMsg(2, "env var %s => %s", dbgStr(var), dbgStr(val));
457 addStr(val, ret, pending, joined);
458 break; }
459 case TokFuncName: {
460 const ProKey &func = pro->getHashStr(tokPtr);
461 debugMsg(2, "function %s", dbgKey(func));
462 ProStringList val;
463 if (evaluateExpandFunction(func, tokPtr, &val) == ReturnError)
464 return ReturnError;
465 addStrList(val, tok, ret, pending, joined);
466 break; }
467 default:
468 debugMsg(2, "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("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("taking 'then' branch");
568 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
569 traceMsg("finished 'then' branch");
570 }
571 tokPtr += blockLen;
572 blockLen = getBlockLen(tokPtr);
573 if (!okey) {
574 traceMsg("taking 'else' branch");
575 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
576 traceMsg("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(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("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(m_valuemapStack.end(),
607 savedValuemapStack, savedValuemapStack.begin());
608 traceMsg("visiting nesting-bypassing block");
609 ret = visitProBlock(tokPtr);
610 traceMsg("visited nesting-bypassing block");
611 savedValuemapStack.splice(savedValuemapStack.begin(),
612 m_valuemapStack, m_valuemapStack.begin());
613 m_valuemapStack = std::move(savedValuemapStack);
614 } else {
615 traceMsg("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(tokPtr);
625 blockLen = getBlockLen(tokPtr);
626 visitProFunctionDef(tok, name, tokPtr);
627 traceMsg("defined %s function %s",
628 tok == TokTestDef ? "test" : "replace", dbgKey(name));
629 } else {
630 traceMsg("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("NOT");
639 invert ^= true;
640 continue;
641 case TokAnd:
642 traceMsg("AND");
643 or_op = false;
644 continue;
645 case TokOr:
646 traceMsg("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(curr.at(0).toQStringView(), true);
657 traceMsg("condition %s is %s", dbgStr(curr.at(0)), dbgBool(okey));
658 okey ^= invert;
659 }
660 } else {
661 traceMsg("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(tokPtr);
673 okey = false;
674 } else {
675 traceMsg("evaluating test function %s", dbgStr(curr.at(0)));
676 ret = evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
677 switch (ret) {
678 case ReturnTrue: okey = true; break;
679 case ReturnFalse: okey = false; break;
680 default:
681 traceMsg("aborting block, function status: %s", dbgReturn(ret));
682 return ret;
683 }
684 traceMsg("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(tokPtr);
698 traceMsg("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("flow control statement '%s', aborting block", dbgReturn(ret));
717 return ret;
718 }
719 traceMsg("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, &curr, 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("aborting block, status: %s", dbgReturn(ret));
733 return ret;
734 }
735 }
736 traceMsg("leaving block, okey=%s", dbgBool(okey));
737 return returnBool(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(name, 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(exprPtr, 0, &it_list_out, true) == ReturnError)
761 return ReturnError;
762 ProString it_list = it_list_out.at(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(_variable);
771 oldVarVal = values(variable);
772 }
773 ProStringList list = values(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("skipping forever loop in cumulative mode");
779 return ReturnFalse;
780 }
781 infinite = true;
782 } else {
783 QStringView itl = it_list.toQStringView();
784 int dotdot = itl.indexOf(statics.strDotDot);
785 if (dotdot != -1) {
786 bool ok;
787 int start = itl.left(dotdot).toInt(&ok);
788 if (ok) {
789 int end = itl.mid(dotdot+2).toInt(&ok);
790 if (ok) {
791 const int absDiff = qAbs(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("skipping excessive loop in cumulative mode");
796 return ReturnFalse;
797 }
798 list.reserve(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("entering infinite loop for %s", dbgKey(variable));
814 else
815 traceMsg("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("loop iteration %d", index);
826 } else {
827 ProString val;
828 do {
829 if (index >= list.count())
830 goto do_break;
831 val = list.at(index++);
832 } while (val.isEmpty()); // stupid, but qmake is like that
833 traceMsg("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("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(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(curr.first());
873
874 if (tok == TokReplace) { // ~=
875 // DEFINES ~= s/a/b/?[gqi]
876
877 ProStringList varVal;
878 if (expandVariableReferences(tokPtr, sizeHint, &varVal, true) == ReturnError)
879 return ReturnError;
880 QStringView val = varVal.at(0).toQStringView();
881 if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
882 evalError(fL1S("The ~= operator can handle only the s/// function."));
883 return ReturnTrue;
884 }
885 QChar sep = val.at(1);
886 auto func = val.split(sep, 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(QLatin1Char('g')) != -1;
895 case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
896 quote = func[3].indexOf(QLatin1Char('q')) != -1;
897 }
898 QString pattern = func[1].toString();
899 QString replace = func[2].toString();
900 if (quote)
901 pattern = QRegularExpression::escape(pattern);
902
903 QRegularExpression regexp(pattern, case_sense ? QRegularExpression::NoPatternOption :
904 QRegularExpression::CaseInsensitiveOption);
905
906 // We could make a union of modified and unmodified values,
907 // but this will break just as much as it fixes, so leave it as is.
908 replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
909 debugMsg(2, "replaced %s with %s", dbgQStr(pattern), dbgQStr(replace));
910 } else {
911 ProStringList varVal;
912 if (expandVariableReferences(tokPtr, sizeHint, &varVal, false) == ReturnError)
913 return ReturnError;
914 switch (tok) {
915 default: // whatever - cannot happen
916 case TokAssign: // =
917 varVal.removeEmpty();
918 // FIXME: add check+warning about accidental value removal.
919 // This may be a bit too noisy, though.
920 m_valuemapStack.top()[varName] = varVal;
921 debugMsg(2, "assigning");
922 break;
923 case TokAppendUnique: // *=
924 valuesRef(varName).insertUnique(varVal);
925 debugMsg(2, "appending unique");
926 break;
927 case TokAppend: // +=
928 varVal.removeEmpty();
929 valuesRef(varName) += varVal;
930 debugMsg(2, "appending");
931 break;
932 case TokRemove: // -=
933 if (!m_cumulative) {
934 valuesRef(varName).removeEach(varVal);
935 } else {
936 // We are stingy with our values.
937 }
938 debugMsg(2, "removing");
939 break;
940 }
941 }
942 traceMsg("%s := %s", dbgKey(varName), dbgStrList(values(varName)));
943
944 if (varName == statics.strTEMPLATE)
945 setTemplate();
946 else if (varName == statics.strQMAKE_PLATFORM)
947 m_featureRoots = nullptr;
948 else if (varName == statics.strQMAKE_DIR_SEP)
949 m_dirSep = first(varName);
950 else if (varName == statics.strQMAKESPEC) {
951 if (!values(varName).isEmpty()) {
952 QString spec = values(varName).first().toQString();
953 if (IoUtils::isAbsolutePath(spec)) {
954 m_qmakespec = spec;
955 m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
956 m_featureRoots = nullptr;
957 }
958 }
959 }
960#ifdef PROEVALUATOR_FULL
961 else if (varName == statics.strREQUIRES)
962 return checkRequirements(values(varName));
963#endif
964
965 return ReturnTrue;
966}
967
968void QMakeEvaluator::setTemplate()
969{
970 ProStringList &values = valuesRef(statics.strTEMPLATE);
971 if (!m_option->user_template.isEmpty()) {
972 // Don't allow override
973 values = ProStringList(ProString(m_option->user_template));
974 } else {
975 if (values.isEmpty())
976 values.append(ProString("app"));
977 else
978 values.erase(values.begin() + 1, values.end());
979 }
980 if (!m_option->user_template_prefix.isEmpty()) {
981 ProString val = values.first();
982 if (!val.startsWith(m_option->user_template_prefix))
983 values = ProStringList(ProString(m_option->user_template_prefix + val));
984 }
985}
986
987#if defined(Q_CC_MSVC)
988static ProString msvcBinDirToQMakeArch(QString subdir)
989{
990 int idx = subdir.indexOf(QLatin1Char('\\'));
991 if (idx == -1)
992 return ProString("x86");
993 subdir.remove(0, idx + 1);
994 idx = subdir.indexOf(QLatin1Char('_'));
995 if (idx >= 0)
996 subdir.remove(0, idx + 1);
997 subdir = subdir.toLower();
998 if (subdir == QLatin1String("amd64"))
999 return ProString("x86_64");
1000 // Since 2017 the folder structure from here is HostX64|X86/x64|x86
1001 idx = subdir.indexOf(QLatin1Char('\\'));
1002 if (idx == -1)
1003 return ProString("x86");
1004 subdir.remove(0, idx + 1);
1005 if (subdir == QLatin1String("x64"))
1006 return ProString("x86_64");
1007 return ProString(subdir);
1008}
1009
1010static ProString defaultMsvcArchitecture()
1011{
1012#if defined(Q_OS_WIN64)
1013 return ProString("x86_64");
1014#else
1015 return ProString("x86");
1016#endif
1017}
1018
1019static ProString msvcArchitecture(const QString &vcInstallDir, const QString &pathVar)
1020{
1021 if (vcInstallDir.isEmpty())
1022 return defaultMsvcArchitecture();
1023 QString vcBinDir = vcInstallDir;
1024 if (vcBinDir.endsWith(QLatin1Char('\\')))
1025 vcBinDir.chop(1);
1026 const auto dirs = pathVar.split(QLatin1Char(';'), Qt::SkipEmptyParts);
1027 for (const QString &dir : dirs) {
1028 if (!dir.startsWith(vcBinDir, Qt::CaseInsensitive))
1029 continue;
1030 const ProString arch = msvcBinDirToQMakeArch(dir.mid(vcBinDir.length() + 1));
1031 if (!arch.isEmpty())
1032 return arch;
1033 }
1034 return defaultMsvcArchitecture();
1035}
1036#endif // defined(Q_CC_MSVC)
1037
1038void QMakeEvaluator::loadDefaults()
1039{
1040 ProValueMap &vars = m_valuemapStack.top();
1041
1042 vars[ProKey("DIR_SEPARATOR")] << ProString(m_option->dir_sep);
1043 vars[ProKey("DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep);
1044 vars[ProKey("_DATE_")] << ProString(QDateTime::currentDateTime().toString());
1045 if (!m_option->qmake_abslocation.isEmpty())
1046 vars[ProKey("QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation);
1047 if (!m_option->qmake_args.isEmpty())
1048 vars[ProKey("QMAKE_ARGS")] = ProStringList(m_option->qmake_args);
1049 if (!m_option->qtconf.isEmpty())
1050 vars[ProKey("QMAKE_QTCONF")] = ProString(m_option->qtconf);
1051 vars[ProKey("QMAKE_HOST.cpu_count")] = ProString(QString::number(idealThreadCount()));
1052#if defined(Q_OS_WIN32)
1053 vars[ProKey("QMAKE_HOST.os")] << ProString("Windows");
1054
1055 DWORD name_length = 1024;
1056 wchar_t name[1024];
1057 if (GetComputerName(name, &name_length))
1058 vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name));
1059
1060 vars[ProKey("QMAKE_HOST.version")] << ProString(QSysInfo::kernelVersion());
1061 vars[ProKey("QMAKE_HOST.version_string")] << ProString(QSysInfo::productVersion());
1062
1063 SYSTEM_INFO info;
1064 GetSystemInfo(&info);
1065 ProString archStr;
1066 switch (info.wProcessorArchitecture) {
1067# ifdef PROCESSOR_ARCHITECTURE_AMD64
1068 case PROCESSOR_ARCHITECTURE_AMD64:
1069 archStr = ProString("x86_64");
1070 break;
1071# endif
1072 case PROCESSOR_ARCHITECTURE_INTEL:
1073 archStr = ProString("x86");
1074 break;
1075 case PROCESSOR_ARCHITECTURE_IA64:
1076# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
1077 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
1078# endif
1079 archStr = ProString("IA64");
1080 break;
1081 default:
1082 archStr = ProString("Unknown");
1083 break;
1084 }
1085 vars[ProKey("QMAKE_HOST.arch")] << archStr;
1086
1087# if defined(Q_CC_MSVC) // ### bogus condition, but nobody x-builds for msvc with a different qmake
1088 // Since VS 2017 we need VCToolsInstallDir instead of VCINSTALLDIR
1089 QString vcInstallDir = m_option->getEnv(QLatin1String("VCToolsInstallDir"));
1090 if (vcInstallDir.isEmpty())
1091 vcInstallDir = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
1092 vars[ProKey("QMAKE_TARGET.arch")] = msvcArchitecture(
1093 vcInstallDir,
1094 m_option->getEnv(QLatin1String("PATH")));
1095# endif
1096#elif defined(Q_OS_UNIX)
1097 struct utsname name;
1098 if (uname(&name) != -1) {
1099 vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
1100 vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename));
1101 vars[ProKey("QMAKE_HOST.version")] << ProString(name.release);
1102 vars[ProKey("QMAKE_HOST.version_string")] << ProString(name.version);
1103 vars[ProKey("QMAKE_HOST.arch")] << ProString(name.machine);
1104 }
1105#endif
1106
1107 m_valuemapInited = true;
1108}
1109
1110bool QMakeEvaluator::prepareProject(const QString &inDir)
1111{
1112 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1113 QString superdir;
1114 if (m_option->do_cache) {
1115 QString conffile;
1116 QString cachefile = m_option->cachefile;
1117 if (cachefile.isEmpty()) { //find it as it has not been specified
1118 if (m_outputDir.isEmpty())
1119 goto no_cache;
1120 superdir = m_outputDir;
1121 forever {
1122 QString superfile = superdir + QLatin1String("/.qmake.super");
1123 if (m_vfs->exists(superfile, flags)) {
1124 m_superfile = QDir::cleanPath(superfile);
1125 break;
1126 }
1127 QFileInfo qdfi(superdir);
1128 if (qdfi.isRoot()) {
1129 superdir.clear();
1130 break;
1131 }
1132 superdir = qdfi.path();
1133 }
1134 QString sdir = inDir;
1135 QString dir = m_outputDir;
1136 forever {
1137 conffile = sdir + QLatin1String("/.qmake.conf");
1138 if (!m_vfs->exists(conffile, flags))
1139 conffile.clear();
1140 cachefile = dir + QLatin1String("/.qmake.cache");
1141 if (!m_vfs->exists(cachefile, flags))
1142 cachefile.clear();
1143 if (!conffile.isEmpty() || !cachefile.isEmpty()) {
1144 if (dir != sdir)
1145 m_sourceRoot = sdir;
1146 m_buildRoot = dir;
1147 break;
1148 }
1149 if (dir == superdir)
1150 goto no_cache;
1151 QFileInfo qsdfi(sdir);
1152 QFileInfo qdfi(dir);
1153 if (qsdfi.isRoot() || qdfi.isRoot())
1154 goto no_cache;
1155 sdir = qsdfi.path();
1156 dir = qdfi.path();
1157 }
1158 } else {
1159 m_buildRoot = QFileInfo(cachefile).path();
1160 }
1161 m_conffile = QDir::cleanPath(conffile);
1162 m_cachefile = QDir::cleanPath(cachefile);
1163 }
1164 no_cache:
1165
1166 QString dir = m_outputDir;
1167 forever {
1168 QString stashfile = dir + QLatin1String("/.qmake.stash");
1169 if (dir == (!superdir.isEmpty() ? superdir : m_buildRoot) || m_vfs->exists(stashfile, flags)) {
1170 m_stashfile = QDir::cleanPath(stashfile);
1171 break;
1172 }
1173 QFileInfo qdfi(dir);
1174 if (qdfi.isRoot())
1175 break;
1176 dir = qdfi.path();
1177 }
1178
1179 return true;
1180}
1181
1182bool QMakeEvaluator::loadSpecInternal()
1183{
1184 if (evaluateFeatureFile(QLatin1String("spec_pre.prf")) != ReturnTrue)
1185 return false;
1186 QString spec = m_qmakespec + QLatin1String("/qmake.conf");
1187 if (evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
1188 evalError(fL1S("Could not read qmake configuration file %1.").arg(spec));
1189 return false;
1190 }
1191#ifndef QT_BUILD_QMAKE
1192 // Legacy support for Qt4 default specs
1193# ifdef Q_OS_UNIX
1194 if (m_qmakespec.endsWith(QLatin1String("/default-host"))
1195 || m_qmakespec.endsWith(QLatin1String("/default"))) {
1196 QString rspec = QFileInfo(m_qmakespec).symLinkTarget();
1197 if (!rspec.isEmpty())
1198 m_qmakespec = QDir::cleanPath(QDir(m_qmakespec).absoluteFilePath(rspec));
1199 }
1200# else
1201 // We can't resolve symlinks as they do on Unix, so configure.exe puts
1202 // the source of the qmake.conf at the end of the default/qmake.conf in
1203 // the QMAKESPEC_ORIGINAL variable.
1204 const ProString &orig_spec = first(ProKey("QMAKESPEC_ORIGINAL"));
1205 if (!orig_spec.isEmpty()) {
1206 QString spec = orig_spec.toQString();
1207 if (IoUtils::isAbsolutePath(spec))
1208 m_qmakespec = spec;
1209 }
1210# endif
1211#endif
1212 valuesRef(ProKey("QMAKESPEC")) = ProString(m_qmakespec);
1213 m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
1214 // This also ensures that m_featureRoots is valid.
1215 if (evaluateFeatureFile(QLatin1String("spec_post.prf")) != ReturnTrue)
1216 return false;
1217 return true;
1218}
1219
1220bool QMakeEvaluator::loadSpec()
1221{
1222 QString qmakespec = m_option->expandEnvVars(
1223 m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
1224
1225 {
1226 QMakeEvaluator evaluator(m_option, m_parser, m_vfs, m_handler);
1227 evaluator.m_sourceRoot = m_sourceRoot;
1228 evaluator.m_buildRoot = m_buildRoot;
1229
1230 if (!m_superfile.isEmpty() && evaluator.evaluateFile(
1231 m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1232 return false;
1233 }
1234 if (!m_conffile.isEmpty() && evaluator.evaluateFile(
1235 m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1236 return false;
1237 }
1238 if (!m_cachefile.isEmpty() && evaluator.evaluateFile(
1239 m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1240 return false;
1241 }
1242 if (qmakespec.isEmpty()) {
1243 if (!m_hostBuild)
1244 qmakespec = evaluator.first(ProKey("XQMAKESPEC")).toQString();
1245 if (qmakespec.isEmpty())
1246 qmakespec = evaluator.first(ProKey("QMAKESPEC")).toQString();
1247 }
1248 m_qmakepath = evaluator.values(ProKey("QMAKEPATH")).toQStringList();
1249 m_qmakefeatures = evaluator.values(ProKey("QMAKEFEATURES")).toQStringList();
1250 }
1251
1252 updateMkspecPaths();
1253 if (qmakespec.isEmpty())
1254 qmakespec = propertyValue(ProKey(m_hostBuild ? "QMAKE_SPEC" : "QMAKE_XSPEC")).toQString();
1255#ifndef QT_BUILD_QMAKE
1256 // Legacy support for Qt4 qmake in Qt Creator, etc.
1257 if (qmakespec.isEmpty())
1258 qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
1259#endif
1260 if (IoUtils::isRelativePath(qmakespec)) {
1261 for (const QString &root : qAsConst(m_mkspecPaths)) {
1262 QString mkspec = root + QLatin1Char('/') + qmakespec;
1263 if (IoUtils::exists(mkspec)) {
1264 qmakespec = mkspec;
1265 goto cool;
1266 }
1267 }
1268 evalError(fL1S("Could not find qmake spec '%1'.").arg(qmakespec));
1269 return false;
1270 }
1271 cool:
1272 m_qmakespec = QDir::cleanPath(qmakespec);
1273
1274 if (!m_superfile.isEmpty()) {
1275 valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
1276 if (evaluateFile(
1277 m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue)
1278 return false;
1279 }
1280 if (!loadSpecInternal())
1281 return false;
1282 if (!m_conffile.isEmpty()) {
1283 valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
1284 if (evaluateFile(
1285 m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1286 return false;
1287 }
1288 if (!m_cachefile.isEmpty()) {
1289 valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
1290 if (evaluateFile(
1291 m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1292 return false;
1293 }
1294 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1295 if (!m_stashfile.isEmpty() && m_vfs->exists(m_stashfile, flags)) {
1296 valuesRef(ProKey("_QMAKE_STASH_")) << ProString(m_stashfile);
1297 if (evaluateFile(
1298 m_stashfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1299 return false;
1300 }
1301 return true;
1302}
1303
1304void QMakeEvaluator::setupProject()
1305{
1306 setTemplate();
1307 ProValueMap &vars = m_valuemapStack.top();
1308 int proFile = currentFileId();
1309 vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName()).setSource(proFile);
1310 vars[ProKey("_PRO_FILE_")] << ProString(currentFileName()).setSource(proFile);
1311 vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory()).setSource(proFile);
1312 vars[ProKey("OUT_PWD")] << ProString(m_outputDir).setSource(proFile);
1313}
1314
1315void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where)
1316{
1317 if (!cmds.isEmpty()) {
1318 ProFile *pro = m_parser->parsedProBlock(QStringView(cmds), 0, where, -1);
1319 if (pro->isOk()) {
1320 m_locationStack.push(m_current);
1321 visitProBlock(pro, pro->tokPtr());
1322 m_current = m_locationStack.pop();
1323 }
1324 pro->deref();
1325 }
1326}
1327
1328void QMakeEvaluator::applyExtraConfigs()
1329{
1330 if (m_extraConfigs.isEmpty())
1331 return;
1332
1333 evaluateCommand(fL1S("CONFIG += ") + m_extraConfigs.join(QLatin1Char(' ')), fL1S("(extra configs)"));
1334}
1335
1336QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConfigFeatures()
1337{
1338 QSet<QString> processed;
1339 forever {
1340 bool finished = true;
1341 ProStringList configs = values(statics.strCONFIG);
1342 for (int i = configs.size() - 1; i >= 0; --i) {
1343 ProStringRoUser u1(configs.at(i), m_tmp1);
1344 QString config = u1.str().toLower();
1345 if (!processed.contains(config)) {
1346 config.detach();
1347 processed.insert(config);
1348 VisitReturn vr = evaluateFeatureFile(config, true);
1349 if (vr == ReturnError && !m_cumulative)
1350 return vr;
1351 if (vr == ReturnTrue) {
1352 finished = false;
1353 break;
1354 }
1355 }
1356 }
1357 if (finished)
1358 break;
1359 }
1360 return ReturnTrue;
1361}
1362
1363QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
1364 ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags)
1365{
1366 if (!m_cumulative && !pro->isOk())
1367 return ReturnFalse;
1368
1369 if (flags & LoadPreFiles) {
1370 if (!prepareProject(pro->directoryName()))
1371 return ReturnFalse;
1372
1373 m_hostBuild = pro->isHostBuild();
1374
1375#ifdef PROEVALUATOR_THREAD_SAFE
1376 m_option->mutex.lock();
1377#endif
1378 QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_stashfile, m_hostBuild)];
1379 if (!*baseEnvPtr)
1380 *baseEnvPtr = new QMakeBaseEnv;
1381 QMakeBaseEnv *baseEnv = *baseEnvPtr;
1382
1383#ifdef PROEVALUATOR_THREAD_SAFE
1384 QMutexLocker locker(&baseEnv->mutex);
1385 m_option->mutex.unlock();
1386 if (baseEnv->inProgress) {
1387 QThreadPool::globalInstance()->releaseThread();
1388 baseEnv->cond.wait(&baseEnv->mutex);
1389 QThreadPool::globalInstance()->reserveThread();
1390 if (!baseEnv->isOk)
1391 return ReturnFalse;
1392 } else
1393#endif
1394 if (!baseEnv->evaluator) {
1395#ifdef PROEVALUATOR_THREAD_SAFE
1396 baseEnv->inProgress = true;
1397 locker.unlock();
1398#endif
1399
1400 QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_vfs, m_handler);
1401 baseEnv->evaluator = baseEval;
1402 baseEval->m_superfile = m_superfile;
1403 baseEval->m_conffile = m_conffile;
1404 baseEval->m_cachefile = m_cachefile;
1405 baseEval->m_stashfile = m_stashfile;
1406 baseEval->m_sourceRoot = m_sourceRoot;
1407 baseEval->m_buildRoot = m_buildRoot;
1408 baseEval->m_hostBuild = m_hostBuild;
1409 bool ok = baseEval->loadSpec();
1410
1411#ifdef PROEVALUATOR_THREAD_SAFE
1412 locker.relock();
1413 baseEnv->isOk = ok;
1414 baseEnv->inProgress = false;
1415 baseEnv->cond.wakeAll();
1416#endif
1417
1418 if (!ok)
1419 return ReturnFalse;
1420 }
1421#ifdef PROEVALUATOR_THREAD_SAFE
1422 else if (!baseEnv->isOk)
1423 return ReturnFalse;
1424#endif
1425
1426 initFrom(baseEnv->evaluator);
1427 } else {
1428 if (!m_valuemapInited)
1429 loadDefaults();
1430 }
1431
1432 VisitReturn vr;
1433
1434 m_handler->aboutToEval(currentProFile(), pro, type);
1435 m_profileStack.push(pro);
1436 valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1437 if (flags & LoadPreFiles) {
1438 setupProject();
1439
1440 if (!m_option->extra_cmds[QMakeEvalEarly].isEmpty())
1441 evaluateCommand(m_option->extra_cmds[QMakeEvalEarly], fL1S("(command line -early)"));
1442
1443 for (ProValueMap::ConstIterator it = m_extraVars.constBegin();
1444 it != m_extraVars.constEnd(); ++it)
1445 m_valuemapStack.front().insert(it.key(), it.value());
1446
1447 // In case default_pre needs to make decisions based on the current
1448 // build pass configuration.
1449 applyExtraConfigs();
1450
1451 if ((vr = evaluateFeatureFile(QLatin1String("default_pre.prf"))) == ReturnError)
1452 goto failed;
1453
1454 if (!m_option->extra_cmds[QMakeEvalBefore].isEmpty()) {
1455 evaluateCommand(m_option->extra_cmds[QMakeEvalBefore], fL1S("(command line)"));
1456
1457 // Again, after user configs, to override them
1458 applyExtraConfigs();
1459 }
1460 }
1461
1462 debugMsg(1, "visiting file %s", qPrintable(pro->fileName()));
1463 if ((vr = visitProBlock(pro, pro->tokPtr())) == ReturnError)
1464 goto failed;
1465 debugMsg(1, "done visiting file %s", qPrintable(pro->fileName()));
1466
1467 if (flags & LoadPostFiles) {
1468 evaluateCommand(m_option->extra_cmds[QMakeEvalAfter], fL1S("(command line -after)"));
1469
1470 // Again, to ensure the project does not mess with us.
1471 // Specifically, do not allow a project to override debug/release within a
1472 // debug_and_release build pass - it's too late for that at this point anyway.
1473 applyExtraConfigs();
1474
1475 if ((vr = evaluateFeatureFile(QLatin1String("default_post.prf"))) == ReturnError)
1476 goto failed;
1477
1478 if (!m_option->extra_cmds[QMakeEvalLate].isEmpty())
1479 evaluateCommand(m_option->extra_cmds[QMakeEvalLate], fL1S("(command line -late)"));
1480
1481 if ((vr = evaluateConfigFeatures()) == ReturnError)
1482 goto failed;
1483 }
1484 vr = ReturnTrue;
1485 failed:
1486 m_profileStack.pop();
1487 valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1488 m_handler->doneWithEval(currentProFile());
1489
1490 return vr;
1491}
1492
1493
1494void QMakeEvaluator::updateMkspecPaths()
1495{
1496 QStringList ret;
1497 const QString concat = QLatin1String("/mkspecs");
1498
1499 const auto paths = m_option->getPathListEnv(QLatin1String("QMAKEPATH"));
1500 for (const QString &it : paths)
1501 ret << it + concat;
1502
1503 for (const QString &it : qAsConst(m_qmakepath))
1504 ret << it + concat;
1505
1506 if (!m_buildRoot.isEmpty())
1507 ret << m_buildRoot + concat;
1508 if (!m_sourceRoot.isEmpty())
1509 ret << m_sourceRoot + concat;
1510
1511 ret << m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + concat;
1512 ret << m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + concat;
1513
1514 ret.removeDuplicates();
1515 m_mkspecPaths = ret;
1516}
1517
1518void QMakeEvaluator::updateFeaturePaths()
1519{
1520 QString mkspecs_concat = QLatin1String("/mkspecs");
1521 QString features_concat = QLatin1String("/features/");
1522
1523 QStringList feature_roots;
1524
1525 feature_roots += m_option->getPathListEnv(QLatin1String("QMAKEFEATURES"));
1526 feature_roots += m_qmakefeatures;
1527 feature_roots += m_option->splitPathList(
1528 m_option->propertyValue(ProKey("QMAKEFEATURES")).toQString());
1529
1530 QStringList feature_bases;
1531 if (!m_buildRoot.isEmpty()) {
1532 feature_bases << m_buildRoot + mkspecs_concat;
1533 feature_bases << m_buildRoot;
1534 }
1535 if (!m_sourceRoot.isEmpty()) {
1536 feature_bases << m_sourceRoot + mkspecs_concat;
1537 feature_bases << m_sourceRoot;
1538 }
1539
1540 const auto items = m_option->getPathListEnv(QLatin1String("QMAKEPATH"));
1541 for (const QString &item : items)
1542 feature_bases << (item + mkspecs_concat);
1543
1544 for (const QString &item : qAsConst(m_qmakepath))
1545 feature_bases << (item + mkspecs_concat);
1546
1547 if (!m_qmakespec.isEmpty()) {
1548 // The spec is already platform-dependent, so no subdirs here.
1549 feature_roots << (m_qmakespec + features_concat);
1550
1551 // Also check directly under the root directory of the mkspecs collection
1552 QDir specdir(m_qmakespec);
1553 while (!specdir.isRoot() && specdir.cdUp()) {
1554 const QString specpath = specdir.path();
1555 if (specpath.endsWith(mkspecs_concat)) {
1556 if (IoUtils::exists(specpath + features_concat))
1557 feature_bases << specpath;
1558 break;
1559 }
1560 }
1561 }
1562
1563 feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + mkspecs_concat);
1564 feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + mkspecs_concat);
1565
1566 for (const QString &fb : qAsConst(feature_bases)) {
1567 const auto sfxs = values(ProKey("QMAKE_PLATFORM"));
1568 for (const ProString &sfx : sfxs)
1569 feature_roots << (fb + features_concat + sfx + QLatin1Char('/'));
1570 feature_roots << (fb + features_concat);
1571 }
1572
1573 for (int i = 0; i < feature_roots.count(); ++i)
1574 if (!feature_roots.at(i).endsWith(QLatin1Char('/')))
1575 feature_roots[i].append(QLatin1Char('/'));
1576
1577 feature_roots.removeDuplicates();
1578
1579 QStringList ret;
1580 for (const QString &root : qAsConst(feature_roots))
1581 if (IoUtils::exists(root))
1582 ret << root;
1583 m_featureRoots = new QMakeFeatureRoots(ret);
1584}
1585
1586ProString QMakeEvaluator::propertyValue(const ProKey &name) const
1587{
1588 if (name == QLatin1String("QMAKE_MKSPECS"))
1589 return ProString(m_mkspecPaths.join(m_option->dirlist_sep));
1590 ProString ret = m_option->propertyValue(name);
1591// if (ret.isNull())
1592// evalError(fL1S("Querying unknown property %1").arg(name.toQStringView()));
1593 return ret;
1594}
1595
1596ProFile *QMakeEvaluator::currentProFile() const
1597{
1598 if (m_profileStack.count() > 0)
1599 return m_profileStack.top();
1600 return nullptr;
1601}
1602
1603int QMakeEvaluator::currentFileId() const
1604{
1605 ProFile *pro = currentProFile();
1606 if (pro)
1607 return pro->id();
1608 return 0;
1609}
1610
1611QString QMakeEvaluator::currentFileName() const
1612{
1613 ProFile *pro = currentProFile();
1614 if (pro)
1615 return pro->fileName();
1616 return QString();
1617}
1618
1619QString QMakeEvaluator::currentDirectory() const
1620{
1621 ProFile *pro = currentProFile();
1622 if (pro)
1623 return pro->directoryName();
1624 return QString();
1625}
1626
1627bool QMakeEvaluator::isActiveConfig(QStringView config, bool regex)
1628{
1629 // magic types for easy flipping
1630 if (config == statics.strtrue)
1631 return true;
1632 if (config == statics.strfalse)
1633 return false;
1634
1635 if (config == statics.strhost_build)
1636 return m_hostBuild;
1637
1638 if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
1639 auto re = QRegularExpression::fromWildcard(config.toString());
1640
1641 // mkspecs
1642 if (re.match(m_qmakespecName).hasMatch())
1643 return true;
1644
1645 // CONFIG variable
1646 const auto configValues = values(statics.strCONFIG);
1647 for (const ProString &configValue : configValues) {
1648 ProStringRoUser u1(configValue, m_tmp[m_toggle ^= 1]);
1649 if (re.match(u1.str()).hasMatch())
1650 return true;
1651 }
1652 } else {
1653 // mkspecs
1654 if (m_qmakespecName == config)
1655 return true;
1656
1657 // CONFIG variable
1658 if (values(statics.strCONFIG).contains(config))
1659 return true;
1660 }
1661
1662 return false;
1663}
1664
1665QMakeEvaluator::VisitReturn QMakeEvaluator::expandVariableReferences(
1666 const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined)
1667{
1668 ret->reserve(sizeHint);
1669 forever {
1670 if (evaluateExpression(tokPtr, ret, joined) == ReturnError)
1671 return ReturnError;
1672 switch (*tokPtr) {
1673 case TokValueTerminator:
1674 case TokFuncTerminator:
1675 tokPtr++;
1676 return ReturnTrue;
1677 case TokArgSeparator:
1678 if (joined) {
1679 tokPtr++;
1680 continue;
1681 }
1682 Q_FALLTHROUGH();
1683 default:
1684 Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
1685 break;
1686 }
1687 }
1688}
1689
1690QMakeEvaluator::VisitReturn QMakeEvaluator::prepareFunctionArgs(
1691 const ushort *&tokPtr, QList<ProStringList> *ret)
1692{
1693 if (*tokPtr != TokFuncTerminator) {
1694 for (;; tokPtr++) {
1695 ProStringList arg;
1696 if (evaluateExpression(tokPtr, &arg, false) == ReturnError)
1697 return ReturnError;
1698 *ret << arg;
1699 if (*tokPtr == TokFuncTerminator)
1700 break;
1701 Q_ASSERT(*tokPtr == TokArgSeparator);
1702 }
1703 }
1704 tokPtr++;
1705 return ReturnTrue;
1706}
1707
1708QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFunction(
1709 const ProFunctionDef &func, const QList<ProStringList> &argumentsList, ProStringList *ret)
1710{
1711 VisitReturn vr;
1712
1713 if (m_valuemapStack.size() >= 100) {
1714 evalError(fL1S("Ran into infinite recursion (depth > 100)."));
1715 vr = ReturnError;
1716 } else {
1717 m_valuemapStack.push(ProValueMap());
1718 m_locationStack.push(m_current);
1719
1720 ProStringList args;
1721 for (int i = 0; i < argumentsList.count(); ++i) {
1722 args += argumentsList[i];
1723 m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i];
1724 }
1725 m_valuemapStack.top()[statics.strARGS] = args;
1726 m_valuemapStack.top()[statics.strARGC] = ProStringList(ProString(QString::number(argumentsList.count())));
1727 vr = visitProBlock(func.pro(), func.tokPtr());
1728 if (vr == ReturnReturn)
1729 vr = ReturnTrue;
1730 if (vr == ReturnTrue)
1731 *ret = m_returnValue;
1732 m_returnValue.clear();
1733
1734 m_current = m_locationStack.pop();
1735 m_valuemapStack.pop();
1736 }
1737 return vr;
1738}
1739
1740QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
1741 const ProFunctionDef &func, const QList<ProStringList> &argumentsList,
1742 const ProString &function)
1743{
1744 ProStringList ret;
1745 VisitReturn vr = evaluateFunction(func, argumentsList, &ret);
1746 if (vr == ReturnTrue) {
1747 if (ret.isEmpty())
1748 return ReturnTrue;
1749 if (ret.at(0) != statics.strfalse) {
1750 if (ret.at(0) == statics.strtrue)
1751 return ReturnTrue;
1752 bool ok;
1753 int val = ret.at(0).toInt(&ok);
1754 if (ok) {
1755 if (val)
1756 return ReturnTrue;
1757 } else {
1758 ProStringRoUser u1(function, m_tmp1);
1759 evalError(fL1S("Unexpected return value from test '%1': %2.")
1760 .arg(u1.str(), ret.join(QLatin1String(" :: "))));
1761 }
1762 }
1763 return ReturnFalse;
1764 }
1765 return vr;
1766}
1767
1768QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
1769 const ProKey &func, const ushort *&tokPtr)
1770{
1771 auto adef = statics.functions.constFind(func);
1772 if (adef != statics.functions.constEnd()) {
1773 //why don't the builtin functions just use args_list? --Sam
1774 ProStringList args;
1775 if (expandVariableReferences(tokPtr, 5, &args, true) == ReturnError)
1776 return ReturnError;
1777 return evaluateBuiltinConditional(*adef, func, args);
1778 }
1779
1780 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1781 m_functionDefs.testFunctions.constFind(func);
1782 if (it != m_functionDefs.testFunctions.constEnd()) {
1783 QList<ProStringList> args;
1784 if (prepareFunctionArgs(tokPtr, &args) == ReturnError)
1785 return ReturnError;
1786 traceMsg("calling %s(%s)", dbgKey(func), dbgStrListList(args));
1787 return evaluateBoolFunction(*it, args, func);
1788 }
1789
1790 skipExpression(tokPtr);
1791 evalError(fL1S("'%1' is not a recognized test function.").arg(func.toQStringView()));
1792 return ReturnFalse;
1793}
1794
1795QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction(
1796 const ProKey &func, const ushort *&tokPtr, ProStringList *ret)
1797{
1798 auto adef = statics.expands.constFind(func);
1799 if (adef != statics.expands.constEnd()) {
1800 //why don't the builtin functions just use args_list? --Sam
1801 ProStringList args;
1802 if (expandVariableReferences(tokPtr, 5, &args, true) == ReturnError)
1803 return ReturnError;
1804 return evaluateBuiltinExpand(*adef, func, args, *ret);
1805 }
1806
1807 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1808 m_functionDefs.replaceFunctions.constFind(func);
1809 if (it != m_functionDefs.replaceFunctions.constEnd()) {
1810 QList<ProStringList> args;
1811 if (prepareFunctionArgs(tokPtr, &args) == ReturnError)
1812 return ReturnError;
1813 traceMsg("calling $$%s(%s)", dbgKey(func), dbgStrListList(args));
1814 return evaluateFunction(*it, args, ret);
1815 }
1816
1817 skipExpression(tokPtr);
1818 evalError(fL1S("'%1' is not a recognized replace function.").arg(func.toQStringView()));
1819 return ReturnFalse;
1820}
1821
1822QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditional(
1823 QStringView cond, const QString &where, int line)
1824{
1825 VisitReturn ret = ReturnFalse;
1826 ProFile *pro = m_parser->parsedProBlock(cond, 0, where, line, QMakeParser::TestGrammar);
1827 if (pro->isOk()) {
1828 m_locationStack.push(m_current);
1829 ret = visitProBlock(pro, pro->tokPtr());
1830 m_current = m_locationStack.pop();
1831 }
1832 pro->deref();
1833 return ret;
1834}
1835
1836#ifdef PROEVALUATOR_FULL
1837QMakeEvaluator::VisitReturn QMakeEvaluator::checkRequirements(const ProStringList &deps)
1838{
1839 ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS"));
1840 for (const ProString &dep : deps) {
1841 VisitReturn vr = evaluateConditional(dep.toQStringView(), m_current.pro->fileName(), m_current.line);
1842 if (vr == ReturnError)
1843 return ReturnError;
1844 if (vr != ReturnTrue)
1845 failed << dep;
1846 }
1847 return ReturnTrue;
1848}
1849#endif
1850
1851static bool isFunctParam(const ProKey &variableName)
1852{
1853 const int len = variableName.size();
1854 const QChar *data = variableName.constData();
1855 for (int i = 0; i < len; i++) {
1856 ushort c = data[i].unicode();
1857 if (c < '0' || c > '9')
1858 return false;
1859 }
1860 return true;
1861}
1862
1863ProValueMap *QMakeEvaluator::findValues(const ProKey &variableName, ProValueMap::Iterator *rit)
1864{
1865 ProValueMapStack::iterator vmi = m_valuemapStack.end();
1866 for (bool first = true; ; first = false) {
1867 --vmi;
1868 ProValueMap::Iterator it = (*vmi).find(variableName);
1869 if (it != (*vmi).end()) {
1870 if (it->constBegin() == statics.fakeValue.constBegin())
1871 break;
1872 *rit = it;
1873 return &(*vmi);
1874 }
1875 if (vmi == m_valuemapStack.begin())
1876 break;
1877 if (first && isFunctParam(variableName))
1878 break;
1879 }
1880 return nullptr;
1881}
1882
1883ProStringList &QMakeEvaluator::valuesRef(const ProKey &variableName)
1884{
1885 ProValueMap::Iterator it = m_valuemapStack.top().find(variableName);
1886 if (it != m_valuemapStack.top().end()) {
1887 if (it->constBegin() == statics.fakeValue.constBegin())
1888 it->clear();
1889 return *it;
1890 }
1891 if (!isFunctParam(variableName)) {
1892 ProValueMapStack::iterator vmi = m_valuemapStack.end();
1893 if (--vmi != m_valuemapStack.begin()) {
1894 do {
1895 --vmi;
1896 ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
1897 if (it != (*vmi).constEnd()) {
1898 ProStringList &ret = m_valuemapStack.top()[variableName];
1899 if (it->constBegin() != statics.fakeValue.constBegin())
1900 ret = *it;
1901 return ret;
1902 }
1903 } while (vmi != m_valuemapStack.begin());
1904 }
1905 }
1906 return m_valuemapStack.top()[variableName];
1907}
1908
1909ProStringList QMakeEvaluator::values(const ProKey &variableName) const
1910{
1911 ProValueMapStack::const_iterator vmi = m_valuemapStack.cend();
1912 for (bool first = true; ; first = false) {
1913 --vmi;
1914 ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
1915 if (it != (*vmi).constEnd()) {
1916 if (it->constBegin() == statics.fakeValue.constBegin())
1917 break;
1918 return *it;
1919 }
1920 if (vmi == m_valuemapStack.cbegin())
1921 break;
1922 if (first && isFunctParam(variableName))
1923 break;
1924 }
1925 return ProStringList();
1926}
1927
1928ProString QMakeEvaluator::first(const ProKey &variableName) const
1929{
1930 const ProStringList &vals = values(variableName);
1931 if (!vals.isEmpty())
1932 return vals.first();
1933 return ProString();
1934}
1935
1936QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
1937 const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
1938{
1939 QMakeParser::ParseFlags pflags = QMakeParser::ParseUseCache;
1940 if (!(flags & LoadSilent))
1941 pflags |= QMakeParser::ParseReportMissing;
1942 if (ProFile *pro = m_parser->parsedProFile(fileName, pflags)) {
1943 m_locationStack.push(m_current);
1944 VisitReturn ok = visitProFile(pro, type, flags);
1945 m_current = m_locationStack.pop();
1946 pro->deref();
1947 if (ok == ReturnTrue && !(flags & LoadHidden)) {
1948 ProStringList &iif = m_valuemapStack.front()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
1949 ProString ifn(fileName);
1950 if (!iif.contains(ifn))
1951 iif << ifn;
1952 }
1953 return ok;
1954 } else {
1955 return ReturnFalse;
1956 }
1957}
1958
1959QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileChecked(
1960 const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
1961{
1962 if (fileName.isEmpty())
1963 return ReturnFalse;
1964 const QMakeEvaluator *ref = this;
1965 do {
1966 for (const ProFile *pf : ref->m_profileStack)
1967 if (pf->fileName() == fileName) {
1968 evalError(fL1S("Circular inclusion of %1.").arg(fileName));
1969 return ReturnFalse;
1970 }
1971 } while ((ref = ref->m_caller));
1972 return evaluateFile(fileName, type, flags);
1973}
1974
1975QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
1976 const QString &fileName, bool silent)
1977{
1978 QString fn = fileName;
1979 if (!fn.endsWith(QLatin1String(".prf")))
1980 fn += QLatin1String(".prf");
1981
1982 if (!m_featureRoots)
1983 updateFeaturePaths();
1984#ifdef PROEVALUATOR_THREAD_SAFE
1985 m_featureRoots->mutex.lock();
1986#endif
1987 QString currFn = currentFileName();
1988 if (IoUtils::fileName(currFn) != IoUtils::fileName(fn))
1989 currFn.clear();
1990 // Null values cannot regularly exist in the hash, so they indicate that the value still
1991 // needs to be determined. Failed lookups are represented via non-null empty strings.
1992 QString *fnp = &m_featureRoots->cache[qMakePair(fn, currFn)];
1993 if (fnp->isNull()) {
1994#ifdef QMAKE_OVERRIDE_PRFS
1995 {
1996 QString ovrfn(QLatin1String(":/qmake/override_features/") + fn);
1997 if (QFileInfo::exists(ovrfn)) {
1998 fn = ovrfn;
1999 goto cool;
2000 }
2001 }
2002#endif
2003 {
2004 int start_root = 0;
2005 const QStringList &paths = m_featureRoots->paths;
2006 if (!currFn.isEmpty()) {
2007 QStringView currPath = IoUtils::pathName(currFn);
2008 for (int root = 0; root < paths.size(); ++root)
2009 if (currPath == paths.at(root)) {
2010 start_root = root + 1;
2011 break;
2012 }
2013 }
2014 for (int root = start_root; root < paths.size(); ++root) {
2015 QString fname = paths.at(root) + fn;
2016 if (IoUtils::exists(fname)) {
2017 fn = fname;
2018 goto cool;
2019 }
2020 }
2021 }
2022#ifdef QMAKE_BUILTIN_PRFS
2023 fn.prepend(QLatin1String(":/qmake/features/"));
2024 if (QFileInfo::exists(fn))
2025 goto cool;
2026#endif
2027 fn = QLatin1String(""); // Indicate failed lookup. See comment above.
2028
2029 cool:
2030 *fnp = fn;
2031 } else {
2032 fn = *fnp;
2033 }
2034#ifdef PROEVALUATOR_THREAD_SAFE
2035 m_featureRoots->mutex.unlock();
2036#endif
2037 if (fn.isEmpty()) {
2038 if (!silent)
2039 evalError(fL1S("Cannot find feature %1").arg(fileName));
2040 return ReturnFalse;
2041 }
2042 ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
2043 ProString afn(fn);
2044 if (already.contains(afn)) {
2045 if (!silent)
2046 languageWarning(fL1S("Feature %1 already included").arg(fileName));
2047 return ReturnTrue;
2048 }
2049 already.append(afn);
2050
2051#ifdef PROEVALUATOR_CUMULATIVE
2052 bool cumulative = m_cumulative;
2053 m_cumulative = false;
2054#endif
2055
2056 // The path is fully normalized already.
2057 VisitReturn ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
2058
2059#ifdef PROEVALUATOR_CUMULATIVE
2060 m_cumulative = cumulative;
2061#endif
2062 return ok;
2063}
2064
2065QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto(
2066 const QString &fileName, ProValueMap *values, LoadFlags flags)
2067{
2068 QMakeEvaluator visitor(m_option, m_parser, m_vfs, m_handler);
2069 visitor.m_caller = this;
2070 visitor.m_outputDir = m_outputDir;
2071 visitor.m_featureRoots = m_featureRoots;
2072 VisitReturn ret = visitor.evaluateFileChecked(fileName, QMakeHandler::EvalAuxFile, flags);
2073 if (ret != ReturnTrue)
2074 return ret;
2075 *values = visitor.m_valuemapStack.top();
2076 ProKey qiif("QMAKE_INTERNAL_INCLUDED_FILES");
2077 ProStringList &iif = m_valuemapStack.front()[qiif];
2078 const auto ifns = values->value(qiif);
2079 for (const ProString &ifn : ifns)
2080 if (!iif.contains(ifn))
2081 iif << ifn;
2082 return ReturnTrue;
2083}
2084
2085void QMakeEvaluator::message(int type, const QString &msg) const
2086{
2087 if (!m_skipLevel)
2088 m_handler->message(type | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0), msg,
2089 m_current.line ? m_current.pro->fileName() : QString(),
2090 m_current.line != 0xffff ? m_current.line : -1);
2091}
2092
2093#ifdef PROEVALUATOR_DEBUG
2094void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const
2095{
2096 va_list ap;
2097
2098 if (level <= m_debugLevel) {
2099 fprintf(stderr, "DEBUG %d: ", level);
2100 va_start(ap, fmt);
2101 vfprintf(stderr, fmt, ap);
2102 va_end(ap);
2103 fputc('\n', stderr);
2104 }
2105}
2106
2107void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const
2108{
2109 va_list ap;
2110
2111 if (!m_current.pro)
2112 fprintf(stderr, "DEBUG 1: ");
2113 else if (m_current.line <= 0)
2114 fprintf(stderr, "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName()));
2115 else
2116 fprintf(stderr, "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line);
2117 va_start(ap, fmt);
2118 vfprintf(stderr, fmt, ap);
2119 va_end(ap);
2120 fputc('\n', stderr);
2121}
2122
2123QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote)
2124{
2125 QString ret;
2126 ret.reserve(val.size() + 2);
2127 const QChar *chars = val.constData();
2128 bool quote = forceQuote || val.isEmpty();
2129 for (int i = 0, l = val.size(); i < l; i++) {
2130 QChar c = chars[i];
2131 ushort uc = c.unicode();
2132 if (uc < 32) {
2133 switch (uc) {
2134 case '\r':
2135 ret += QLatin1String("\\r");
2136 break;
2137 case '\n':
2138 ret += QLatin1String("\\n");
2139 break;
2140 case '\t':
2141 ret += QLatin1String("\\t");
2142 break;
2143 default:
2144 ret += QString::fromLatin1("\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
2145 break;
2146 }
2147 } else {
2148 switch (uc) {
2149 case '\\':
2150 ret += QLatin1String("\\\\");
2151 break;
2152 case '"':
2153 ret += QLatin1String("\\\"");
2154 break;
2155 case '\'':
2156 ret += QLatin1String("\\'");
2157 break;
2158 case 32:
2159 quote = true;
2160 Q_FALLTHROUGH();
2161 default:
2162 ret += c;
2163 break;
2164 }
2165 }
2166 }
2167 if (quote) {
2168 ret.prepend(QLatin1Char('"'));
2169 ret.append(QLatin1Char('"'));
2170 }
2171 return ret;
2172}
2173
2174QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas)
2175{
2176 QString ret;
2177
2178 for (const ProString &str : vals) {
2179 if (!ret.isEmpty()) {
2180 if (commas)
2181 ret += QLatin1Char(',');
2182 ret += QLatin1Char(' ');
2183 }
2184 ret += formatValue(str);
2185 }
2186 return ret;
2187}
2188
2189QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists)
2190{
2191 QString ret;
2192
2193 for (const ProStringList &list : lists) {
2194 if (!ret.isEmpty())
2195 ret += QLatin1String(", ");
2196 ret += formatValueList(list);
2197 }
2198 return ret;
2199}
2200#endif
2201
2202QT_END_NAMESPACE
2203