Changeset 561 for trunk/tools/linguist/shared/profileevaluator.cpp
- Timestamp:
- Feb 11, 2010, 11:19:06 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk
-
Property svn:mergeinfo
set to (toggle deleted branches)
/branches/vendor/nokia/qt/4.6.1 merged eligible /branches/vendor/nokia/qt/current merged eligible /branches/vendor/trolltech/qt/current 3-149
-
Property svn:mergeinfo
set to (toggle deleted branches)
-
trunk/tools/linguist/shared/profileevaluator.cpp
r2 r561 2 2 ** 3 3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 4 ** Contact: Qt Software Information (qt-info@nokia.com) 4 ** All rights reserved. 5 ** Contact: Nokia Corporation (qt-info@nokia.com) 5 6 ** 6 7 ** This file is part of the Qt Linguist of the Qt Toolkit. … … 21 22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 22 23 ** 23 ** In addition, as a special exception, Nokia gives you certain 24 ** additional rights. These rights are described in the Nokia Qt LGPL 25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 26 ** package. 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 27 ** 28 28 ** GNU General Public License Usage … … 34 34 ** met: http://www.gnu.org/copyleft/gpl.html. 35 35 ** 36 ** If you are unsure which license is appropriate for your use, please37 ** contact the sales department at qt-sales@nokia.com.36 ** If you have questions regarding the use of this file, please contact 37 ** Nokia at qt-info@nokia.com. 38 38 ** $QT_END_LICENSE$ 39 39 ** … … 45 45 46 46 #include <QtCore/QByteArray> 47 #include <QtCore/QDateTime> 47 48 #include <QtCore/QDebug> 48 49 #include <QtCore/QDir> … … 57 58 #include <QtCore/QTextStream> 58 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 59 69 #ifdef Q_OS_WIN32 60 70 #define QT_POPEN _popen 71 #define QT_PCLOSE _pclose 61 72 #else 62 73 #define QT_POPEN popen 74 #define QT_PCLOSE pclose 63 75 #endif 64 76 65 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 } 66 130 67 131 /////////////////////////////////////////////////////////////////////// … … 75 139 public: 76 140 Private(ProFileEvaluator *q_); 141 142 ProFileEvaluator *q; 143 int m_lineNo; // Error reporting 144 bool m_verbose; 145 146 /////////////// Reading pro file 77 147 78 148 bool read(ProFile *pro); … … 88 158 void finalizeBlock(); 89 159 90 // implementation of AbstractProItemVisitor91 bool visitBeginProBlock(ProBlock *block);92 bool visitEndProBlock(ProBlock *block);93 bool visitBeginProVariable(ProVariable *variable);94 bool visitEndProVariable(ProVariable *variable);95 bool visitBeginProFile(ProFile *value);96 bool visitEndProFile(ProFile *value);97 bool visitProValue(ProValue *value);98 bool visitProFunction(ProFunction *function);99 bool visitProOperator(ProOperator *oper);100 bool visitProCondition(ProCondition *condition);101 102 QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }103 QStringList values(const QString &variableName) const;104 QStringList values(const QString &variableName, const ProFile *pro) const;105 QString propertyValue(const QString &val) const;106 107 bool isActiveConfig(const QString &config, bool regex = false);108 QStringList expandPattern(const QString &pattern);109 void expandPatternHelper(const QString &relName, const QString &absName,110 QStringList &sources_out);111 QStringList expandVariableReferences(const QString &value);112 QStringList evaluateExpandFunction(const QString &function, const QString &arguments);113 QString format(const char *format) const;114 115 QString currentFileName() const;116 QString getcwd() const;117 ProFile *currentProFile() const;118 119 bool evaluateConditionalFunction(const QString &function, const QString &arguments, bool *result);120 bool evaluateFile(const QString &fileName, bool *result);121 bool evaluateFeatureFile(const QString &fileName, bool *result);122 123 QStringList qmakeFeaturePaths();124 125 ProFileEvaluator *q;126 127 160 QStack<ProBlock *> m_blockstack; 128 161 ProBlock *m_block; … … 133 166 bool m_syntaxError; 134 167 bool m_contNextLine; 135 bool m_condition; 136 bool m_invertNext; 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; 137 223 QString m_lastVarName; 138 224 ProVariable::VariableOperator m_variableOperator; 139 int m_lineNo; // Error reporting225 QString m_origfile; 140 226 QString m_oldPath; // To restore the current path to the path 141 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 ...) 142 241 143 242 QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii. 144 243 QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file 145 244 QHash<QString, QString> m_properties; 146 QString m_origfile; 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; 147 254 148 255 int m_prevLineNo; // Checking whether we're assigning the same TARGET 149 256 ProFile *m_prevProFile; // See m_prevLineNo 150 151 bool m_verbose;152 257 }; 258 259 Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE); 260 Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE); 153 261 154 262 ProFileEvaluator::Private::Private(ProFileEvaluator *q_) 155 263 : q(q_) 156 264 { 265 // Global parser state 157 266 m_prevLineNo = 0; 158 267 m_prevProFile = 0; 268 269 // Configuration, more or less 159 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(); 160 280 } 161 281 … … 168 288 } 169 289 290 // Parser state 170 291 m_block = 0; 171 292 m_commentItem = 0; 293 m_inQuote = false; 294 m_parens = 0; 172 295 m_contNextLine = false; 173 296 m_syntaxError = false; … … 193 316 return false; 194 317 195 ushort quote = 0;196 int parens = 0;197 bool contNextLine= false;318 int parens = m_parens; 319 bool inQuote = m_inQuote; 320 bool escaped = false; 198 321 QString line = line0.simplified(); 199 322 200 323 for (int i = 0; !m_syntaxError && i < line.length(); ++i) { 201 324 ushort c = line.at(i).unicode(); 202 if (quote && c == quote) 203 quote = 0; 204 else if (c == '(') 205 ++parens; 206 else if (c == ')') 207 --parens; 208 else if (c == '"' && (i == 0 || line.at(i - 1).unicode() != '\\')) 209 quote = c; 210 else if (!parens && !quote) { 211 if (c == '#') { 212 insertComment(line.mid(i + 1)); 213 contNextLine = m_contNextLine; 214 break; 215 } 216 if (c == '\\' && i >= line.count() - 1) { 217 updateItem(); 218 contNextLine = true; 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; 219 334 continue; 220 } 221 if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) { 222 if (c == ' ') 223 updateItem(); 224 else 225 m_proitem += c; 335 } else if (c == '"') { 336 inQuote = !inQuote; 337 m_proitem += c; 226 338 continue; 227 339 } 228 if (c == ':') { 229 enterScope(false); 230 continue; 231 } 232 if (c == '{') { 233 enterScope(true); 234 continue; 235 } 236 if (c == '}') { 237 leaveScope(); 238 continue; 239 } 240 if (c == '=') { 241 insertVariable(line, &i); 242 continue; 243 } 244 if (c == '|' || c == '!') { 245 insertOperator(c); 246 continue; 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 } 247 376 } 248 377 } … … 250 379 m_proitem += c; 251 380 } 252 m_contNextLine = contNextLine; 253 254 if (!m_syntaxError) { 381 m_inQuote = inQuote; 382 m_parens = parens; 383 m_contNextLine = escaped; 384 if (escaped) { 385 m_proitem.chop(1); 255 386 updateItem(); 256 if (!m_contNextLine) 387 return true; 388 } else { 389 if (!m_syntaxError) { 390 updateItem(); 257 391 finalizeBlock(); 258 } 259 return !m_syntaxError; 392 return true; 393 } 394 return false; 395 } 260 396 } 261 397 … … 276 412 ProVariable::VariableOperator opkind; 277 413 414 if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker 415 return; 416 278 417 switch (m_proitem.at(m_proitem.length() - 1).unicode()) { 279 418 case '+': … … 450 589 451 590 452 bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block) 453 { 454 if (block->blockKind() == ProBlock::ScopeKind) { 455 m_invertNext = false; 456 m_condition = false; 457 } 458 return true; 459 } 460 461 bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block) 462 { 463 Q_UNUSED(block); 464 return true; 465 } 466 467 bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable) 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) 468 671 { 469 672 m_lastVarName = variable->variable(); 470 673 m_variableOperator = variable->variableOperator(); 471 return true; 472 } 473 474 bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable) 674 m_isFirstVariableValue = true; 675 m_tempValuemap = m_valuemap; 676 m_tempFilevaluemap = m_filevaluemap; 677 } 678 679 void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable) 475 680 { 476 681 Q_UNUSED(variable); 682 m_valuemap = m_tempValuemap; 683 m_filevaluemap = m_tempFilevaluemap; 477 684 m_lastVarName.clear(); 478 return true; 479 } 480 481 bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper) 685 } 686 687 void ProFileEvaluator::Private::visitProOperator(ProOperator *oper) 482 688 { 483 689 m_invertNext = (oper->operatorKind() == ProOperator::NotOperator); 484 return true; 485 } 486 487 bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond) 488 { 489 if (!m_condition) { 490 if (m_invertNext) 491 m_condition |= !isActiveConfig(cond->text(), true); 492 else 493 m_condition |= isActiveConfig(cond->text(), true); 494 } 495 return true; 496 } 497 498 bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro) 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) 499 707 { 500 708 PRE(pro); 501 bool ok = true;502 709 m_lineNo = pro->lineNumber(); 710 if (m_origfile.isEmpty()) 711 m_origfile = pro->fileName(); 503 712 if (m_oldPath.isEmpty()) { 504 713 // change the working directory for the initial profile we visit, since 505 714 // that is *the* profile. All the other times we reach this function will be due to 506 715 // include(file) or load(file) 716 507 717 m_oldPath = QDir::currentPath(); 718 508 719 m_profileStack.push(pro); 509 ok = QDir::setCurrent(pro->directoryName()); 510 } 511 512 if (m_origfile.isEmpty()) 513 m_origfile = pro->fileName(); 514 515 return ok; 516 } 517 518 bool ProFileEvaluator::Private::visitEndProFile(ProFile * 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) 519 739 { 520 740 PRE(pro); 521 bool ok = true;522 741 m_lineNo = pro->lineNumber(); 523 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 524 779 m_profileStack.pop(); 525 ok = QDir::setCurrent(m_oldPath); 526 } 527 return ok; 528 } 529 530 bool ProFileEvaluator::Private::visitProValue(ProValue *value) 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) 531 805 { 532 806 PRE(value); … … 545 819 && m_lineNo == m_prevLineNo 546 820 && currentProFile() == m_prevProFile) { 547 QStringList targets = m_ valuemap.value(QLatin1String("TARGET"));548 m_ valuemap.remove(QLatin1String("TARGET"));821 QStringList targets = m_tempValuemap.value(QLatin1String("TARGET")); 822 m_tempValuemap.remove(QLatin1String("TARGET")); 549 823 QStringList lastTarget(targets.takeLast()); 550 824 lastTarget << v.join(QLatin1String(" ")); … … 555 829 m_prevProFile = currentProFile(); 556 830 557 // The following two blocks fix bug 180128 by making all "interesting"558 // file name absolute in each .pro file, not just the top most one559 if (varName == QLatin1String("SOURCES")560 || varName == QLatin1String("HEADERS")561 || varName == QLatin1String("INTERFACES")562 || varName == QLatin1String("FORMS")563 || varName == QLatin1String("FORMS3")564 || varName == QLatin1String("RESOURCES")) {565 // matches only existent files, expand certain(?) patterns566 QStringList vv;567 for (int i = v.count(); --i >= 0; )568 vv << expandPattern(v[i]);569 v = vv;570 }571 572 if (varName == QLatin1String("TRANSLATIONS")) {573 // also matches non-existent files, but does not expand pattern574 QString dir = QFileInfo(currentFileName()).absolutePath();575 dir += QLatin1Char('/');576 for (int i = v.count(); --i >= 0; )577 v[i] = QFileInfo(dir, v[i]).absoluteFilePath();578 }579 580 831 switch (m_variableOperator) { 581 case ProVariable::UniqueAddOperator: // *582 insertUnique(&m_valuemap, varName, v, true);583 insertUnique(&m_filevaluemap[currentProFile()], varName, v, true);584 break;585 832 case ProVariable::SetOperator: // = 586 case ProVariable::AddOperator: // + 587 insertUnique(&m_valuemap, varName, v, false); 588 insertUnique(&m_filevaluemap[currentProFile()], varName, v, false); 589 break; 590 case ProVariable::RemoveOperator: // - 591 // fix me: interaction between AddOperator and RemoveOperator 592 insertUnique(&m_valuemap, varName.prepend(QLatin1Char('-')), v, false); 593 insertUnique(&m_filevaluemap[currentProFile()], 594 varName.prepend(QLatin1Char('-')), v, false); 595 break; 596 case ProVariable::ReplaceOperator: // ~ 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: // ~= 597 872 { 598 873 // DEFINES ~= s/a/b/?[gqi] 599 874 600 /* Create a superset by executing replacement + adding items that have changed 601 to original list. We're not sure if this is really the right approach, so for 602 the time being we will just do nothing ... 603 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 } 604 880 QChar sep = val.at(1); 605 881 QStringList func = val.split(sep); 606 882 if (func.count() < 3 || func.count() > 4) { 607 q->logMessage(format("'~= operator '(function s///) expects 3 or 4 arguments.")); 608 return false; 609 } 610 if (func[0] != QLatin1String("s")) { 611 q->logMessage(format("~= operator can only handle s/// function.")); 612 return false; 883 q->logMessage(format("the s/// function expects 3 or 4 arguments.")); 884 break; 613 885 } 614 886 … … 626 898 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive); 627 899 628 QStringList replaceList = replaceInList(m_valuemap.value(varName), regexp, replace, 629 global); 630 // Add changed entries to list 631 foreach (const QString &entry, replaceList) 632 if (!m_valuemap.value(varName).contains(entry)) 633 insertUnique(&m_valuemap, varName, QStringList() << entry, false); 634 635 replaceList = replaceInList(m_filevaluemap[currentProFile()].value(varName), regexp, 636 replace, global); 637 foreach (const QString &entry, replaceList) 638 if (!m_filevaluemap[currentProFile()].value(varName).contains(entry)) 639 insertUnique(&m_filevaluemap[currentProFile()], varName, 640 QStringList() << entry, false); */ 641 } 642 break; 643 644 } 645 return true; 646 } 647 648 bool ProFileEvaluator::Private::visitProFunction(ProFunction *func) 649 { 650 m_lineNo = func->lineNumber(); 651 bool result = true; 652 bool ok = true; 653 QString text = func->text(); 654 int lparen = text.indexOf(QLatin1Char('(')); 655 int rparen = text.lastIndexOf(QLatin1Char(')')); 656 Q_ASSERT(lparen < rparen); 657 658 QString arguments = text.mid(lparen + 1, rparen - lparen - 1); 659 QString funcName = text.left(lparen); 660 ok &= evaluateConditionalFunction(funcName.trimmed(), arguments, &result); 661 return ok; 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; 662 935 } 663 936 … … 667 940 QStringList concat; 668 941 { 669 const QString base_concat = QDir::separator() + Q String(QLatin1String("features"));942 const QString base_concat = QDir::separator() + QLatin1String("features"); 670 943 concat << base_concat + QDir::separator() + QLatin1String("mac"); 671 944 concat << base_concat + QDir::separator() + QLatin1String("macx"); … … 676 949 concat << base_concat; 677 950 } 678 const QString mkspecs_concat = QDir::separator() + Q String(QLatin1String("mkspecs"));951 const QString mkspecs_concat = QDir::separator() + QLatin1String("mkspecs"); 679 952 QStringList feature_roots; 680 953 QByteArray mkspec_path = qgetenv("QMAKEFEATURES"); … … 779 1052 } 780 1053 781 QString ProFileEvaluator::Private:: getcwd() const1054 QString ProFileEvaluator::Private::currentDirectory() const 782 1055 { 783 1056 ProFile *cur = m_profileStack.top(); 784 1057 return cur->directoryName(); 1058 } 1059 1060 void ProFileEvaluator::Private::doVariableReplace(QString *str) 1061 { 1062 *str = expandVariableReferences(*str).join(QString(Option::field_sep)); 785 1063 } 786 1064 … … 999 1277 } 1000 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 1001 1315 QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments) 1002 1316 { 1003 1317 QStringList argumentsList = split_arg_list(arguments); 1318 1319 if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0)) 1320 return evaluateFunction(funcPtr, argumentsList, 0); 1004 1321 1005 1322 QStringList args; … … 1013 1330 E_REPLACE }; 1014 1331 1015 static QHash<QString, int> *expands = 0; 1016 if (!expands) { 1017 expands = new QHash<QString, int>; 1018 expands->insert(QLatin1String("member"), E_MEMBER); //v (implemented) 1019 expands->insert(QLatin1String("first"), E_FIRST); //v 1020 expands->insert(QLatin1String("last"), E_LAST); //v 1021 expands->insert(QLatin1String("cat"), E_CAT); 1022 expands->insert(QLatin1String("fromfile"), E_FROMFILE); 1023 expands->insert(QLatin1String("eval"), E_EVAL); 1024 expands->insert(QLatin1String("list"), E_LIST); 1025 expands->insert(QLatin1String("sprintf"), E_SPRINTF); 1026 expands->insert(QLatin1String("join"), E_JOIN); //v 1027 expands->insert(QLatin1String("split"), E_SPLIT); //v 1028 expands->insert(QLatin1String("basename"), E_BASENAME); //v 1029 expands->insert(QLatin1String("dirname"), E_DIRNAME); //v 1030 expands->insert(QLatin1String("section"), E_SECTION); 1031 expands->insert(QLatin1String("find"), E_FIND); 1032 expands->insert(QLatin1String("system"), E_SYSTEM); //v 1033 expands->insert(QLatin1String("unique"), E_UNIQUE); 1034 expands->insert(QLatin1String("quote"), E_QUOTE); //v 1035 expands->insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND); 1036 expands->insert(QLatin1String("upper"), E_UPPER); 1037 expands->insert(QLatin1String("lower"), E_LOWER); 1038 expands->insert(QLatin1String("re_escape"), E_RE_ESCAPE); 1039 expands->insert(QLatin1String("files"), E_FILES); 1040 expands->insert(QLatin1String("prompt"), E_PROMPT); 1041 expands->insert(QLatin1String("replace"), E_REPLACE); 1042 } 1043 ExpandFunc func_t = ExpandFunc(expands->value(func.toLower())); 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())); 1044 1360 1045 1361 QStringList ret; … … 1087 1403 break; 1088 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; 1089 1415 case E_JOIN: { 1090 1416 if (args.count() < 1 || args.count() > 4) { … … 1106 1432 case E_SPLIT: { 1107 1433 if (args.count() != 2) { 1108 q->logMessage(format("split(var, sep) requires two arguments"));1434 q->logMessage(format("split(var, sep) requires one or two arguments")); 1109 1435 } else { 1110 QString sep = args.at(1);1436 const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep); 1111 1437 foreach (const QString &var, values(args.first())) 1112 1438 foreach (const QString &splt, var.split(sep)) … … 1179 1505 break; 1180 1506 } 1181 case E_SYSTEM: { 1182 if (m_condition) { 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) { 1183 1583 if (args.count() < 1 || args.count() > 2) { 1184 1584 q->logMessage(format("system(execute) requires one or two arguments.")); … … 1188 1588 bool singleLine = true; 1189 1589 if (args.count() > 1) 1190 singleLine = ( args[1].toLower() == QLatin1String("true"));1590 singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive)); 1191 1591 QString output; 1192 1592 while (proc && !feof(proc)) { … … 1202 1602 } 1203 1603 ret += split_value_list(output); 1204 } 1205 } 1206 break; } 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; 1207 1618 case E_QUOTE: 1208 1619 for (int i = 0; i < args.count(); ++i) 1209 1620 ret += QStringList(args.at(i)); 1210 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; 1211 1714 case 0: 1212 q->logMessage(format("'%1' is not a function").arg(func));1715 q->logMessage(format("'%1' is not a recognized replace function").arg(func)); 1213 1716 break; 1214 1717 default: … … 1220 1723 } 1221 1724 1222 bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &function, 1223 const QString &arguments, bool *result)1725 ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( 1726 const QString &function, const QString &arguments) 1224 1727 { 1225 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 1226 1757 QString sep; 1227 1758 sep.append(Option::field_sep); 1228 1229 1759 QStringList args; 1230 1760 for (int i = 0; i < argumentsList.count(); ++i) 1231 1761 args += expandVariableReferences(argumentsList[i]).join(sep); 1232 1762 1233 enum ConditionFunc { CF_CONFIG = 1, CF_CONTAINS, CF_COUNT, CF_EXISTS, CF_INCLUDE, 1234 CF_LOAD, CF_ISEMPTY, CF_SYSTEM, CF_MESSAGE}; 1235 1236 static QHash<QString, int> *functions = 0; 1237 if (!functions) { 1238 functions = new QHash<QString, int>; 1239 functions->insert(QLatin1String("load"), CF_LOAD); //v 1240 functions->insert(QLatin1String("include"), CF_INCLUDE); //v 1241 functions->insert(QLatin1String("message"), CF_MESSAGE); //v 1242 functions->insert(QLatin1String("warning"), CF_MESSAGE); //v 1243 functions->insert(QLatin1String("error"), CF_MESSAGE); //v 1244 } 1245 1246 bool cond = false; 1247 bool ok = true; 1248 1249 ConditionFunc func_t = (ConditionFunc)functions->value(function); 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); 1250 1805 1251 1806 switch (func_t) { 1252 case CF_CONFIG: { 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: { 1253 2019 if (args.count() < 1 || args.count() > 2) { 1254 2020 q->logMessage(format("CONFIG(config) requires one or two arguments.")); 1255 ok = false; 1256 break; 2021 return ProItem::ReturnFalse; 1257 2022 } 1258 2023 if (args.count() == 1) { 1259 2024 //cond = isActiveConfig(args.first()); XXX 1260 break;2025 return ProItem::ReturnFalse; 1261 2026 } 1262 2027 const QStringList mutuals = args[1].split(QLatin1Char('|')); 1263 2028 const QStringList &configs = valuesDirect(QLatin1String("CONFIG")); 1264 for (int i = configs.size() - 1 && ok; i >= 0; i--) {2029 for (int i = configs.size() - 1; i >= 0; i--) { 1265 2030 for (int mut = 0; mut < mutuals.count(); mut++) { 1266 2031 if (configs[i] == mutuals[mut].trimmed()) { 1267 cond = (configs[i] == args[0]); 1268 goto done_T_CONFIG; 2032 return returnBool(configs[i] == args[0]); 1269 2033 } 1270 2034 } 1271 2035 } 1272 done_T_CONFIG: 1273 break; 1274 } 1275 case CF_CONTAINS: { 2036 return ProItem::ReturnFalse; 2037 } 2038 case T_CONTAINS: { 1276 2039 if (args.count() < 2 || args.count() > 3) { 1277 2040 q->logMessage(format("contains(var, val) requires two or three arguments.")); 1278 ok = false; 1279 break; 2041 return ProItem::ReturnFalse; 1280 2042 } 1281 2043 … … 1286 2048 const QString val = l[i]; 1287 2049 if (regx.exactMatch(val) || val == args[1]) { 1288 cond = true; 1289 break; 2050 return ProItem::ReturnTrue; 1290 2051 } 1291 2052 } … … 1296 2057 for (int mut = 0; mut < mutuals.count(); mut++) { 1297 2058 if (val == mutuals[mut].trimmed()) { 1298 cond = (regx.exactMatch(val) || val == args[1]); 1299 goto done_T_CONTAINS; 2059 return returnBool(regx.exactMatch(val) || val == args[1]); 1300 2060 } 1301 2061 } 1302 2062 } 1303 2063 } 1304 done_T_CONTAINS: 1305 break; 1306 } 1307 case CF_COUNT: { 2064 return ProItem::ReturnFalse; 2065 } 2066 case T_COUNT: { 1308 2067 if (args.count() != 2 && args.count() != 3) { 1309 q->logMessage(format("count(var, count) requires two or three arguments.")); 1310 ok = false; 1311 break; 2068 q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments.")); 2069 return ProItem::ReturnFalse; 1312 2070 } 1313 2071 if (args.count() == 3) { 1314 2072 QString comp = args[2]; 1315 2073 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) { 1316 cond = values(args.first()).count() > args[1].toInt();2074 return returnBool(values(args.first()).count() > args[1].toInt()); 1317 2075 } else if (comp == QLatin1String(">=")) { 1318 cond = values(args.first()).count() >= args[1].toInt();2076 return returnBool(values(args.first()).count() >= args[1].toInt()); 1319 2077 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) { 1320 cond = values(args.first()).count() < args[1].toInt();2078 return returnBool(values(args.first()).count() < args[1].toInt()); 1321 2079 } else if (comp == QLatin1String("<=")) { 1322 cond = values(args.first()).count() <= args[1].toInt(); 1323 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual") || comp == QLatin1String("=") || comp == QLatin1String("==")) { 1324 cond = values(args.first()).count() == args[1].toInt(); 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()); 1325 2084 } else { 1326 ok = false;1327 2085 q->logMessage(format("unexpected modifier to count(%2)").arg(comp)); 1328 } 1329 break; 1330 } 1331 cond = values(args.first()).count() == args[1].toInt(); 1332 break; 1333 } 1334 case CF_INCLUDE: { 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; 1335 2147 QString parseInto; 1336 if (args.count() == 2) { 2148 // the third optional argument to include() controls warnings 2149 // and is not used here 2150 if ((args.count() == 2) || (args.count() == 3)) { 1337 2151 parseInto = args[1]; 1338 2152 } else if (args.count() != 1) { 1339 q->logMessage(format("include(file) requires one or two arguments.")); 1340 ok = false; 1341 break; 2153 q->logMessage(format("include(file) requires one, two or three arguments.")); 2154 return ProItem::ReturnFalse; 1342 2155 } 1343 2156 QString fileName = args.first(); 1344 2157 // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style. 1345 QDir currentProPath( getcwd());2158 QDir currentProPath(currentDirectory()); 1346 2159 fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName)); 1347 ok = evaluateFile(fileName, &ok); 1348 break; 1349 } 1350 case CF_LOAD: { 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; 1351 2168 QString parseInto; 1352 2169 bool ignore_error = false; 1353 2170 if (args.count() == 2) { 1354 2171 QString sarg = args[1]; 1355 ignore_error = ( sarg.toLower() == QLatin1String("true") || sarg.toInt());2172 ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt()); 1356 2173 } else if (args.count() != 1) { 1357 2174 q->logMessage(format("load(feature) requires one or two arguments.")); 1358 ok = false; 1359 break; 1360 } 1361 ok = evaluateFeatureFile( args.first(), &cond); 1362 break; 1363 } 1364 case CF_MESSAGE: { 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: { 1365 2184 if (args.count() != 1) { 1366 2185 q->logMessage(format("%1(message) requires one argument.").arg(function)); 1367 ok = false; 1368 break; 1369 } 1370 QString msg = args.first(); 1371 if (function == QLatin1String("error")) { 1372 QStringList parents; 1373 foreach (ProFile *proFile, m_profileStack) 1374 parents.append(proFile->fileName()); 1375 if (!parents.isEmpty()) 1376 parents.takeLast(); 1377 if (parents.isEmpty()) 1378 q->fileMessage(format("Project ERROR: %1").arg(msg)); 1379 else 1380 q->fileMessage(format("Project ERROR: %1. File was included from: '%2'") 1381 .arg(msg).arg(parents.join(QLatin1String("', '")))); 1382 } else { 1383 q->fileMessage(format("Project MESSAGE: %1").arg(msg)); 1384 } 1385 break; 1386 } 1387 case CF_SYSTEM: { 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: { 1388 2195 if (args.count() != 1) { 1389 2196 q->logMessage(format("system(exec) requires one argument.")); 1390 ok = false; 1391 break; 1392 } 1393 ok = system(args.first().toLatin1().constData()) == 0; 1394 break; 1395 } 1396 case CF_ISEMPTY: { 2197 ProItem::ReturnFalse; 2198 } 2199 return returnBool(system(args.first().toLatin1().constData()) == 0); 2200 } 2201 #endif 2202 case T_ISEMPTY: { 1397 2203 if (args.count() != 1) { 1398 2204 q->logMessage(format("isEmpty(var) requires one argument.")); 1399 ok = false; 1400 break; 2205 return ProItem::ReturnFalse; 1401 2206 } 1402 2207 QStringList sl = values(args.first()); 1403 2208 if (sl.count() == 0) { 1404 cond = true;2209 return ProItem::ReturnTrue; 1405 2210 } else if (sl.count() > 0) { 1406 2211 QString var = sl.first(); 1407 cond = (var.isEmpty()); 1408 } 1409 break; 1410 } 1411 case CF_EXISTS: { 2212 if (var.isEmpty()) 2213 return ProItem::ReturnTrue; 2214 } 2215 return ProItem::ReturnFalse; 2216 } 2217 case T_EXISTS: { 1412 2218 if (args.count() != 1) { 1413 2219 q->logMessage(format("exists(file) requires one argument.")); 1414 ok = false; 1415 break; 2220 return ProItem::ReturnFalse; 1416 2221 } 1417 2222 QString file = args.first(); 1418 1419 file = QDir::cleanPath(file); 2223 file = Option::fixPathToLocalOS(file); 1420 2224 1421 2225 if (QFile::exists(file)) { 1422 cond = true; 1423 break; 2226 return ProItem::ReturnTrue; 1424 2227 } 1425 2228 //regular expression I guess 1426 QString dirstr = getcwd();2229 QString dirstr = currentDirectory(); 1427 2230 int slsh = file.lastIndexOf(Option::dir_sep); 1428 2231 if (slsh != -1) { … … 1430 2233 file = file.right(file.length() - slsh - 1); 1431 2234 } 1432 cond = QDir(dirstr).entryList(QStringList(file)).count(); 1433 1434 break; 1435 } 1436 } 1437 1438 if (result) 1439 *result = cond; 1440 1441 return ok; 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; 1442 2365 } 1443 2366 1444 2367 QStringList ProFileEvaluator::Private::values(const QString &variableName) const 1445 2368 { 1446 if (variableName == QLatin1String("TARGET")) { 1447 QStringList list = m_valuemap.value(variableName); 1448 if (!m_origfile.isEmpty()) 1449 list.append(QFileInfo(m_origfile).baseName()); 1450 return list; 1451 } 1452 if (variableName == QLatin1String("PWD")) { 1453 return QStringList(getcwd()); 1454 } 1455 return m_valuemap.value(variableName); 2369 return values(variableName, m_valuemap, currentProFile()); 1456 2370 } 1457 2371 1458 2372 QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const 1459 2373 { 1460 if (variableName == QLatin1String("TARGET")) { 1461 QStringList list = m_filevaluemap[pro].value(variableName); 1462 if (!m_origfile.isEmpty()) 1463 list.append(QFileInfo(m_origfile).baseName()); 1464 return list; 1465 } 1466 if (variableName == QLatin1String("PWD")) { 1467 return QStringList(QFileInfo(pro->fileName()).absoluteFilePath()); 1468 } 1469 return m_filevaluemap[pro].value(variableName); 2374 return values(variableName, m_filevaluemap[pro], pro); 1470 2375 } 1471 2376 … … 1474 2379 QFileInfo fi(fileName); 1475 2380 if (fi.exists()) { 1476 ProFile *pro = new ProFile(fi.absoluteFilePath()); 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); 1477 2388 if (d->read(pro)) 1478 2389 return pro; … … 1487 2398 } 1488 2399 1489 bool ProFileEvaluator::Private::evaluateFile(const QString &fileName, bool *result) 1490 { 1491 bool ok = true; 2400 bool ProFileEvaluator::Private::evaluateFile(const QString &fileName) 2401 { 1492 2402 ProFile *pro = q->parsedProFile(fileName); 1493 2403 if (pro) { 1494 2404 m_profileStack.push(pro); 1495 ok = (currentProFile() ? pro->Accept(this) : false);2405 bool ok = (pro->Accept(this) == ProItem::ReturnTrue); 1496 2406 m_profileStack.pop(); 1497 2407 q->releaseParsedProFile(pro); 1498 1499 if (result) 1500 *result = true; 2408 return ok; 1501 2409 } else { 1502 if (result) 1503 *result = false; 1504 } 1505 /* if (ok && readFeatures) { 1506 QStringList configs = values("CONFIG"); 1507 QSet<QString> processed; 1508 foreach (const QString &fn, configs) { 1509 if (!processed.contains(fn)) { 1510 processed.insert(fn); 1511 evaluateFeatureFile(fn, 0); 1512 } 1513 } 1514 } */ 1515 1516 return ok; 1517 } 1518 1519 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, bool *result) 2410 return false; 2411 } 2412 } 2413 2414 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName) 1520 2415 { 1521 2416 QString fn; … … 1532 2427 } 1533 2428 } 1534 return fn.isEmpty() ? false : evaluateFile(fn, result); 1535 } 1536 1537 void ProFileEvaluator::Private::expandPatternHelper(const QString &relName, const QString &absName, 1538 QStringList &sources_out) 1539 { 1540 const QStringList vpaths = values(QLatin1String("VPATH")) 1541 + values(QLatin1String("QMAKE_ABSOLUTE_SOURCE_PATH")) 1542 + values(QLatin1String("DEPENDPATH")) 1543 + values(QLatin1String("VPATH_SOURCES")); 1544 1545 QFileInfo fi(absName); 1546 bool found = fi.exists(); 1547 // Search in all vpaths 1548 if (!found) { 1549 foreach (const QString &vpath, vpaths) { 1550 fi.setFile(vpath + QDir::separator() + relName); 1551 if (fi.exists()) { 1552 found = true; 1553 break; 1554 } 1555 } 1556 } 1557 1558 if (found) { 1559 sources_out += fi.absoluteFilePath(); // Not resolving symlinks 1560 } else { 1561 QString val = relName; 1562 QString dir; 1563 QString wildcard = val; 1564 QString real_dir; 1565 if (wildcard.lastIndexOf(QLatin1Char('/')) != -1) { 1566 dir = wildcard.left(wildcard.lastIndexOf(QLatin1Char('/')) + 1); 1567 real_dir = dir; 1568 wildcard = wildcard.right(wildcard.length() - dir.length()); 1569 } 1570 1571 if (real_dir.isEmpty() || QFileInfo(real_dir).exists()) { 1572 QStringList files = QDir(real_dir).entryList(QStringList(wildcard)); 1573 if (files.isEmpty()) { 1574 q->logMessage(format("Failure to find %1").arg(val)); 1575 } else { 1576 QString a; 1577 for (int i = files.count() - 1; i >= 0; --i) { 1578 if (files[i] == QLatin1String(".") || files[i] == QLatin1String("..")) 1579 continue; 1580 a = dir + files[i]; 1581 sources_out += a; 1582 } 1583 } 1584 } else { 1585 q->logMessage(format("Cannot match %1/%2, as %3 does not exist.") 1586 .arg(real_dir).arg(wildcard).arg(real_dir)); 1587 } 1588 } 1589 } 1590 1591 1592 /* 1593 * Lookup of files are done in this order: 1594 * 1. look in pwd 1595 * 2. look in vpaths 1596 * 3. expand wild card files relative from the profiles folder 1597 **/ 1598 1599 // FIXME: This code supports something that I'd consider a flaw in .pro file syntax 1600 // which is not even documented. So arguably this can be ditched completely... 1601 QStringList ProFileEvaluator::Private::expandPattern(const QString& pattern) 1602 { 1603 if (!currentProFile()) 1604 return QStringList(); 1605 1606 QStringList sources_out; 1607 const QString absName = QDir::cleanPath(QDir::current().absoluteFilePath(pattern)); 1608 1609 expandPatternHelper(pattern, absName, sources_out); 1610 return sources_out; 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; 1611 2436 } 1612 2437 … … 1642 2467 } 1643 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 1644 2478 QStringList ProFileEvaluator::values(const QString &variableName) const 1645 2479 { 1646 return d->values(variableName);2480 return fixEnvVariables(d->values(variableName)); 1647 2481 } 1648 2482 1649 2483 QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const 1650 2484 { 1651 return d->values(variableName, pro); 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; 1652 2539 } 1653 2540 … … 1656 2543 QStringList templ = values(QLatin1String("TEMPLATE")); 1657 2544 if (templ.count() >= 1) { 1658 QString t = templ.last().toLower();1659 if ( t == QLatin1String("app"))2545 const QString &t = templ.last(); 2546 if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive)) 1660 2547 return TT_Application; 1661 if ( t == QLatin1String("lib"))2548 if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive)) 1662 2549 return TT_Library; 1663 if (t == QLatin1String("subdirs")) 2550 if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive)) 2551 return TT_Script; 2552 if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive)) 1664 2553 return TT_Subdirs; 1665 2554 } … … 1705 2594 void ProFileEvaluator::logMessage(const QString &message) 1706 2595 { 1707 if (d->m_verbose )2596 if (d->m_verbose && !d->m_skipLevel) 1708 2597 qWarning("%s", qPrintable(message)); 1709 2598 } … … 1711 2600 void ProFileEvaluator::fileMessage(const QString &message) 1712 2601 { 1713 qWarning("%s", qPrintable(message)); 2602 if (!d->m_skipLevel) 2603 qWarning("%s", qPrintable(message)); 1714 2604 } 1715 2605 1716 2606 void ProFileEvaluator::errorMessage(const QString &message) 1717 2607 { 1718 qWarning("%s", qPrintable(message)); 2608 if (!d->m_skipLevel) 2609 qWarning("%s", qPrintable(message)); 1719 2610 } 1720 2611 … … 1724 2615 } 1725 2616 1726 void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap) 1727 { 1728 QStringList sourceFiles; 1729 QString codecForTr; 1730 QString codecForSource; 1731 QStringList tsFileNames; 1732 1733 // app/lib template 1734 sourceFiles += visitor.values(QLatin1String("SOURCES")); 1735 sourceFiles += visitor.values(QLatin1String("HEADERS")); 1736 tsFileNames = visitor.values(QLatin1String("TRANSLATIONS")); 1737 1738 QStringList trcodec = visitor.values(QLatin1String("CODEC")) 1739 + visitor.values(QLatin1String("DEFAULTCODEC")) 1740 + visitor.values(QLatin1String("CODECFORTR")); 1741 if (!trcodec.isEmpty()) 1742 codecForTr = trcodec.last(); 1743 1744 QStringList srccodec = visitor.values(QLatin1String("CODECFORSRC")); 1745 if (!srccodec.isEmpty()) 1746 codecForSource = srccodec.last(); 1747 1748 QStringList forms = visitor.values(QLatin1String("INTERFACES")) 1749 + visitor.values(QLatin1String("FORMS")) 1750 + visitor.values(QLatin1String("FORMS3")); 1751 sourceFiles << forms; 1752 1753 sourceFiles.sort(); 1754 sourceFiles.removeDuplicates(); 1755 tsFileNames.sort(); 1756 tsFileNames.removeDuplicates(); 1757 1758 varMap->insert("SOURCES", sourceFiles); 1759 varMap->insert("CODECFORTR", QStringList() << codecForTr); 1760 varMap->insert("CODECFORSRC", QStringList() << codecForSource); 1761 varMap->insert("TRANSLATIONS", tsFileNames); 1762 } 1763 1764 bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap) 1765 { 1766 QFileInfo fi(fileName); 1767 if (!fi.exists()) 1768 return false; 1769 1770 ProFile pro(fi.absoluteFilePath()); 1771 1772 ProFileEvaluator visitor; 1773 visitor.setVerbose(verbose); 1774 1775 if (!visitor.queryProFile(&pro)) 1776 return false; 1777 1778 if (!visitor.accept(&pro)) 1779 return false; 1780 1781 evaluateProFile(visitor, varMap); 1782 1783 return true; 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; 1784 2625 } 1785 2626
Note:
See TracChangeset
for help on using the changeset viewer.