00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "profileevaluator.h"
00025 #include "proreader.h"
00026 #include <QtCore/QString>
00027 #include <QtCore/QSet>
00028 #include <QtCore/QRegExp>
00029 #include <QtCore/QByteArray>
00030 #include <QtCore/QFileInfo>
00031 #include <QtCore/QFile>
00032 #include <QtCore/QDir>
00033 #include "proparserutils.h"
00034
00035 #ifdef Q_OS_UNIX
00036 #include <unistd.h>
00037 #endif
00038
00039 #ifdef Q_OS_WIN32
00040 #define QT_POPEN _popen
00041 #else
00042 #define QT_POPEN popen
00043 #endif
00044
00045 QStringList ProFileEvaluator::qmake_feature_paths()
00046 {
00047 QStringList concat;
00048 {
00049 const QString base_concat = QDir::separator() + QString("features");
00050 concat << base_concat + QDir::separator() + "mac";
00051 concat << base_concat + QDir::separator() + "macx";
00052 concat << base_concat + QDir::separator() + "unix";
00053 concat << base_concat + QDir::separator() + "win32";
00054 concat << base_concat + QDir::separator() + "mac9";
00055 concat << base_concat + QDir::separator() + "qnx6";
00056 concat << base_concat;
00057 }
00058 const QString mkspecs_concat = QDir::separator() + QString("mkspecs");
00059 QStringList feature_roots;
00060 QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
00061 if(!mkspec_path.isNull())
00062 feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077 QByteArray qmakepath = qgetenv("QMAKEPATH");
00078 if (!qmakepath.isNull()) {
00079 const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
00080 for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) {
00081 for(QStringList::Iterator concat_it = concat.begin();
00082 concat_it != concat.end(); ++concat_it)
00083 feature_roots << ((*it) + mkspecs_concat + (*concat_it));
00084 }
00085 }
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102 for(QStringList::Iterator concat_it = concat.begin();
00103 concat_it != concat.end(); ++concat_it)
00104 feature_roots << (propertyValue("QT_INSTALL_PREFIX") +
00105 mkspecs_concat + (*concat_it));
00106 for(QStringList::Iterator concat_it = concat.begin();
00107 concat_it != concat.end(); ++concat_it)
00108 feature_roots << (propertyValue("QT_INSTALL_DATA") +
00109 mkspecs_concat + (*concat_it));
00110 return feature_roots;
00111 }
00112
00113 ProFile *ProFileEvaluator::currentProFile() const
00114 {
00115 if (m_profileStack.count() > 0) {
00116 return m_profileStack.top();
00117 }
00118 return 0;
00119 }
00120
00121 QString ProFileEvaluator::currentFileName() const
00122 {
00123 ProFile *pro = currentProFile();
00124 if (pro) return pro->fileName();
00125 return QString();
00126 }
00127
00128 QString ProFileEvaluator::getcwd() const
00129 {
00130 ProFile *cur = m_profileStack.top();
00131 QFileInfo fi(cur->fileName());
00132 return fi.absolutePath();
00133 }
00134
00135 ProFileEvaluator::ProFileEvaluator()
00136 {
00137 Option::init();
00138 }
00139
00140 ProFileEvaluator::~ProFileEvaluator()
00141 {
00142 }
00143
00144 bool ProFileEvaluator::visitBeginProFile(ProFile * pro)
00145 {
00146 PRE(pro);
00147 bool ok = true;
00148 m_lineNo = pro->getLineNumber();
00149 if (m_oldPath.isEmpty()) {
00150
00151
00152
00153 m_oldPath = QDir::currentPath();
00154 m_profileStack.push(pro);
00155 QString fn = pro->fileName();
00156 ok = QDir::setCurrent(QFileInfo(fn).absolutePath());
00157 }
00158
00159 if (m_origfile.isEmpty())
00160 m_origfile = pro->fileName();
00161
00162 return ok;
00163 }
00164
00165 bool ProFileEvaluator::visitEndProFile(ProFile * pro)
00166 {
00167 PRE(pro);
00168 bool ok = true;
00169 m_lineNo = pro->getLineNumber();
00170 if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
00171 m_profileStack.pop();
00172 ok = QDir::setCurrent(m_oldPath);
00173 }
00174 return ok;
00175 }
00176
00177 bool ProFileEvaluator::visitProValue(ProValue *value)
00178 {
00179 PRE(value);
00180 m_lineNo = value->getLineNumber();
00181 QString val(value->value());
00182
00183 QByteArray varName = m_lastVarName;
00184
00185 QString v = expandVariableReferences(val);
00186 unquote(&v);
00187
00188 switch (m_variableOperator) {
00189 case ProVariable::UniqueAddOperator:
00190 insertUnique(&m_valuemap, varName, v, true);
00191 break;
00192 case ProVariable::SetOperator:
00193 case ProVariable::AddOperator:
00194 insertUnique(&m_valuemap, varName, v, false);
00195 break;
00196 case ProVariable::RemoveOperator:
00197 break;
00198 case ProVariable::ReplaceOperator:
00199 {
00200
00201 QStringList vm = m_valuemap.value(varName);
00202 QChar sep = val.at(1);
00203 QStringList func = val.split(sep);
00204 if (func.count() < 3 || func.count() > 4) {
00205 logMessage(QString::fromAscii("~= operator (function s///) expects 3 or 4 arguments.\n"), MT_DebugLevel1);
00206 return false;
00207 }
00208 if (func[0] != QLatin1String("s")) {
00209 logMessage(QString::fromAscii("~= operator can only handle s/// function.\n"), MT_DebugLevel1);
00210 return false;
00211 }
00212 bool global = false, quote = false, case_sense = false;
00213
00214 if (func.count() == 4) {
00215 global = func[3].indexOf('g') != -1;
00216 case_sense = func[3].indexOf('i') == -1;
00217 quote = func[3].indexOf('q') != -1;
00218 }
00219 QString pattern = func[1];
00220 QString replace = func[2];
00221 if(quote)
00222 pattern = QRegExp::escape(pattern);
00223 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
00224
00225 QStringList varlist = m_valuemap.value(varName);
00226 for(QStringList::Iterator varit = varlist.begin(); varit != varlist.end();) {
00227 if((*varit).contains(regexp)) {
00228 (*varit) = (*varit).replace(regexp, replace);
00229 if ((*varit).isEmpty())
00230 varit = varlist.erase(varit);
00231 else
00232 ++varit;
00233 if(!global)
00234 break;
00235 } else
00236 ++varit;
00237 }
00238 }
00239 break;
00240
00241 }
00242 return true;
00243 }
00244
00245
00246 bool ProFileEvaluator::visitProFunction(ProFunction *func)
00247 {
00248 m_lineNo = func->getLineNumber();
00249 bool result = true;
00250 bool ok = true;
00251 QByteArray text = func->text();
00252 int lparen = text.indexOf('(');
00253 int rparen = text.lastIndexOf(')');
00254 Q_ASSERT(lparen < rparen);
00255
00256 QString arguments(text.mid(lparen + 1, rparen - lparen - 1));
00257 QByteArray funcName = text.left(lparen);
00258 ok &= evaluateConditionalFunction(funcName, arguments, &result);
00259 return ok;
00260 }
00261
00262 bool ProFileEvaluator::visitBeginProBlock(ProBlock * block)
00263 {
00264 if (block->blockKind() == ProBlock::ScopeKind) {
00265 m_invertNext = false;
00266 m_condition = false;
00267 }
00268 return true;
00269 }
00270 bool ProFileEvaluator::visitEndProBlock(ProBlock * )
00271 {
00272 return true;
00273 }
00274
00275 bool ProFileEvaluator::visitBeginProVariable(ProVariable *variable)
00276 {
00277 m_lastVarName = variable->variable();
00278 m_variableOperator = variable->variableOperator();
00279 return true;
00280 }
00281 bool ProFileEvaluator::visitEndProVariable(ProVariable * )
00282 {
00283 m_lastVarName.clear();
00284 return true;
00285 }
00286
00287 bool ProFileEvaluator::visitProOperator(ProOperator * oper)
00288 {
00289 m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
00290 return true;
00291 }
00292 bool ProFileEvaluator::visitProCondition(ProCondition * cond)
00293 {
00294 if (!m_condition) {
00295 if (m_invertNext) {
00296 m_condition |= !isActiveConfig(cond->text(), true);
00297 } else {
00298 m_condition |= isActiveConfig(cond->text(), true);
00299 }
00300 }
00301 return true;
00302 }
00303
00304
00305 QString ProFileEvaluator::expandVariableReferences(const QString &str)
00306 {
00307 bool fOK;
00308 bool *ok = &fOK;
00309 QString ret;
00310 if(ok)
00311 *ok = true;
00312 if(str.isEmpty())
00313 return ret;
00314
00315 const ushort LSQUARE = '[';
00316 const ushort RSQUARE = ']';
00317 const ushort LCURLY = '{';
00318 const ushort RCURLY = '}';
00319 const ushort LPAREN = '(';
00320 const ushort RPAREN = ')';
00321 const ushort DOLLAR = '$';
00322 const ushort SLASH = '\\';
00323 const ushort UNDERSCORE = '_';
00324 const ushort DOT = '.';
00325 const ushort SPACE = ' ';
00326 const ushort TAB = '\t';
00327
00328 ushort unicode;
00329 const QChar *str_data = str.data();
00330 const int str_len = str.length();
00331
00332 ushort term;
00333 QString var, args;
00334
00335 int replaced = 0;
00336 QString current;
00337 for(int i = 0; i < str_len; ++i) {
00338 unicode = (str_data+i)->unicode();
00339 const int start_var = i;
00340 if(unicode == SLASH) {
00341 bool escape = false;
00342 const char *symbols = "[]{}()$\\";
00343 for(const char *s = symbols; *s; ++s) {
00344 if(*(str_data+i+1) == (ushort)*s) {
00345 i++;
00346 escape = true;
00347 if(!(replaced++))
00348 current = str.left(start_var);
00349 current.append(str.at(i));
00350 break;
00351 }
00352 }
00353 if(!escape && replaced)
00354 current.append(QChar(unicode));
00355 continue;
00356 }
00357 if(unicode == SPACE || unicode == TAB) {
00358 unicode = 0;
00359 if(!current.isEmpty()) {
00360 ret.append(current);
00361 current.clear();
00362 }
00363 } else if(unicode == DOLLAR && str_len > i+2) {
00364 unicode = (str_data+(++i))->unicode();
00365 if(unicode == DOLLAR) {
00366 term = 0;
00367 var.clear();
00368 args.clear();
00369 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
00370 unicode = (str_data+(++i))->unicode();
00371 if(unicode == LSQUARE) {
00372 unicode = (str_data+(++i))->unicode();
00373 term = RSQUARE;
00374 var_type = PROPERTY;
00375 } else if(unicode == LCURLY) {
00376 unicode = (str_data+(++i))->unicode();
00377 var_type = VAR;
00378 term = RCURLY;
00379 } else if(unicode == LPAREN) {
00380 unicode = (str_data+(++i))->unicode();
00381 var_type = ENVIRON;
00382 term = RPAREN;
00383 }
00384 while(1) {
00385 if(!(unicode & (0xFF<<8)) &&
00386 unicode != DOT && unicode != UNDERSCORE &&
00387 (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
00388 (unicode < '0' || unicode > '9'))
00389 break;
00390 var.append(QChar(unicode));
00391 if(++i == str_len)
00392 break;
00393 unicode = (str_data+i)->unicode();
00394 }
00395 if(var_type == VAR && unicode == LPAREN) {
00396 var_type = FUNCTION;
00397 int depth = 0;
00398 while(1) {
00399 if(++i == str_len)
00400 break;
00401 unicode = (str_data+i)->unicode();
00402 if(unicode == LPAREN) {
00403 depth++;
00404 } else if(unicode == RPAREN) {
00405 if(!depth)
00406 break;
00407 --depth;
00408 }
00409 args.append(QChar(unicode));
00410 }
00411 if(i < str_len-1)
00412 unicode = (str_data+(++i))->unicode();
00413 else
00414 unicode = 0;
00415 }
00416 if(term) {
00417 if(unicode != term) {
00418 logMessage("Missing " + QString(term) + " terminator [found " + QString(unicode) + "]", MT_DebugLevel1);
00419 if(ok)
00420 *ok = false;
00421 return QString();
00422 }
00423 unicode = 0;
00424 } else if(i > str_len-1) {
00425 unicode = 0;
00426 }
00427
00428 QString replacement;
00429 if(var_type == ENVIRON) {
00430 replacement = QString::fromLocal8Bit(qgetenv(var.toLatin1().constData()));
00431 } else if(var_type == PROPERTY) {
00432 replacement = propertyValue(var);
00433
00434
00435 } else if(var_type == FUNCTION) {
00436 replacement = evaluateExpandFunction( var.toAscii(), args );
00437 } else if(var_type == VAR) {
00438 replacement = values(var).join(" ");
00439 }
00440 if(!(replaced++) && start_var)
00441 current = str.left(start_var);
00442 if(!replacement.isEmpty()) {
00443 current.append(replacement);
00444 }
00445 logMessage(MT_DebugLevel2, "Project Parser [var replace]: %s -> %s",
00446 str.toLatin1().constData(), var.toLatin1().constData(),
00447 replacement.toLatin1().constData());
00448 } else {
00449 if(replaced)
00450 current.append("$");
00451 }
00452 }
00453 if(replaced && unicode)
00454 current.append(QChar(unicode));
00455 }
00456 if(!replaced)
00457 ret = str;
00458 else if(!current.isEmpty())
00459 ret.append(current);
00460 return ret;
00461 }
00462
00463 bool ProFileEvaluator::isActiveConfig(const QByteArray &config, bool regex)
00464 {
00465
00466 if(config == "true")
00467 return true;
00468 else if(config == "false")
00469 return false;
00470
00471
00472 if((Option::target_mode == Option::TARG_MACX_MODE || Option::target_mode == Option::TARG_QNX6_MODE ||
00473 Option::target_mode == Option::TARG_UNIX_MODE) && config == "unix")
00474 return true;
00475 else if(Option::target_mode == Option::TARG_MACX_MODE && config == "macx")
00476 return true;
00477 else if(Option::target_mode == Option::TARG_QNX6_MODE && config == "qnx6")
00478 return true;
00479 else if(Option::target_mode == Option::TARG_MAC9_MODE && config == "mac9")
00480 return true;
00481 else if((Option::target_mode == Option::TARG_MAC9_MODE || Option::target_mode == Option::TARG_MACX_MODE) &&
00482 config == "mac")
00483 return true;
00484 else if(Option::target_mode == Option::TARG_WIN_MODE && config == "win32")
00485 return true;
00486
00487 QRegExp re(config, Qt::CaseSensitive, QRegExp::Wildcard);
00488 QString spec = Option::qmakespec;
00489 if((regex && re.exactMatch(spec)) || (!regex && spec == config))
00490 return true;
00491
00492 return false;
00493 }
00494
00495 QString ProFileEvaluator::evaluateExpandFunction(const QByteArray &func, const QString &arguments)
00496 {
00497 const char field_sep = ' ';
00498
00499 QStringList args = split_arg_list(arguments);
00500 for (int i = 0; i < args.count(); ++i) {
00501 args[i] = expandVariableReferences(args[i]);
00502 }
00503 enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
00504 E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
00505 E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
00506 E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
00507 E_REPLACE };
00508
00509 static QMap<QByteArray, int> *expands = 0;
00510 if(!expands) {
00511 expands = new QMap<QByteArray, int>;
00512 expands->insert("member", E_MEMBER);
00513 expands->insert("first", E_FIRST);
00514 expands->insert("last", E_LAST);
00515 expands->insert("cat", E_CAT);
00516 expands->insert("fromfile", E_FROMFILE);
00517 expands->insert("eval", E_EVAL);
00518 expands->insert("list", E_LIST);
00519 expands->insert("sprintf", E_SPRINTF);
00520 expands->insert("join", E_JOIN);
00521 expands->insert("split", E_SPLIT);
00522 expands->insert("basename", E_BASENAME);
00523 expands->insert("dirname", E_DIRNAME);
00524 expands->insert("section", E_SECTION);
00525 expands->insert("find", E_FIND);
00526 expands->insert("system", E_SYSTEM);
00527 expands->insert("unique", E_UNIQUE);
00528 expands->insert("quote", E_QUOTE);
00529 expands->insert("escape_expand", E_ESCAPE_EXPAND);
00530 expands->insert("upper", E_UPPER);
00531 expands->insert("lower", E_LOWER);
00532 expands->insert("re_escape", E_RE_ESCAPE);
00533 expands->insert("files", E_FILES);
00534 expands->insert("prompt", E_PROMPT);
00535 expands->insert("replace", E_REPLACE);
00536 }
00537 ExpandFunc func_t = (ExpandFunc)expands->value(func.toLower());
00538
00539 QString ret;
00540
00541 switch(func_t) {
00542 case E_BASENAME:
00543 case E_DIRNAME:
00544 case E_SECTION: {
00545 bool regexp = false;
00546 QString sep, var;
00547 int beg=0, end=-1;
00548 if(func_t == E_SECTION) {
00549 if(args.count() != 3 && args.count() != 4) {
00550 logMessage(QString::fromAscii("%2(var) section(var, sep, begin, end) requires three arguments.\n").arg(
00551 QString(func)));
00552 } else {
00553 var = args[0];
00554 sep = args[1];
00555 beg = args[2].toInt();
00556 if(args.count() == 4)
00557 end = args[3].toInt();
00558 }
00559 } else {
00560 if(args.count() != 1) {
00561 logMessage(QString::fromAscii("%2(var) requires one argument.\n").arg(
00562 QString(func)));
00563 } else {
00564 var = args[0];
00565 regexp = true;
00566 sep = "[\\\\/]";
00567 if(func_t == E_DIRNAME)
00568 end = -2;
00569 else
00570 beg = -1;
00571 }
00572 }
00573 if(!var.isNull()) {
00574 const QStringList l = values(var);
00575 for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
00576 QString separator = sep;
00577 if(!ret.isEmpty())
00578 ret += QLatin1Char(field_sep);
00579 if(regexp)
00580 ret += (*it).section(QRegExp(separator), beg, end);
00581 else
00582 ret += (*it).section(separator, beg, end);
00583 }
00584 }
00585 break; }
00586 case E_JOIN: {
00587 if(args.count() < 1 || args.count() > 4) {
00588 logMessage(QString::fromAscii("join(var, glue, before, after) requires four arguments.\n"));
00589 } else {
00590 QString glue, before, after;
00591 if(args.count() >= 2)
00592 glue = args[1];
00593 if(args.count() >= 3)
00594 before = args[2];
00595 if(args.count() == 4)
00596 after = args[3];
00597 const QStringList &var = values(args.first());
00598 if(!var.isEmpty())
00599 ret = before + var.join(glue) + after;
00600 }
00601 break; }
00602 case E_SPLIT: {
00603 if(args.count() < 2 || args.count() > 3) {
00604 logMessage(QString::fromAscii("split(var, sep, join) requires three arguments\n"));
00605 } else {
00606 QString sep = args[1], join = QString(field_sep);
00607 if(args.count() == 3)
00608 join = args[2];
00609 QStringList var = values(args.first());
00610 for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) {
00611 QStringList lst = (*vit).split(sep);
00612 for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit) {
00613 if(!ret.isEmpty())
00614 ret += join;
00615 ret += (*spltit);
00616 }
00617 }
00618 }
00619 break; }
00620
00621 case E_MEMBER: {
00622 if(args.count() < 1 || args.count() > 3) {
00623 logMessage(QString::fromAscii("member(var, start, end) requires three arguments.\n"));
00624 } else {
00625 bool ok = true;
00626 const QStringList var = values(args.first());
00627 int start = 0, end = 0;
00628 if(args.count() >= 2) {
00629 QString start_str = args[1];
00630 start = start_str.toInt(&ok);
00631 if(!ok) {
00632 if(args.count() == 2) {
00633 int dotdot = start_str.indexOf("..");
00634 if(dotdot != -1) {
00635 start = start_str.left(dotdot).toInt(&ok);
00636 if(ok)
00637 end = start_str.mid(dotdot+2).toInt(&ok);
00638 }
00639 }
00640 if(!ok)
00641 logMessage(QString::fromAscii("member() argument 2 (start) '%2' invalid.\n").arg(
00642 start_str), MT_DebugLevel1 );
00643 } else {
00644 end = start;
00645 if(args.count() == 3)
00646 end = args[2].toInt(&ok);
00647 if(!ok)
00648 logMessage(QString::fromAscii("member() argument 3 (end) '%2' invalid.\n").arg(
00649 args[2]), MT_DebugLevel1 );
00650 }
00651 }
00652 if(ok) {
00653 if(start < 0)
00654 start += var.count();
00655 if(end < 0)
00656 end += var.count();
00657 if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
00658
00659 } else if(start < end) {
00660 for(int i = start; i <= end && (int)var.count() >= i; i++) {
00661 if(!ret.isEmpty())
00662 ret += field_sep;
00663 ret += var[i];
00664 }
00665 } else {
00666 for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--) {
00667 if(!ret.isEmpty())
00668 ret += field_sep;
00669 ret += var[i];
00670 }
00671 }
00672 }
00673 }
00674 break; }
00675 case E_FIRST:
00676 case E_LAST: {
00677 if(args.count() != 1) {
00678 logMessage(QString::fromAscii("%2(var) requires one argument.\n").arg(
00679 QString(func)));
00680 } else {
00681 const QStringList var = values(args.first());
00682 if(!var.isEmpty()) {
00683 if(func_t == E_FIRST)
00684 ret = var[0];
00685 else
00686 ret = var[var.size()-1];
00687 }
00688 }
00689 break; }
00690
00691 case E_SYSTEM: {
00692 if (m_condition) {
00693 if(args.count() < 1 || args.count() > 2) {
00694 logMessage(QString::fromAscii("system(execut) requires one or two arguments.\n"));
00695 } else {
00696 char buff[256];
00697 FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
00698 bool singleLine = true;
00699 if(args.count() > 1)
00700 singleLine = (args[1].toLower() == "true");
00701 while(proc && !feof(proc)) {
00702 int read_in = int(fread(buff, 1, 255, proc));
00703 if(!read_in)
00704 break;
00705 for(int i = 0; i < read_in; i++) {
00706 if((singleLine && buff[i] == '\n') || buff[i] == '\t')
00707 buff[i] = ' ';
00708 }
00709 buff[read_in] = '\0';
00710 ret += buff;
00711 }
00712 }
00713 }
00714 break; }
00715 case 0: {
00716 logMessage(MT_DebugLevel2, "'%s' is not a function\n", func.data());
00717 break; }
00718 default: {
00719 logMessage(MT_DebugLevel2, "Function '%s' is not implemented\n", func.data());
00720 break; }
00721 }
00722
00723 return ret;
00724 }
00725
00726 bool ProFileEvaluator::evaluateConditionalFunction(const QByteArray &function, const QString &arguments, bool *result)
00727 {
00728 QStringList args = split_arg_list(arguments);
00729 for (int i = 0; i < args.count(); ++i) {
00730 args[i] = expandVariableReferences(args[i]);
00731 }
00732 enum ConditionFunc { CF_CONFIG = 1, CF_CONTAINS, CF_COUNT, CF_EXISTS, CF_INCLUDE,
00733 CF_LOAD, CF_ISEMPTY, CF_SYSTEM, CF_MESSAGE};
00734
00735 static QMap<QByteArray, int> *functions = 0;
00736 if(!functions) {
00737 functions = new QMap<QByteArray, int>;
00738 functions->insert("load", CF_LOAD);
00739 functions->insert("include", CF_INCLUDE);
00740 functions->insert("message", CF_MESSAGE);
00741 functions->insert("warning", CF_MESSAGE);
00742 functions->insert("error", CF_MESSAGE);
00743 }
00744
00745 bool cond = false;
00746 bool ok = true;
00747
00748 ConditionFunc func_t = (ConditionFunc)functions->value(function);
00749
00750 switch (func_t) {
00751 case CF_CONFIG: {
00752 if(args.count() < 1 || args.count() > 2) {
00753 logMessage(QString::fromAscii("CONFIG(config) requires one or two arguments.\n"), MT_DebugLevel1);
00754 ok = false;
00755 break;
00756 }
00757 if(args.count() == 1) {
00758
00759 break;
00760 }
00761 const QStringList mutuals = args[1].split('|');
00762 const QStringList &configs = m_valuemap.value("CONFIG");
00763 for(int i = configs.size() - 1 && ok; i >= 0; i--) {
00764 for(int mut = 0; mut < mutuals.count(); mut++) {
00765 if(configs[i] == mutuals[mut].trimmed()) {
00766 cond = (configs[i] == args[0]);
00767 break;
00768 }
00769 }
00770 }
00771 break; }
00772 case CF_CONTAINS: {
00773 if(args.count() < 2 || args.count() > 3) {
00774 logMessage(QString::fromAscii("contains(var, val) requires at least two arguments.\n"), MT_DebugLevel1);
00775 ok = false;
00776 break;
00777 }
00778
00779 QRegExp regx(args[1]);
00780 const QStringList &l = values(args.first());
00781 if(args.count() == 2) {
00782 for(int i = 0; i < l.size(); ++i) {
00783 const QString val = l[i];
00784 if(regx.exactMatch(val) || val == args[1]) {
00785 cond = true;
00786 break;
00787 }
00788 }
00789 } else {
00790 const QStringList mutuals = args[2].split('|');
00791 for(int i = l.size()-1; i >= 0; i--) {
00792 const QString val = l[i];
00793 for(int mut = 0; mut < mutuals.count(); mut++) {
00794 if(val == mutuals[mut].trimmed()) {
00795 cond = (regx.exactMatch(val) || val == args[1]);
00796 break;
00797 }
00798 }
00799 }
00800 }
00801
00802 break; }
00803 case CF_COUNT: {
00804 if(args.count() != 2 && args.count() != 3) {
00805 logMessage(QString::fromAscii("count(var, count) requires at least two arguments.\n"), MT_DebugLevel1);
00806 ok = false;
00807 break;
00808 }
00809 if(args.count() == 3) {
00810 QString comp = args[2];
00811 if(comp == ">" || comp == "greaterThan") {
00812 cond = values(args.first()).count() > args[1].toInt();
00813 } else if(comp == ">=") {
00814 cond = values(args.first()).count() >= args[1].toInt();
00815 } else if(comp == "<" || comp == "lessThan") {
00816 cond = values(args.first()).count() < args[1].toInt();
00817 } else if(comp == "<=") {
00818 cond = values(args.first()).count() <= args[1].toInt();
00819 } else if(comp == "equals" || comp == "isEqual" || comp == "=" || comp == "==") {
00820 cond = values(args.first()).count() == args[1].toInt();
00821 } else {
00822 ok = false;
00823 logMessage(QString::fromAscii("unexpected modifier to count(%2)\n").arg(
00824 comp), MT_DebugLevel1);
00825 }
00826 break;
00827 }
00828 cond = values(args.first()).count() == args[1].toInt();
00829 break; }
00830 case CF_INCLUDE: {
00831 QString parseInto;
00832 if(args.count() == 2) {
00833 parseInto = args[1];
00834 } else if(args.count() != 1) {
00835 logMessage(QString::fromAscii("include(file) requires one argument.\n"), MT_DebugLevel1);
00836 ok = false;
00837 break;
00838 }
00839 ok = evaluateFile(args.first(), &ok);
00840 break; }
00841 case CF_LOAD: {
00842 QString parseInto;
00843 bool ignore_error = false;
00844 if(args.count() == 2) {
00845 QString sarg = args[1];
00846 ignore_error = (sarg.toLower() == "true" || sarg.toInt());
00847 } else if(args.count() != 1) {
00848 logMessage(QString::fromAscii("load(feature) requires one argument.\n"), MT_DebugLevel1);
00849 ok = false;
00850 break;
00851 }
00852 ok = evaluateFeatureFile( args.first(), &cond);
00853 break; }
00854
00855 case CF_MESSAGE: {
00856 if(args.count() != 1) {
00857 logMessage(QString::fromAscii("%2(message) requires one argument.\n").arg(
00858 QString(function)), MT_DebugLevel1);
00859 ok = false;
00860 break;
00861 }
00862 QString msg = args.first();
00863 bool isError = (function == "error");
00864 logMessage(QString::fromAscii("%2\n").arg(msg), isError ? MT_ProError : MT_ProMessage);
00865 break; }
00866
00867 case CF_SYSTEM: {
00868 if(args.count() != 1) {
00869 logMessage(QString::fromAscii("system(exec) requires one argument.\n"), MT_DebugLevel1);
00870 ok = false;
00871 break;
00872 }
00873 ok = system(args.first().toLatin1().constData()) == 0;
00874 break; }
00875
00876 case CF_ISEMPTY: {
00877 if(args.count() != 1) {
00878 logMessage(QString::fromAscii("isEmpty(var) requires one argument.\n"), MT_DebugLevel1);
00879 ok = false;
00880 break;
00881 }
00882 QStringList sl = values(args.first());
00883 if (sl.count() == 0) {
00884 cond = true;
00885 }else if (sl.count() > 0) {
00886 QString var = sl.first();
00887 cond = (var.isEmpty());
00888 }
00889 break; }
00890 case CF_EXISTS: {
00891 if(args.count() != 1) {
00892 logMessage(QString::fromAscii("exists(file) requires one argument.\n"), MT_DebugLevel1);
00893 ok = false;
00894 break;
00895 }
00896 QString file = args.first();
00897
00898 file = QDir::cleanPath(file);
00899
00900 if (QFile::exists(file)) {
00901 cond = true;
00902 break;
00903 }
00904
00905 QString dirstr = getcwd();
00906 int slsh = file.lastIndexOf(Option::dir_sep);
00907 if(slsh != -1) {
00908 dirstr = file.left(slsh+1);
00909 file = file.right(file.length() - slsh - 1);
00910 }
00911 cond = QDir(dirstr).entryList(QStringList(file)).count();
00912
00913 break; }
00914
00915 }
00916
00917 if (result) *result = cond;
00918
00919 return ok;
00920 }
00921
00922 bool ProFileEvaluator::contains(const QString &variableName) const
00923 {
00924 return m_valuemap.contains(variableName.toAscii());
00925 }
00926
00927 QStringList ProFileEvaluator::values(const QString &variableName) const
00928 {
00929 if (variableName == QLatin1String("PWD")) {
00930 return QStringList(getcwd());
00931 }
00932 return m_valuemap.value(variableName.toAscii());
00933 }
00934
00935 bool ProFileEvaluator::evaluateFile(const QString &fileName, bool *result)
00936 {
00937 bool ok = true;
00938
00939 QString fn = fileName;
00940
00941 QFileInfo fi(fn);
00942 if (fi.exists()) {
00943 logMessage(QString::fromAscii("Reading %2\n").arg(fileName), MT_DebugLevel3);
00944 ProFile *pro = queryProFile(fi.absoluteFilePath());
00945 if (pro) {
00946 m_profileStack.push_back(pro);
00947 ok &= currentProFile() ? pro->Accept(this) : false;
00948 if (ok) {
00949 if (m_profileStack.count() > 0) {
00950 ProFile *pro = m_profileStack.pop();
00951 releaseProFile(pro);
00952 }
00953 }
00954 }
00955 if (result) *result = true;
00956 }else{
00957 if (result) *result = false;
00958 }
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972 return ok;
00973 }
00974
00975
00976 bool ProFileEvaluator::evaluateFeatureFile(const QString &fileName, bool *result)
00977 {
00978 QString fn;
00979 QStringList feature_paths = qmake_feature_paths();
00980 for(QStringList::ConstIterator it = feature_paths.begin(); it != feature_paths.end(); ++it) {
00981 QString fname = *it + QLatin1Char('/') + fileName;
00982 if (QFileInfo(fname).exists()) {
00983 fn = fname;
00984 break;
00985 }
00986 fname += QLatin1String(".prf");
00987 if (QFileInfo(fname).exists()) {
00988 fn = fname;
00989 break;
00990 }
00991 }
00992 return fn.isEmpty() ? false : evaluateFile(fn, result);
00993 }
00994
00995 ProFileEvaluator::TemplateType ProFileEvaluator::templateType()
00996 {
00997 QStringList templ = m_valuemap.value("TEMPLATE");
00998 if (templ.count() >= 1) {
00999 QByteArray t = templ.last().toAscii().toLower();
01000 if (t == "app") return TT_Application;
01001 if (t == "lib") return TT_Library;
01002 if (t == "subdirs") return TT_Subdirs;
01003 }
01004 return TT_Unknown;
01005 }
01006
01007
01008
01009
01010
01011
01012
01013 QStringList ProFileEvaluator::absFileNames(const QString &variableName)
01014 {
01015
01016 QStringList vpaths = values(QLatin1String("VPATH"))
01017 + values(QLatin1String("QMAKE_ABSOLUTE_SOURCE_PATH"))
01018 + values(QLatin1String("DEPENDPATH"))
01019 + values(QLatin1String("VPATH_SOURCES"));
01020
01021 QStringList sources_out;
01022 QStringList sources = values(variableName);
01023 QFileInfo fi(m_origfile);
01024 QDir dir(fi.absoluteDir());
01025 for (int i = 0; i < sources.count(); ++i) {
01026 QString fn = sources[i];
01027 QString absName = QDir::cleanPath(dir.absoluteFilePath(sources[i]));
01028 QFileInfo fi(absName);
01029 bool found = fi.exists();
01030
01031 for(QStringList::Iterator vpath_it = vpaths.begin(); vpath_it != vpaths.end() && !found; ++vpath_it) {
01032 QDir vpath(*vpath_it);
01033 fi.setFile(*vpath_it + QDir::separator() + fn);
01034 if (fi.exists()) {
01035 absName = fi.absoluteFilePath();
01036 found = true;
01037 break;
01038 }
01039 }
01040 if (found) {
01041 sources_out+=fi.canonicalFilePath();
01042 } else {
01043 QString val = fn;
01044 QString dir, regex = val, real_dir;
01045 if(regex.lastIndexOf(QLatin1Char('/')) != -1) {
01046 dir = regex.left(regex.lastIndexOf(QLatin1Char('/')) + 1);
01047 real_dir = dir;
01048 regex = regex.right(regex.length() - dir.length());
01049 }
01050 if(real_dir.isEmpty() || QFileInfo(real_dir).exists()) {
01051 QStringList files = QDir(real_dir).entryList(QStringList(regex));
01052 if(files.isEmpty()) {
01053 logMessage(MT_DebugLevel2, "%s:%d Failure to find %s",
01054 __FILE__, __LINE__,
01055 val.toLatin1().constData());
01056 } else {
01057 QString a;
01058 for(int i = (int)files.count()-1; i >= 0; i--) {
01059 if(files[i] == "." || files[i] == "..")
01060 continue;
01061 a = dir + files[i];
01062 sources_out+=a;
01063 }
01064 }
01065 } else {
01066 logMessage(MT_DebugLevel2, "%s:%d Cannot match %s%c%s, as %s does not exist.",
01067 __FILE__, __LINE__, real_dir.toLatin1().constData(),
01068 '/',
01069 regex.toLatin1().constData(), real_dir.toLatin1().constData());
01070 }
01071
01072 }
01073 }
01074 return sources_out;
01075 }
01076
01077 ProFile *ProFileEvaluator::queryProFile(const QString &filename)
01078 {
01079 ProReader pr;
01080 pr.setEnableBackSlashFixing(false);
01081
01082 ProFile *pro = pr.read(filename);
01083 if (!pro) {
01084 LogMessage msg;
01085 msg.m_msg = QLatin1String("parse failure.");
01086 msg.m_filename = filename;
01087 msg.m_linenumber = pr.currentLine();
01088 msg.m_type = MT_Error;
01089 logMessage(msg);
01090 }
01091
01092 return pro;
01093 }
01094
01095 void ProFileEvaluator::releaseProFile(ProFile *pro)
01096 {
01097 delete pro;
01098 }
01099
01100 QString ProFileEvaluator::propertyValue(const QString &val) const
01101 {
01102 return getPropertyValue(val);
01103 }
01104
01105 void ProFileEvaluator::logMessage(const ProFileEvaluator::LogMessage &msg)
01106 {
01107 QByteArray locstr = QString("%1(%2):").arg(msg.m_filename).arg(msg.m_linenumber).toAscii();
01108 QByteArray text = msg.m_msg.toAscii();
01109 switch (msg.m_type) {
01110 case MT_DebugLevel3:
01111 fprintf(stderr, "%s profileevaluator information: %s", locstr.data(), text.data());
01112 break;
01113 case MT_DebugLevel2:
01114 fprintf(stderr, "%s profileevaluator warning: %s", locstr.data(), text.data());
01115 break;
01116 case MT_DebugLevel1:
01117 fprintf(stderr, "%s profileevaluator critical error: %s", locstr.data(), text.data());
01118 break;
01119 case MT_ProMessage:
01120 fprintf(stderr, "%s Project MESSAGE: %s", locstr.data(), text.data());
01121 break;
01122 case MT_ProError:
01123 fprintf(stderr, "%s Project ERROR: %s", locstr.data(), text.data());
01124 break;
01125 case MT_Error:
01126 fprintf(stderr, "%s ERROR: %s", locstr.data(), text.data());
01127 break;
01128 }
01129 }
01130
01131 void ProFileEvaluator::logMessage(const QString &message, MessageType mt)
01132 {
01133 LogMessage msg;
01134 msg.m_msg = message;
01135 msg.m_type = mt;
01136
01137 ProFile *pro = currentProFile();
01138 if (pro) {
01139 msg.m_filename = pro->fileName();
01140 msg.m_linenumber = m_lineNo;
01141 } else {
01142 msg.m_filename = "Not a file";
01143 msg.m_linenumber = 0;
01144 }
01145
01146 logMessage(msg);
01147 }
01148
01149 void ProFileEvaluator::logMessage(MessageType mt, const char *msg, ...)
01150 {
01151 #define MAX_MESSAGE_LENGTH 1024
01152 char buf[MAX_MESSAGE_LENGTH];
01153 va_list ap;
01154 va_start(ap, msg);
01155 qvsnprintf(buf, MAX_MESSAGE_LENGTH - 1, msg, ap);
01156 va_end(ap);
01157 buf[MAX_MESSAGE_LENGTH - 1] = '\0';
01158 logMessage(QString::fromAscii(buf), mt);
01159 }