diff options
Diffstat (limited to 'qmake/library')
-rw-r--r-- | qmake/library/proitems.cpp | 13 | ||||
-rw-r--r-- | qmake/library/proitems.h | 11 | ||||
-rw-r--r-- | qmake/library/qmakebuiltins.cpp | 334 | ||||
-rw-r--r-- | qmake/library/qmakeevaluator.cpp | 22 | ||||
-rw-r--r-- | qmake/library/qmakeevaluator.h | 12 | ||||
-rw-r--r-- | qmake/library/qmakeglobals.cpp | 11 | ||||
-rw-r--r-- | qmake/library/qmakeglobals.h | 2 | ||||
-rw-r--r-- | qmake/library/qmakeparser.cpp | 46 | ||||
-rw-r--r-- | qmake/library/qmakeparser.h | 9 |
9 files changed, 321 insertions, 139 deletions
diff --git a/qmake/library/proitems.cpp b/qmake/library/proitems.cpp index 5f923744be..66db190bc1 100644 --- a/qmake/library/proitems.cpp +++ b/qmake/library/proitems.cpp @@ -365,6 +365,11 @@ static QString ProStringList_join(const ProStringList &this_, const QChar *sep, return res; } +QString ProStringList::join(const ProString &sep) const +{ + return ProStringList_join(*this, sep.constData(), sep.size()); +} + QString ProStringList::join(const QString &sep) const { return ProStringList_join(*this, sep.constData(), sep.size()); @@ -453,6 +458,14 @@ bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const return false; } +bool ProStringList::contains(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + for (int i = 0; i < size(); i++) + if (!at(i).toQStringRef().compare(str, cs)) + return true; + return false; +} + bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const { for (int i = 0; i < size(); i++) diff --git a/qmake/library/proitems.h b/qmake/library/proitems.h index dcff970600..a3d2c01760 100644 --- a/qmake/library/proitems.h +++ b/qmake/library/proitems.h @@ -100,6 +100,7 @@ public: bool operator!=(const QString &other) const { return !(*this == other); } bool operator!=(QLatin1String other) const { return !(*this == other); } bool operator!=(const char *other) const { return !(*this == other); } + bool operator<(const ProString &other) const { return toQStringRef() < other.toQStringRef(); } bool isNull() const { return m_string.isNull(); } bool isEmpty() const { return !m_length; } int length() const { return m_length; } @@ -207,9 +208,13 @@ inline QString operator+(const QString &one, const ProString &two) { return ProString(one) + two; } inline QString operator+(const ProString &one, const char *two) - { return one + ProString(two); } // XXX optimize + { QString ret = one.toQStringRef() + two; ret.detach(); return ret; } inline QString operator+(const char *one, const ProString &two) - { return ProString(one) + two; } // XXX optimize + { QString ret = one + two.toQStringRef(); ret.detach(); return ret; } +inline QString operator+(const ProString &one, QChar two) + { return one.toQStringRef() + two; } +inline QString operator+(QChar one, const ProString &two) + { return one + two.toQStringRef(); } inline QString &operator+=(QString &that, const ProString &other) { return that += other.toQStringRef(); } @@ -233,6 +238,7 @@ public: int length() const { return size(); } + QString join(const ProString &sep) const; QString join(const QString &sep) const; QString join(QChar sep) const; @@ -246,6 +252,7 @@ public: void removeDuplicates(); bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return contains(ProString(str), cs); } bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; diff --git a/qmake/library/qmakebuiltins.cpp b/qmake/library/qmakebuiltins.cpp index 199c49e315..544ccac6d2 100644 --- a/qmake/library/qmakebuiltins.cpp +++ b/qmake/library/qmakebuiltins.cpp @@ -52,6 +52,8 @@ # include <qthreadpool.h> #endif +#include <algorithm> + #ifdef Q_OS_UNIX #include <time.h> #include <utime.h> @@ -84,9 +86,10 @@ QT_BEGIN_NAMESPACE #define fL1S(s) QString::fromLatin1(s) enum ExpandFunc { - E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST, - E_SPRINTF, E_FORMAT_NUMBER, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, - E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND, + E_INVALID = 0, E_MEMBER, E_STR_MEMBER, E_FIRST, E_TAKE_FIRST, E_LAST, E_TAKE_LAST, + E_SIZE, E_STR_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST, E_SPRINTF, E_FORMAT_NUMBER, + E_NUM_ADD, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, + E_FIND, E_SYSTEM, E_UNIQUE, E_SORTED, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND, E_UPPER, E_LOWER, E_TITLE, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE, E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS, E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH, @@ -96,7 +99,7 @@ enum ExpandFunc { enum TestFunc { T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, - T_DEFINED, T_CONTAINS, T_INFILE, + T_DEFINED, T_DISCARD_FROM, T_CONTAINS, T_INFILE, T_COUNT, T_ISEMPTY, T_PARSE_JSON, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF, T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE }; @@ -108,15 +111,20 @@ void QMakeEvaluator::initFunctionStatics() const ExpandFunc func; } expandInits[] = { { "member", E_MEMBER }, + { "str_member", E_STR_MEMBER }, { "first", E_FIRST }, + { "take_first", E_TAKE_FIRST }, { "last", E_LAST }, + { "take_last", E_TAKE_LAST }, { "size", E_SIZE }, + { "str_size", E_STR_SIZE }, { "cat", E_CAT }, { "fromfile", E_FROMFILE }, { "eval", E_EVAL }, { "list", E_LIST }, { "sprintf", E_SPRINTF }, { "format_number", E_FORMAT_NUMBER }, + { "num_add", E_NUM_ADD }, { "join", E_JOIN }, { "split", E_SPLIT }, { "basename", E_BASENAME }, @@ -125,6 +133,7 @@ void QMakeEvaluator::initFunctionStatics() { "find", E_FIND }, { "system", E_SYSTEM }, { "unique", E_UNIQUE }, + { "sorted", E_SORTED }, { "reverse", E_REVERSE }, { "quote", E_QUOTE }, { "escape_expand", E_ESCAPE_EXPAND }, @@ -171,6 +180,7 @@ void QMakeEvaluator::initFunctionStatics() { "if", T_IF }, { "isActiveConfig", T_CONFIG }, { "system", T_SYSTEM }, + { "discard_from", T_DISCARD_FROM }, { "defined", T_DEFINED }, { "contains", T_CONTAINS }, { "infile", T_INFILE }, @@ -201,6 +211,49 @@ static bool isTrue(const ProString &str) return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt(); } +bool +QMakeEvaluator::getMemberArgs(const ProKey &func, int srclen, const ProStringList &args, + int *start, int *end) +{ + *start = 0, *end = 0; + if (args.count() >= 2) { + bool ok = true; + const ProString &start_str = args.at(1); + *start = start_str.toInt(&ok); + if (!ok) { + if (args.count() == 2) { + int dotdot = start_str.indexOf(statics.strDotDot); + if (dotdot != -1) { + *start = start_str.left(dotdot).toInt(&ok); + if (ok) + *end = start_str.mid(dotdot+2).toInt(&ok); + } + } + if (!ok) { + evalError(fL1S("%1() argument 2 (start) '%2' invalid.") + .arg(func.toQString(m_tmp1), start_str.toQString(m_tmp2))); + return false; + } + } else { + *end = *start; + if (args.count() == 3) + *end = args.at(2).toInt(&ok); + if (!ok) { + evalError(fL1S("%1() argument 3 (end) '%2' invalid.") + .arg(func.toQString(m_tmp1), args.at(2).toQString(m_tmp2))); + return false; + } + } + } + if (*start < 0) + *start += srclen; + if (*end < 0) + *end += srclen; + if (*start < 0 || *start >= srclen || *end < 0 || *end >= srclen) + return false; + return true; +} + #if defined(Q_OS_WIN) && defined(PROEVALUATOR_FULL) static QString windowsErrorCode() { @@ -271,7 +324,7 @@ QMakeEvaluator::quoteValue(const ProString &val) break; case 32: quote = true; - // fallthrough + Q_FALLTHROUGH(); default: ret += c; break; @@ -343,11 +396,16 @@ static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProV } } -static QMakeEvaluator::VisitReturn parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value) +QMakeEvaluator::VisitReturn QMakeEvaluator::parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value) { - QJsonDocument document = QJsonDocument::fromJson(json); - if (document.isNull()) + QJsonParseError error; + QJsonDocument document = QJsonDocument::fromJson(json, &error); + if (document.isNull()) { + if (error.error != QJsonParseError::NoError) + evalError(fL1S("Error parsing json at offset %1: %2") + .arg(error.offset).arg(error.errorString())); return QMakeEvaluator::ReturnFalse; + } QString currentKey = into + QLatin1Char('.'); @@ -395,12 +453,13 @@ void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const } #endif -QByteArray QMakeEvaluator::getCommandOutput(const QString &args) const +QByteArray QMakeEvaluator::getCommandOutput(const QString &args, int *exitCode) const { QByteArray out; #ifndef QT_BOOTSTRAPPED QProcess proc; runProcess(&proc, args); + *exitCode = (proc.exitStatus() == QProcess::NormalExit) ? proc.exitCode() : -1; QByteArray errout = proc.readAllStandardError(); # ifdef PROEVALUATOR_FULL // FIXME: Qt really should have the option to set forwarding per channel @@ -430,7 +489,12 @@ QByteArray QMakeEvaluator::getCommandOutput(const QString &args) const break; out += QByteArray(buff, read_in); } - QT_PCLOSE(proc); + int ec = QT_PCLOSE(proc); +# ifdef Q_OS_WIN + *exitCode = ec >= 0 ? ec : -1; +# else + *exitCode = WIFEXITED(ec) ? WEXITSTATUS(ec) : -1; +# endif } # ifdef Q_OS_WIN out.replace("\r\n", "\n"); @@ -541,31 +605,30 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( bool leftalign = false; enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign; if (args.count() >= 2) { - const auto opts = split_value_list(args.at(1).toQString(m_tmp2)); + const auto opts = split_value_list(args.at(1).toQStringRef()); for (const ProString &opt : opts) { - opt.toQString(m_tmp3); - if (m_tmp3.startsWith(QLatin1String("ibase="))) { - ibase = m_tmp3.mid(6).toInt(); - } else if (m_tmp3.startsWith(QLatin1String("obase="))) { - obase = m_tmp3.mid(6).toInt(); - } else if (m_tmp3.startsWith(QLatin1String("width="))) { - width = m_tmp3.mid(6).toInt(); - } else if (m_tmp3 == QLatin1String("zeropad")) { + if (opt.startsWith(QLatin1String("ibase="))) { + ibase = opt.mid(6).toInt(); + } else if (opt.startsWith(QLatin1String("obase="))) { + obase = opt.mid(6).toInt(); + } else if (opt.startsWith(QLatin1String("width="))) { + width = opt.mid(6).toInt(); + } else if (opt == QLatin1String("zeropad")) { zeropad = true; - } else if (m_tmp3 == QLatin1String("padsign")) { + } else if (opt == QLatin1String("padsign")) { sign = PadSign; - } else if (m_tmp3 == QLatin1String("alwayssign")) { + } else if (opt == QLatin1String("alwayssign")) { sign = AlwaysSign; - } else if (m_tmp3 == QLatin1String("leftalign")) { + } else if (opt == QLatin1String("leftalign")) { leftalign = true; } else { - evalError(fL1S("format_number(): invalid format option %1.").arg(m_tmp3)); + evalError(fL1S("format_number(): invalid format option %1.") + .arg(opt.toQString(m_tmp3))); goto formfail; } } } - args.at(0).toQString(m_tmp3); - if (m_tmp3.contains(QLatin1Char('.'))) { + if (args.at(0).contains(QLatin1Char('.'))) { evalError(fL1S("format_number(): floats are currently not supported.")); break; } @@ -573,7 +636,7 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( qlonglong num = args.at(0).toLongLong(&ok, ibase); if (!ok) { evalError(fL1S("format_number(): malformed number %2 for base %1.") - .arg(ibase).arg(m_tmp3)); + .arg(ibase).arg(args.at(0).toQString(m_tmp3))); break; } QString outstr; @@ -601,14 +664,36 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( } formfail: break; + case E_NUM_ADD: + if (args.count() < 1 || args.at(0).isEmpty()) { + evalError(fL1S("num_add(num, ...) requires at least one argument.")); + } else { + qlonglong sum = 0; + for (const ProString &arg : qAsConst(args)) { + if (arg.contains(QLatin1Char('.'))) { + evalError(fL1S("num_add(): floats are currently not supported.")); + goto nafail; + } + bool ok; + qlonglong num = arg.toLongLong(&ok); + if (!ok) { + evalError(fL1S("num_add(): malformed number %1.") + .arg(arg.toQString(m_tmp3))); + goto nafail; + } + sum += num; + } + ret += ProString(QString::number(sum)); + } + nafail: + break; case E_JOIN: { if (args.count() < 1 || args.count() > 4) { evalError(fL1S("join(var, glue, before, after) requires one to four arguments.")); } else { - QString glue; - ProString before, after; + ProString glue, before, after; if (args.count() >= 2) - glue = args.at(1).toQString(m_tmp1); + glue = args.at(1); if (args.count() >= 3) before = args[2]; if (args.count() == 4) @@ -643,47 +728,37 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( if (args.count() < 1 || args.count() > 3) { evalError(fL1S("member(var, start, end) requires one to three arguments.")); } else { - bool ok = true; - const ProStringList &var = values(map(args.at(0))); - int start = 0, end = 0; - if (args.count() >= 2) { - const ProString &start_str = args.at(1); - start = start_str.toInt(&ok); - if (!ok) { - if (args.count() == 2) { - int dotdot = start_str.indexOf(statics.strDotDot); - if (dotdot != -1) { - start = start_str.left(dotdot).toInt(&ok); - if (ok) - end = start_str.mid(dotdot+2).toInt(&ok); - } - } - if (!ok) - evalError(fL1S("member() argument 2 (start) '%2' invalid.") - .arg(start_str.toQString(m_tmp1))); + const ProStringList &src = values(map(args.at(0))); + int start, end; + if (getMemberArgs(func, src.size(), args, &start, &end)) { + ret.reserve(qAbs(end - start) + 1); + if (start < end) { + for (int i = start; i <= end && src.size() >= i; i++) + ret += src.at(i); } else { - end = start; - if (args.count() == 3) - end = args.at(2).toInt(&ok); - if (!ok) - evalError(fL1S("member() argument 3 (end) '%2' invalid.") - .arg(args.at(2).toQString(m_tmp1))); + for (int i = start; i >= end && src.size() >= i && i >= 0; i--) + ret += src.at(i); } } - if (ok) { - if (start < 0) - start += var.count(); - if (end < 0) - end += var.count(); - if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) { - //nothing - } else if (start < end) { - for (int i = start; i <= end && var.count() >= i; i++) - ret.append(var[i]); + } + break; + case E_STR_MEMBER: + if (args.count() < 1 || args.count() > 3) { + evalError(fL1S("str_member(str, start, end) requires one to three arguments.")); + } else { + const ProString &src = args.at(0); + int start, end; + if (getMemberArgs(func, src.size(), args, &start, &end)) { + QString res; + res.reserve(qAbs(end - start) + 1); + if (start < end) { + for (int i = start; i <= end && src.size() >= i; i++) + res += src.at(i); } else { - for (int i = start; i >= end && var.count() >= i && i >= 0; i--) - ret += var[i]; + for (int i = start; i >= end && src.size() >= i && i >= 0; i--) + res += src.at(i); } + ret += ProString(res); } } break; @@ -701,12 +776,32 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( } } break; + case E_TAKE_FIRST: + case E_TAKE_LAST: + if (args.count() != 1) { + evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1))); + } else { + ProStringList &var = valuesRef(map(args.at(0))); + if (!var.isEmpty()) { + if (func_t == E_TAKE_FIRST) + ret.append(var.takeFirst()); + else + ret.append(var.takeLast()); + } + } + break; case E_SIZE: if (args.count() != 1) evalError(fL1S("size(var) requires one argument.")); else ret.append(ProString(QString::number(values(map(args.at(0))).size()))); break; + case E_STR_SIZE: + if (args.count() != 1) + evalError(fL1S("str_size(str) requires one argument.")); + else + ret.append(ProString(QString::number(args.at(0).size()))); + break; case E_CAT: if (args.count() < 1 || args.count() > 2) { evalError(fL1S("cat(file, singleline=true) requires one or two arguments.")); @@ -717,12 +812,11 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( bool lines = false; bool singleLine = true; if (args.count() > 1) { - args.at(1).toQString(m_tmp2); - if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive)) + if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive)) singleLine = false; - else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive)) + else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive)) blob = true; - else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive)) + else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive)) lines = true; } @@ -736,7 +830,8 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( if (lines) { ret += ProString(stream.readLine()); } else { - ret += split_value_list(stream.readLine().trimmed()); + const QString &line = stream.readLine(); + ret += split_value_list(QStringRef(&line).trimmed()); if (!singleLine) ret += ProString("\n"); } @@ -768,7 +863,7 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( ret = ProStringList(ProString(tmp)); ProStringList lst; for (const ProString &arg : args) - lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy + lst += split_value_list(arg.toQStringRef(), arg.sourceFile()); // Relies on deep copy m_valuemapStack.top()[ret.at(0).toKey()] = lst; break; } case E_FIND: @@ -787,22 +882,26 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( break; case E_SYSTEM: if (!m_skipLevel) { - if (args.count() < 1 || args.count() > 2) { - evalError(fL1S("system(execute) requires one or two arguments.")); + if (args.count() < 1 || args.count() > 3) { + evalError(fL1S("system(command, [mode], [stsvar]) requires one to three arguments.")); } else { bool blob = false; bool lines = false; bool singleLine = true; if (args.count() > 1) { - args.at(1).toQString(m_tmp2); - if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive)) + if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive)) singleLine = false; - else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive)) + else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive)) blob = true; - else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive)) + else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive)) lines = true; } - QByteArray bytes = getCommandOutput(args.at(0).toQString(m_tmp2)); + int exitCode; + QByteArray bytes = getCommandOutput(args.at(0).toQString(m_tmp2), &exitCode); + if (args.count() > 2 && !args.at(2).isEmpty()) { + m_valuemapStack.top()[args.at(2).toKey()] = + ProStringList(ProString(QString::number(exitCode))); + } if (lines) { QTextStream stream(bytes); while (!stream.atEnd()) @@ -815,7 +914,7 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( output.replace(QLatin1Char('\t'), QLatin1Char(' ')); if (singleLine) output.replace(QLatin1Char('\n'), QLatin1Char(' ')); - ret += split_value_list(output); + ret += split_value_list(QStringRef(&output)); } } } @@ -829,6 +928,14 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( ret.removeDuplicates(); } break; + case E_SORTED: + if (args.count() != 1) { + evalError(fL1S("sorted(var) requires one argument.")); + } else { + ret = values(map(args.at(0))); + std::sort(ret.begin(), ret.end()); + } + break; case E_REVERSE: if (args.count() != 1) { evalError(fL1S("reverse(var) requires one argument.")); @@ -964,7 +1071,8 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( QFile qfile; if (qfile.open(stdin, QIODevice::ReadOnly)) { QTextStream t(&qfile); - ret = split_value_list(t.readLine()); + const QString &line = t.readLine(); + ret = split_value_list(QStringRef(&line)); } } break; } @@ -998,7 +1106,7 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( ProString priosfx = args.count() < 4 ? ProString(".priority") : args.at(3); populateDeps(orgList, prefix, args.count() < 3 ? ProStringList(ProString(".depends")) - : split_value_list(args.at(2).toQString(m_tmp2)), + : split_value_list(args.at(2).toQStringRef()), priosfx, dependencies, dependees, rootSet); while (!rootSet.isEmpty()) { QMultiMap<int, ProString>::iterator it = rootSet.begin(); @@ -1189,6 +1297,38 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } return ReturnTrue; } + case T_DISCARD_FROM: { + if (args.count() != 1 || args.at(0).isEmpty()) { + evalError(fL1S("discard_from(file) requires one argument.")); + return ReturnFalse; + } + if (m_valuemapStack.count() != 1) { + evalError(fL1S("discard_from() cannot be called from functions.")); + return ReturnFalse; + } + QString fn = resolvePath(args.at(0).toQString(m_tmp1)); + ProFile *pro = m_parser->parsedProFile(fn, QMakeParser::ParseOnlyCached); + if (!pro) + return ReturnFalse; + ProValueMap &vmap = m_valuemapStack.first(); + for (auto vit = vmap.begin(); vit != vmap.end(); ) { + if (!vit->isEmpty()) { + auto isFrom = [pro](const ProString &s) { + return s.sourceFile() == pro; + }; + vit->erase(std::remove_if(vit->begin(), vit->end(), isFrom), vit->end()); + if (vit->isEmpty()) { + // When an initially non-empty variable becomes entirely empty, + // undefine it altogether. + vit = vmap.erase(vit); + continue; + } + } + ++vit; + } + pro->deref(); + return ReturnTrue; + } case T_INFILE: if (args.count() < 2 || args.count() > 3) { evalError(fL1S("infile(file, var, [values]) requires two or three arguments.")); @@ -1225,7 +1365,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; // Another qmake breakage case T_EVAL: { VisitReturn ret = ReturnFalse; - ProFile *pro = m_parser->parsedProBlock(args.join(statics.field_sep), + QString contents = args.join(statics.field_sep); + ProFile *pro = m_parser->parsedProBlock(QStringRef(&contents), m_current.pro->fileName(), m_current.line); if (m_cumulative || pro->isOk()) { m_locationStack.push(m_current); @@ -1241,7 +1382,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( evalError(fL1S("if(condition) requires one argument.")); return ReturnFalse; } - return evaluateConditional(args.at(0).toQString(), + return evaluateConditional(args.at(0).toQStringRef(), m_current.pro->fileName(), m_current.line); } case T_CONFIG: { @@ -1250,7 +1391,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; } if (args.count() == 1) - return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2))); + return returnBool(isActiveConfig(args.at(0).toQStringRef())); const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|')); const ProStringList &configs = values(statics.strCONFIG); @@ -1333,8 +1474,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( .arg(function.toQString(m_tmp1))); return ReturnFalse; } - const QString &rhs(args.at(1).toQString(m_tmp1)), - &lhs(values(map(args.at(0))).join(statics.field_sep)); + const ProString &rhs = args.at(1); + const QString &lhs = values(map(args.at(0))).join(statics.field_sep); bool ok; int rhs_int = rhs.toInt(&ok); if (ok) { // do integer compare @@ -1346,8 +1487,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } } if (func_t == T_GREATERTHAN) - return returnBool(lhs > rhs); - return returnBool(lhs < rhs); + return returnBool(lhs > rhs.toQStringRef()); + return returnBool(lhs < rhs.toQStringRef()); } case T_EQUALS: if (args.count() != 2) { @@ -1415,7 +1556,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( if (m_cumulative) flags = LoadSilent; if (args.count() >= 2) { - parseInto = args.at(1).toQString(m_tmp2); + if (!args.at(1).isEmpty()) + parseInto = args.at(1) + QLatin1Char('.'); if (args.count() >= 3 && isTrue(args.at(2))) flags = LoadSilent; } @@ -1432,17 +1574,15 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( it = m_valuemapStack.top().constBegin(), end = m_valuemapStack.top().constEnd(); it != end; ++it) { - const QString &ky = it.key().toQString(m_tmp1); - if (!(ky.startsWith(parseInto) && - (ky.length() == parseInto.length() - || ky.at(parseInto.length()) == QLatin1Char('.')))) + const ProString &ky = it.key(); + if (!ky.startsWith(parseInto)) newMap[it.key()] = it.value(); } for (ProValueMap::ConstIterator it = symbols.constBegin(); it != symbols.constEnd(); ++it) { const QString &ky = it.key().toQString(m_tmp1); if (!ky.startsWith(QLatin1Char('.'))) - newMap.insert(ProKey(parseInto + QLatin1Char('.') + ky), it.value()); + newMap.insert(ProKey(parseInto + ky), it.value()); } m_valuemapStack.top() = newMap; } @@ -1494,7 +1634,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( #ifdef PROEVALUATOR_FULL fputs(msg.toLatin1().constData(), stderr); #endif - } else { + } else if (!msg.isEmpty() || func_t != T_ERROR) { m_handler->fileMessage( (func_t == T_ERROR ? QMakeHandler::ErrorMessage : func_t == T_WARNING ? QMakeHandler::WarningMessage : @@ -1588,7 +1728,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( if (!vals.isEmpty()) contents = vals.join(QLatin1Char('\n')) + QLatin1Char('\n'); if (args.count() >= 3) { - const auto opts = split_value_list(args.at(2).toQString(m_tmp2)); + const auto opts = split_value_list(args.at(2).toQStringRef()); for (const ProString &opt : opts) { opt.toQString(m_tmp3); if (m_tmp3 == QLatin1String("append")) { @@ -1661,7 +1801,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet; ProKey srcvar; if (args.count() >= 2) { - const auto opts = split_value_list(args.at(1).toQString(m_tmp2)); + const auto opts = split_value_list(args.at(1).toQStringRef()); for (const ProString &opt : opts) { opt.toQString(m_tmp3); if (m_tmp3 == QLatin1String("transient")) { diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp index e828dd85f7..338131d06b 100644 --- a/qmake/library/qmakeevaluator.cpp +++ b/qmake/library/qmakeevaluator.cpp @@ -270,7 +270,7 @@ void QMakeEvaluator::skipHashStr(const ushort *&tokPtr) // FIXME: this should not build new strings for direct sections. // Note that the E_SPRINTF and E_LIST implementations rely on the deep copy. -ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFile *source) +ProStringList QMakeEvaluator::split_value_list(const QStringRef &vals, const ProFile *source) { QString build; ProStringList ret; @@ -318,7 +318,7 @@ ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFil --x; } } - // fallthrough + Q_FALLTHROUGH(); default: hadWord = true; break; @@ -629,7 +629,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( evalError(fL1S("Conditional must expand to exactly one word.")); okey = false; } else { - okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true); + okey = isActiveConfig(curr.at(0).toQStringRef(), true); traceMsg("condition %s is %s", dbgStr(curr.at(0)), dbgBool(okey)); okey ^= invert; } @@ -1291,7 +1291,7 @@ void QMakeEvaluator::setupProject() void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where) { if (!cmds.isEmpty()) { - ProFile *pro = m_parser->parsedProBlock(cmds, where, -1); + ProFile *pro = m_parser->parsedProBlock(QStringRef(&cmds), where, -1); if (pro->isOk()) { m_locationStack.push(m_current); visitProBlock(pro, pro->tokPtr()); @@ -1585,7 +1585,7 @@ QString QMakeEvaluator::currentDirectory() const return QString(); } -bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex) +bool QMakeEvaluator::isActiveConfig(const QStringRef &config, bool regex) { // magic types for easy flipping if (config == statics.strtrue) @@ -1597,7 +1597,7 @@ bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex) return m_hostBuild; if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) { - QString cfg = config; + QString cfg = config.toString(); cfg.detach(); // Keep m_tmp out of QRegExp's cache QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard); @@ -1619,7 +1619,7 @@ bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex) return true; // CONFIG variable - if (values(statics.strCONFIG).contains(ProString(config))) + if (values(statics.strCONFIG).contains(config)) return true; } @@ -1643,7 +1643,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::expandVariableReferences( tokPtr++; continue; } - // fallthrough + Q_FALLTHROUGH(); default: Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token"); break; @@ -1783,7 +1783,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction( } QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditional( - const QString &cond, const QString &where, int line) + const QStringRef &cond, const QString &where, int line) { VisitReturn ret = ReturnFalse; ProFile *pro = m_parser->parsedProBlock(cond, where, line, QMakeParser::TestGrammar); @@ -1801,7 +1801,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::checkRequirements(const ProStringLis { ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS")); for (const ProString &dep : deps) { - VisitReturn vr = evaluateConditional(dep.toQString(), m_current.pro->fileName(), m_current.line); + VisitReturn vr = evaluateConditional(dep.toQStringRef(), m_current.pro->fileName(), m_current.line); if (vr == ReturnError) return ReturnError; if (vr != ReturnTrue) @@ -2109,7 +2109,7 @@ QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote) break; case 32: quote = true; - // fallthrough + Q_FALLTHROUGH(); default: ret += c; break; diff --git a/qmake/library/qmakeevaluator.h b/qmake/library/qmakeevaluator.h index 2fdf6718a6..3f2a22c567 100644 --- a/qmake/library/qmakeevaluator.h +++ b/qmake/library/qmakeevaluator.h @@ -176,7 +176,7 @@ public: void setTemplate(); - ProStringList split_value_list(const QString &vals, const ProFile *source = 0); + ProStringList split_value_list(const QStringRef &vals, const ProFile *source = 0); VisitReturn expandVariableReferences(const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined); QString currentFileName() const; @@ -215,7 +215,7 @@ public: ProStringList evaluateBuiltinExpand(int func_t, const ProKey &function, const ProStringList &args); VisitReturn evaluateBuiltinConditional(int func_t, const ProKey &function, const ProStringList &args); - VisitReturn evaluateConditional(const QString &cond, const QString &where, int line = -1); + VisitReturn evaluateConditional(const QStringRef &cond, const QString &where, int line = -1); #ifdef PROEVALUATOR_FULL VisitReturn checkRequirements(const ProStringList &deps); #endif @@ -223,7 +223,7 @@ public: void updateMkspecPaths(); void updateFeaturePaths(); - bool isActiveConfig(const QString &config, bool regex = false); + bool isActiveConfig(const QStringRef &config, bool regex = false); void populateDeps( const ProStringList &deps, const ProString &prefix, const ProStringList &suffixes, @@ -231,12 +231,16 @@ public: QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees, QMultiMap<int, ProString> &rootSet) const; + bool getMemberArgs(const ProKey &name, int srclen, const ProStringList &args, + int *start, int *end); + VisitReturn parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value); + VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, bool exe, const QString &contents); #ifndef QT_BOOTSTRAPPED void runProcess(QProcess *proc, const QString &command) const; #endif - QByteArray getCommandOutput(const QString &args) const; + QByteArray getCommandOutput(const QString &args, int *exitCode) const; QMakeEvaluator *m_caller; #ifdef PROEVALUATOR_CUMULATIVE diff --git a/qmake/library/qmakeglobals.cpp b/qmake/library/qmakeglobals.cpp index 82764e8732..b02bf4aaf8 100644 --- a/qmake/library/qmakeglobals.cpp +++ b/qmake/library/qmakeglobals.cpp @@ -136,6 +136,11 @@ QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments( break; default: if (arg.startsWith(QLatin1Char('-'))) { + if (arg == QLatin1String("--")) { + state.extraargs = args.mid(*pos + 1); + *pos = args.size(); + return ArgumentsOk; + } if (arg == QLatin1String("-after")) state.after = true; else if (arg == QLatin1String("-config")) @@ -181,6 +186,12 @@ void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state) { if (!state.preconfigs.isEmpty()) state.precmds << (fL1S("CONFIG += ") + state.preconfigs.join(QLatin1Char(' '))); + if (!state.extraargs.isEmpty()) { + QString extra = fL1S("QMAKE_EXTRA_ARGS ="); + for (const QString &ea : qAsConst(state.extraargs)) + extra += QLatin1Char(' ') + QMakeEvaluator::quoteValue(ProString(ea)); + state.precmds << extra; + } precmds = state.precmds.join(QLatin1Char('\n')); if (!state.postconfigs.isEmpty()) state.postcmds << (fL1S("CONFIG += ") + state.postconfigs.join(QLatin1Char(' '))); diff --git a/qmake/library/qmakeglobals.h b/qmake/library/qmakeglobals.h index 79237e6ebe..96c39fa168 100644 --- a/qmake/library/qmakeglobals.h +++ b/qmake/library/qmakeglobals.h @@ -85,7 +85,7 @@ class QMAKE_EXPORT QMakeCmdLineParserState public: QMakeCmdLineParserState(const QString &_pwd) : pwd(_pwd), after(false) {} QString pwd; - QStringList precmds, preconfigs, postcmds, postconfigs; + QStringList precmds, preconfigs, postcmds, postconfigs, extraargs; bool after; void flush() { after = false; } diff --git a/qmake/library/qmakeparser.cpp b/qmake/library/qmakeparser.cpp index fc97310bbd..56b217dfbb 100644 --- a/qmake/library/qmakeparser.cpp +++ b/qmake/library/qmakeparser.cpp @@ -165,7 +165,7 @@ QMakeParser::QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) { ProFile *pro; - if ((flags & ParseUseCache) && m_cache) { + if ((flags & (ParseUseCache|ParseOnlyCached)) && m_cache) { ProFileCache::Entry *ent; #ifdef PROPARSER_THREAD_SAFE QMutexLocker locker(&m_cache->mutex); @@ -187,7 +187,7 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) #endif if ((pro = ent->pro)) pro->ref(); - } else { + } else if (!(flags & ParseOnlyCached)) { ent = &m_cache->parsed_files[fileName]; #ifdef PROPARSER_THREAD_SAFE ent->locker = new ProFileCache::Entry::Locker; @@ -212,19 +212,23 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) ent->locker = 0; } #endif + } else { + pro = 0; } - } else { + } else if (!(flags & ParseOnlyCached)) { pro = new ProFile(fileName); if (!read(pro, flags)) { delete pro; pro = 0; } + } else { + pro = 0; } return pro; } ProFile *QMakeParser::parsedProBlock( - const QString &contents, const QString &name, int line, SubGrammar grammar) + const QStringRef &contents, const QString &name, int line, SubGrammar grammar) { ProFile *pro = new ProFile(name); read(pro, contents, line, grammar); @@ -247,7 +251,7 @@ bool QMakeParser::read(ProFile *pro, ParseFlags flags) fL1S("Cannot read %1: %2").arg(pro->fileName(), errStr)); return false; } - read(pro, content, 1, FullGrammar); + read(pro, QStringRef(&content), 1, FullGrammar); return true; } @@ -289,7 +293,7 @@ void QMakeParser::finalizeHashStr(ushort *buf, uint len) buf[-2] = (ushort)(hash >> 16); } -void QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar grammar) +void QMakeParser::read(ProFile *pro, const QStringRef &in, int line, SubGrammar grammar) { m_proFile = pro; m_lineNo = line; @@ -337,8 +341,8 @@ void QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar gra QStack<ParseCtx> xprStack; xprStack.reserve(10); - // We rely on QStrings being null-terminated, so don't maintain a global end pointer. const ushort *cur = (const ushort *)in.unicode(); + const ushort *inend = cur + in.length(); m_canElse = false; freshLine: m_state = StNew; @@ -421,7 +425,7 @@ void QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar gra int indent; if (context == CtxPureValue) { - end = (const ushort *)in.unicode() + in.length(); + end = inend; cptr = 0; lineCont = false; indent = 0; // just gcc being stupid @@ -433,24 +437,30 @@ void QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar gra // First, skip leading whitespace for (indent = 0; ; ++cur, ++indent) { + if (cur == inend) { + cur = 0; + goto flushLine; + } c = *cur; if (c == '\n') { ++cur; goto flushLine; - } else if (!c) { - cur = 0; - goto flushLine; - } else if (c != ' ' && c != '\t' && c != '\r') { - break; } + if (c != ' ' && c != '\t' && c != '\r') + break; } // Then strip comments. Yep - no escaping is possible. for (cptr = cur;; ++cptr) { + if (cptr == inend) { + end = cptr; + break; + } c = *cptr; if (c == '#') { - for (end = cptr; (c = *++cptr);) { - if (c == '\n') { + end = cptr; + while (++cptr < inend) { + if (*cptr == '\n') { ++cptr; break; } @@ -463,10 +473,6 @@ void QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar gra } break; } - if (!c) { - end = cptr; - break; - } if (c == '\n') { end = cptr++; break; @@ -1218,7 +1224,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg bool QMakeParser::resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr, ushort **buf, QString *xprBuff, ushort **tokPtr, QString *tokBuff, - const ushort *cur, const QString &in) + const ushort *cur, const QStringRef &in) { QString out; m_tmp.setRawData((const QChar *)xprPtr, tlen); diff --git a/qmake/library/qmakeparser.h b/qmake/library/qmakeparser.h index f78b3215bb..6557ad65b5 100644 --- a/qmake/library/qmakeparser.h +++ b/qmake/library/qmakeparser.h @@ -78,7 +78,8 @@ public: enum ParseFlag { ParseDefault = 0, ParseUseCache = 1, - ParseReportMissing = 2 + ParseOnlyCached = 2, + ParseReportMissing = 4 }; Q_DECLARE_FLAGS(ParseFlags, ParseFlag) @@ -87,7 +88,7 @@ public: enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar }; // fileName is expected to be absolute and cleanPath()ed. ProFile *parsedProFile(const QString &fileName, ParseFlags flags = ParseDefault); - ProFile *parsedProBlock(const QString &contents, const QString &name, int line = 0, + ProFile *parsedProBlock(const QStringRef &contents, const QString &name, int line = 0, SubGrammar grammar = FullGrammar); void discardFileFromCache(const QString &fileName); @@ -130,7 +131,7 @@ private: }; bool read(ProFile *pro, ParseFlags flags); - void read(ProFile *pro, const QString &content, int line, SubGrammar grammar); + void read(ProFile *pro, const QStringRef &content, int line, SubGrammar grammar); ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok); ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len); @@ -141,7 +142,7 @@ private: ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr, ushort **buf, QString *xprBuff, ushort **tokPtr, QString *tokBuff, - const ushort *cur, const QString &in); + const ushort *cur, const QStringRef &in); void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount); void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc); void warnOperator(const char *msg); |