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