source: trunk/tools/linguist/shared/profileevaluator.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 96.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt Linguist of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "profileevaluator.h"
43#include "proparserutils.h"
44#include "proitems.h"
45
46#include <QtCore/QByteArray>
47#include <QtCore/QDateTime>
48#include <QtCore/QDebug>
49#include <QtCore/QDir>
50#include <QtCore/QFile>
51#include <QtCore/QFileInfo>
52#include <QtCore/QList>
53#include <QtCore/QRegExp>
54#include <QtCore/QSet>
55#include <QtCore/QStack>
56#include <QtCore/QString>
57#include <QtCore/QStringList>
58#include <QtCore/QTextStream>
59
60#if defined(Q_OS_UNIX)
61#include <unistd.h>
62#include <sys/utsname.h>
63#elif defined(Q_OS_WIN32)
64#include <Windows.h>
65#elif defined(Q_OS_OS2)
66#endif
67#include <stdio.h>
68#include <stdlib.h>
69
70#ifdef Q_OS_WIN32
71#define QT_POPEN _popen
72#define QT_PCLOSE _pclose
73#else
74#define QT_POPEN popen
75#define QT_PCLOSE pclose
76#endif
77
78QT_BEGIN_NAMESPACE
79
80///////////////////////////////////////////////////////////////////////
81//
82// Option
83//
84///////////////////////////////////////////////////////////////////////
85
86QString
87Option::fixString(QString string, uchar flags)
88{
89 // XXX Ripped out caching, so this will be slow. Should not matter for current uses.
90
91 //fix the environment variables
92 if (flags & Option::FixEnvVars) {
93 int rep;
94 QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)"));
95 reg_variableName.setMinimal(true);
96 while ((rep = reg_variableName.indexIn(string)) != -1)
97 string.replace(rep, reg_variableName.matchedLength(),
98 QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
99 }
100
101 //canonicalize it (and treat as a path)
102 if (flags & Option::FixPathCanonicalize) {
103#if 0
104 string = QFileInfo(string).canonicalFilePath();
105#endif
106 string = QDir::cleanPath(string);
107 }
108
109 if (string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':'))
110 string[0] = string[0].toLower();
111
112 //fix separators
113 Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators)));
114 if (flags & Option::FixPathToLocalSeparators) {
115#if defined(Q_OS_WIN32) || defined(Q_OS_OS2)
116 string = string.replace(QLatin1Char('/'), QLatin1Char('\\'));
117#else
118 string = string.replace(QLatin1Char('\\'), QLatin1Char('/'));
119#endif
120 } else if (flags & Option::FixPathToTargetSeparators) {
121 string = string.replace(QLatin1Char('/'), Option::dir_sep)
122 .replace(QLatin1Char('\\'), Option::dir_sep);
123 }
124
125 if ((string.startsWith(QLatin1Char('"')) && string.endsWith(QLatin1Char('"'))) ||
126 (string.startsWith(QLatin1Char('\'')) && string.endsWith(QLatin1Char('\''))))
127 string = string.mid(1, string.length() - 2);
128
129 return string;
130}
131
132///////////////////////////////////////////////////////////////////////
133//
134// ProFileEvaluator::Private
135//
136///////////////////////////////////////////////////////////////////////
137
138class ProFileEvaluator::Private : public AbstractProItemVisitor
139{
140public:
141 Private(ProFileEvaluator *q_);
142
143 ProFileEvaluator *q;
144 int m_lineNo; // Error reporting
145 bool m_verbose;
146
147 /////////////// Reading pro file
148
149 bool read(ProFile *pro);
150
151 ProBlock *currentBlock();
152 void updateItem();
153 bool parseLine(const QString &line);
154 void insertVariable(const QString &line, int *i);
155 void insertOperator(const char op);
156 void insertComment(const QString &comment);
157 void enterScope(bool multiLine);
158 void leaveScope();
159 void finalizeBlock();
160
161 QStack<ProBlock *> m_blockstack;
162 ProBlock *m_block;
163
164 ProItem *m_commentItem;
165 QString m_proitem;
166 QString m_pendingComment;
167 bool m_syntaxError;
168 bool m_contNextLine;
169 bool m_inQuote;
170 int m_parens;
171
172 /////////////// Evaluating pro file contents
173
174 // implementation of AbstractProItemVisitor
175 ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
176 void visitEndProBlock(ProBlock *block);
177 ProItem::ProItemReturn visitProLoopIteration();
178 void visitProLoopCleanup();
179 void visitBeginProVariable(ProVariable *variable);
180 void visitEndProVariable(ProVariable *variable);
181 ProItem::ProItemReturn visitBeginProFile(ProFile *value);
182 ProItem::ProItemReturn visitEndProFile(ProFile *value);
183 void visitProValue(ProValue *value);
184 ProItem::ProItemReturn visitProFunction(ProFunction *function);
185 void visitProOperator(ProOperator *oper);
186 void visitProCondition(ProCondition *condition);
187
188 QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
189 QStringList values(const QString &variableName) const;
190 QStringList values(const QString &variableName, const ProFile *pro) const;
191 QStringList values(const QString &variableName, const QHash<QString, QStringList> &place,
192 const ProFile *pro) const;
193 QString propertyValue(const QString &val) const;
194
195 bool isActiveConfig(const QString &config, bool regex = false);
196 QStringList expandVariableReferences(const QString &value);
197 void doVariableReplace(QString *str);
198 QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
199 QString format(const char *format) const;
200
201 QString currentFileName() const;
202 QString currentDirectory() const;
203 ProFile *currentProFile() const;
204
205 ProItem::ProItemReturn evaluateConditionalFunction(const QString &function, const QString &arguments);
206 bool evaluateFile(const QString &fileName);
207 bool evaluateFeatureFile(const QString &fileName);
208
209 static inline ProItem::ProItemReturn returnBool(bool b)
210 { return b ? ProItem::ReturnTrue : ProItem::ReturnFalse; }
211
212 QStringList evaluateFunction(ProBlock *funcPtr, const QStringList &argumentsList, bool *ok);
213
214 QStringList qmakeFeaturePaths();
215
216 struct State {
217 bool condition;
218 bool prevCondition;
219 } m_sts;
220 bool m_invertNext; // Short-lived, so not in State
221 int m_skipLevel;
222 bool m_cumulative;
223 bool m_isFirstVariableValue;
224 QString m_lastVarName;
225 ProVariable::VariableOperator m_variableOperator;
226 QString m_origfile;
227 QString m_oldPath; // To restore the current path to the path
228 QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
229 struct ProLoop {
230 QString variable;
231 QStringList oldVarVal;
232 QStringList list;
233 int index;
234 bool infinite;
235 };
236 QStack<ProLoop> m_loopStack;
237
238 // we need the following two variables for handling
239 // CONFIG = foo bar $$CONFIG
240 QHash<QString, QStringList> m_tempValuemap; // used while evaluating (variable operator value1 value2 ...)
241 QHash<const ProFile*, QHash<QString, QStringList> > m_tempFilevaluemap; // used while evaluating (variable operator value1 value2 ...)
242
243 QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii.
244 QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
245 QHash<QString, QString> m_properties;
246 QString m_outputDir;
247
248 bool m_definingTest;
249 QString m_definingFunc;
250 QHash<QString, ProBlock *> m_testFunctions;
251 QHash<QString, ProBlock *> m_replaceFunctions;
252 QStringList m_returnValue;
253 QStack<QHash<QString, QStringList> > m_valuemapStack;
254 QStack<QHash<const ProFile*, QHash<QString, QStringList> > > m_filevaluemapStack;
255
256 int m_prevLineNo; // Checking whether we're assigning the same TARGET
257 ProFile *m_prevProFile; // See m_prevLineNo
258};
259
260Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
261Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
262
263ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
264 : q(q_)
265{
266 // Global parser state
267 m_prevLineNo = 0;
268 m_prevProFile = 0;
269
270 // Configuration, more or less
271 m_verbose = true;
272 m_cumulative = true;
273
274 // Evaluator state
275 m_sts.condition = false;
276 m_sts.prevCondition = false;
277 m_invertNext = false;
278 m_skipLevel = 0;
279 m_isFirstVariableValue = true;
280 m_definingFunc.clear();
281}
282
283bool ProFileEvaluator::Private::read(ProFile *pro)
284{
285 QFile file(pro->fileName());
286 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
287 q->errorMessage(format("%1 not readable.").arg(pro->fileName()));
288 return false;
289 }
290
291 // Parser state
292 m_block = 0;
293 m_commentItem = 0;
294 m_inQuote = false;
295 m_parens = 0;
296 m_contNextLine = false;
297 m_syntaxError = false;
298 m_lineNo = 1;
299 m_blockstack.clear();
300 m_blockstack.push(pro);
301
302 QTextStream ts(&file);
303 while (!ts.atEnd()) {
304 QString line = ts.readLine();
305 if (!parseLine(line)) {
306 q->errorMessage(format(".pro parse failure."));
307 return false;
308 }
309 ++m_lineNo;
310 }
311 return true;
312}
313
314bool ProFileEvaluator::Private::parseLine(const QString &line0)
315{
316 if (m_blockstack.isEmpty())
317 return false;
318
319 int parens = m_parens;
320 bool inQuote = m_inQuote;
321 bool escaped = false;
322 QString line = line0.simplified();
323
324 for (int i = 0; !m_syntaxError && i < line.length(); ++i) {
325 ushort c = line.at(i).unicode();
326 if (c == '#') { // Yep - no escaping possible
327 insertComment(line.mid(i + 1));
328 escaped = m_contNextLine;
329 break;
330 }
331 if (!escaped) {
332 if (c == '\\') {
333 escaped = true;
334 m_proitem += c;
335 continue;
336 } else if (c == '"') {
337 inQuote = !inQuote;
338 m_proitem += c;
339 continue;
340 }
341 } else {
342 escaped = false;
343 }
344 if (!inQuote) {
345 if (c == '(') {
346 ++parens;
347 } else if (c == ')') {
348 --parens;
349 } else if (!parens) {
350 if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
351 if (c == ' ')
352 updateItem();
353 else
354 m_proitem += c;
355 continue;
356 }
357 if (c == ':') {
358 enterScope(false);
359 continue;
360 }
361 if (c == '{') {
362 enterScope(true);
363 continue;
364 }
365 if (c == '}') {
366 leaveScope();
367 continue;
368 }
369 if (c == '=') {
370 insertVariable(line, &i);
371 continue;
372 }
373 if (c == '|' || c == '!') {
374 insertOperator(c);
375 continue;
376 }
377 }
378 }
379
380 m_proitem += c;
381 }
382 m_inQuote = inQuote;
383 m_parens = parens;
384 m_contNextLine = escaped;
385 if (escaped) {
386 m_proitem.chop(1);
387 updateItem();
388 return true;
389 } else {
390 if (!m_syntaxError) {
391 updateItem();
392 finalizeBlock();
393 return true;
394 }
395 return false;
396 }
397}
398
399void ProFileEvaluator::Private::finalizeBlock()
400{
401 if (m_blockstack.isEmpty()) {
402 m_syntaxError = true;
403 } else {
404 if (m_blockstack.top()->blockKind() & ProBlock::SingleLine)
405 leaveScope();
406 m_block = 0;
407 m_commentItem = 0;
408 }
409}
410
411void ProFileEvaluator::Private::insertVariable(const QString &line, int *i)
412{
413 ProVariable::VariableOperator opkind;
414
415 if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker
416 return;
417
418 switch (m_proitem.at(m_proitem.length() - 1).unicode()) {
419 case '+':
420 m_proitem.chop(1);
421 opkind = ProVariable::AddOperator;
422 break;
423 case '-':
424 m_proitem.chop(1);
425 opkind = ProVariable::RemoveOperator;
426 break;
427 case '*':
428 m_proitem.chop(1);
429 opkind = ProVariable::UniqueAddOperator;
430 break;
431 case '~':
432 m_proitem.chop(1);
433 opkind = ProVariable::ReplaceOperator;
434 break;
435 default:
436 opkind = ProVariable::SetOperator;
437 }
438
439 ProBlock *block = m_blockstack.top();
440 m_proitem = m_proitem.trimmed();
441 ProVariable *variable = new ProVariable(m_proitem, block);
442 variable->setLineNumber(m_lineNo);
443 variable->setVariableOperator(opkind);
444 block->appendItem(variable);
445 m_block = variable;
446
447 if (!m_pendingComment.isEmpty()) {
448 m_block->setComment(m_pendingComment);
449 m_pendingComment.clear();
450 }
451 m_commentItem = variable;
452
453 m_proitem.clear();
454
455 if (opkind == ProVariable::ReplaceOperator) {
456 // skip util end of line or comment
457 while (1) {
458 ++(*i);
459
460 // end of line?
461 if (*i >= line.count())
462 break;
463
464 // comment?
465 if (line.at(*i).unicode() == '#') {
466 --(*i);
467 break;
468 }
469
470 m_proitem += line.at(*i);
471 }
472 m_proitem = m_proitem.trimmed();
473 }
474}
475
476void ProFileEvaluator::Private::insertOperator(const char op)
477{
478 updateItem();
479
480 ProOperator::OperatorKind opkind;
481 switch(op) {
482 case '!':
483 opkind = ProOperator::NotOperator;
484 break;
485 case '|':
486 opkind = ProOperator::OrOperator;
487 break;
488 default:
489 opkind = ProOperator::OrOperator;
490 }
491
492 ProBlock * const block = currentBlock();
493 ProOperator * const proOp = new ProOperator(opkind);
494 proOp->setLineNumber(m_lineNo);
495 block->appendItem(proOp);
496 m_commentItem = proOp;
497}
498
499void ProFileEvaluator::Private::insertComment(const QString &comment)
500{
501 updateItem();
502
503 QString strComment;
504 if (!m_commentItem)
505 strComment = m_pendingComment;
506 else
507 strComment = m_commentItem->comment();
508
509 if (strComment.isEmpty())
510 strComment = comment;
511 else {
512 strComment += QLatin1Char('\n');
513 strComment += comment.trimmed();
514 }
515
516 strComment = strComment.trimmed();
517
518 if (!m_commentItem)
519 m_pendingComment = strComment;
520 else
521 m_commentItem->setComment(strComment);
522}
523
524void ProFileEvaluator::Private::enterScope(bool multiLine)
525{
526 updateItem();
527
528 ProBlock *parent = currentBlock();
529 ProBlock *block = new ProBlock(parent);
530 block->setLineNumber(m_lineNo);
531 parent->setBlockKind(ProBlock::ScopeKind);
532
533 parent->appendItem(block);
534
535 if (multiLine)
536 block->setBlockKind(ProBlock::ScopeContentsKind);
537 else
538 block->setBlockKind(ProBlock::ScopeContentsKind|ProBlock::SingleLine);
539
540 m_blockstack.push(block);
541 m_block = 0;
542}
543
544void ProFileEvaluator::Private::leaveScope()
545{
546 updateItem();
547 m_blockstack.pop();
548 finalizeBlock();
549}
550
551ProBlock *ProFileEvaluator::Private::currentBlock()
552{
553 if (m_block)
554 return m_block;
555
556 ProBlock *parent = m_blockstack.top();
557 m_block = new ProBlock(parent);
558 m_block->setLineNumber(m_lineNo);
559 parent->appendItem(m_block);
560
561 if (!m_pendingComment.isEmpty()) {
562 m_block->setComment(m_pendingComment);
563 m_pendingComment.clear();
564 }
565
566 m_commentItem = m_block;
567
568 return m_block;
569}
570
571void ProFileEvaluator::Private::updateItem()
572{
573 m_proitem = m_proitem.trimmed();
574 if (m_proitem.isEmpty())
575 return;
576
577 ProBlock *block = currentBlock();
578 if (block->blockKind() & ProBlock::VariableKind) {
579 m_commentItem = new ProValue(m_proitem, static_cast<ProVariable*>(block));
580 } else if (m_proitem.endsWith(QLatin1Char(')'))) {
581 m_commentItem = new ProFunction(m_proitem);
582 } else {
583 m_commentItem = new ProCondition(m_proitem);
584 }
585 m_commentItem->setLineNumber(m_lineNo);
586 block->appendItem(m_commentItem);
587
588 m_proitem.clear();
589}
590
591
592ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
593{
594 if (block->blockKind() & ProBlock::ScopeContentsKind) {
595 if (!m_definingFunc.isEmpty()) {
596 if (!m_skipLevel || m_cumulative) {
597 QHash<QString, ProBlock *> *hash =
598 (m_definingTest ? &m_testFunctions : &m_replaceFunctions);
599 if (ProBlock *def = hash->value(m_definingFunc))
600 def->deref();
601 hash->insert(m_definingFunc, block);
602 block->ref();
603 block->setBlockKind(block->blockKind() | ProBlock::FunctionBodyKind);
604 }
605 m_definingFunc.clear();
606 return ProItem::ReturnSkip;
607 } else if (!(block->blockKind() & ProBlock::FunctionBodyKind)) {
608 if (!m_sts.condition)
609 ++m_skipLevel;
610 else
611 Q_ASSERT(!m_skipLevel);
612 }
613 } else {
614 if (!m_skipLevel) {
615 if (m_sts.condition) {
616 m_sts.prevCondition = true;
617 m_sts.condition = false;
618 }
619 } else {
620 Q_ASSERT(!m_sts.condition);
621 }
622 }
623 return ProItem::ReturnTrue;
624}
625
626void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
627{
628 if ((block->blockKind() & ProBlock::ScopeContentsKind)
629 && !(block->blockKind() & ProBlock::FunctionBodyKind)) {
630 if (m_skipLevel) {
631 Q_ASSERT(!m_sts.condition);
632 --m_skipLevel;
633 } else if (!(block->blockKind() & ProBlock::SingleLine)) {
634 // Conditionals contained inside this block may have changed the state.
635 // So we reset it here to make an else following us do the right thing.
636 m_sts.condition = true;
637 }
638 }
639}
640
641ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration()
642{
643 ProLoop &loop = m_loopStack.top();
644
645 if (loop.infinite) {
646 if (!loop.variable.isEmpty())
647 m_valuemap[loop.variable] = QStringList(QString::number(loop.index++));
648 if (loop.index > 1000) {
649 q->errorMessage(format("ran into infinite loop (> 1000 iterations)."));
650 return ProItem::ReturnFalse;
651 }
652 } else {
653 QString val;
654 do {
655 if (loop.index >= loop.list.count())
656 return ProItem::ReturnFalse;
657 val = loop.list.at(loop.index++);
658 } while (val.isEmpty()); // stupid, but qmake is like that
659 m_valuemap[loop.variable] = QStringList(val);
660 }
661 return ProItem::ReturnTrue;
662}
663
664void ProFileEvaluator::Private::visitProLoopCleanup()
665{
666 ProLoop &loop = m_loopStack.top();
667 m_valuemap[loop.variable] = loop.oldVarVal;
668 m_loopStack.pop_back();
669}
670
671void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
672{
673 m_lastVarName = variable->variable();
674 m_variableOperator = variable->variableOperator();
675 m_isFirstVariableValue = true;
676 m_tempValuemap = m_valuemap;
677 m_tempFilevaluemap = m_filevaluemap;
678}
679
680void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
681{
682 Q_UNUSED(variable);
683 m_valuemap = m_tempValuemap;
684 m_filevaluemap = m_tempFilevaluemap;
685 m_lastVarName.clear();
686}
687
688void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
689{
690 m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
691}
692
693void ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
694{
695 if (!m_skipLevel) {
696 if (!cond->text().compare(QLatin1String("else"), Qt::CaseInsensitive)) {
697 m_sts.condition = !m_sts.prevCondition;
698 } else {
699 m_sts.prevCondition = false;
700 if (!m_sts.condition && isActiveConfig(cond->text(), true) ^ m_invertNext)
701 m_sts.condition = true;
702 }
703 }
704 m_invertNext = false;
705}
706
707ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
708{
709 PRE(pro);
710 m_lineNo = pro->lineNumber();
711 if (m_origfile.isEmpty())
712 m_origfile = pro->fileName();
713 if (m_oldPath.isEmpty()) {
714 // change the working directory for the initial profile we visit, since
715 // that is *the* profile. All the other times we reach this function will be due to
716 // include(file) or load(file)
717
718 m_oldPath = QDir::currentPath();
719
720 m_profileStack.push(pro);
721
722 const QString mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
723 if (!mkspecDirectory.isEmpty()) {
724 bool cumulative = m_cumulative;
725 m_cumulative = false;
726 // This is what qmake does, everything set in the mkspec is also set
727 // But this also creates a lot of problems
728 evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"));
729 evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"));
730 m_cumulative = cumulative;
731 }
732
733 return returnBool(QDir::setCurrent(pro->directoryName()));
734 }
735
736 return ProItem::ReturnTrue;
737}
738
739ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
740{
741 PRE(pro);
742 m_lineNo = pro->lineNumber();
743 if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
744 const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
745 if (!mkspecDirectory.isEmpty()) {
746 bool cumulative = m_cumulative;
747 m_cumulative = false;
748
749 evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"));
750
751 QSet<QString> processed;
752 forever {
753 bool finished = true;
754 QStringList configs = valuesDirect(QLatin1String("CONFIG"));
755 for (int i = configs.size() - 1; i >= 0; --i) {
756 const QString config = configs[i].toLower();
757 if (!processed.contains(config)) {
758 processed.insert(config);
759 if (evaluateFile(mkspecDirectory + QLatin1String("/features/")
760 + config + QLatin1String(".prf"))) {
761 finished = false;
762 break;
763 }
764 }
765 }
766 if (finished)
767 break;
768 }
769
770 foreach (ProBlock *itm, m_replaceFunctions)
771 itm->deref();
772 m_replaceFunctions.clear();
773 foreach (ProBlock *itm, m_testFunctions)
774 itm->deref();
775 m_testFunctions.clear();
776
777 m_cumulative = cumulative;
778 }
779
780 m_profileStack.pop();
781 return returnBool(QDir::setCurrent(m_oldPath));
782 }
783
784 return ProItem::ReturnTrue;
785}
786
787static void replaceInList(QStringList *varlist,
788 const QRegExp &regexp, const QString &replace, bool global)
789{
790 for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
791 if ((*varit).contains(regexp)) {
792 (*varit).replace(regexp, replace);
793 if ((*varit).isEmpty())
794 varit = varlist->erase(varit);
795 else
796 ++varit;
797 if(!global)
798 break;
799 } else {
800 ++varit;
801 }
802 }
803}
804
805void ProFileEvaluator::Private::visitProValue(ProValue *value)
806{
807 PRE(value);
808 m_lineNo = value->lineNumber();
809 QString val = value->value();
810
811 QString varName = m_lastVarName;
812
813 QStringList v = expandVariableReferences(val);
814
815 // Since qmake combines different values for the TARGET variable, we join
816 // TARGET values that are on the same line. We can't do this later with all
817 // values because this parser isn't scope-aware, so we'd risk joining
818 // scope-specific targets together.
819 if (varName == QLatin1String("TARGET")
820 && m_lineNo == m_prevLineNo
821 && currentProFile() == m_prevProFile) {
822 QStringList targets = m_tempValuemap.value(QLatin1String("TARGET"));
823 m_tempValuemap.remove(QLatin1String("TARGET"));
824 QStringList lastTarget(targets.takeLast());
825 lastTarget << v.join(QLatin1String(" "));
826 targets.push_back(lastTarget.join(QLatin1String(" ")));
827 v = targets;
828 }
829 m_prevLineNo = m_lineNo;
830 m_prevProFile = currentProFile();
831
832 switch (m_variableOperator) {
833 case ProVariable::SetOperator: // =
834 if (!m_cumulative) {
835 if (!m_skipLevel) {
836 if (m_isFirstVariableValue) {
837 m_tempValuemap[varName] = v;
838 m_tempFilevaluemap[currentProFile()][varName] = v;
839 } else { // handle lines "CONFIG = foo bar"
840 m_tempValuemap[varName] += v;
841 m_tempFilevaluemap[currentProFile()][varName] += v;
842 }
843 }
844 } else {
845 // We are greedy for values.
846 m_tempValuemap[varName] += v;
847 m_tempFilevaluemap[currentProFile()][varName] += v;
848 }
849 break;
850 case ProVariable::UniqueAddOperator: // *=
851 if (!m_skipLevel || m_cumulative) {
852 insertUnique(&m_tempValuemap, varName, v);
853 insertUnique(&m_tempFilevaluemap[currentProFile()], varName, v);
854 }
855 break;
856 case ProVariable::AddOperator: // +=
857 if (!m_skipLevel || m_cumulative) {
858 m_tempValuemap[varName] += v;
859 m_tempFilevaluemap[currentProFile()][varName] += v;
860 }
861 break;
862 case ProVariable::RemoveOperator: // -=
863 if (!m_cumulative) {
864 if (!m_skipLevel) {
865 removeEach(&m_tempValuemap, varName, v);
866 removeEach(&m_tempFilevaluemap[currentProFile()], varName, v);
867 }
868 } else {
869 // We are stingy with our values, too.
870 }
871 break;
872 case ProVariable::ReplaceOperator: // ~=
873 {
874 // DEFINES ~= s/a/b/?[gqi]
875
876 doVariableReplace(&val);
877 if (val.length() < 4 || val[0] != QLatin1Char('s')) {
878 q->logMessage(format("the ~= operator can handle only the s/// function."));
879 break;
880 }
881 QChar sep = val.at(1);
882 QStringList func = val.split(sep);
883 if (func.count() < 3 || func.count() > 4) {
884 q->logMessage(format("the s/// function expects 3 or 4 arguments."));
885 break;
886 }
887
888 bool global = false, quote = false, case_sense = false;
889 if (func.count() == 4) {
890 global = func[3].indexOf(QLatin1Char('g')) != -1;
891 case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
892 quote = func[3].indexOf(QLatin1Char('q')) != -1;
893 }
894 QString pattern = func[1];
895 QString replace = func[2];
896 if (quote)
897 pattern = QRegExp::escape(pattern);
898
899 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
900
901 if (!m_skipLevel || m_cumulative) {
902 // We could make a union of modified and unmodified values,
903 // but this will break just as much as it fixes, so leave it as is.
904 replaceInList(&m_tempValuemap[varName], regexp, replace, global);
905 replaceInList(&m_tempFilevaluemap[currentProFile()][varName], regexp, replace, global);
906 }
907 }
908 break;
909
910 }
911 m_isFirstVariableValue = false;
912}
913
914ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
915{
916 // Make sure that called subblocks don't inherit & destroy the state
917 bool invertThis = m_invertNext;
918 m_invertNext = false;
919 if (!m_skipLevel)
920 m_sts.prevCondition = false;
921 if (m_cumulative || !m_sts.condition) {
922 QString text = func->text();
923 int lparen = text.indexOf(QLatin1Char('('));
924 int rparen = text.lastIndexOf(QLatin1Char(')'));
925 Q_ASSERT(lparen < rparen);
926 QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
927 QString funcName = text.left(lparen);
928 m_lineNo = func->lineNumber();
929 ProItem::ProItemReturn result = evaluateConditionalFunction(funcName.trimmed(), arguments);
930 if (result != ProItem::ReturnFalse && result != ProItem::ReturnTrue)
931 return result;
932 if (!m_skipLevel && ((result == ProItem::ReturnTrue) ^ invertThis))
933 m_sts.condition = true;
934 }
935 return ProItem::ReturnTrue;
936}
937
938
939QStringList ProFileEvaluator::Private::qmakeFeaturePaths()
940{
941 QStringList concat;
942 {
943 const QString base_concat = QDir::separator() + QLatin1String("features");
944 concat << base_concat + QDir::separator() + QLatin1String("mac");
945 concat << base_concat + QDir::separator() + QLatin1String("macx");
946 concat << base_concat + QDir::separator() + QLatin1String("unix");
947 concat << base_concat + QDir::separator() + QLatin1String("win32");
948 concat << base_concat + QDir::separator() + QLatin1String("mac9");
949 concat << base_concat + QDir::separator() + QLatin1String("qnx6");
950 concat << base_concat;
951 }
952 const QString mkspecs_concat = QDir::separator() + QLatin1String("mkspecs");
953 QStringList feature_roots;
954 QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
955 if (!mkspec_path.isNull())
956 feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
957 /*
958 if (prop)
959 feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
960 if (!Option::mkfile::cachefile.isEmpty()) {
961 QString path;
962 int last_slash = Option::mkfile::cachefile.lastIndexOf(Option::dir_sep);
963 if (last_slash != -1)
964 path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash));
965 foreach (const QString &concat_it, concat)
966 feature_roots << (path + concat_it);
967 }
968 */
969
970 QByteArray qmakepath = qgetenv("QMAKEPATH");
971 if (!qmakepath.isNull()) {
972 const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
973 foreach (const QString &item, lst) {
974 foreach (const QString &concat_it, concat)
975 feature_roots << (item + mkspecs_concat + concat_it);
976 }
977 }
978 //if (!Option::mkfile::qmakespec.isEmpty())
979 // feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
980 //if (!Option::mkfile::qmakespec.isEmpty()) {
981 // QFileInfo specfi(Option::mkfile::qmakespec);
982 // QDir specdir(specfi.absoluteFilePath());
983 // while (!specdir.isRoot()) {
984 // if (!specdir.cdUp() || specdir.isRoot())
985 // break;
986 // if (QFile::exists(specdir.path() + QDir::separator() + "features")) {
987 // foreach (const QString &concat_it, concat)
988 // feature_roots << (specdir.path() + concat_it);
989 // break;
990 // }
991 // }
992 //}
993 foreach (const QString &concat_it, concat)
994 feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
995 mkspecs_concat + concat_it);
996 foreach (const QString &concat_it, concat)
997 feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
998 mkspecs_concat + concat_it);
999 return feature_roots;
1000}
1001
1002QString ProFileEvaluator::Private::propertyValue(const QString &name) const
1003{
1004 if (m_properties.contains(name))
1005 return m_properties.value(name);
1006 if (name == QLatin1String("QT_INSTALL_PREFIX"))
1007 return QLibraryInfo::location(QLibraryInfo::PrefixPath);
1008 if (name == QLatin1String("QT_INSTALL_DATA"))
1009 return QLibraryInfo::location(QLibraryInfo::DataPath);
1010 if (name == QLatin1String("QT_INSTALL_DOCS"))
1011 return QLibraryInfo::location(QLibraryInfo::DocumentationPath);
1012 if (name == QLatin1String("QT_INSTALL_HEADERS"))
1013 return QLibraryInfo::location(QLibraryInfo::HeadersPath);
1014 if (name == QLatin1String("QT_INSTALL_LIBS"))
1015 return QLibraryInfo::location(QLibraryInfo::LibrariesPath);
1016 if (name == QLatin1String("QT_INSTALL_BINS"))
1017 return QLibraryInfo::location(QLibraryInfo::BinariesPath);
1018 if (name == QLatin1String("QT_INSTALL_PLUGINS"))
1019 return QLibraryInfo::location(QLibraryInfo::PluginsPath);
1020 if (name == QLatin1String("QT_INSTALL_TRANSLATIONS"))
1021 return QLibraryInfo::location(QLibraryInfo::TranslationsPath);
1022 if (name == QLatin1String("QT_INSTALL_CONFIGURATION"))
1023 return QLibraryInfo::location(QLibraryInfo::SettingsPath);
1024 if (name == QLatin1String("QT_INSTALL_EXAMPLES"))
1025 return QLibraryInfo::location(QLibraryInfo::ExamplesPath);
1026 if (name == QLatin1String("QT_INSTALL_DEMOS"))
1027 return QLibraryInfo::location(QLibraryInfo::DemosPath);
1028 if (name == QLatin1String("QMAKE_MKSPECS"))
1029 return qmake_mkspec_paths().join(Option::dirlist_sep);
1030 if (name == QLatin1String("QMAKE_VERSION"))
1031 return QLatin1String("1.0"); //### FIXME
1032 //return qmake_version();
1033#ifdef QT_VERSION_STR
1034 if (name == QLatin1String("QT_VERSION"))
1035 return QLatin1String(QT_VERSION_STR);
1036#endif
1037 return QLatin1String("UNKNOWN"); //###
1038}
1039
1040ProFile *ProFileEvaluator::Private::currentProFile() const
1041{
1042 if (m_profileStack.count() > 0)
1043 return m_profileStack.top();
1044 return 0;
1045}
1046
1047QString ProFileEvaluator::Private::currentFileName() const
1048{
1049 ProFile *pro = currentProFile();
1050 if (pro)
1051 return pro->fileName();
1052 return QString();
1053}
1054
1055QString ProFileEvaluator::Private::currentDirectory() const
1056{
1057 ProFile *cur = m_profileStack.top();
1058 return cur->directoryName();
1059}
1060
1061void ProFileEvaluator::Private::doVariableReplace(QString *str)
1062{
1063 *str = expandVariableReferences(*str).join(QString(Option::field_sep));
1064}
1065
1066QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
1067{
1068 QStringList ret;
1069// if (ok)
1070// *ok = true;
1071 if (str.isEmpty())
1072 return ret;
1073
1074 const ushort LSQUARE = '[';
1075 const ushort RSQUARE = ']';
1076 const ushort LCURLY = '{';
1077 const ushort RCURLY = '}';
1078 const ushort LPAREN = '(';
1079 const ushort RPAREN = ')';
1080 const ushort DOLLAR = '$';
1081 const ushort BACKSLASH = '\\';
1082 const ushort UNDERSCORE = '_';
1083 const ushort DOT = '.';
1084 const ushort SPACE = ' ';
1085 const ushort TAB = '\t';
1086 const ushort SINGLEQUOTE = '\'';
1087 const ushort DOUBLEQUOTE = '"';
1088
1089 ushort unicode, quote = 0;
1090 const QChar *str_data = str.data();
1091 const int str_len = str.length();
1092
1093 ushort term;
1094 QString var, args;
1095
1096 int replaced = 0;
1097 QString current;
1098 for (int i = 0; i < str_len; ++i) {
1099 unicode = str_data[i].unicode();
1100 const int start_var = i;
1101 if (unicode == DOLLAR && str_len > i+2) {
1102 unicode = str_data[++i].unicode();
1103 if (unicode == DOLLAR) {
1104 term = 0;
1105 var.clear();
1106 args.clear();
1107 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
1108 unicode = str_data[++i].unicode();
1109 if (unicode == LSQUARE) {
1110 unicode = str_data[++i].unicode();
1111 term = RSQUARE;
1112 var_type = PROPERTY;
1113 } else if (unicode == LCURLY) {
1114 unicode = str_data[++i].unicode();
1115 var_type = VAR;
1116 term = RCURLY;
1117 } else if (unicode == LPAREN) {
1118 unicode = str_data[++i].unicode();
1119 var_type = ENVIRON;
1120 term = RPAREN;
1121 }
1122 forever {
1123 if (!(unicode & (0xFF<<8)) &&
1124 unicode != DOT && unicode != UNDERSCORE &&
1125 //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
1126 (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
1127 (unicode < '0' || unicode > '9'))
1128 break;
1129 var.append(QChar(unicode));
1130 if (++i == str_len)
1131 break;
1132 unicode = str_data[i].unicode();
1133 // at this point, i points to either the 'term' or 'next' character (which is in unicode)
1134 }
1135 if (var_type == VAR && unicode == LPAREN) {
1136 var_type = FUNCTION;
1137 int depth = 0;
1138 forever {
1139 if (++i == str_len)
1140 break;
1141 unicode = str_data[i].unicode();
1142 if (unicode == LPAREN) {
1143 depth++;
1144 } else if (unicode == RPAREN) {
1145 if (!depth)
1146 break;
1147 --depth;
1148 }
1149 args.append(QChar(unicode));
1150 }
1151 if (++i < str_len)
1152 unicode = str_data[i].unicode();
1153 else
1154 unicode = 0;
1155 // at this point i is pointing to the 'next' character (which is in unicode)
1156 // this might actually be a term character since you can do $${func()}
1157 }
1158 if (term) {
1159 if (unicode != term) {
1160 q->logMessage(format("Missing %1 terminator [found %2]")
1161 .arg(QChar(term))
1162 .arg(unicode ? QString(unicode) : QString::fromLatin1(("end-of-line"))));
1163// if (ok)
1164// *ok = false;
1165 return QStringList();
1166 }
1167 } else {
1168 // move the 'cursor' back to the last char of the thing we were looking at
1169 --i;
1170 }
1171 // since i never points to the 'next' character, there is no reason for this to be set
1172 unicode = 0;
1173
1174 QStringList replacement;
1175 if (var_type == ENVIRON) {
1176 replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
1177 } else if (var_type == PROPERTY) {
1178 replacement << propertyValue(var);
1179 } else if (var_type == FUNCTION) {
1180 replacement << evaluateExpandFunction(var, args);
1181 } else if (var_type == VAR) {
1182 replacement = values(var);
1183 }
1184 if (!(replaced++) && start_var)
1185 current = str.left(start_var);
1186 if (!replacement.isEmpty()) {
1187 if (quote) {
1188 current += replacement.join(QString(Option::field_sep));
1189 } else {
1190 current += replacement.takeFirst();
1191 if (!replacement.isEmpty()) {
1192 if (!current.isEmpty())
1193 ret.append(current);
1194 current = replacement.takeLast();
1195 if (!replacement.isEmpty())
1196 ret += replacement;
1197 }
1198 }
1199 }
1200 } else {
1201 if (replaced)
1202 current.append(QLatin1Char('$'));
1203 }
1204 }
1205 if (quote && unicode == quote) {
1206 unicode = 0;
1207 quote = 0;
1208 } else if (unicode == BACKSLASH) {
1209 bool escape = false;
1210 const char *symbols = "[]{}()$\\'\"";
1211 for (const char *s = symbols; *s; ++s) {
1212 if (str_data[i+1].unicode() == (ushort)*s) {
1213 i++;
1214 escape = true;
1215 if (!(replaced++))
1216 current = str.left(start_var);
1217 current.append(str.at(i));
1218 break;
1219 }
1220 }
1221 if (escape || !replaced)
1222 unicode =0;
1223 } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
1224 quote = unicode;
1225 unicode = 0;
1226 if (!(replaced++) && i)
1227 current = str.left(i);
1228 } else if (!quote && (unicode == SPACE || unicode == TAB)) {
1229 unicode = 0;
1230 if (!current.isEmpty()) {
1231 ret.append(current);
1232 current.clear();
1233 }
1234 }
1235 if (replaced && unicode)
1236 current.append(QChar(unicode));
1237 }
1238 if (!replaced)
1239 ret = QStringList(str);
1240 else if (!current.isEmpty())
1241 ret.append(current);
1242 return ret;
1243}
1244
1245bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
1246{
1247 // magic types for easy flipping
1248 if (config == QLatin1String("true"))
1249 return true;
1250 if (config == QLatin1String("false"))
1251 return false;
1252
1253 // mkspecs
1254 if ((Option::target_mode == Option::TARG_MACX_MODE
1255 || Option::target_mode == Option::TARG_QNX6_MODE
1256 || Option::target_mode == Option::TARG_UNIX_MODE)
1257 && config == QLatin1String("unix"))
1258 return true;
1259 if (Option::target_mode == Option::TARG_MACX_MODE && config == QLatin1String("macx"))
1260 return true;
1261 if (Option::target_mode == Option::TARG_QNX6_MODE && config == QLatin1String("qnx6"))
1262 return true;
1263 if (Option::target_mode == Option::TARG_MAC9_MODE && config == QLatin1String("mac9"))
1264 return true;
1265 if ((Option::target_mode == Option::TARG_MAC9_MODE
1266 || Option::target_mode == Option::TARG_MACX_MODE)
1267 && config == QLatin1String("mac"))
1268 return true;
1269 if (Option::target_mode == Option::TARG_WIN_MODE && config == QLatin1String("win32"))
1270 return true;
1271
1272 QRegExp re(config, Qt::CaseSensitive, QRegExp::Wildcard);
1273 QString spec = Option::qmakespec;
1274 if ((regex && re.exactMatch(spec)) || (!regex && spec == config))
1275 return true;
1276
1277 return false;
1278}
1279
1280QStringList ProFileEvaluator::Private::evaluateFunction(
1281 ProBlock *funcPtr, const QStringList &argumentsList, bool *ok)
1282{
1283 bool oki;
1284 QStringList ret;
1285
1286 if (m_valuemapStack.count() >= 100) {
1287 q->errorMessage(format("ran into infinite recursion (depth > 100)."));
1288 oki = false;
1289 } else {
1290 State sts = m_sts;
1291 m_valuemapStack.push(m_valuemap);
1292 m_filevaluemapStack.push(m_filevaluemap);
1293
1294 QStringList args;
1295 for (int i = 0; i < argumentsList.count(); ++i) {
1296 QStringList theArgs = expandVariableReferences(argumentsList[i]);
1297 args += theArgs;
1298 m_valuemap[QString::number(i+1)] = theArgs;
1299 }
1300 m_valuemap[QLatin1String("ARGS")] = args;
1301 oki = (funcPtr->Accept(this) != ProItem::ReturnFalse); // True || Return
1302 ret = m_returnValue;
1303 m_returnValue.clear();
1304
1305 m_valuemap = m_valuemapStack.pop();
1306 m_filevaluemap = m_filevaluemapStack.pop();
1307 m_sts = sts;
1308 }
1309 if (ok)
1310 *ok = oki;
1311 if (oki)
1312 return ret;
1313 return QStringList();
1314}
1315
1316QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
1317{
1318 QStringList argumentsList = split_arg_list(arguments);
1319
1320 if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0))
1321 return evaluateFunction(funcPtr, argumentsList, 0);
1322
1323 QStringList args;
1324 for (int i = 0; i < argumentsList.count(); ++i)
1325 args += expandVariableReferences(argumentsList[i]).join(Option::field_sep);
1326
1327 enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
1328 E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
1329 E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
1330 E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
1331 E_REPLACE };
1332
1333 static QHash<QString, int> expands;
1334 if (expands.isEmpty()) {
1335 expands.insert(QLatin1String("member"), E_MEMBER);
1336 expands.insert(QLatin1String("first"), E_FIRST);
1337 expands.insert(QLatin1String("last"), E_LAST);
1338 expands.insert(QLatin1String("cat"), E_CAT);
1339 expands.insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below)
1340 expands.insert(QLatin1String("eval"), E_EVAL);
1341 expands.insert(QLatin1String("list"), E_LIST);
1342 expands.insert(QLatin1String("sprintf"), E_SPRINTF);
1343 expands.insert(QLatin1String("join"), E_JOIN);
1344 expands.insert(QLatin1String("split"), E_SPLIT);
1345 expands.insert(QLatin1String("basename"), E_BASENAME);
1346 expands.insert(QLatin1String("dirname"), E_DIRNAME);
1347 expands.insert(QLatin1String("section"), E_SECTION);
1348 expands.insert(QLatin1String("find"), E_FIND);
1349 expands.insert(QLatin1String("system"), E_SYSTEM);
1350 expands.insert(QLatin1String("unique"), E_UNIQUE);
1351 expands.insert(QLatin1String("quote"), E_QUOTE);
1352 expands.insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
1353 expands.insert(QLatin1String("upper"), E_UPPER);
1354 expands.insert(QLatin1String("lower"), E_LOWER);
1355 expands.insert(QLatin1String("re_escape"), E_RE_ESCAPE);
1356 expands.insert(QLatin1String("files"), E_FILES);
1357 expands.insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented
1358 expands.insert(QLatin1String("replace"), E_REPLACE);
1359 }
1360 ExpandFunc func_t = ExpandFunc(expands.value(func.toLower()));
1361
1362 QStringList ret;
1363
1364 switch (func_t) {
1365 case E_BASENAME:
1366 case E_DIRNAME:
1367 case E_SECTION: {
1368 bool regexp = false;
1369 QString sep, var;
1370 int beg = 0;
1371 int end = -1;
1372 if (func_t == E_SECTION) {
1373 if (args.count() != 3 && args.count() != 4) {
1374 q->logMessage(format("%1(var) section(var, sep, begin, end) "
1375 "requires three or four arguments.").arg(func));
1376 } else {
1377 var = args[0];
1378 sep = args[1];
1379 beg = args[2].toInt();
1380 if (args.count() == 4)
1381 end = args[3].toInt();
1382 }
1383 } else {
1384 if (args.count() != 1) {
1385 q->logMessage(format("%1(var) requires one argument.").arg(func));
1386 } else {
1387 var = args[0];
1388 regexp = true;
1389 sep = QLatin1String("[\\\\/]");
1390 if (func_t == E_DIRNAME)
1391 end = -2;
1392 else
1393 beg = -1;
1394 }
1395 }
1396 if (!var.isNull()) {
1397 foreach (const QString str, values(var)) {
1398 if (regexp)
1399 ret += str.section(QRegExp(sep), beg, end);
1400 else
1401 ret += str.section(sep, beg, end);
1402 }
1403 }
1404 break;
1405 }
1406 case E_SPRINTF:
1407 if(args.count() < 1) {
1408 q->logMessage(format("sprintf(format, ...) requires at least one argument"));
1409 } else {
1410 QString tmp = args.at(0);
1411 for (int i = 1; i < args.count(); ++i)
1412 tmp = tmp.arg(args.at(i));
1413 ret = split_value_list(tmp);
1414 }
1415 break;
1416 case E_JOIN: {
1417 if (args.count() < 1 || args.count() > 4) {
1418 q->logMessage(format("join(var, glue, before, after) requires one to four arguments."));
1419 } else {
1420 QString glue, before, after;
1421 if (args.count() >= 2)
1422 glue = args[1];
1423 if (args.count() >= 3)
1424 before = args[2];
1425 if (args.count() == 4)
1426 after = args[3];
1427 const QStringList &var = values(args.first());
1428 if (!var.isEmpty())
1429 ret.append(before + var.join(glue) + after);
1430 }
1431 break;
1432 }
1433 case E_SPLIT: {
1434 if (args.count() != 2) {
1435 q->logMessage(format("split(var, sep) requires one or two arguments"));
1436 } else {
1437 const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep);
1438 foreach (const QString &var, values(args.first()))
1439 foreach (const QString &splt, var.split(sep))
1440 ret.append(splt);
1441 }
1442 break;
1443 }
1444 case E_MEMBER: {
1445 if (args.count() < 1 || args.count() > 3) {
1446 q->logMessage(format("member(var, start, end) requires one to three arguments."));
1447 } else {
1448 bool ok = true;
1449 const QStringList var = values(args.first());
1450 int start = 0, end = 0;
1451 if (args.count() >= 2) {
1452 QString start_str = args[1];
1453 start = start_str.toInt(&ok);
1454 if (!ok) {
1455 if (args.count() == 2) {
1456 int dotdot = start_str.indexOf(QLatin1String(".."));
1457 if (dotdot != -1) {
1458 start = start_str.left(dotdot).toInt(&ok);
1459 if (ok)
1460 end = start_str.mid(dotdot+2).toInt(&ok);
1461 }
1462 }
1463 if (!ok)
1464 q->logMessage(format("member() argument 2 (start) '%2' invalid.")
1465 .arg(start_str));
1466 } else {
1467 end = start;
1468 if (args.count() == 3)
1469 end = args[2].toInt(&ok);
1470 if (!ok)
1471 q->logMessage(format("member() argument 3 (end) '%2' invalid.\n")
1472 .arg(args[2]));
1473 }
1474 }
1475 if (ok) {
1476 if (start < 0)
1477 start += var.count();
1478 if (end < 0)
1479 end += var.count();
1480 if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
1481 //nothing
1482 } else if (start < end) {
1483 for (int i = start; i <= end && var.count() >= i; i++)
1484 ret.append(var[i]);
1485 } else {
1486 for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
1487 ret += var[i];
1488 }
1489 }
1490 }
1491 break;
1492 }
1493 case E_FIRST:
1494 case E_LAST: {
1495 if (args.count() != 1) {
1496 q->logMessage(format("%1(var) requires one argument.").arg(func));
1497 } else {
1498 const QStringList var = values(args.first());
1499 if (!var.isEmpty()) {
1500 if (func_t == E_FIRST)
1501 ret.append(var[0]);
1502 else
1503 ret.append(var.last());
1504 }
1505 }
1506 break;
1507 }
1508 case E_CAT:
1509 if (args.count() < 1 || args.count() > 2) {
1510 q->logMessage(format("cat(file, singleline=true) requires one or two arguments."));
1511 } else {
1512 QString file = args[0];
1513 file = Option::fixPathToLocalOS(file);
1514
1515 bool singleLine = true;
1516 if (args.count() > 1)
1517 singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
1518
1519 QFile qfile(file);
1520 if (qfile.open(QIODevice::ReadOnly)) {
1521 QTextStream stream(&qfile);
1522 while (!stream.atEnd()) {
1523 ret += split_value_list(stream.readLine().trimmed());
1524 if (!singleLine)
1525 ret += QLatin1String("\n");
1526 }
1527 qfile.close();
1528 }
1529 }
1530 break;
1531#if 0 // Used only by Qt's configure for caching
1532 case E_FROMFILE:
1533 if (args.count() != 2) {
1534 q->logMessage(format("fromfile(file, variable) requires two arguments."));
1535 } else {
1536 QString file = args[0], seek_variableName = args[1];
1537
1538 ProFile pro(Option::fixPathToLocalOS(file));
1539
1540 ProFileEvaluator visitor;
1541 visitor.setVerbose(m_verbose);
1542 visitor.setCumulative(m_cumulative);
1543
1544 if (!visitor.queryProFile(&pro))
1545 break;
1546
1547 if (!visitor.accept(&pro))
1548 break;
1549
1550 ret = visitor.values(seek_variableName);
1551 }
1552 break;
1553#endif
1554 case E_EVAL: {
1555 if (args.count() != 1) {
1556 q->logMessage(format("eval(variable) requires one argument"));
1557
1558 } else {
1559 ret += values(args.at(0));
1560 }
1561 break; }
1562 case E_LIST: {
1563 static int x = 0;
1564 QString tmp;
1565 tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", x++);
1566 ret = QStringList(tmp);
1567 QStringList lst;
1568 foreach (const QString &arg, args)
1569 lst += split_value_list(arg);
1570 m_valuemap[tmp] = lst;
1571 break; }
1572 case E_FIND:
1573 if (args.count() != 2) {
1574 q->logMessage(format("find(var, str) requires two arguments."));
1575 } else {
1576 QRegExp regx(args[1]);
1577 foreach (const QString &val, values(args.first()))
1578 if (regx.indexIn(val) != -1)
1579 ret += val;
1580 }
1581 break;
1582 case E_SYSTEM:
1583 if (!m_skipLevel) {
1584 if (args.count() < 1 || args.count() > 2) {
1585 q->logMessage(format("system(execute) requires one or two arguments."));
1586 } else {
1587 char buff[256];
1588 FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
1589 bool singleLine = true;
1590 if (args.count() > 1)
1591 singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
1592 QString output;
1593 while (proc && !feof(proc)) {
1594 int read_in = int(fread(buff, 1, 255, proc));
1595 if (!read_in)
1596 break;
1597 for (int i = 0; i < read_in; i++) {
1598 if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
1599 buff[i] = ' ';
1600 }
1601 buff[read_in] = '\0';
1602 output += QLatin1String(buff);
1603 }
1604 ret += split_value_list(output);
1605 if (proc)
1606 QT_PCLOSE(proc);
1607 }
1608 }
1609 break;
1610 case E_UNIQUE:
1611 if(args.count() != 1) {
1612 q->logMessage(format("unique(var) requires one argument."));
1613 } else {
1614 foreach (const QString &var, values(args.first()))
1615 if (!ret.contains(var))
1616 ret.append(var);
1617 }
1618 break;
1619 case E_QUOTE:
1620 for (int i = 0; i < args.count(); ++i)
1621 ret += QStringList(args.at(i));
1622 break;
1623 case E_ESCAPE_EXPAND:
1624 for (int i = 0; i < args.size(); ++i) {
1625 QChar *i_data = args[i].data();
1626 int i_len = args[i].length();
1627 for (int x = 0; x < i_len; ++x) {
1628 if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
1629 if (*(i_data+x+1) == QLatin1Char('\\')) {
1630 ++x;
1631 } else {
1632 struct {
1633 char in, out;
1634 } mapped_quotes[] = {
1635 { 'n', '\n' },
1636 { 't', '\t' },
1637 { 'r', '\r' },
1638 { 0, 0 }
1639 };
1640 for (int i = 0; mapped_quotes[i].in; ++i) {
1641 if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
1642 *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
1643 if (x < i_len-2)
1644 memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
1645 --i_len;
1646 break;
1647 }
1648 }
1649 }
1650 }
1651 }
1652 ret.append(QString(i_data, i_len));
1653 }
1654 break;
1655 case E_RE_ESCAPE:
1656 for (int i = 0; i < args.size(); ++i)
1657 ret += QRegExp::escape(args[i]);
1658 break;
1659 case E_UPPER:
1660 case E_LOWER:
1661 for (int i = 0; i < args.count(); ++i)
1662 if (func_t == E_UPPER)
1663 ret += args[i].toUpper();
1664 else
1665 ret += args[i].toLower();
1666 break;
1667 case E_FILES:
1668 if (args.count() != 1 && args.count() != 2) {
1669 q->logMessage(format("files(pattern, recursive=false) requires one or two arguments"));
1670 } else {
1671 bool recursive = false;
1672 if (args.count() == 2)
1673 recursive = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive) || args[1].toInt());
1674 QStringList dirs;
1675 QString r = Option::fixPathToLocalOS(args[0]);
1676 int slash = r.lastIndexOf(QDir::separator());
1677 if (slash != -1) {
1678 dirs.append(r.left(slash));
1679 r = r.mid(slash+1);
1680 } else {
1681 dirs.append(QString());
1682 }
1683
1684 const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
1685 for (int d = 0; d < dirs.count(); d++) {
1686 QString dir = dirs[d];
1687 if (!dir.isEmpty() && !dir.endsWith(Option::dir_sep))
1688 dir += QLatin1Char('/');
1689
1690 QDir qdir(dir);
1691 for (int i = 0; i < (int)qdir.count(); ++i) {
1692 if (qdir[i] == QLatin1String(".") || qdir[i] == QLatin1String(".."))
1693 continue;
1694 QString fname = dir + qdir[i];
1695 if (QFileInfo(fname).isDir()) {
1696 if (recursive)
1697 dirs.append(fname);
1698 }
1699 if (regex.exactMatch(qdir[i]))
1700 ret += fname;
1701 }
1702 }
1703 }
1704 break;
1705 case E_REPLACE:
1706 if(args.count() != 3 ) {
1707 q->logMessage(format("replace(var, before, after) requires three arguments"));
1708 } else {
1709 const QRegExp before(args[1]);
1710 const QString after(args[2]);
1711 foreach (QString val, values(args.first()))
1712 ret += val.replace(before, after);
1713 }
1714 break;
1715 case 0:
1716 q->logMessage(format("'%1' is not a recognized replace function").arg(func));
1717 break;
1718 default:
1719 q->logMessage(format("Function '%1' is not implemented").arg(func));
1720 break;
1721 }
1722
1723 return ret;
1724}
1725
1726ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
1727 const QString &function, const QString &arguments)
1728{
1729 QStringList argumentsList = split_arg_list(arguments);
1730
1731 if (ProBlock *funcPtr = m_testFunctions.value(function, 0)) {
1732 bool ok;
1733 QStringList ret = evaluateFunction(funcPtr, argumentsList, &ok);
1734 if (ok) {
1735 if (ret.isEmpty()) {
1736 return ProItem::ReturnTrue;
1737 } else {
1738 if (ret.first() != QLatin1String("false")) {
1739 if (ret.first() == QLatin1String("true")) {
1740 return ProItem::ReturnTrue;
1741 } else {
1742 bool ok;
1743 int val = ret.first().toInt(&ok);
1744 if (ok) {
1745 if (val)
1746 return ProItem::ReturnTrue;
1747 } else {
1748 q->logMessage(format("Unexpected return value from test '%1': %2")
1749 .arg(function).arg(ret.join(QLatin1String(" :: "))));
1750 }
1751 }
1752 }
1753 }
1754 }
1755 return ProItem::ReturnFalse;
1756 }
1757
1758 QString sep;
1759 sep.append(Option::field_sep);
1760 QStringList args;
1761 for (int i = 0; i < argumentsList.count(); ++i)
1762 args += expandVariableReferences(argumentsList[i]).join(sep);
1763
1764 enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
1765 T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
1766 T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
1767 T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF,
1768 T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE };
1769
1770 static QHash<QString, int> functions;
1771 if (functions.isEmpty()) {
1772 functions.insert(QLatin1String("requires"), T_REQUIRES);
1773 functions.insert(QLatin1String("greaterThan"), T_GREATERTHAN);
1774 functions.insert(QLatin1String("lessThan"), T_LESSTHAN);
1775 functions.insert(QLatin1String("equals"), T_EQUALS);
1776 functions.insert(QLatin1String("isEqual"), T_EQUALS);
1777 functions.insert(QLatin1String("exists"), T_EXISTS);
1778 functions.insert(QLatin1String("export"), T_EXPORT);
1779 functions.insert(QLatin1String("clear"), T_CLEAR);
1780 functions.insert(QLatin1String("unset"), T_UNSET);
1781 functions.insert(QLatin1String("eval"), T_EVAL);
1782 functions.insert(QLatin1String("CONFIG"), T_CONFIG);
1783 functions.insert(QLatin1String("if"), T_IF);
1784 functions.insert(QLatin1String("isActiveConfig"), T_CONFIG);
1785 functions.insert(QLatin1String("system"), T_SYSTEM);
1786 functions.insert(QLatin1String("return"), T_RETURN);
1787 functions.insert(QLatin1String("break"), T_BREAK);
1788 functions.insert(QLatin1String("next"), T_NEXT);
1789 functions.insert(QLatin1String("defined"), T_DEFINED);
1790 functions.insert(QLatin1String("contains"), T_CONTAINS);
1791 functions.insert(QLatin1String("infile"), T_INFILE);
1792 functions.insert(QLatin1String("count"), T_COUNT);
1793 functions.insert(QLatin1String("isEmpty"), T_ISEMPTY);
1794 functions.insert(QLatin1String("load"), T_LOAD); //v
1795 functions.insert(QLatin1String("include"), T_INCLUDE); //v
1796 functions.insert(QLatin1String("debug"), T_DEBUG);
1797 functions.insert(QLatin1String("message"), T_MESSAGE); //v
1798 functions.insert(QLatin1String("warning"), T_MESSAGE); //v
1799 functions.insert(QLatin1String("error"), T_MESSAGE); //v
1800 functions.insert(QLatin1String("for"), T_FOR); //v
1801 functions.insert(QLatin1String("defineTest"), T_DEFINE_TEST); //v
1802 functions.insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE); //v
1803 }
1804
1805 TestFunc func_t = (TestFunc)functions.value(function);
1806
1807 switch (func_t) {
1808 case T_DEFINE_TEST:
1809 m_definingTest = true;
1810 goto defineFunc;
1811 case T_DEFINE_REPLACE:
1812 m_definingTest = false;
1813 defineFunc:
1814 if (args.count() != 1) {
1815 q->logMessage(format("%s(function) requires one argument.").arg(function));
1816 return ProItem::ReturnFalse;
1817 }
1818 m_definingFunc = args.first();
1819 return ProItem::ReturnTrue;
1820 case T_DEFINED:
1821 if (args.count() < 1 || args.count() > 2) {
1822 q->logMessage(format("defined(function, [\"test\"|\"replace\"])"
1823 " requires one or two arguments."));
1824 return ProItem::ReturnFalse;
1825 }
1826 if (args.count() > 1) {
1827 if (args[1] == QLatin1String("test"))
1828 return returnBool(m_testFunctions.contains(args[0]));
1829 else if (args[1] == QLatin1String("replace"))
1830 return returnBool(m_replaceFunctions.contains(args[0]));
1831 q->logMessage(format("defined(function, type):"
1832 " unexpected type [%1].\n").arg(args[1]));
1833 return ProItem::ReturnFalse;
1834 }
1835 return returnBool(m_replaceFunctions.contains(args[0])
1836 || m_testFunctions.contains(args[0]));
1837 case T_RETURN:
1838 m_returnValue = args;
1839 // It is "safe" to ignore returns - due to qmake brokeness
1840 // they cannot be used to terminate loops anyway.
1841 if (m_skipLevel || m_cumulative)
1842 return ProItem::ReturnTrue;
1843 if (m_valuemapStack.isEmpty()) {
1844 q->logMessage(format("unexpected return()."));
1845 return ProItem::ReturnFalse;
1846 }
1847 return ProItem::ReturnReturn;
1848 case T_EXPORT:
1849 if (m_skipLevel && !m_cumulative)
1850 return ProItem::ReturnTrue;
1851 if (args.count() != 1) {
1852 q->logMessage(format("export(variable) requires one argument."));
1853 return ProItem::ReturnFalse;
1854 }
1855 for (int i = 0; i < m_valuemapStack.size(); ++i) {
1856 m_valuemapStack[i][args[0]] = m_valuemap[args[0]];
1857 m_filevaluemapStack[i][currentProFile()][args[0]] =
1858 m_filevaluemap[currentProFile()][args[0]];
1859 }
1860 return ProItem::ReturnTrue;
1861#if 0
1862 case T_INFILE:
1863 case T_REQUIRES:
1864 case T_EVAL:
1865#endif
1866 case T_FOR: {
1867 if (m_cumulative) // This is a no-win situation, so just pretend it's no loop
1868 return ProItem::ReturnTrue;
1869 if (m_skipLevel)
1870 return ProItem::ReturnFalse;
1871 if (args.count() > 2 || args.count() < 1) {
1872 q->logMessage(format("for({var, list|var, forever|ever})"
1873 " requires one or two arguments."));
1874 return ProItem::ReturnFalse;
1875 }
1876 ProLoop loop;
1877 loop.infinite = false;
1878 loop.index = 0;
1879 QString it_list;
1880 if (args.count() == 1) {
1881 doVariableReplace(&args[0]);
1882 it_list = args[0];
1883 if (args[0] != QLatin1String("ever")) {
1884 q->logMessage(format("for({var, list|var, forever|ever})"
1885 " requires one or two arguments."));
1886 return ProItem::ReturnFalse;
1887 }
1888 it_list = QLatin1String("forever");
1889 } else {
1890 loop.variable = args[0];
1891 loop.oldVarVal = m_valuemap.value(loop.variable);
1892 doVariableReplace(&args[1]);
1893 it_list = args[1];
1894 }
1895 loop.list = m_valuemap[it_list];
1896 if (loop.list.isEmpty()) {
1897 if (it_list == QLatin1String("forever")) {
1898 loop.infinite = true;
1899 } else {
1900 int dotdot = it_list.indexOf(QLatin1String(".."));
1901 if (dotdot != -1) {
1902 bool ok;
1903 int start = it_list.left(dotdot).toInt(&ok);
1904 if (ok) {
1905 int end = it_list.mid(dotdot+2).toInt(&ok);
1906 if (ok) {
1907 if (start < end) {
1908 for (int i = start; i <= end; i++)
1909 loop.list << QString::number(i);
1910 } else {
1911 for (int i = start; i >= end; i--)
1912 loop.list << QString::number(i);
1913 }
1914 }
1915 }
1916 }
1917 }
1918 }
1919 m_loopStack.push(loop);
1920 m_sts.condition = true;
1921 return ProItem::ReturnLoop;
1922 }
1923 case T_BREAK:
1924 if (m_skipLevel)
1925 return ProItem::ReturnFalse;
1926 if (!m_loopStack.isEmpty())
1927 return ProItem::ReturnBreak;
1928 // ### missing: breaking out of multiline blocks
1929 q->logMessage(format("unexpected break()."));
1930 return ProItem::ReturnFalse;
1931 case T_NEXT:
1932 if (m_skipLevel)
1933 return ProItem::ReturnFalse;
1934 if (!m_loopStack.isEmpty())
1935 return ProItem::ReturnNext;
1936 q->logMessage(format("unexpected next()."));
1937 return ProItem::ReturnFalse;
1938 case T_IF: {
1939 if (args.count() != 1) {
1940 q->logMessage(format("if(condition) requires one argument."));
1941 return ProItem::ReturnFalse;
1942 }
1943 QString cond = args.first();
1944 bool escaped = false; // This is more than qmake does
1945 bool quoted = false;
1946 bool ret = true;
1947 bool orOp = false;
1948 bool invert = false;
1949 bool isFunc = false;
1950 int parens = 0;
1951 QString test;
1952 test.reserve(20);
1953 QString args;
1954 args.reserve(50);
1955 const QChar *d = cond.unicode();
1956 const QChar *ed = d + cond.length();
1957 while (d < ed) {
1958 ushort c = (d++)->unicode();
1959 if (!escaped) {
1960 if (c == '\\') {
1961 escaped = true;
1962 args += c; // Assume no-one quotes the test name
1963 continue;
1964 } else if (c == '"') {
1965 quoted = !quoted;
1966 args += c; // Ditto
1967 continue;
1968 }
1969 } else {
1970 escaped = false;
1971 }
1972 if (quoted) {
1973 args += c; // Ditto
1974 } else {
1975 bool isOp = false;
1976 if (c == '(') {
1977 isFunc = true;
1978 if (parens)
1979 args += c;
1980 ++parens;
1981 } else if (c == ')') {
1982 --parens;
1983 if (parens)
1984 args += c;
1985 } else if (!parens) {
1986 if (c == ':' || c == '|')
1987 isOp = true;
1988 else if (c == '!')
1989 invert = true;
1990 else
1991 test += c;
1992 } else {
1993 args += c;
1994 }
1995 if (!parens && (isOp || d == ed)) {
1996 // Yes, qmake doesn't shortcut evaluations here. We can't, either,
1997 // as some test functions have side effects.
1998 bool success;
1999 if (isFunc) {
2000 success = evaluateConditionalFunction(test, args);
2001 } else {
2002 success = isActiveConfig(test, true);
2003 }
2004 success ^= invert;
2005 if (orOp)
2006 ret |= success;
2007 else
2008 ret &= success;
2009 orOp = (c == '|');
2010 invert = false;
2011 isFunc = false;
2012 test.clear();
2013 args.clear();
2014 }
2015 }
2016 }
2017 return returnBool(ret);
2018 }
2019 case T_CONFIG: {
2020 if (args.count() < 1 || args.count() > 2) {
2021 q->logMessage(format("CONFIG(config) requires one or two arguments."));
2022 return ProItem::ReturnFalse;
2023 }
2024 if (args.count() == 1) {
2025 //cond = isActiveConfig(args.first()); XXX
2026 return ProItem::ReturnFalse;
2027 }
2028 const QStringList mutuals = args[1].split(QLatin1Char('|'));
2029 const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
2030 for (int i = configs.size() - 1; i >= 0; i--) {
2031 for (int mut = 0; mut < mutuals.count(); mut++) {
2032 if (configs[i] == mutuals[mut].trimmed()) {
2033 return returnBool(configs[i] == args[0]);
2034 }
2035 }
2036 }
2037 return ProItem::ReturnFalse;
2038 }
2039 case T_CONTAINS: {
2040 if (args.count() < 2 || args.count() > 3) {
2041 q->logMessage(format("contains(var, val) requires two or three arguments."));
2042 return ProItem::ReturnFalse;
2043 }
2044
2045 QRegExp regx(args[1]);
2046 const QStringList &l = values(args.first());
2047 if (args.count() == 2) {
2048 for (int i = 0; i < l.size(); ++i) {
2049 const QString val = l[i];
2050 if (regx.exactMatch(val) || val == args[1]) {
2051 return ProItem::ReturnTrue;
2052 }
2053 }
2054 } else {
2055 const QStringList mutuals = args[2].split(QLatin1Char('|'));
2056 for (int i = l.size() - 1; i >= 0; i--) {
2057 const QString val = l[i];
2058 for (int mut = 0; mut < mutuals.count(); mut++) {
2059 if (val == mutuals[mut].trimmed()) {
2060 return returnBool(regx.exactMatch(val) || val == args[1]);
2061 }
2062 }
2063 }
2064 }
2065 return ProItem::ReturnFalse;
2066 }
2067 case T_COUNT: {
2068 if (args.count() != 2 && args.count() != 3) {
2069 q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments."));
2070 return ProItem::ReturnFalse;
2071 }
2072 if (args.count() == 3) {
2073 QString comp = args[2];
2074 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
2075 return returnBool(values(args.first()).count() > args[1].toInt());
2076 } else if (comp == QLatin1String(">=")) {
2077 return returnBool(values(args.first()).count() >= args[1].toInt());
2078 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
2079 return returnBool(values(args.first()).count() < args[1].toInt());
2080 } else if (comp == QLatin1String("<=")) {
2081 return returnBool(values(args.first()).count() <= args[1].toInt());
2082 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
2083 || comp == QLatin1String("=") || comp == QLatin1String("==")) {
2084 return returnBool(values(args.first()).count() == args[1].toInt());
2085 } else {
2086 q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
2087 return ProItem::ReturnFalse;
2088 }
2089 }
2090 return returnBool(values(args.first()).count() == args[1].toInt());
2091 }
2092 case T_GREATERTHAN:
2093 case T_LESSTHAN: {
2094 if (args.count() != 2) {
2095 q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
2096 return ProItem::ReturnFalse;
2097 }
2098 QString rhs(args[1]), lhs(values(args[0]).join(QString(Option::field_sep)));
2099 bool ok;
2100 int rhs_int = rhs.toInt(&ok);
2101 if (ok) { // do integer compare
2102 int lhs_int = lhs.toInt(&ok);
2103 if (ok) {
2104 if (func_t == T_GREATERTHAN)
2105 return returnBool(lhs_int > rhs_int);
2106 return returnBool(lhs_int < rhs_int);
2107 }
2108 }
2109 if (func_t == T_GREATERTHAN)
2110 return returnBool(lhs > rhs);
2111 return returnBool(lhs < rhs);
2112 }
2113 case T_EQUALS:
2114 if (args.count() != 2) {
2115 q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
2116 return ProItem::ReturnFalse;
2117 }
2118 return returnBool(values(args[0]).join(QString(Option::field_sep)) == args[1]);
2119 case T_CLEAR: {
2120 if (m_skipLevel && !m_cumulative)
2121 return ProItem::ReturnFalse;
2122 if (args.count() != 1) {
2123 q->logMessage(format("%1(variable) requires one argument.").arg(function));
2124 return ProItem::ReturnFalse;
2125 }
2126 QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
2127 if (it == m_valuemap.end())
2128 return ProItem::ReturnFalse;
2129 it->clear();
2130 return ProItem::ReturnTrue;
2131 }
2132 case T_UNSET: {
2133 if (m_skipLevel && !m_cumulative)
2134 return ProItem::ReturnFalse;
2135 if (args.count() != 1) {
2136 q->logMessage(format("%1(variable) requires one argument.").arg(function));
2137 return ProItem::ReturnFalse;
2138 }
2139 QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
2140 if (it == m_valuemap.end())
2141 return ProItem::ReturnFalse;
2142 m_valuemap.erase(it);
2143 return ProItem::ReturnTrue;
2144 }
2145 case T_INCLUDE: {
2146 if (m_skipLevel && !m_cumulative)
2147 return ProItem::ReturnFalse;
2148 QString parseInto;
2149 // the third optional argument to include() controls warnings
2150 // and is not used here
2151 if ((args.count() == 2) || (args.count() == 3)) {
2152 parseInto = args[1];
2153 } else if (args.count() != 1) {
2154 q->logMessage(format("include(file) requires one, two or three arguments."));
2155 return ProItem::ReturnFalse;
2156 }
2157 QString fileName = args.first();
2158 // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
2159 QDir currentProPath(currentDirectory());
2160 fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
2161 State sts = m_sts;
2162 bool ok = evaluateFile(fileName);
2163 m_sts = sts;
2164 return returnBool(ok);
2165 }
2166 case T_LOAD: {
2167 if (m_skipLevel && !m_cumulative)
2168 return ProItem::ReturnFalse;
2169 QString parseInto;
2170 bool ignore_error = false;
2171 if (args.count() == 2) {
2172 QString sarg = args[1];
2173 ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt());
2174 } else if (args.count() != 1) {
2175 q->logMessage(format("load(feature) requires one or two arguments."));
2176 return ProItem::ReturnFalse;
2177 }
2178 // XXX ignore_error unused
2179 return returnBool(evaluateFeatureFile(args.first()));
2180 }
2181 case T_DEBUG:
2182 // Yup - do nothing. Nothing is going to enable debug output anyway.
2183 return ProItem::ReturnFalse;
2184 case T_MESSAGE: {
2185 if (args.count() != 1) {
2186 q->logMessage(format("%1(message) requires one argument.").arg(function));
2187 return ProItem::ReturnFalse;
2188 }
2189 QString msg = fixEnvVariables(args.first());
2190 q->fileMessage(QString::fromLatin1("Project %1: %2").arg(function.toUpper(), msg));
2191 // ### Consider real termination in non-cumulative mode
2192 return returnBool(function != QLatin1String("error"));
2193 }
2194#if 0 // Way too dangerous to enable.
2195 case T_SYSTEM: {
2196 if (args.count() != 1) {
2197 q->logMessage(format("system(exec) requires one argument."));
2198 ProItem::ReturnFalse;
2199 }
2200 return returnBool(system(args.first().toLatin1().constData()) == 0);
2201 }
2202#endif
2203 case T_ISEMPTY: {
2204 if (args.count() != 1) {
2205 q->logMessage(format("isEmpty(var) requires one argument."));
2206 return ProItem::ReturnFalse;
2207 }
2208 QStringList sl = values(args.first());
2209 if (sl.count() == 0) {
2210 return ProItem::ReturnTrue;
2211 } else if (sl.count() > 0) {
2212 QString var = sl.first();
2213 if (var.isEmpty())
2214 return ProItem::ReturnTrue;
2215 }
2216 return ProItem::ReturnFalse;
2217 }
2218 case T_EXISTS: {
2219 if (args.count() != 1) {
2220 q->logMessage(format("exists(file) requires one argument."));
2221 return ProItem::ReturnFalse;
2222 }
2223 QString file = args.first();
2224 file = Option::fixPathToLocalOS(file);
2225
2226 if (QFile::exists(file)) {
2227 return ProItem::ReturnTrue;
2228 }
2229 //regular expression I guess
2230 QString dirstr = currentDirectory();
2231 int slsh = file.lastIndexOf(Option::dir_sep);
2232 if (slsh != -1) {
2233 dirstr = file.left(slsh+1);
2234 file = file.right(file.length() - slsh - 1);
2235 }
2236 if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?')))
2237 if (!QDir(dirstr).entryList(QStringList(file)).isEmpty())
2238 return ProItem::ReturnTrue;
2239
2240 return ProItem::ReturnFalse;
2241 }
2242 case 0:
2243 q->logMessage(format("'%1' is not a recognized test function").arg(function));
2244 return ProItem::ReturnFalse;
2245 default:
2246 q->logMessage(format("Function '%1' is not implemented").arg(function));
2247 return ProItem::ReturnFalse;
2248 }
2249}
2250
2251QStringList ProFileEvaluator::Private::values(const QString &variableName,
2252 const QHash<QString, QStringList> &place,
2253 const ProFile *pro) const
2254{
2255 if (variableName == QLatin1String("LITERAL_WHITESPACE")) //a real space in a token
2256 return QStringList(QLatin1String("\t"));
2257 if (variableName == QLatin1String("LITERAL_DOLLAR")) //a real $
2258 return QStringList(QLatin1String("$"));
2259 if (variableName == QLatin1String("LITERAL_HASH")) //a real #
2260 return QStringList(QLatin1String("#"));
2261 if (variableName == QLatin1String("OUT_PWD")) //the out going dir
2262 return QStringList(m_outputDir);
2263 if (variableName == QLatin1String("PWD") || //current working dir (of _FILE_)
2264 variableName == QLatin1String("IN_PWD"))
2265 return QStringList(currentDirectory());
2266 if (variableName == QLatin1String("DIR_SEPARATOR"))
2267 return QStringList(Option::dir_sep);
2268 if (variableName == QLatin1String("DIRLIST_SEPARATOR"))
2269 return QStringList(Option::dirlist_sep);
2270 if (variableName == QLatin1String("_LINE_")) //parser line number
2271 return QStringList(QString::number(m_lineNo));
2272 if (variableName == QLatin1String("_FILE_")) //parser file; qmake is a bit weird here
2273 return QStringList(m_profileStack.size() == 1 ? pro->fileName() : QFileInfo(pro->fileName()).fileName());
2274 if (variableName == QLatin1String("_DATE_")) //current date/time
2275 return QStringList(QDateTime::currentDateTime().toString());
2276 if (variableName == QLatin1String("_PRO_FILE_"))
2277 return QStringList(m_origfile);
2278 if (variableName == QLatin1String("_PRO_FILE_PWD_"))
2279 return QStringList(QFileInfo(m_origfile).absolutePath());
2280 if (variableName == QLatin1String("_QMAKE_CACHE_"))
2281 return QStringList(); // FIXME?
2282 if (variableName.startsWith(QLatin1String("QMAKE_HOST."))) {
2283 QString ret, type = variableName.mid(11);
2284#if defined(Q_OS_WIN32)
2285 if (type == QLatin1String("os")) {
2286 ret = QLatin1String("Windows");
2287 } else if (type == QLatin1String("name")) {
2288 DWORD name_length = 1024;
2289 wchar_t name[1024];
2290 if (GetComputerName(name, &name_length))
2291 ret = QString::fromWCharArray(name);
2292 } else if (type == QLatin1String("version") || type == QLatin1String("version_string")) {
2293 QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
2294 if (type == QLatin1String("version"))
2295 ret = QString::number(ver);
2296 else if (ver == QSysInfo::WV_Me)
2297 ret = QLatin1String("WinMe");
2298 else if (ver == QSysInfo::WV_95)
2299 ret = QLatin1String("Win95");
2300 else if (ver == QSysInfo::WV_98)
2301 ret = QLatin1String("Win98");
2302 else if (ver == QSysInfo::WV_NT)
2303 ret = QLatin1String("WinNT");
2304 else if (ver == QSysInfo::WV_2000)
2305 ret = QLatin1String("Win2000");
2306 else if (ver == QSysInfo::WV_2000)
2307 ret = QLatin1String("Win2003");
2308 else if (ver == QSysInfo::WV_XP)
2309 ret = QLatin1String("WinXP");
2310 else if (ver == QSysInfo::WV_VISTA)
2311 ret = QLatin1String("WinVista");
2312 else
2313 ret = QLatin1String("Unknown");
2314 } else if (type == QLatin1String("arch")) {
2315 SYSTEM_INFO info;
2316 GetSystemInfo(&info);
2317 switch(info.wProcessorArchitecture) {
2318#ifdef PROCESSOR_ARCHITECTURE_AMD64
2319 case PROCESSOR_ARCHITECTURE_AMD64:
2320 ret = QLatin1String("x86_64");
2321 break;
2322#endif
2323 case PROCESSOR_ARCHITECTURE_INTEL:
2324 ret = QLatin1String("x86");
2325 break;
2326 case PROCESSOR_ARCHITECTURE_IA64:
2327#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
2328 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
2329#endif
2330 ret = QLatin1String("IA64");
2331 break;
2332 default:
2333 ret = QLatin1String("Unknown");
2334 break;
2335 }
2336 }
2337#elif defined(Q_OS_OS2)
2338 if (type == QLatin1String("os"))
2339 ret = QString::fromLatin1("OS/2");
2340 else if (type == QLatin1String("name"))
2341 ret = QString::fromLocal8Bit(qgetenv("HOSTNAME"));
2342 // @todo use QSysInfo::OS2Version for version/version_string once it's there
2343 else if (type == QLatin1String("version"))
2344 ret = QString::fromLatin1("unknown");
2345 else if (type == QLatin1String("version_string"))
2346 ret = QString::fromLatin1("unknown");
2347 else if (type == QLatin1String("arch"))
2348 ret = QString::fromLatin1("x86");
2349#elif defined(Q_OS_UNIX)
2350 struct utsname name;
2351 if (!uname(&name)) {
2352 if (type == QLatin1String("os"))
2353 ret = QString::fromLatin1(name.sysname);
2354 else if (type == QLatin1String("name"))
2355 ret = QString::fromLatin1(name.nodename);
2356 else if (type == QLatin1String("version"))
2357 ret = QString::fromLatin1(name.release);
2358 else if (type == QLatin1String("version_string"))
2359 ret = QString::fromLatin1(name.version);
2360 else if (type == QLatin1String("arch"))
2361 ret = QString::fromLatin1(name.machine);
2362 }
2363#endif
2364 return QStringList(ret);
2365 }
2366
2367 QStringList result = place[variableName];
2368 if (result.isEmpty()) {
2369 if (variableName == QLatin1String("TARGET")) {
2370 result.append(QFileInfo(m_origfile).baseName());
2371 } else if (variableName == QLatin1String("TEMPLATE")) {
2372 result.append(QLatin1String("app"));
2373 } else if (variableName == QLatin1String("QMAKE_DIR_SEP")) {
2374 result.append(Option::dirlist_sep);
2375 }
2376 }
2377 return result;
2378}
2379
2380QStringList ProFileEvaluator::Private::values(const QString &variableName) const
2381{
2382 return values(variableName, m_valuemap, currentProFile());
2383}
2384
2385QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const
2386{
2387 return values(variableName, m_filevaluemap[pro], pro);
2388}
2389
2390ProFile *ProFileEvaluator::parsedProFile(const QString &fileName)
2391{
2392 QFileInfo fi(fileName);
2393 if (fi.exists()) {
2394 QString fn = QDir::cleanPath(fi.absoluteFilePath());
2395 foreach (const ProFile *pf, d->m_profileStack)
2396 if (pf->fileName() == fn) {
2397 errorMessage(d->format("circular inclusion of %1").arg(fn));
2398 return 0;
2399 }
2400 ProFile *pro = new ProFile(fn);
2401 if (d->read(pro))
2402 return pro;
2403 delete pro;
2404 }
2405 return 0;
2406}
2407
2408void ProFileEvaluator::releaseParsedProFile(ProFile *proFile)
2409{
2410 delete proFile;
2411}
2412
2413bool ProFileEvaluator::Private::evaluateFile(const QString &fileName)
2414{
2415 ProFile *pro = q->parsedProFile(fileName);
2416 if (pro) {
2417 m_profileStack.push(pro);
2418 bool ok = (pro->Accept(this) == ProItem::ReturnTrue);
2419 m_profileStack.pop();
2420 q->releaseParsedProFile(pro);
2421 return ok;
2422 } else {
2423 return false;
2424 }
2425}
2426
2427bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
2428{
2429 QString fn;
2430 foreach (const QString &path, qmakeFeaturePaths()) {
2431 QString fname = path + QLatin1Char('/') + fileName;
2432 if (QFileInfo(fname).exists()) {
2433 fn = fname;
2434 break;
2435 }
2436 fname += QLatin1String(".prf");
2437 if (QFileInfo(fname).exists()) {
2438 fn = fname;
2439 break;
2440 }
2441 }
2442 if (fn.isEmpty())
2443 return false;
2444 bool cumulative = m_cumulative;
2445 m_cumulative = false;
2446 bool ok = evaluateFile(fn);
2447 m_cumulative = cumulative;
2448 return ok;
2449}
2450
2451QString ProFileEvaluator::Private::format(const char *fmt) const
2452{
2453 ProFile *pro = currentProFile();
2454 QString fileName = pro ? pro->fileName() : QLatin1String("Not a file");
2455 int lineNumber = pro ? m_lineNo : 0;
2456 return QString::fromLatin1("%1(%2):").arg(fileName).arg(lineNumber) + QString::fromAscii(fmt);
2457}
2458
2459
2460///////////////////////////////////////////////////////////////////////
2461//
2462// ProFileEvaluator
2463//
2464///////////////////////////////////////////////////////////////////////
2465
2466ProFileEvaluator::ProFileEvaluator()
2467 : d(new Private(this))
2468{
2469 Option::init();
2470}
2471
2472ProFileEvaluator::~ProFileEvaluator()
2473{
2474 delete d;
2475}
2476
2477bool ProFileEvaluator::contains(const QString &variableName) const
2478{
2479 return d->m_valuemap.contains(variableName);
2480}
2481
2482inline QStringList fixEnvVariables(const QStringList &x)
2483{
2484 QStringList ret;
2485 foreach (const QString &str, x)
2486 ret << Option::fixString(str, Option::FixEnvVars);
2487 return ret;
2488}
2489
2490
2491QStringList ProFileEvaluator::values(const QString &variableName) const
2492{
2493 return fixEnvVariables(d->values(variableName));
2494}
2495
2496QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
2497{
2498 return fixEnvVariables(d->values(variableName, pro));
2499}
2500
2501QStringList ProFileEvaluator::absolutePathValues(
2502 const QString &variable, const QString &baseDirectory) const
2503{
2504 QStringList result;
2505 foreach (const QString &el, values(variable)) {
2506 const QFileInfo info = QFileInfo(baseDirectory, el);
2507 if (info.isDir())
2508 result << QDir::cleanPath(info.absoluteFilePath());
2509 }