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
65 using namespace QMakeInternal;
66
67 QT_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
idealThreadCount()73 static 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
QMakeBaseKey(const QString & _root,const QString & _stash,bool _hostBuild)105 QMakeBaseKey::QMakeBaseKey(const QString &_root, const QString &_stash, bool _hostBuild)
106 : root(_root), stash(_stash), hostBuild(_hostBuild)
107 {
108 }
109
qHash(const QMakeBaseKey & key)110 uint qHash(const QMakeBaseKey &key)
111 {
112 return qHash(key.root) ^ qHash(key.stash) ^ (uint)key.hostBuild;
113 }
114
operator ==(const QMakeBaseKey & one,const QMakeBaseKey & two)115 bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
116 {
117 return one.root == two.root && one.stash == two.stash && one.hostBuild == two.hostBuild;
118 }
119
QMakeBaseEnv()120 QMakeBaseEnv::QMakeBaseEnv()
121 : evaluator(nullptr)
122 {
123 #ifdef PROEVALUATOR_THREAD_SAFE
124 inProgress = false;
125 #endif
126 }
127
~QMakeBaseEnv()128 QMakeBaseEnv::~QMakeBaseEnv()
129 {
130 delete evaluator;
131 }
132
133 namespace QMakeInternal {
134 QMakeStatics statics;
135 }
136
initStatics()137 void 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
map(const ProKey & var)195 const 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
QMakeEvaluator(QMakeGlobals * option,QMakeParser * parser,QMakeVfs * vfs,QMakeHandler * handler)206 QMakeEvaluator::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
~QMakeEvaluator()234 QMakeEvaluator::~QMakeEvaluator()
235 {
236 }
237
initFrom(const QMakeEvaluator * other)238 void 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
getBlockLen(const ushort * & tokPtr)253 uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr)
254 {
255 uint len = *tokPtr++;
256 len |= (uint)*tokPtr++ << 16;
257 return len;
258 }
259
skipStr(const ushort * & tokPtr)260 void QMakeEvaluator::skipStr(const ushort *&tokPtr)
261 {
262 uint len = *tokPtr++;
263 tokPtr += len;
264 }
265
skipHashStr(const ushort * & tokPtr)266 void 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.
split_value_list(const QStringRef & vals,int source)275 ProStringList 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
replaceInList(ProStringList * varlist,const QRegExp & regexp,const QString & replace,bool global,QString & tmp)336 static void replaceInList(ProStringList *varlist,
337 const QRegExp ®exp, 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
addStr(const ProString & str,ProStringList * ret,bool & pending,bool joined)361 static 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
addStrList(const ProStringList & list,ushort tok,ProStringList * ret,bool & pending,bool joined)376 static 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
evaluateExpression(const ushort * & tokPtr,ProStringList * ret,bool joined)412 QMakeEvaluator::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
skipExpression(const ushort * & pTokPtr)475 void 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
visitProBlock(ProFile * pro,const ushort * tokPtr)515 QMakeEvaluator::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
visitProBlock(const ushort * tokPtr)523 QMakeEvaluator::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).toQStringRef(), 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
visitProFunctionDef(ushort tok,const ProKey & name,const ushort * tokPtr)741 void 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
visitProLoop(const ProKey & _variable,const ushort * exprPtr,const ushort * tokPtr)751 QMakeEvaluator::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 const QStringRef &itl = it_list.toQStringRef();
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
visitProVariable(ushort tok,const ProStringList & curr,const ushort * & tokPtr)861 QMakeEvaluator::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 const QStringRef &val = varVal.at(0).toQStringRef();
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 = QRegExp::escape(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(&valuesRef(varName), regexp, replace, global, m_tmp2);
908 debugMsg(2, "replaced %s with %s", dbgQStr(pattern), dbgQStr(replace));
909 } else {
910 ProStringList varVal;
911 if (expandVariableReferences(tokPtr, sizeHint, &varVal, 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(2, "assigning");
921 break;
922 case TokAppendUnique: // *=
923 valuesRef(varName).insertUnique(varVal);
924 debugMsg(2, "appending unique");
925 break;
926 case TokAppend: // +=
927 varVal.removeEmpty();
928 valuesRef(varName) += varVal;
929 debugMsg(2, "appending");
930 break;
931 case TokRemove: // -=
932 if (!m_cumulative) {
933 valuesRef(varName).removeEach(varVal);
934 } else {
935 // We are stingy with our values.
936 }
937 debugMsg(2, "removing");
938 break;
939 }
940 }
941 traceMsg("%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(varName);
949 else if (varName == statics.strQMAKESPEC) {
950 if (!values(varName).isEmpty()) {
951 QString spec = values(varName).first().toQString();
952 if (IoUtils::isAbsolutePath(spec)) {
953 m_qmakespec = spec;
954 m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
955 m_featureRoots = nullptr;
956 }
957 }
958 }
959 #ifdef PROEVALUATOR_FULL
960 else if (varName == statics.strREQUIRES)
961 return checkRequirements(values(varName));
962 #endif
963
964 return ReturnTrue;
965 }
966
setTemplate()967 void QMakeEvaluator::setTemplate()
968 {
969 ProStringList &values = valuesRef(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(ProString("app"));
976 else
977 values.erase(values.begin() + 1, values.end());
978 }
979 if (!m_option->user_template_prefix.isEmpty()) {
980 ProString val = values.first();
981 if (!val.startsWith(m_option->user_template_prefix))
982 values = ProStringList(ProString(m_option->user_template_prefix + val));
983 }
984 }
985
986 #if defined(Q_CC_MSVC)
msvcBinDirToQMakeArch(QString subdir)987 static 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
defaultMsvcArchitecture()1009 static ProString defaultMsvcArchitecture()
1010 {
1011 #if defined(Q_OS_WIN64)
1012 return ProString("x86_64");
1013 #else
1014 return ProString("x86");
1015 #endif
1016 }
1017
msvcArchitecture(const QString & vcInstallDir,const QString & pathVar)1018 static 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
loadDefaults()1037 void 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) != -1) {
1098 vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
1099 vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(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
prepareProject(const QString & inDir)1109 bool 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(superfile, flags)) {
1123 m_superfile = QDir::cleanPath(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(conffile, flags))
1138 conffile.clear();
1139 cachefile = dir + QLatin1String("/.qmake.cache");
1140 if (!m_vfs->exists(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(conffile);
1161 m_cachefile = QDir::cleanPath(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(stashfile, flags)) {
1169 m_stashfile = QDir::cleanPath(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
loadSpecInternal()1181 bool QMakeEvaluator::loadSpecInternal()
1182 {
1183 if (evaluateFeatureFile(QLatin1String("spec_pre.prf")) != ReturnTrue)
1184 return false;
1185 QString spec = m_qmakespec + QLatin1String("/qmake.conf");
1186 if (evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
1187 evalError(fL1S("Could not read qmake configuration file %1.").arg(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(QLatin1String("/default-host"))
1194 || m_qmakespec.endsWith(QLatin1String("/default"))) {
1195 QString rspec = QFileInfo(m_qmakespec).symLinkTarget();
1196 if (!rspec.isEmpty())
1197 m_qmakespec = QDir::cleanPath(QDir(m_qmakespec).absoluteFilePath(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(ProKey("QMAKESPEC")) = ProString(m_qmakespec);
1212 m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
1213 // This also ensures that m_featureRoots is valid.
1214 if (evaluateFeatureFile(QLatin1String("spec_post.prf")) != ReturnTrue)
1215 return false;
1216 return true;
1217 }
1218
loadSpec()1219 bool QMakeEvaluator::loadSpec()
1220 {
1221 QString qmakespec = m_option->expandEnvVars(
1222 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 m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1231 return false;
1232 }
1233 if (!m_conffile.isEmpty() && evaluator.evaluateFile(
1234 m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1235 return false;
1236 }
1237 if (!m_cachefile.isEmpty() && evaluator.evaluateFile(
1238 m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
1239 return false;
1240 }
1241 if (qmakespec.isEmpty()) {
1242 if (!m_hostBuild)
1243 qmakespec = evaluator.first(ProKey("XQMAKESPEC")).toQString();
1244 if (qmakespec.isEmpty())
1245 qmakespec = evaluator.first(ProKey("QMAKESPEC")).toQString();
1246 }
1247 m_qmakepath = evaluator.values(ProKey("QMAKEPATH")).toQStringList();
1248 m_qmakefeatures = evaluator.values(ProKey("QMAKEFEATURES")).toQStringList();
1249 }
1250
1251 updateMkspecPaths();
1252 if (qmakespec.isEmpty())
1253 qmakespec = propertyValue(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(qmakespec)) {
1260 for (const QString &root : qAsConst(m_mkspecPaths)) {
1261 QString mkspec = root + QLatin1Char('/') + qmakespec;
1262 if (IoUtils::exists(mkspec)) {
1263 qmakespec = mkspec;
1264 goto cool;
1265 }
1266 }
1267 evalError(fL1S("Could not find qmake spec '%1'.").arg(qmakespec));
1268 return false;
1269 }
1270 cool:
1271 m_qmakespec = QDir::cleanPath(qmakespec);
1272
1273 if (!m_superfile.isEmpty()) {
1274 valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
1275 if (evaluateFile(
1276 m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue)
1277 return false;
1278 }
1279 if (!loadSpecInternal())
1280 return false;
1281 if (!m_conffile.isEmpty()) {
1282 valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
1283 if (evaluateFile(
1284 m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1285 return false;
1286 }
1287 if (!m_cachefile.isEmpty()) {
1288 valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
1289 if (evaluateFile(
1290 m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1291 return false;
1292 }
1293 QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
1294 if (!m_stashfile.isEmpty() && m_vfs->exists(m_stashfile, flags)) {
1295 valuesRef(ProKey("_QMAKE_STASH_")) << ProString(m_stashfile);
1296 if (evaluateFile(
1297 m_stashfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
1298 return false;
1299 }
1300 return true;
1301 }
1302
setupProject()1303 void 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
evaluateCommand(const QString & cmds,const QString & where)1314 void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where)
1315 {
1316 if (!cmds.isEmpty()) {
1317 ProFile *pro = m_parser->parsedProBlock(QStringRef(&cmds), 0, where, -1);
1318 if (pro->isOk()) {
1319 m_locationStack.push(m_current);
1320 visitProBlock(pro, pro->tokPtr());
1321 m_current = m_locationStack.pop();
1322 }
1323 pro->deref();
1324 }
1325 }
1326
applyExtraConfigs()1327 void QMakeEvaluator::applyExtraConfigs()
1328 {
1329 if (m_extraConfigs.isEmpty())
1330 return;
1331
1332 evaluateCommand(fL1S("CONFIG += ") + m_extraConfigs.join(QLatin1Char(' ')), fL1S("(extra configs)"));
1333 }
1334
evaluateConfigFeatures()1335 QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConfigFeatures()
1336 {
1337 QSet<QString> processed;
1338 forever {
1339 bool finished = true;
1340 ProStringList configs = values(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(config)) {
1345 config.detach();
1346 processed.insert(config);
1347 VisitReturn vr = evaluateFeatureFile(config, 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
visitProFile(ProFile * pro,QMakeHandler::EvalFileType type,LoadFlags flags)1362 QMakeEvaluator::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(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(baseEnv->evaluator);
1426 } else {
1427 if (!m_valuemapInited)
1428 loadDefaults();
1429 }
1430
1431 VisitReturn vr;
1432
1433 m_handler->aboutToEval(currentProFile(), pro, type);
1434 m_profileStack.push(pro);
1435 valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1436 if (flags & LoadPreFiles) {
1437 setupProject();
1438
1439 if (!m_option->extra_cmds[QMakeEvalEarly].isEmpty())
1440 evaluateCommand(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(it.key(), 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(QLatin1String("default_pre.prf"))) == ReturnError)
1451 goto failed;
1452
1453 if (!m_option->extra_cmds[QMakeEvalBefore].isEmpty()) {
1454 evaluateCommand(m_option->extra_cmds[QMakeEvalBefore], fL1S("(command line)"));
1455
1456 // Again, after user configs, to override them
1457 applyExtraConfigs();
1458 }
1459 }
1460
1461 debugMsg(1, "visiting file %s", qPrintable(pro->fileName()));
1462 if ((vr = visitProBlock(pro, pro->tokPtr())) == ReturnError)
1463 goto failed;
1464 debugMsg(1, "done visiting file %s", qPrintable(pro->fileName()));
1465
1466 if (flags & LoadPostFiles) {
1467 evaluateCommand(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(QLatin1String("default_post.prf"))) == ReturnError)
1475 goto failed;
1476
1477 if (!m_option->extra_cmds[QMakeEvalLate].isEmpty())
1478 evaluateCommand(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(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
1487 m_handler->doneWithEval(currentProFile());
1488
1489 return vr;
1490 }
1491
1492
updateMkspecPaths()1493 void QMakeEvaluator::updateMkspecPaths()
1494 {
1495 QStringList ret;
1496 const QString concat = QLatin1String("/mkspecs");
1497
1498 const auto paths = m_option->getPathListEnv(QLatin1String("QMAKEPATH"));
1499 for (const QString &it : paths)
1500 ret << it + concat;
1501
1502 for (const QString &it : qAsConst(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(ProKey("QT_HOST_DATA/get")) + concat;
1511 ret << m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + concat;
1512
1513 ret.removeDuplicates();
1514 m_mkspecPaths = ret;
1515 }
1516
updateFeaturePaths()1517 void 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(QLatin1String("QMAKEFEATURES"));
1525 feature_roots += m_qmakefeatures;
1526 feature_roots += m_option->splitPathList(
1527 m_option->propertyValue(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(QLatin1String("QMAKEPATH"));
1540 for (const QString &item : items)
1541 feature_bases << (item + mkspecs_concat);
1542
1543 for (const QString &item : qAsConst(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(mkspecs_concat)) {
1555 if (IoUtils::exists(specpath + features_concat))
1556 feature_bases << specpath;
1557 break;
1558 }
1559 }
1560 }
1561
1562 feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + mkspecs_concat);
1563 feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + mkspecs_concat);
1564
1565 for (const QString &fb : qAsConst(feature_bases)) {
1566 const auto sfxs = values(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(QLatin1Char('/')))
1574 feature_roots[i].append(QLatin1Char('/'));
1575
1576 feature_roots.removeDuplicates();
1577
1578 QStringList ret;
1579 for (const QString &root : qAsConst(feature_roots))
1580 if (IoUtils::exists(root))
1581 ret << root;
1582 m_featureRoots = new QMakeFeatureRoots(ret);
1583 }
1584
propertyValue(const ProKey & name) const1585 ProString QMakeEvaluator::propertyValue(const ProKey &name) const
1586 {
1587 if (name == QLatin1String("QMAKE_MKSPECS"))
1588 return ProString(m_mkspecPaths.join(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
currentProFile() const1595 ProFile *QMakeEvaluator::currentProFile() const
1596 {
1597 if (m_profileStack.count() > 0)
1598 return m_profileStack.top();
1599 return nullptr;
1600 }
1601
currentFileId() const1602 int QMakeEvaluator::currentFileId() const
1603 {
1604 ProFile *pro = currentProFile();
1605 if (pro)
1606 return pro->id();
1607 return 0;
1608 }
1609
currentFileName() const1610 QString QMakeEvaluator::currentFileName() const
1611 {
1612 ProFile *pro = currentProFile();
1613 if (pro)
1614 return pro->fileName();
1615 return QString();
1616 }
1617
currentDirectory() const1618 QString QMakeEvaluator::currentDirectory() const
1619 {
1620 ProFile *pro = currentProFile();
1621 if (pro)
1622 return pro->directoryName();
1623 return QString();
1624 }
1625
isActiveConfig(const QStringRef & config,bool regex)1626 bool 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(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
1638 QRegExp re(config.toString(), Qt::CaseSensitive, QRegExp::Wildcard);
1639
1640 // mkspecs
1641 if (re.exactMatch(m_qmakespecName))
1642 return true;
1643
1644 // CONFIG variable
1645 const auto configValues = values(statics.strCONFIG);
1646 for (const ProString &configValue : configValues) {
1647 ProStringRoUser u1(configValue, m_tmp[m_toggle ^= 1]);
1648 if (re.exactMatch(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(statics.strCONFIG).contains(config))
1658 return true;
1659 }
1660
1661 return false;
1662 }
1663
expandVariableReferences(const ushort * & tokPtr,int sizeHint,ProStringList * ret,bool joined)1664 QMakeEvaluator::VisitReturn QMakeEvaluator::expandVariableReferences(
1665 const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined)
1666 {
1667 ret->reserve(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
prepareFunctionArgs(const ushort * & tokPtr,QList<ProStringList> * ret)1689 QMakeEvaluator::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, &arg, 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
evaluateFunction(const ProFunctionDef & func,const QList<ProStringList> & argumentsList,ProStringList * ret)1707 QMakeEvaluator::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(ProValueMap());
1717 m_locationStack.push(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(func.pro(), 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
evaluateBoolFunction(const ProFunctionDef & func,const QList<ProStringList> & argumentsList,const ProString & function)1739 QMakeEvaluator::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);
1745 if (vr == ReturnTrue) {
1746 if (ret.isEmpty())
1747 return ReturnTrue;
1748 if (ret.at(0) != statics.strfalse) {
1749 if (ret.at(0) == statics.strtrue)
1750 return ReturnTrue;
1751 bool ok;
1752 int val = ret.at(0).toInt(&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(u1.str(), ret.join(QLatin1String(" :: "))));
1760 }
1761 }
1762 return ReturnFalse;
1763 }
1764 return vr;
1765 }
1766
evaluateConditionalFunction(const ProKey & func,const ushort * & tokPtr)1767 QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
1768 const ProKey &func, const ushort *&tokPtr)
1769 {
1770 auto adef = statics.functions.constFind(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, 5, &args, true) == ReturnError)
1775 return ReturnError;
1776 return evaluateBuiltinConditional(*adef, func, args);
1777 }
1778
1779 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1780 m_functionDefs.testFunctions.constFind(func);
1781 if (it != m_functionDefs.testFunctions.constEnd()) {
1782 QList<ProStringList> args;
1783 if (prepareFunctionArgs(tokPtr, &args) == ReturnError)
1784 return ReturnError;
1785 traceMsg("calling %s(%s)", dbgKey(func), dbgStrListList(args));
1786 return evaluateBoolFunction(*it, args, func);
1787 }
1788
1789 skipExpression(tokPtr);
1790 evalError(fL1S("'%1' is not a recognized test function.").arg(func.toQStringView()));
1791 return ReturnFalse;
1792 }
1793
evaluateExpandFunction(const ProKey & func,const ushort * & tokPtr,ProStringList * ret)1794 QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction(
1795 const ProKey &func, const ushort *&tokPtr, ProStringList *ret)
1796 {
1797 auto adef = statics.expands.constFind(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, 5, &args, true) == ReturnError)
1802 return ReturnError;
1803 return evaluateBuiltinExpand(*adef, func, args, *ret);
1804 }
1805
1806 QHash<ProKey, ProFunctionDef>::ConstIterator it =
1807 m_functionDefs.replaceFunctions.constFind(func);
1808 if (it != m_functionDefs.replaceFunctions.constEnd()) {
1809 QList<ProStringList> args;
1810 if (prepareFunctionArgs(tokPtr, &args) == ReturnError)
1811 return ReturnError;
1812 traceMsg("calling $$%s(%s)", dbgKey(func), dbgStrListList(args));
1813 return evaluateFunction(*it, args, ret);
1814 }
1815
1816 skipExpression(tokPtr);
1817 evalError(fL1S("'%1' is not a recognized replace function.").arg(func.toQStringView()));
1818 return ReturnFalse;
1819 }
1820
evaluateConditional(const QStringRef & cond,const QString & where,int line)1821 QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditional(
1822 const QStringRef &cond, const QString &where, int line)
1823 {
1824 VisitReturn ret = ReturnFalse;
1825 ProFile *pro = m_parser->parsedProBlock(cond, 0, where, line, QMakeParser::TestGrammar);
1826 if (pro->isOk()) {
1827 m_locationStack.push(m_current);
1828 ret = visitProBlock(pro, pro->tokPtr());
1829 m_current = m_locationStack.pop();
1830 }
1831 pro->deref();
1832 return ret;
1833 }
1834
1835 #ifdef PROEVALUATOR_FULL
checkRequirements(const ProStringList & deps)1836 QMakeEvaluator::VisitReturn QMakeEvaluator::checkRequirements(const ProStringList &deps)
1837 {
1838 ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS"));
1839 for (const ProString &dep : deps) {
1840 VisitReturn vr = evaluateConditional(dep.toQStringRef(), m_current.pro->fileName(), 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
isFunctParam(const ProKey & variableName)1850 static 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
findValues(const ProKey & variableName,ProValueMap::Iterator * rit)1862 ProValueMap *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(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
valuesRef(const ProKey & variableName)1882 ProStringList &QMakeEvaluator::valuesRef(const ProKey &variableName)
1883 {
1884 ProValueMap::Iterator it = m_valuemapStack.top().find(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(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
values(const ProKey & variableName) const1908 ProStringList 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(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
first(const ProKey & variableName) const1927 ProString 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
evaluateFile(const QString & fileName,QMakeHandler::EvalFileType type,LoadFlags flags)1935 QMakeEvaluator::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, pflags)) {
1942 m_locationStack.push(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(ifn))
1950 iif << ifn;
1951 }
1952 return ok;
1953 } else {
1954 return ReturnFalse;
1955 }
1956 }
1957
evaluateFileChecked(const QString & fileName,QMakeHandler::EvalFileType type,LoadFlags flags)1958 QMakeEvaluator::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(fileName));
1968 return ReturnFalse;
1969 }
1970 } while ((ref = ref->m_caller));
1971 return evaluateFile(fileName, type, flags);
1972 }
1973
evaluateFeatureFile(const QString & fileName,bool silent)1974 QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
1975 const QString &fileName, bool silent)
1976 {
1977 QString fn = fileName;
1978 if (!fn.endsWith(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(currFn) != IoUtils::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(fn, 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(currFn);
2007 for (int root = 0; root < paths.size(); ++root)
2008 if (currPath == paths.at(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(root) + fn;
2015 if (IoUtils::exists(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(fileName));
2039 return ReturnFalse;
2040 }
2041 ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
2042 ProString afn(fn);
2043 if (already.contains(afn)) {
2044 if (!silent)
2045 languageWarning(fL1S("Feature %1 already included").arg(fileName));
2046 return ReturnTrue;
2047 }
2048 already.append(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(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
2057
2058 #ifdef PROEVALUATOR_CUMULATIVE
2059 m_cumulative = cumulative;
2060 #endif
2061 return ok;
2062 }
2063
evaluateFileInto(const QString & fileName,ProValueMap * values,LoadFlags flags)2064 QMakeEvaluator::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, 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(qiif);
2078 for (const ProString &ifn : ifns)
2079 if (!iif.contains(ifn))
2080 iif << ifn;
2081 return ReturnTrue;
2082 }
2083
message(int type,const QString & msg) const2084 void QMakeEvaluator::message(int type, const QString &msg) const
2085 {
2086 if (!m_skipLevel)
2087 m_handler->message(type | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0), msg,
2088 m_current.line ? m_current.pro->fileName() : QString(),
2089 m_current.line != 0xffff ? m_current.line : -1);
2090 }
2091
2092 #ifdef PROEVALUATOR_DEBUG
debugMsgInternal(int level,const char * fmt,...) const2093 void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const
2094 {
2095 va_list ap;
2096
2097 if (level <= m_debugLevel) {
2098 fprintf(stderr, "DEBUG %d: ", level);
2099 va_start(ap, fmt);
2100 vfprintf(stderr, fmt, ap);
2101 va_end(ap);
2102 fputc('\n', stderr);
2103 }
2104 }
2105
traceMsgInternal(const char * fmt,...) const2106 void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const
2107 {
2108 va_list ap;
2109
2110 if (!m_current.pro)
2111 fprintf(stderr, "DEBUG 1: ");
2112 else if (m_current.line <= 0)
2113 fprintf(stderr, "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName()));
2114 else
2115 fprintf(stderr, "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line);
2116 va_start(ap, fmt);
2117 vfprintf(stderr, fmt, ap);
2118 va_end(ap);
2119 fputc('\n', stderr);
2120 }
2121
formatValue(const ProString & val,bool forceQuote)2122 QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote)
2123 {
2124 QString ret;
2125 ret.reserve(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("\\x%1").arg(uc, 2, 16, 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(QLatin1Char('"'));
2168 ret.append(QLatin1Char('"'));
2169 }
2170 return ret;
2171 }
2172
formatValueList(const ProStringList & vals,bool commas)2173 QString 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(str);
2184 }
2185 return ret;
2186 }
2187
formatValueListList(const QList<ProStringList> & lists)2188 QString 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(list);
2196 }
2197 return ret;
2198 }
2199 #endif
2200
2201 QT_END_NAMESPACE
2202