2012-09-05 16:29:19 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2012-09-05 16:29:19 +00:00
|
|
|
**
|
|
|
|
** This file is part of the qmake application of the Qt Toolkit.
|
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
2012-09-19 12:28:29 +00:00
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2015-01-28 08:44:43 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-15 12:36:27 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2012-09-05 16:29:19 +00:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qmakeevaluator.h"
|
|
|
|
|
|
|
|
#include "qmakeevaluator_p.h"
|
|
|
|
#include "qmakeglobals.h"
|
|
|
|
#include "qmakeparser.h"
|
2013-05-29 18:18:51 +00:00
|
|
|
#include "qmakevfs.h"
|
2012-09-05 16:29:19 +00:00
|
|
|
#include "ioutils.h"
|
|
|
|
|
|
|
|
#include <qbytearray.h>
|
|
|
|
#include <qdir.h>
|
|
|
|
#include <qfile.h>
|
|
|
|
#include <qfileinfo.h>
|
|
|
|
#include <qlist.h>
|
|
|
|
#include <qregexp.h>
|
|
|
|
#include <qset.h>
|
|
|
|
#include <qstringlist.h>
|
|
|
|
#include <qtextstream.h>
|
2013-09-05 05:55:49 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
# include <qjsondocument.h>
|
|
|
|
# include <qjsonobject.h>
|
|
|
|
# include <qjsonarray.h>
|
|
|
|
#endif
|
2013-07-23 15:57:44 +00:00
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
# include <qthreadpool.h>
|
|
|
|
#endif
|
2017-01-07 09:23:47 +00:00
|
|
|
#include <qversionnumber.h>
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2016-08-23 12:05:54 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef Q_OS_UNIX
|
|
|
|
#include <time.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
2016-07-14 16:10:50 +00:00
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/wait.h>
|
2012-09-05 16:29:19 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
#else
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
#define QT_POPEN _popen
|
2016-06-16 13:38:22 +00:00
|
|
|
#define QT_POPEN_READ "rb"
|
2012-09-05 16:29:19 +00:00
|
|
|
#define QT_PCLOSE _pclose
|
|
|
|
#else
|
|
|
|
#define QT_POPEN popen
|
2016-06-16 13:38:22 +00:00
|
|
|
#define QT_POPEN_READ "r"
|
2012-09-05 16:29:19 +00:00
|
|
|
#define QT_PCLOSE pclose
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace QMakeInternal;
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
#define fL1S(s) QString::fromLatin1(s)
|
|
|
|
|
|
|
|
enum ExpandFunc {
|
2016-05-18 14:35:01 +00:00
|
|
|
E_INVALID = 0, E_MEMBER, E_STR_MEMBER, E_FIRST, E_TAKE_FIRST, E_LAST, E_TAKE_LAST,
|
2016-05-13 13:58:20 +00:00
|
|
|
E_SIZE, E_STR_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST, E_SPRINTF, E_FORMAT_NUMBER,
|
2016-05-13 11:31:14 +00:00
|
|
|
E_NUM_ADD, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
|
2016-05-13 14:53:01 +00:00
|
|
|
E_FIND, E_SYSTEM, E_UNIQUE, E_SORTED, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND,
|
2013-09-19 11:21:26 +00:00
|
|
|
E_UPPER, E_LOWER, E_TITLE, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE,
|
2012-09-05 16:29:19 +00:00
|
|
|
E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS,
|
|
|
|
E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH,
|
2014-03-11 14:56:52 +00:00
|
|
|
E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE, E_GETENV
|
2012-09-05 16:29:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum TestFunc {
|
|
|
|
T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
|
2017-01-07 09:23:47 +00:00
|
|
|
T_VERSION_AT_LEAST, T_VERSION_AT_MOST,
|
2012-09-05 16:29:19 +00:00
|
|
|
T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
|
2016-08-23 12:05:54 +00:00
|
|
|
T_DEFINED, T_DISCARD_FROM, T_CONTAINS, T_INFILE,
|
2013-09-05 05:55:49 +00:00
|
|
|
T_COUNT, T_ISEMPTY, T_PARSE_JSON, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
|
2016-11-24 17:41:48 +00:00
|
|
|
T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE, T_RELOAD_PROPERTIES
|
2012-09-05 16:29:19 +00:00
|
|
|
};
|
|
|
|
|
2017-08-11 17:22:58 +00:00
|
|
|
QMakeBuiltin::QMakeBuiltin(const QMakeBuiltinInit &d)
|
|
|
|
: index(d.func), minArgs(qMax(0, d.min_args)), maxArgs(d.max_args)
|
|
|
|
{
|
|
|
|
static const char * const nstr[6] = { "no", "one", "two", "three", "four", "five" };
|
|
|
|
// For legacy reasons, there is actually no such thing as "no arguments"
|
|
|
|
// - there is only "empty first argument", which needs to be mapped back.
|
|
|
|
// -1 means "one, which may be empty", which is effectively zero, except
|
|
|
|
// for the error message if there are too many arguments.
|
|
|
|
int dmin = qAbs(d.min_args);
|
|
|
|
int dmax = d.max_args;
|
|
|
|
if (dmax == QMakeBuiltinInit::VarArgs) {
|
|
|
|
Q_ASSERT_X(dmin < 2, "init", d.name);
|
|
|
|
if (dmin == 1) {
|
|
|
|
Q_ASSERT_X(d.args != nullptr, "init", d.name);
|
|
|
|
usage = fL1S("%1(%2) requires at least one argument.")
|
|
|
|
.arg(fL1S(d.name), fL1S(d.args));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int arange = dmax - dmin;
|
|
|
|
Q_ASSERT_X(arange >= 0, "init", d.name);
|
|
|
|
Q_ASSERT_X(d.args != nullptr, "init", d.name);
|
|
|
|
usage = arange > 1
|
|
|
|
? fL1S("%1(%2) requires %3 to %4 arguments.")
|
|
|
|
.arg(fL1S(d.name), fL1S(d.args), fL1S(nstr[dmin]), fL1S(nstr[dmax]))
|
|
|
|
: arange > 0
|
|
|
|
? fL1S("%1(%2) requires %3 or %4 arguments.")
|
|
|
|
.arg(fL1S(d.name), fL1S(d.args), fL1S(nstr[dmin]), fL1S(nstr[dmax]))
|
|
|
|
: dmax != 1
|
|
|
|
? fL1S("%1(%2) requires %3 arguments.")
|
|
|
|
.arg(fL1S(d.name), fL1S(d.args), fL1S(nstr[dmax]))
|
|
|
|
: fL1S("%1(%2) requires one argument.")
|
|
|
|
.arg(fL1S(d.name), fL1S(d.args));
|
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
void QMakeEvaluator::initFunctionStatics()
|
|
|
|
{
|
2017-08-11 17:22:58 +00:00
|
|
|
static const QMakeBuiltinInit expandInits[] = {
|
|
|
|
{ "member", E_MEMBER, 1, 3, "var, [start, [end]]" },
|
|
|
|
{ "str_member", E_STR_MEMBER, -1, 3, "str, [start, [end]]" },
|
|
|
|
{ "first", E_FIRST, 1, 1, "var" },
|
|
|
|
{ "take_first", E_TAKE_FIRST, 1, 1, "var" },
|
|
|
|
{ "last", E_LAST, 1, 1, "var" },
|
|
|
|
{ "take_last", E_TAKE_LAST, 1, 1, "var" },
|
|
|
|
{ "size", E_SIZE, 1, 1, "var" },
|
|
|
|
{ "str_size", E_STR_SIZE, -1, 1, "str" },
|
|
|
|
{ "cat", E_CAT, 1, 2, "file, [mode=true|blob|lines]" },
|
|
|
|
{ "fromfile", E_FROMFILE, 2, 2, "file, var" },
|
|
|
|
{ "eval", E_EVAL, 1, 1, "var" },
|
|
|
|
{ "list", E_LIST, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "sprintf", E_SPRINTF, 1, QMakeBuiltinInit::VarArgs, "format, ..." },
|
|
|
|
{ "format_number", E_FORMAT_NUMBER, 1, 2, "number, [options...]" },
|
|
|
|
{ "num_add", E_NUM_ADD, 1, QMakeBuiltinInit::VarArgs, "num, ..." },
|
|
|
|
{ "join", E_JOIN, 1, 4, "var, [glue, [before, [after]]]" },
|
|
|
|
{ "split", E_SPLIT, 1, 2, "var, sep" },
|
|
|
|
{ "basename", E_BASENAME, 1, 1, "var" },
|
|
|
|
{ "dirname", E_DIRNAME, 1, 1, "var" },
|
|
|
|
{ "section", E_SECTION, 3, 4, "var, sep, begin, [end]" },
|
|
|
|
{ "find", E_FIND, 2, 2, "var, str" },
|
|
|
|
{ "system", E_SYSTEM, 1, 3, "command, [mode], [stsvar]" },
|
|
|
|
{ "unique", E_UNIQUE, 1, 1, "var" },
|
|
|
|
{ "sorted", E_SORTED, 1, 1, "var" },
|
|
|
|
{ "reverse", E_REVERSE, 1, 1, "var" },
|
|
|
|
{ "quote", E_QUOTE, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "escape_expand", E_ESCAPE_EXPAND, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "upper", E_UPPER, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "lower", E_LOWER, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "title", E_TITLE, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "re_escape", E_RE_ESCAPE, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "val_escape", E_VAL_ESCAPE, 1, 1, "var" },
|
|
|
|
{ "files", E_FILES, 1, 2, "pattern, [recursive=false]" },
|
|
|
|
{ "prompt", E_PROMPT, 1, 2, "question, [decorate=true]" },
|
|
|
|
{ "replace", E_REPLACE, 3, 3, "var, before, after" },
|
|
|
|
{ "sort_depends", E_SORT_DEPENDS, 1, 4, "var, [prefix, [suffixes, [prio-suffix]]]" },
|
|
|
|
{ "resolve_depends", E_RESOLVE_DEPENDS, 1, 4, "var, [prefix, [suffixes, [prio-suffix]]]" },
|
|
|
|
{ "enumerate_vars", E_ENUMERATE_VARS, 0, 0, "" },
|
|
|
|
{ "shadowed", E_SHADOWED, 1, 1, "path" },
|
|
|
|
{ "absolute_path", E_ABSOLUTE_PATH, -1, 2, "path, [base]" },
|
|
|
|
{ "relative_path", E_RELATIVE_PATH, -1, 2, "path, [base]" },
|
|
|
|
{ "clean_path", E_CLEAN_PATH, -1, 1, "path" },
|
|
|
|
{ "system_path", E_SYSTEM_PATH, -1, 1, "path" },
|
|
|
|
{ "shell_path", E_SHELL_PATH, -1, 1, "path" },
|
|
|
|
{ "system_quote", E_SYSTEM_QUOTE, -1, 1, "arg" },
|
|
|
|
{ "shell_quote", E_SHELL_QUOTE, -1, 1, "arg" },
|
|
|
|
{ "getenv", E_GETENV, 1, 1, "arg" },
|
2012-09-05 16:29:19 +00:00
|
|
|
};
|
2016-02-01 08:49:22 +00:00
|
|
|
statics.expands.reserve((int)(sizeof(expandInits)/sizeof(expandInits[0])));
|
2012-09-05 16:29:19 +00:00
|
|
|
for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
|
2017-08-11 17:22:58 +00:00
|
|
|
statics.expands.insert(ProKey(expandInits[i].name), QMakeBuiltin(expandInits[i]));
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2017-08-11 17:22:58 +00:00
|
|
|
static const QMakeBuiltinInit testInits[] = {
|
|
|
|
{ "requires", T_REQUIRES, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "greaterThan", T_GREATERTHAN, 2, 2, "var, val" },
|
|
|
|
{ "lessThan", T_LESSTHAN, 2, 2, "var, val" },
|
|
|
|
{ "equals", T_EQUALS, 2, 2, "var, val" },
|
|
|
|
{ "isEqual", T_EQUALS, 2, 2, "var, val" },
|
|
|
|
{ "versionAtLeast", T_VERSION_AT_LEAST, 2, 2, "var, version" },
|
|
|
|
{ "versionAtMost", T_VERSION_AT_MOST, 2, 2, "var, version" },
|
|
|
|
{ "exists", T_EXISTS, 1, 1, "file" },
|
|
|
|
{ "export", T_EXPORT, 1, 1, "var" },
|
|
|
|
{ "clear", T_CLEAR, 1, 1, "var" },
|
|
|
|
{ "unset", T_UNSET, 1, 1, "var" },
|
|
|
|
{ "eval", T_EVAL, 0, QMakeBuiltinInit::VarArgs, nullptr },
|
|
|
|
{ "CONFIG", T_CONFIG, 1, 2, "config, [mutuals]" },
|
|
|
|
{ "if", T_IF, 1, 1, "condition" },
|
|
|
|
{ "isActiveConfig", T_CONFIG, 1, 2, "config, [mutuals]" },
|
|
|
|
{ "system", T_SYSTEM, 1, 1, "exec" },
|
|
|
|
{ "discard_from", T_DISCARD_FROM, 1, 1, "file" },
|
|
|
|
{ "defined", T_DEFINED, 1, 2, "object, [\"test\"|\"replace\"|\"var\"]" },
|
|
|
|
{ "contains", T_CONTAINS, 2, 3, "var, val, [mutuals]" },
|
|
|
|
{ "infile", T_INFILE, 2, 3, "file, var, [values]" },
|
|
|
|
{ "count", T_COUNT, 2, 3, "var, count, [op=operator]" },
|
|
|
|
{ "isEmpty", T_ISEMPTY, 1, 1, "var" },
|
2013-09-05 05:55:49 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
2017-08-11 17:22:58 +00:00
|
|
|
{ "parseJson", T_PARSE_JSON, 2, 2, "var, into" },
|
2013-09-05 05:55:49 +00:00
|
|
|
#endif
|
2017-08-11 17:22:58 +00:00
|
|
|
{ "load", T_LOAD, 1, 2, "feature, [ignore_errors=false]" },
|
|
|
|
{ "include", T_INCLUDE, 1, 3, "file, [into, [silent]]" },
|
|
|
|
{ "debug", T_DEBUG, 2, 2, "level, message" },
|
|
|
|
{ "log", T_LOG, 1, 1, "message" },
|
|
|
|
{ "message", T_MESSAGE, 1, 1, "message" },
|
|
|
|
{ "warning", T_WARNING, 1, 1, "message" },
|
|
|
|
{ "error", T_ERROR, 0, 1, "message" },
|
|
|
|
{ "mkpath", T_MKPATH, 1, 1, "path" },
|
|
|
|
{ "write_file", T_WRITE_FILE, 1, 3, "name, [content var, [append] [exe]]" },
|
|
|
|
{ "touch", T_TOUCH, 2, 2, "file, reffile" },
|
|
|
|
{ "cache", T_CACHE, 0, 3, "[var], [set|add|sub] [transient] [super|stash], [srcvar]" },
|
|
|
|
{ "reload_properties", T_RELOAD_PROPERTIES, 0, 0, "" },
|
2012-09-05 16:29:19 +00:00
|
|
|
};
|
2016-02-01 08:49:22 +00:00
|
|
|
statics.functions.reserve((int)(sizeof(testInits)/sizeof(testInits[0])));
|
2012-09-05 16:29:19 +00:00
|
|
|
for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
|
2017-08-11 17:22:58 +00:00
|
|
|
statics.functions.insert(ProKey(testInits[i].name), QMakeBuiltin(testInits[i]));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
2016-05-13 11:45:48 +00:00
|
|
|
static bool isTrue(const ProString &str)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
|
|
|
|
}
|
|
|
|
|
2016-05-18 14:35:01 +00:00
|
|
|
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) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(func, m_tmp1);
|
|
|
|
ProStringRoUser u2(start_str, m_tmp2);
|
|
|
|
evalError(fL1S("%1() argument 2 (start) '%2' invalid.").arg(u1.str(), u2.str()));
|
2016-05-18 14:35:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*end = *start;
|
|
|
|
if (args.count() == 3)
|
|
|
|
*end = args.at(2).toInt(&ok);
|
|
|
|
if (!ok) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(func, m_tmp1);
|
|
|
|
ProStringRoUser u2(args.at(2), m_tmp2);
|
|
|
|
evalError(fL1S("%1() argument 3 (end) '%2' invalid.").arg(u1.str(), u2.str()));
|
2016-05-18 14:35:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-11-17 19:55:33 +00:00
|
|
|
QString
|
|
|
|
QMakeEvaluator::quoteValue(const ProString &val)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
QString ret;
|
|
|
|
ret.reserve(val.size());
|
|
|
|
const QChar *chars = val.constData();
|
|
|
|
bool quote = val.isEmpty();
|
|
|
|
bool escaping = false;
|
|
|
|
for (int i = 0, l = val.size(); i < l; i++) {
|
|
|
|
QChar c = chars[i];
|
|
|
|
ushort uc = c.unicode();
|
|
|
|
if (uc < 32) {
|
|
|
|
if (!escaping) {
|
|
|
|
escaping = true;
|
|
|
|
ret += QLatin1String("$$escape_expand(");
|
|
|
|
}
|
|
|
|
switch (uc) {
|
|
|
|
case '\r':
|
|
|
|
ret += QLatin1String("\\\\r");
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
ret += QLatin1String("\\\\n");
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
ret += QLatin1String("\\\\t");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret += QString::fromLatin1("\\\\x%1").arg(uc, 2, 16, QLatin1Char('0'));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (escaping) {
|
|
|
|
escaping = false;
|
|
|
|
ret += QLatin1Char(')');
|
|
|
|
}
|
|
|
|
switch (uc) {
|
|
|
|
case '\\':
|
|
|
|
ret += QLatin1String("\\\\");
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
ret += QLatin1String("\\\"");
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
ret += QLatin1String("\\'");
|
|
|
|
break;
|
|
|
|
case '$':
|
|
|
|
ret += QLatin1String("\\$");
|
|
|
|
break;
|
|
|
|
case '#':
|
|
|
|
ret += QLatin1String("$${LITERAL_HASH}");
|
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
quote = true;
|
2016-06-27 13:43:33 +00:00
|
|
|
Q_FALLTHROUGH();
|
2012-09-05 16:29:19 +00:00
|
|
|
default:
|
|
|
|
ret += c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (escaping)
|
|
|
|
ret += QLatin1Char(')');
|
|
|
|
if (quote) {
|
|
|
|
ret.prepend(QLatin1Char('"'));
|
|
|
|
ret.append(QLatin1Char('"'));
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-09-05 05:55:49 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProValueMap *map);
|
|
|
|
|
|
|
|
static void insertJsonKeyValue(const QString &key, const QStringList &values, ProValueMap *map)
|
|
|
|
{
|
|
|
|
map->insert(ProKey(key), ProStringList(values));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addJsonArray(const QJsonArray &array, const QString &keyPrefix, ProValueMap *map)
|
|
|
|
{
|
|
|
|
QStringList keys;
|
2016-02-01 07:42:04 +00:00
|
|
|
const int size = array.count();
|
|
|
|
keys.reserve(size);
|
|
|
|
for (int i = 0; i < size; ++i) {
|
2016-02-08 09:27:28 +00:00
|
|
|
const QString number = QString::number(i);
|
|
|
|
keys.append(number);
|
|
|
|
addJsonValue(array.at(i), keyPrefix + number, map);
|
2013-09-05 05:55:49 +00:00
|
|
|
}
|
|
|
|
insertJsonKeyValue(keyPrefix + QLatin1String("_KEYS_"), keys, map);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addJsonObject(const QJsonObject &object, const QString &keyPrefix, ProValueMap *map)
|
|
|
|
{
|
2016-02-01 07:42:04 +00:00
|
|
|
QStringList keys;
|
|
|
|
keys.reserve(object.size());
|
|
|
|
for (auto it = object.begin(), end = object.end(); it != end; ++it) {
|
|
|
|
const QString key = it.key();
|
|
|
|
keys.append(key);
|
|
|
|
addJsonValue(it.value(), keyPrefix + key, map);
|
|
|
|
}
|
|
|
|
insertJsonKeyValue(keyPrefix + QLatin1String("_KEYS_"), keys, map);
|
2013-09-05 05:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProValueMap *map)
|
|
|
|
{
|
|
|
|
switch (value.type()) {
|
|
|
|
case QJsonValue::Bool:
|
|
|
|
insertJsonKeyValue(keyPrefix, QStringList() << (value.toBool() ? QLatin1String("true") : QLatin1String("false")), map);
|
|
|
|
break;
|
|
|
|
case QJsonValue::Double:
|
|
|
|
insertJsonKeyValue(keyPrefix, QStringList() << QString::number(value.toDouble()), map);
|
|
|
|
break;
|
|
|
|
case QJsonValue::String:
|
|
|
|
insertJsonKeyValue(keyPrefix, QStringList() << value.toString(), map);
|
|
|
|
break;
|
|
|
|
case QJsonValue::Array:
|
|
|
|
addJsonArray(value.toArray(), keyPrefix + QLatin1Char('.'), map);
|
|
|
|
break;
|
|
|
|
case QJsonValue::Object:
|
|
|
|
addJsonObject(value.toObject(), keyPrefix + QLatin1Char('.'), map);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-11 14:22:45 +00:00
|
|
|
struct ErrorPosition {
|
|
|
|
int line;
|
|
|
|
int column;
|
|
|
|
};
|
|
|
|
|
|
|
|
static ErrorPosition calculateErrorPosition(const QByteArray &json, int offset)
|
|
|
|
{
|
|
|
|
ErrorPosition pos = { 0, 0 };
|
|
|
|
offset--; // offset is 1-based, switching to 0-based
|
|
|
|
for (int i = 0; i < offset; ++i) {
|
|
|
|
switch (json.at(i)) {
|
|
|
|
case '\n':
|
|
|
|
pos.line++;
|
|
|
|
pos.column = 0;
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
pos.column = (pos.column + 8) & ~7;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pos.column++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Lines and columns in text editors are 1-based:
|
|
|
|
pos.line++;
|
|
|
|
pos.column++;
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2016-05-19 13:40:55 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value)
|
2013-09-05 05:55:49 +00:00
|
|
|
{
|
2016-05-19 13:40:55 +00:00
|
|
|
QJsonParseError error;
|
|
|
|
QJsonDocument document = QJsonDocument::fromJson(json, &error);
|
|
|
|
if (document.isNull()) {
|
2016-11-11 14:22:45 +00:00
|
|
|
if (error.error != QJsonParseError::NoError) {
|
|
|
|
ErrorPosition errorPos = calculateErrorPosition(json, error.offset);
|
|
|
|
evalError(fL1S("Error parsing JSON at %1:%2: %3")
|
|
|
|
.arg(errorPos.line).arg(errorPos.column).arg(error.errorString()));
|
|
|
|
}
|
2013-09-05 05:55:49 +00:00
|
|
|
return QMakeEvaluator::ReturnFalse;
|
2016-05-19 13:40:55 +00:00
|
|
|
}
|
2013-09-05 05:55:49 +00:00
|
|
|
|
|
|
|
QString currentKey = into + QLatin1Char('.');
|
|
|
|
|
|
|
|
// top-level item is either an array or object
|
|
|
|
if (document.isArray())
|
|
|
|
addJsonArray(document.array(), currentKey, value);
|
|
|
|
else if (document.isObject())
|
|
|
|
addJsonObject(document.object(), currentKey, value);
|
|
|
|
else
|
|
|
|
return QMakeEvaluator::ReturnFalse;
|
|
|
|
|
|
|
|
return QMakeEvaluator::ReturnTrue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
QMakeEvaluator::VisitReturn
|
|
|
|
QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
|
2016-10-24 17:30:24 +00:00
|
|
|
QMakeVfs::VfsFlags flags, const QString &contents)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2016-10-24 17:30:24 +00:00
|
|
|
int oldId = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsAccessedOnly);
|
|
|
|
int id = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsCreate);
|
2012-09-05 16:29:19 +00:00
|
|
|
QString errStr;
|
2016-10-24 17:30:24 +00:00
|
|
|
if (!m_vfs->writeFile(id, mode, flags, contents, &errStr)) {
|
2015-05-13 18:53:19 +00:00
|
|
|
evalError(fL1S("Cannot write %1file %2: %3")
|
2013-05-29 15:12:11 +00:00
|
|
|
.arg(ctx, QDir::toNativeSeparators(fn), errStr));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2016-10-24 17:30:24 +00:00
|
|
|
if (oldId)
|
|
|
|
m_parser->discardFileFromCache(oldId);
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
|
2017-03-02 10:29:08 +00:00
|
|
|
#if QT_CONFIG(process)
|
2012-09-05 16:29:19 +00:00
|
|
|
void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
|
|
|
|
{
|
|
|
|
proc->setWorkingDirectory(currentDirectory());
|
|
|
|
# ifdef PROEVALUATOR_SETENV
|
|
|
|
if (!m_option->environment.isEmpty())
|
|
|
|
proc->setProcessEnvironment(m_option->environment);
|
|
|
|
# endif
|
|
|
|
# ifdef Q_OS_WIN
|
|
|
|
proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
|
|
|
|
proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
|
|
|
|
# else
|
|
|
|
proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
|
|
|
|
# endif
|
|
|
|
proc->waitForFinished(-1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-15 16:09:24 +00:00
|
|
|
QByteArray QMakeEvaluator::getCommandOutput(const QString &args, int *exitCode) const
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2012-09-17 19:18:10 +00:00
|
|
|
QByteArray out;
|
2017-03-02 10:29:08 +00:00
|
|
|
#if QT_CONFIG(process)
|
2012-09-05 16:29:19 +00:00
|
|
|
QProcess proc;
|
|
|
|
runProcess(&proc, args);
|
2016-07-15 16:09:24 +00:00
|
|
|
*exitCode = (proc.exitStatus() == QProcess::NormalExit) ? proc.exitCode() : -1;
|
2012-09-05 16:29:19 +00:00
|
|
|
QByteArray errout = proc.readAllStandardError();
|
|
|
|
# ifdef PROEVALUATOR_FULL
|
|
|
|
// FIXME: Qt really should have the option to set forwarding per channel
|
|
|
|
fputs(errout.constData(), stderr);
|
|
|
|
# else
|
|
|
|
if (!errout.isEmpty()) {
|
|
|
|
if (errout.endsWith('\n'))
|
|
|
|
errout.chop(1);
|
2015-12-17 15:27:23 +00:00
|
|
|
m_handler->message(
|
|
|
|
QMakeHandler::EvalError | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0),
|
|
|
|
QString::fromLocal8Bit(errout));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
# endif
|
2012-09-17 19:18:10 +00:00
|
|
|
out = proc.readAllStandardOutput();
|
|
|
|
# ifdef Q_OS_WIN
|
|
|
|
// FIXME: Qt's line end conversion on sequential files should really be fixed
|
|
|
|
out.replace("\r\n", "\n");
|
|
|
|
# endif
|
2012-09-05 16:29:19 +00:00
|
|
|
#else
|
|
|
|
if (FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
|
|
|
|
+ IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
|
2016-06-16 13:38:22 +00:00
|
|
|
+ QLatin1String(" && ") + args).toLocal8Bit().constData(), QT_POPEN_READ)) {
|
2012-09-05 16:29:19 +00:00
|
|
|
while (!feof(proc)) {
|
|
|
|
char buff[10 * 1024];
|
|
|
|
int read_in = int(fread(buff, 1, sizeof(buff), proc));
|
|
|
|
if (!read_in)
|
|
|
|
break;
|
|
|
|
out += QByteArray(buff, read_in);
|
|
|
|
}
|
2016-07-15 16:09:24 +00:00
|
|
|
int ec = QT_PCLOSE(proc);
|
|
|
|
# ifdef Q_OS_WIN
|
|
|
|
*exitCode = ec >= 0 ? ec : -1;
|
|
|
|
# else
|
|
|
|
*exitCode = WIFEXITED(ec) ? WEXITSTATUS(ec) : -1;
|
|
|
|
# endif
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2016-06-16 13:38:22 +00:00
|
|
|
# ifdef Q_OS_WIN
|
|
|
|
out.replace("\r\n", "\n");
|
|
|
|
# endif
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2012-09-17 19:18:10 +00:00
|
|
|
return out;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::populateDeps(
|
2013-02-01 19:03:02 +00:00
|
|
|
const ProStringList &deps, const ProString &prefix, const ProStringList &suffixes,
|
2014-07-25 17:37:57 +00:00
|
|
|
const ProString &priosfx,
|
2012-09-05 16:29:19 +00:00
|
|
|
QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees,
|
2014-07-25 17:37:57 +00:00
|
|
|
QMultiMap<int, ProString> &rootSet) const
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &item : deps)
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!dependencies.contains(item.toKey())) {
|
|
|
|
QSet<ProKey> &dset = dependencies[item.toKey()]; // Always create entry
|
2013-02-01 19:03:02 +00:00
|
|
|
ProStringList depends;
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &suffix : suffixes)
|
2013-02-01 19:03:02 +00:00
|
|
|
depends += values(ProKey(prefix + item + suffix));
|
2012-09-05 16:29:19 +00:00
|
|
|
if (depends.isEmpty()) {
|
2014-07-25 17:37:57 +00:00
|
|
|
rootSet.insert(first(ProKey(prefix + item + priosfx)).toInt(), item);
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &dep : qAsConst(depends)) {
|
2012-09-05 16:29:19 +00:00
|
|
|
dset.insert(dep.toKey());
|
|
|
|
dependees[dep.toKey()] << item;
|
|
|
|
}
|
2014-07-25 17:37:57 +00:00
|
|
|
populateDeps(depends, prefix, suffixes, priosfx, dependencies, dependees, rootSet);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-14 09:58:21 +00:00
|
|
|
QString QMakeEvaluator::filePathArg0(const ProStringList &args)
|
|
|
|
{
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
QString fn = resolvePath(u1.str());
|
2017-08-14 09:58:21 +00:00
|
|
|
fn.detach();
|
|
|
|
return fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QMakeEvaluator::filePathEnvArg0(const ProStringList &args)
|
|
|
|
{
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
QString fn = resolvePath(m_option->expandEnvVars(u1.str()));
|
2017-08-14 09:58:21 +00:00
|
|
|
fn.detach();
|
|
|
|
return fn;
|
|
|
|
}
|
|
|
|
|
2016-11-25 19:41:20 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
|
2017-08-11 17:22:58 +00:00
|
|
|
const QMakeBuiltin &adef, const ProKey &func, const ProStringList &args, ProStringList &ret)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args));
|
2017-08-11 17:22:58 +00:00
|
|
|
int asz = args.size() > 1 ? args.size() : args.at(0).isEmpty() ? 0 : 1;
|
|
|
|
if (asz < adef.minArgs || asz > adef.maxArgs) {
|
|
|
|
evalError(adef.usage);
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2017-08-11 17:22:58 +00:00
|
|
|
int func_t = adef.index;
|
2012-09-05 16:29:19 +00:00
|
|
|
switch (func_t) {
|
|
|
|
case E_BASENAME:
|
|
|
|
case E_DIRNAME:
|
|
|
|
case E_SECTION: {
|
|
|
|
bool regexp = false;
|
|
|
|
QString sep;
|
|
|
|
ProString var;
|
|
|
|
int beg = 0;
|
|
|
|
int end = -1;
|
|
|
|
if (func_t == E_SECTION) {
|
2017-08-11 17:22:58 +00:00
|
|
|
var = args[0];
|
|
|
|
sep = args.at(1).toQString();
|
|
|
|
beg = args.at(2).toInt();
|
|
|
|
if (args.count() == 4)
|
|
|
|
end = args.at(3).toInt();
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2017-08-11 17:22:58 +00:00
|
|
|
var = args[0];
|
|
|
|
regexp = true;
|
|
|
|
sep = QLatin1String("[\\\\/]");
|
|
|
|
if (func_t == E_DIRNAME)
|
|
|
|
end = -2;
|
|
|
|
else
|
|
|
|
beg = -1;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
if (!var.isEmpty()) {
|
2016-01-26 13:38:54 +00:00
|
|
|
const auto strings = values(map(var));
|
2012-09-05 16:29:19 +00:00
|
|
|
if (regexp) {
|
|
|
|
QRegExp sepRx(sep);
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &str : strings) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(str, m_tmp[m_toggle ^= 1]);
|
|
|
|
ret << u1.extract(u1.str().section(sepRx, beg, end));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &str : strings) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(str, m_tmp1);
|
|
|
|
ret << u1.extract(u1.str().section(sep, beg, end));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_SPRINTF: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
QString tmp = u1.str();
|
2017-08-11 17:22:58 +00:00
|
|
|
for (int i = 1; i < args.count(); ++i)
|
|
|
|
tmp = tmp.arg(args.at(i).toQStringView());
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(tmp);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_FORMAT_NUMBER: {
|
|
|
|
int ibase = 10;
|
|
|
|
int obase = 10;
|
|
|
|
int width = 0;
|
|
|
|
bool zeropad = false;
|
|
|
|
bool leftalign = false;
|
|
|
|
enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign;
|
|
|
|
if (args.count() >= 2) {
|
|
|
|
const auto opts = split_value_list(args.at(1).toQStringRef());
|
|
|
|
for (const ProString &opt : opts) {
|
|
|
|
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 (opt == QLatin1String("padsign")) {
|
|
|
|
sign = PadSign;
|
|
|
|
} else if (opt == QLatin1String("alwayssign")) {
|
|
|
|
sign = AlwaysSign;
|
|
|
|
} else if (opt == QLatin1String("leftalign")) {
|
|
|
|
leftalign = true;
|
|
|
|
} else {
|
|
|
|
evalError(fL1S("format_number(): invalid format option %1.")
|
|
|
|
.arg(opt.toQStringView()));
|
|
|
|
goto allfail;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
if (args.at(0).contains(QLatin1Char('.'))) {
|
|
|
|
evalError(fL1S("format_number(): floats are currently not supported."));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bool ok;
|
|
|
|
qlonglong num = args.at(0).toLongLong(&ok, ibase);
|
|
|
|
if (!ok) {
|
|
|
|
evalError(fL1S("format_number(): malformed number %2 for base %1.")
|
|
|
|
.arg(ibase).arg(args.at(0).toQStringView()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QString outstr;
|
|
|
|
if (num < 0) {
|
|
|
|
num = -num;
|
|
|
|
outstr = QLatin1Char('-');
|
|
|
|
} else if (sign == AlwaysSign) {
|
|
|
|
outstr = QLatin1Char('+');
|
|
|
|
} else if (sign == PadSign) {
|
|
|
|
outstr = QLatin1Char(' ');
|
|
|
|
}
|
|
|
|
QString numstr = QString::number(num, obase);
|
|
|
|
int space = width - outstr.length() - numstr.length();
|
|
|
|
if (space <= 0) {
|
|
|
|
outstr += numstr;
|
|
|
|
} else if (leftalign) {
|
|
|
|
outstr += numstr + QString(space, QLatin1Char(' '));
|
|
|
|
} else if (zeropad) {
|
|
|
|
outstr += QString(space, QLatin1Char('0')) + numstr;
|
|
|
|
} else {
|
|
|
|
outstr.prepend(QString(space, QLatin1Char(' ')));
|
|
|
|
outstr += numstr;
|
|
|
|
}
|
|
|
|
ret += ProString(outstr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case E_NUM_ADD: {
|
|
|
|
qlonglong sum = 0;
|
|
|
|
for (const ProString &arg : qAsConst(args)) {
|
|
|
|
if (arg.contains(QLatin1Char('.'))) {
|
|
|
|
evalError(fL1S("num_add(): floats are currently not supported."));
|
|
|
|
goto allfail;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
bool ok;
|
2017-08-11 17:22:58 +00:00
|
|
|
qlonglong num = arg.toLongLong(&ok);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!ok) {
|
2017-08-11 17:22:58 +00:00
|
|
|
evalError(fL1S("num_add(): malformed number %1.")
|
|
|
|
.arg(arg.toQStringView()));
|
|
|
|
goto allfail;
|
2016-05-13 11:31:14 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
sum += num;
|
2016-05-13 11:31:14 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
ret += ProString(QString::number(sum));
|
2016-05-13 11:31:14 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_JOIN: {
|
2017-08-11 17:22:58 +00:00
|
|
|
ProString glue, before, after;
|
|
|
|
if (args.count() >= 2)
|
|
|
|
glue = args.at(1);
|
|
|
|
if (args.count() >= 3)
|
|
|
|
before = args[2];
|
|
|
|
if (args.count() == 4)
|
|
|
|
after = args[3];
|
|
|
|
const ProStringList &var = values(map(args.at(0)));
|
|
|
|
if (!var.isEmpty()) {
|
|
|
|
int src = currentFileId();
|
|
|
|
for (const ProString &v : var)
|
|
|
|
if (int s = v.sourceFile()) {
|
|
|
|
src = s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret << ProString(before + var.join(glue) + after).setSource(src);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_SPLIT: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(m_tmp1);
|
|
|
|
const QString &sep = (args.count() == 2) ? u1.set(args.at(1)) : statics.field_sep;
|
2017-08-11 17:22:58 +00:00
|
|
|
const auto vars = values(map(args.at(0)));
|
|
|
|
for (const ProString &var : vars) {
|
|
|
|
// FIXME: this is inconsistent with the "there are no empty strings" dogma.
|
|
|
|
const auto splits = var.toQStringRef().split(sep, QString::KeepEmptyParts);
|
|
|
|
for (const auto &splt : splits)
|
|
|
|
ret << ProString(splt).setSource(var);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_MEMBER: {
|
|
|
|
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 {
|
|
|
|
for (int i = start; i >= end && src.size() >= i && i >= 0; i--)
|
|
|
|
ret += src.at(i);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2016-05-18 14:35:01 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_STR_MEMBER: {
|
|
|
|
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 && src.size() >= i && i >= 0; i--)
|
|
|
|
res += src.at(i);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
ret += ProString(res);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_FIRST:
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_LAST: {
|
|
|
|
const ProStringList &var = values(map(args.at(0)));
|
|
|
|
if (!var.isEmpty()) {
|
|
|
|
if (func_t == E_FIRST)
|
|
|
|
ret.append(var[0]);
|
|
|
|
else
|
|
|
|
ret.append(var.last());
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2016-05-12 18:38:54 +00:00
|
|
|
case E_TAKE_FIRST:
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_TAKE_LAST: {
|
|
|
|
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());
|
2016-05-12 18:38:54 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_SIZE:
|
2017-08-11 17:22:58 +00:00
|
|
|
ret.append(ProString(QString::number(values(map(args.at(0))).size())));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2016-05-13 13:58:20 +00:00
|
|
|
case E_STR_SIZE:
|
2017-08-11 17:22:58 +00:00
|
|
|
ret.append(ProString(QString::number(args.at(0).size())));
|
2016-05-13 13:58:20 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_CAT: {
|
|
|
|
bool blob = false;
|
|
|
|
bool lines = false;
|
|
|
|
bool singleLine = true;
|
|
|
|
if (args.count() > 1) {
|
|
|
|
if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive))
|
|
|
|
singleLine = false;
|
|
|
|
else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive))
|
|
|
|
blob = true;
|
|
|
|
else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive))
|
|
|
|
lines = true;
|
|
|
|
}
|
2017-08-14 09:58:21 +00:00
|
|
|
QString fn = filePathEnvArg0(args);
|
2017-08-11 17:22:58 +00:00
|
|
|
QFile qfile(fn);
|
|
|
|
if (qfile.open(QIODevice::ReadOnly)) {
|
|
|
|
QTextStream stream(&qfile);
|
|
|
|
if (blob) {
|
|
|
|
ret += ProString(stream.readAll());
|
|
|
|
} else {
|
|
|
|
while (!stream.atEnd()) {
|
|
|
|
if (lines) {
|
|
|
|
ret += ProString(stream.readLine());
|
|
|
|
} else {
|
|
|
|
const QString &line = stream.readLine();
|
|
|
|
ret += split_value_list(QStringRef(&line).trimmed());
|
|
|
|
if (!singleLine)
|
|
|
|
ret += ProString("\n");
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_FROMFILE: {
|
|
|
|
ProValueMap vars;
|
2017-08-14 09:58:21 +00:00
|
|
|
QString fn = filePathEnvArg0(args);
|
2017-08-11 17:22:58 +00:00
|
|
|
if (evaluateFileInto(fn, &vars, LoadProOnly) == ReturnTrue)
|
|
|
|
ret = vars.value(map(args.at(1)));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_EVAL:
|
2017-08-11 17:22:58 +00:00
|
|
|
ret += values(map(args.at(0)));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
|
|
|
case E_LIST: {
|
|
|
|
QString tmp;
|
|
|
|
tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
|
|
|
|
ret = ProStringList(ProString(tmp));
|
|
|
|
ProStringList lst;
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &arg : args)
|
2016-05-13 13:40:00 +00:00
|
|
|
lst += split_value_list(arg.toQStringRef(), arg.sourceFile()); // Relies on deep copy
|
2012-09-05 16:29:19 +00:00
|
|
|
m_valuemapStack.top()[ret.at(0).toKey()] = lst;
|
|
|
|
break; }
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_FIND: {
|
|
|
|
QRegExp regx(args.at(1).toQString());
|
|
|
|
const auto vals = values(map(args.at(0)));
|
|
|
|
for (const ProString &val : vals) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(val, m_tmp[m_toggle ^= 1]);
|
|
|
|
if (regx.indexIn(u1.str()) != -1)
|
2017-08-11 17:22:58 +00:00
|
|
|
ret += val;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_SYSTEM: {
|
|
|
|
if (m_skipLevel)
|
|
|
|
break;
|
|
|
|
bool blob = false;
|
|
|
|
bool lines = false;
|
|
|
|
bool singleLine = true;
|
|
|
|
if (args.count() > 1) {
|
|
|
|
if (!args.at(1).compare(QLatin1String("false"), Qt::CaseInsensitive))
|
|
|
|
singleLine = false;
|
|
|
|
else if (!args.at(1).compare(QLatin1String("blob"), Qt::CaseInsensitive))
|
|
|
|
blob = true;
|
|
|
|
else if (!args.at(1).compare(QLatin1String("lines"), Qt::CaseInsensitive))
|
|
|
|
lines = true;
|
|
|
|
}
|
|
|
|
int exitCode;
|
|
|
|
QByteArray bytes = getCommandOutput(args.at(0).toQString(), &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())
|
|
|
|
ret += ProString(stream.readLine());
|
|
|
|
} else {
|
|
|
|
QString output = QString::fromLocal8Bit(bytes);
|
|
|
|
if (blob) {
|
|
|
|
ret += ProString(output);
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2017-08-11 17:22:58 +00:00
|
|
|
output.replace(QLatin1Char('\t'), QLatin1Char(' '));
|
|
|
|
if (singleLine)
|
|
|
|
output.replace(QLatin1Char('\n'), QLatin1Char(' '));
|
|
|
|
ret += split_value_list(QStringRef(&output));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_UNIQUE:
|
2017-08-11 17:22:58 +00:00
|
|
|
ret = values(map(args.at(0)));
|
|
|
|
ret.removeDuplicates();
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2016-05-13 14:53:01 +00:00
|
|
|
case E_SORTED:
|
2017-08-11 17:22:58 +00:00
|
|
|
ret = values(map(args.at(0)));
|
|
|
|
std::sort(ret.begin(), ret.end());
|
2016-05-13 14:53:01 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_REVERSE: {
|
|
|
|
ProStringList var = values(args.at(0).toKey());
|
|
|
|
for (int i = 0; i < var.size() / 2; i++)
|
|
|
|
qSwap(var[i], var[var.size() - i - 1]);
|
|
|
|
ret += var;
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_QUOTE:
|
|
|
|
ret += args;
|
|
|
|
break;
|
|
|
|
case E_ESCAPE_EXPAND:
|
|
|
|
for (int i = 0; i < args.size(); ++i) {
|
|
|
|
QString str = args.at(i).toQString();
|
|
|
|
QChar *i_data = str.data();
|
|
|
|
int i_len = str.length();
|
|
|
|
for (int x = 0; x < i_len; ++x) {
|
|
|
|
if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
|
|
|
|
if (*(i_data+x+1) == QLatin1Char('\\')) {
|
|
|
|
++x;
|
|
|
|
} else {
|
|
|
|
struct {
|
|
|
|
char in, out;
|
|
|
|
} mapped_quotes[] = {
|
|
|
|
{ 'n', '\n' },
|
|
|
|
{ 't', '\t' },
|
|
|
|
{ 'r', '\r' },
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
for (int i = 0; mapped_quotes[i].in; ++i) {
|
|
|
|
if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
|
|
|
|
*(i_data+x) = QLatin1Char(mapped_quotes[i].out);
|
|
|
|
if (x < i_len-2)
|
|
|
|
memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
|
|
|
|
--i_len;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret.append(ProString(QString(i_data, i_len)).setSource(args.at(i)));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case E_RE_ESCAPE:
|
|
|
|
for (int i = 0; i < args.size(); ++i) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(i), m_tmp1);
|
|
|
|
ret << u1.extract(QRegExp::escape(u1.str()));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_VAL_ESCAPE: {
|
|
|
|
const ProStringList &vals = values(args.at(0).toKey());
|
|
|
|
ret.reserve(vals.size());
|
|
|
|
for (const ProString &str : vals)
|
|
|
|
ret += ProString(quoteValue(str));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_UPPER:
|
|
|
|
case E_LOWER:
|
2013-09-19 11:21:26 +00:00
|
|
|
case E_TITLE:
|
2012-09-05 16:29:19 +00:00
|
|
|
for (int i = 0; i < args.count(); ++i) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(i), m_tmp1);
|
|
|
|
QString rstr = u1.str();
|
2013-09-19 11:21:26 +00:00
|
|
|
if (func_t == E_UPPER) {
|
|
|
|
rstr = rstr.toUpper();
|
|
|
|
} else {
|
|
|
|
rstr = rstr.toLower();
|
|
|
|
if (func_t == E_TITLE && rstr.length() > 0)
|
|
|
|
rstr[0] = rstr.at(0).toTitleCase();
|
|
|
|
}
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(rstr);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_FILES: {
|
|
|
|
bool recursive = false;
|
|
|
|
if (args.count() == 2)
|
|
|
|
recursive = isTrue(args.at(1));
|
|
|
|
QStringList dirs;
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
QString r = m_option->expandEnvVars(u1.str())
|
2017-08-11 17:22:58 +00:00
|
|
|
.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
|
|
|
QString pfx;
|
|
|
|
if (IoUtils::isRelativePath(r)) {
|
|
|
|
pfx = currentDirectory();
|
|
|
|
if (!pfx.endsWith(QLatin1Char('/')))
|
|
|
|
pfx += QLatin1Char('/');
|
|
|
|
}
|
|
|
|
int slash = r.lastIndexOf(QLatin1Char('/'));
|
|
|
|
if (slash != -1) {
|
|
|
|
dirs.append(r.left(slash+1));
|
|
|
|
r = r.mid(slash+1);
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2017-08-11 17:22:58 +00:00
|
|
|
dirs.append(QString());
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2017-08-11 17:22:58 +00:00
|
|
|
r.detach(); // Keep m_tmp out of QRegExp's cache
|
|
|
|
QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
|
|
|
|
for (int d = 0; d < dirs.count(); d++) {
|
|
|
|
QString dir = dirs[d];
|
|
|
|
QDir qdir(pfx + dir);
|
|
|
|
for (int i = 0; i < (int)qdir.count(); ++i) {
|
|
|
|
if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
|
|
|
|
continue;
|
|
|
|
QString fname = dir + qdir[i];
|
|
|
|
if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
|
|
|
|
if (recursive)
|
|
|
|
dirs.append(fname + QLatin1Char('/'));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
if (regex.exactMatch(qdir[i]))
|
|
|
|
ret += ProString(fname).setSource(currentFileId());
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
|
|
|
case E_PROMPT: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
QString msg = m_option->expandEnvVars(u1.str());
|
2017-08-11 17:22:58 +00:00
|
|
|
bool decorate = true;
|
|
|
|
if (args.count() == 2)
|
|
|
|
decorate = isTrue(args.at(1));
|
|
|
|
if (decorate) {
|
|
|
|
if (!msg.endsWith(QLatin1Char('?')))
|
|
|
|
msg += QLatin1Char('?');
|
|
|
|
fprintf(stderr, "Project PROMPT: %s ", qPrintable(msg));
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2017-08-11 17:22:58 +00:00
|
|
|
fputs(qPrintable(msg), stderr);
|
|
|
|
}
|
|
|
|
QFile qfile;
|
|
|
|
if (qfile.open(stdin, QIODevice::ReadOnly)) {
|
|
|
|
QTextStream t(&qfile);
|
|
|
|
const QString &line = t.readLine();
|
|
|
|
if (t.atEnd()) {
|
|
|
|
fputs("\n", stderr);
|
|
|
|
evalError(fL1S("Unexpected EOF."));
|
|
|
|
return ReturnError;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
ret = split_value_list(QStringRef(&line));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_REPLACE: {
|
|
|
|
const QRegExp before(args.at(1).toQString());
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u2(args.at(2), m_tmp2);
|
|
|
|
const QString &after = u2.str();
|
2017-08-11 17:22:58 +00:00
|
|
|
const auto vals = values(map(args.at(0)));
|
|
|
|
for (const ProString &val : vals) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(val, m_tmp1);
|
|
|
|
QString rstr = u1.str();
|
2017-08-11 17:22:58 +00:00
|
|
|
QString copy = rstr; // Force a detach on modify
|
|
|
|
rstr.replace(before, after);
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(rstr, u2);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_SORT_DEPENDS:
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_RESOLVE_DEPENDS: {
|
|
|
|
QHash<ProKey, QSet<ProKey> > dependencies;
|
|
|
|
ProValueMap dependees;
|
|
|
|
QMultiMap<int, ProString> rootSet;
|
|
|
|
ProStringList orgList = values(args.at(0).toKey());
|
|
|
|
ProString prefix = args.count() < 2 ? ProString() : args.at(1);
|
|
|
|
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).toQStringRef()),
|
|
|
|
priosfx, dependencies, dependees, rootSet);
|
|
|
|
while (!rootSet.isEmpty()) {
|
|
|
|
QMultiMap<int, ProString>::iterator it = rootSet.begin();
|
|
|
|
const ProString item = *it;
|
|
|
|
rootSet.erase(it);
|
|
|
|
if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item))
|
|
|
|
ret.prepend(item);
|
|
|
|
for (const ProString &dep : qAsConst(dependees[item.toKey()])) {
|
|
|
|
QSet<ProKey> &dset = dependencies[dep.toKey()];
|
|
|
|
dset.remove(item.toKey());
|
|
|
|
if (dset.isEmpty())
|
|
|
|
rootSet.insert(first(ProKey(prefix + dep + priosfx)).toInt(), dep);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case E_ENUMERATE_VARS: {
|
|
|
|
QSet<ProString> keys;
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProValueMap &vmap : qAsConst(m_valuemapStack))
|
2012-09-05 16:29:19 +00:00
|
|
|
for (ProValueMap::ConstIterator it = vmap.constBegin(); it != vmap.constEnd(); ++it)
|
|
|
|
keys.insert(it.key());
|
|
|
|
ret.reserve(keys.size());
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &key : qAsConst(keys))
|
2012-09-05 16:29:19 +00:00
|
|
|
ret << key;
|
|
|
|
break; }
|
2017-08-11 17:22:58 +00:00
|
|
|
case E_SHADOWED: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
QString rstr = m_option->shadowedPath(resolvePath(u1.str()));
|
|
|
|
if (!rstr.isEmpty())
|
|
|
|
ret << u1.extract(rstr);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_ABSOLUTE_PATH: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
ProStringRwUser u2(m_tmp2);
|
2017-08-11 17:22:58 +00:00
|
|
|
QString baseDir = args.count() > 1
|
2017-08-14 10:06:35 +00:00
|
|
|
? IoUtils::resolvePath(currentDirectory(), u2.set(args.at(1)))
|
2017-08-11 17:22:58 +00:00
|
|
|
: currentDirectory();
|
2017-08-14 10:06:35 +00:00
|
|
|
QString rstr = u1.str().isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, u1.str());
|
|
|
|
ret << u1.extract(rstr, u2);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_RELATIVE_PATH: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
ProStringRoUser u2(m_tmp2);
|
2017-08-11 17:22:58 +00:00
|
|
|
QString baseDir = args.count() > 1
|
2017-08-14 10:06:35 +00:00
|
|
|
? IoUtils::resolvePath(currentDirectory(), u2.set(args.at(1)))
|
2017-08-11 17:22:58 +00:00
|
|
|
: currentDirectory();
|
2017-08-14 10:06:35 +00:00
|
|
|
QString absArg = u1.str().isEmpty() ? baseDir : IoUtils::resolvePath(baseDir, u1.str());
|
2017-08-11 17:22:58 +00:00
|
|
|
QString rstr = QDir(baseDir).relativeFilePath(absArg);
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(rstr);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_CLEAN_PATH: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
ret << u1.extract(QDir::cleanPath(u1.str()));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_SYSTEM_PATH: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
QString rstr = u1.str();
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef Q_OS_WIN
|
2017-08-11 17:22:58 +00:00
|
|
|
rstr.replace(QLatin1Char('/'), QLatin1Char('\\'));
|
2012-09-05 16:29:19 +00:00
|
|
|
#else
|
2017-08-11 17:22:58 +00:00
|
|
|
rstr.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(rstr);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_SHELL_PATH: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
QString rstr = u1.str();
|
2017-08-11 17:22:58 +00:00
|
|
|
if (m_dirSep.startsWith(QLatin1Char('\\'))) {
|
|
|
|
rstr.replace(QLatin1Char('/'), QLatin1Char('\\'));
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2017-08-11 17:22:58 +00:00
|
|
|
rstr.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
2014-04-29 18:39:13 +00:00
|
|
|
#ifdef Q_OS_WIN
|
2017-08-11 17:22:58 +00:00
|
|
|
// Convert d:/foo/bar to msys-style /d/foo/bar.
|
|
|
|
if (rstr.length() > 2 && rstr.at(1) == QLatin1Char(':') && rstr.at(2) == QLatin1Char('/')) {
|
|
|
|
rstr[1] = rstr.at(0);
|
|
|
|
rstr[0] = QLatin1Char('/');
|
2014-04-29 18:39:13 +00:00
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
#endif
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(rstr);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_SYSTEM_QUOTE: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
ret << u1.extract(IoUtils::shellQuote(u1.str()));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_SHELL_QUOTE: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRwUser u1(args.at(0), m_tmp1);
|
|
|
|
QString rstr = u1.str();
|
2017-08-11 17:22:58 +00:00
|
|
|
if (m_dirSep.startsWith(QLatin1Char('\\')))
|
|
|
|
rstr = IoUtils::shellQuoteWin(rstr);
|
|
|
|
else
|
|
|
|
rstr = IoUtils::shellQuoteUnix(rstr);
|
2017-08-14 10:06:35 +00:00
|
|
|
ret << u1.extract(rstr);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
|
|
|
case E_GETENV: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
ret << ProString(m_option->getEnv(u1.str()));
|
2014-03-11 14:56:52 +00:00
|
|
|
break;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
default:
|
2017-04-07 15:10:37 +00:00
|
|
|
evalError(fL1S("Function '%1' is not implemented.").arg(func.toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-08-11 17:22:58 +00:00
|
|
|
allfail:
|
2016-11-25 19:41:20 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
|
2017-08-11 17:22:58 +00:00
|
|
|
const QMakeInternal::QMakeBuiltin &adef, const ProKey &function, const ProStringList &args)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args));
|
2017-08-11 17:22:58 +00:00
|
|
|
int asz = args.size() > 1 ? args.size() : args.at(0).isEmpty() ? 0 : 1;
|
|
|
|
if (asz < adef.minArgs || asz > adef.maxArgs) {
|
|
|
|
evalError(adef.usage);
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2017-08-11 17:22:58 +00:00
|
|
|
int func_t = adef.index;
|
2012-09-05 16:29:19 +00:00
|
|
|
switch (func_t) {
|
|
|
|
case T_DEFINED: {
|
|
|
|
const ProKey &var = args.at(0).toKey();
|
|
|
|
if (args.count() > 1) {
|
|
|
|
if (args[1] == QLatin1String("test")) {
|
|
|
|
return returnBool(m_functionDefs.testFunctions.contains(var));
|
|
|
|
} else if (args[1] == QLatin1String("replace")) {
|
|
|
|
return returnBool(m_functionDefs.replaceFunctions.contains(var));
|
|
|
|
} else if (args[1] == QLatin1String("var")) {
|
|
|
|
ProValueMap::Iterator it;
|
|
|
|
return returnBool(findValues(var, &it));
|
|
|
|
}
|
|
|
|
evalError(fL1S("defined(function, type): unexpected type [%1].")
|
2017-04-07 15:10:37 +00:00
|
|
|
.arg(args.at(1).toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
return returnBool(m_functionDefs.replaceFunctions.contains(var)
|
|
|
|
|| m_functionDefs.testFunctions.contains(var));
|
|
|
|
}
|
|
|
|
case T_EXPORT: {
|
|
|
|
const ProKey &var = map(args.at(0));
|
|
|
|
for (ProValueMapStack::Iterator vmi = m_valuemapStack.end();
|
|
|
|
--vmi != m_valuemapStack.begin(); ) {
|
|
|
|
ProValueMap::Iterator it = (*vmi).find(var);
|
|
|
|
if (it != (*vmi).end()) {
|
|
|
|
if (it->constBegin() == statics.fakeValue.constBegin()) {
|
|
|
|
// This is stupid, but qmake doesn't propagate deletions
|
|
|
|
m_valuemapStack.first()[var] = ProStringList();
|
|
|
|
} else {
|
|
|
|
m_valuemapStack.first()[var] = *it;
|
|
|
|
}
|
|
|
|
(*vmi).erase(it);
|
|
|
|
while (--vmi != m_valuemapStack.begin())
|
|
|
|
(*vmi).remove(var);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
2016-08-23 12:05:54 +00:00
|
|
|
case T_DISCARD_FROM: {
|
|
|
|
if (m_valuemapStack.count() != 1) {
|
|
|
|
evalError(fL1S("discard_from() cannot be called from functions."));
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
QString fn = resolvePath(u1.str());
|
2016-10-24 17:30:24 +00:00
|
|
|
QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
|
|
|
|
int pro = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsAccessedOnly);
|
2016-08-23 12:05:54 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-11-11 19:01:53 +00:00
|
|
|
for (auto fit = m_functionDefs.testFunctions.begin(); fit != m_functionDefs.testFunctions.end(); ) {
|
2017-08-14 16:30:29 +00:00
|
|
|
if (fit->pro()->id() == pro)
|
2016-11-11 19:01:53 +00:00
|
|
|
fit = m_functionDefs.testFunctions.erase(fit);
|
|
|
|
else
|
|
|
|
++fit;
|
|
|
|
}
|
|
|
|
for (auto fit = m_functionDefs.replaceFunctions.begin(); fit != m_functionDefs.replaceFunctions.end(); ) {
|
2017-08-14 16:30:29 +00:00
|
|
|
if (fit->pro()->id() == pro)
|
2016-11-11 19:01:53 +00:00
|
|
|
fit = m_functionDefs.replaceFunctions.erase(fit);
|
|
|
|
else
|
|
|
|
++fit;
|
|
|
|
}
|
2016-11-11 13:45:48 +00:00
|
|
|
ProStringList &iif = m_valuemapStack.first()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
|
|
|
|
int idx = iif.indexOf(ProString(fn));
|
|
|
|
if (idx >= 0)
|
|
|
|
iif.removeAt(idx);
|
2016-08-23 12:05:54 +00:00
|
|
|
return ReturnTrue;
|
|
|
|
}
|
2017-08-11 17:22:58 +00:00
|
|
|
case T_INFILE: {
|
|
|
|
ProValueMap vars;
|
2017-08-14 09:58:21 +00:00
|
|
|
QString fn = filePathEnvArg0(args);
|
2017-08-11 17:22:58 +00:00
|
|
|
VisitReturn ok = evaluateFileInto(fn, &vars, LoadProOnly);
|
|
|
|
if (ok != ReturnTrue)
|
|
|
|
return ok;
|
|
|
|
if (args.count() == 2)
|
|
|
|
return returnBool(vars.contains(map(args.at(1))));
|
|
|
|
QRegExp regx;
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(2), m_tmp1);
|
|
|
|
const QString &qry = u1.str();
|
2017-08-11 17:22:58 +00:00
|
|
|
if (qry != QRegExp::escape(qry)) {
|
|
|
|
QString copy = qry;
|
|
|
|
copy.detach();
|
|
|
|
regx.setPattern(copy);
|
|
|
|
}
|
|
|
|
const auto strings = vars.value(map(args.at(1)));
|
|
|
|
for (const ProString &s : strings) {
|
2017-08-14 10:06:35 +00:00
|
|
|
if (s == qry)
|
2017-08-11 17:22:58 +00:00
|
|
|
return ReturnTrue;
|
2017-08-14 10:06:35 +00:00
|
|
|
if (!regx.isEmpty()) {
|
|
|
|
ProStringRoUser u2(s, m_tmp[m_toggle ^= 1]);
|
|
|
|
if (regx.exactMatch(u2.str()))
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
return ReturnFalse;
|
2017-08-11 17:22:58 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case T_REQUIRES:
|
2013-05-16 13:21:20 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
2016-06-30 14:02:29 +00:00
|
|
|
if (checkRequirements(args) == ReturnError)
|
|
|
|
return ReturnError;
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2013-05-16 13:21:20 +00:00
|
|
|
return ReturnFalse; // Another qmake breakage
|
2012-09-05 16:29:19 +00:00
|
|
|
case T_EVAL: {
|
2017-08-11 17:22:58 +00:00
|
|
|
VisitReturn ret = ReturnFalse;
|
|
|
|
QString contents = args.join(statics.field_sep);
|
|
|
|
ProFile *pro = m_parser->parsedProBlock(QStringRef(&contents),
|
|
|
|
0, m_current.pro->fileName(), m_current.line);
|
|
|
|
if (m_cumulative || pro->isOk()) {
|
|
|
|
m_locationStack.push(m_current);
|
|
|
|
visitProBlock(pro, pro->tokPtr());
|
|
|
|
ret = ReturnTrue; // This return value is not too useful, but that's qmake
|
|
|
|
m_current = m_locationStack.pop();
|
|
|
|
}
|
|
|
|
pro->deref();
|
|
|
|
return ret;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case T_IF: {
|
2016-07-19 17:51:41 +00:00
|
|
|
return evaluateConditional(args.at(0).toQStringRef(),
|
2016-06-29 13:40:17 +00:00
|
|
|
m_current.pro->fileName(), m_current.line);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
case T_CONFIG: {
|
|
|
|
if (args.count() == 1)
|
2016-05-13 13:26:42 +00:00
|
|
|
return returnBool(isActiveConfig(args.at(0).toQStringRef()));
|
2018-05-29 14:36:53 +00:00
|
|
|
const auto mutuals = args.at(1).toQStringRef().split(QLatin1Char('|'),
|
|
|
|
QString::SkipEmptyParts);
|
2012-09-05 16:29:19 +00:00
|
|
|
const ProStringList &configs = values(statics.strCONFIG);
|
|
|
|
|
|
|
|
for (int i = configs.size() - 1; i >= 0; i--) {
|
|
|
|
for (int mut = 0; mut < mutuals.count(); mut++) {
|
2017-04-07 15:07:12 +00:00
|
|
|
if (configs[i].toQStringRef() == mutuals[mut].trimmed())
|
2012-09-05 16:29:19 +00:00
|
|
|
return returnBool(configs[i] == args[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
case T_CONTAINS: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(1), m_tmp1);
|
|
|
|
const QString &qry = u1.str();
|
2012-09-05 16:29:19 +00:00
|
|
|
QRegExp regx;
|
|
|
|
if (qry != QRegExp::escape(qry)) {
|
|
|
|
QString copy = qry;
|
|
|
|
copy.detach();
|
|
|
|
regx.setPattern(copy);
|
|
|
|
}
|
|
|
|
const ProStringList &l = values(map(args.at(0)));
|
|
|
|
if (args.count() == 2) {
|
|
|
|
for (int i = 0; i < l.size(); ++i) {
|
|
|
|
const ProString &val = l[i];
|
2017-08-14 10:06:35 +00:00
|
|
|
if (val == qry)
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnTrue;
|
2017-08-14 10:06:35 +00:00
|
|
|
if (!regx.isEmpty()) {
|
|
|
|
ProStringRoUser u2(val, m_tmp[m_toggle ^= 1]);
|
|
|
|
if (regx.exactMatch(u2.str()))
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-05-29 14:36:53 +00:00
|
|
|
const auto mutuals = args.at(2).toQStringRef().split(QLatin1Char('|'),
|
|
|
|
QString::SkipEmptyParts);
|
2012-09-05 16:29:19 +00:00
|
|
|
for (int i = l.size() - 1; i >= 0; i--) {
|
2017-08-14 10:06:35 +00:00
|
|
|
const ProString &val = l[i];
|
2012-09-05 16:29:19 +00:00
|
|
|
for (int mut = 0; mut < mutuals.count(); mut++) {
|
2017-04-07 15:07:12 +00:00
|
|
|
if (val.toQStringRef() == mutuals[mut].trimmed()) {
|
2017-08-14 10:06:35 +00:00
|
|
|
if (val == qry)
|
|
|
|
return ReturnTrue;
|
|
|
|
if (!regx.isEmpty()) {
|
|
|
|
ProStringRoUser u2(val, m_tmp[m_toggle ^= 1]);
|
|
|
|
if (regx.exactMatch(u2.str()))
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
case T_COUNT: {
|
|
|
|
int cnt = values(map(args.at(0))).count();
|
2016-05-13 11:44:42 +00:00
|
|
|
int val = args.at(1).toInt();
|
2012-09-05 16:29:19 +00:00
|
|
|
if (args.count() == 3) {
|
|
|
|
const ProString &comp = args.at(2);
|
|
|
|
if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
|
|
|
|
return returnBool(cnt > val);
|
|
|
|
} else if (comp == QLatin1String(">=")) {
|
|
|
|
return returnBool(cnt >= val);
|
|
|
|
} else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
|
|
|
|
return returnBool(cnt < val);
|
|
|
|
} else if (comp == QLatin1String("<=")) {
|
|
|
|
return returnBool(cnt <= val);
|
|
|
|
} else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
|
|
|
|
|| comp == QLatin1String("=") || comp == QLatin1String("==")) {
|
2014-04-02 18:38:57 +00:00
|
|
|
// fallthrough
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2017-04-07 15:10:37 +00:00
|
|
|
evalError(fL1S("Unexpected modifier to count(%2).").arg(comp.toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
}
|
2014-04-02 18:38:57 +00:00
|
|
|
return returnBool(cnt == val);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
case T_GREATERTHAN:
|
|
|
|
case T_LESSTHAN: {
|
2016-05-13 13:17:54 +00:00
|
|
|
const ProString &rhs = args.at(1);
|
|
|
|
const QString &lhs = values(map(args.at(0))).join(statics.field_sep);
|
2012-09-05 16:29:19 +00:00
|
|
|
bool ok;
|
|
|
|
int rhs_int = rhs.toInt(&ok);
|
|
|
|
if (ok) { // do integer compare
|
|
|
|
int lhs_int = lhs.toInt(&ok);
|
|
|
|
if (ok) {
|
|
|
|
if (func_t == T_GREATERTHAN)
|
|
|
|
return returnBool(lhs_int > rhs_int);
|
|
|
|
return returnBool(lhs_int < rhs_int);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (func_t == T_GREATERTHAN)
|
2016-05-13 13:17:54 +00:00
|
|
|
return returnBool(lhs > rhs.toQStringRef());
|
|
|
|
return returnBool(lhs < rhs.toQStringRef());
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
case T_EQUALS:
|
|
|
|
return returnBool(values(map(args.at(0))).join(statics.field_sep)
|
2017-04-07 15:10:37 +00:00
|
|
|
== args.at(1).toQStringView());
|
2017-01-07 09:23:47 +00:00
|
|
|
case T_VERSION_AT_LEAST:
|
|
|
|
case T_VERSION_AT_MOST: {
|
|
|
|
const QVersionNumber lvn = QVersionNumber::fromString(values(args.at(0).toKey()).join('.'));
|
2017-04-07 20:31:59 +00:00
|
|
|
const QVersionNumber rvn = QVersionNumber::fromString(args.at(1).toQStringView());
|
2017-01-07 09:23:47 +00:00
|
|
|
if (func_t == T_VERSION_AT_LEAST)
|
|
|
|
return returnBool(lvn >= rvn);
|
|
|
|
return returnBool(lvn <= rvn);
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case T_CLEAR: {
|
|
|
|
ProValueMap *hsh;
|
|
|
|
ProValueMap::Iterator it;
|
|
|
|
const ProKey &var = map(args.at(0));
|
|
|
|
if (!(hsh = findValues(var, &it)))
|
|
|
|
return ReturnFalse;
|
|
|
|
if (hsh == &m_valuemapStack.top())
|
|
|
|
it->clear();
|
|
|
|
else
|
|
|
|
m_valuemapStack.top()[var].clear();
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
case T_UNSET: {
|
|
|
|
ProValueMap *hsh;
|
|
|
|
ProValueMap::Iterator it;
|
|
|
|
const ProKey &var = map(args.at(0));
|
|
|
|
if (!(hsh = findValues(var, &it)))
|
|
|
|
return ReturnFalse;
|
|
|
|
if (m_valuemapStack.size() == 1)
|
|
|
|
hsh->erase(it);
|
|
|
|
else if (hsh == &m_valuemapStack.top())
|
|
|
|
*it = statics.fakeValue;
|
|
|
|
else
|
|
|
|
m_valuemapStack.top()[var] = statics.fakeValue;
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
2013-09-05 05:55:49 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
case T_PARSE_JSON: {
|
|
|
|
QByteArray json = values(args.at(0).toKey()).join(QLatin1Char(' ')).toUtf8();
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(1), m_tmp2);
|
|
|
|
QString parseInto = u1.str();
|
2013-09-05 05:55:49 +00:00
|
|
|
return parseJsonInto(json, parseInto, &m_valuemapStack.top());
|
|
|
|
}
|
|
|
|
#endif
|
2012-09-05 16:29:19 +00:00
|
|
|
case T_INCLUDE: {
|
|
|
|
QString parseInto;
|
|
|
|
LoadFlags flags = 0;
|
2013-08-08 16:01:22 +00:00
|
|
|
if (m_cumulative)
|
|
|
|
flags = LoadSilent;
|
2012-09-05 16:29:19 +00:00
|
|
|
if (args.count() >= 2) {
|
2016-08-22 15:25:02 +00:00
|
|
|
if (!args.at(1).isEmpty())
|
|
|
|
parseInto = args.at(1) + QLatin1Char('.');
|
2016-05-13 11:45:48 +00:00
|
|
|
if (args.count() >= 3 && isTrue(args.at(2)))
|
2012-09-05 16:29:19 +00:00
|
|
|
flags = LoadSilent;
|
|
|
|
}
|
2017-08-14 09:58:21 +00:00
|
|
|
QString fn = filePathEnvArg0(args);
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn ok;
|
2012-09-05 16:29:19 +00:00
|
|
|
if (parseInto.isEmpty()) {
|
|
|
|
ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags);
|
|
|
|
} else {
|
|
|
|
ProValueMap symbols;
|
2012-09-19 19:56:16 +00:00
|
|
|
if ((ok = evaluateFileInto(fn, &symbols, LoadAll | flags)) == ReturnTrue) {
|
2012-09-05 16:29:19 +00:00
|
|
|
ProValueMap newMap;
|
|
|
|
for (ProValueMap::ConstIterator
|
|
|
|
it = m_valuemapStack.top().constBegin(),
|
|
|
|
end = m_valuemapStack.top().constEnd();
|
|
|
|
it != end; ++it) {
|
2016-08-22 15:25:02 +00:00
|
|
|
const ProString &ky = it.key();
|
|
|
|
if (!ky.startsWith(parseInto))
|
2012-09-05 16:29:19 +00:00
|
|
|
newMap[it.key()] = it.value();
|
|
|
|
}
|
|
|
|
for (ProValueMap::ConstIterator it = symbols.constBegin();
|
|
|
|
it != symbols.constEnd(); ++it) {
|
2018-04-27 18:39:37 +00:00
|
|
|
if (!it.key().startsWith(QLatin1Char('.')))
|
|
|
|
newMap.insert(ProKey(parseInto + it.key()), it.value());
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
m_valuemapStack.top() = newMap;
|
|
|
|
}
|
|
|
|
}
|
2012-09-19 19:56:16 +00:00
|
|
|
if (ok == ReturnFalse && (flags & LoadSilent))
|
|
|
|
ok = ReturnTrue;
|
|
|
|
return ok;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
case T_LOAD: {
|
2017-08-11 17:22:58 +00:00
|
|
|
bool ignore_error = (args.count() == 2 && isTrue(args.at(1)));
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn ok = evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()),
|
|
|
|
ignore_error);
|
|
|
|
if (ok == ReturnFalse && ignore_error)
|
|
|
|
ok = ReturnTrue;
|
|
|
|
return ok;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
case T_DEBUG: {
|
|
|
|
#ifdef PROEVALUATOR_DEBUG
|
|
|
|
int level = args.at(0).toInt();
|
|
|
|
if (level <= m_debugLevel) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(1), m_tmp1);
|
|
|
|
const QString &msg = m_option->expandEnvVars(u1.str());
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(level, "Project DEBUG: %s", qPrintable(msg));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
case T_LOG:
|
|
|
|
case T_ERROR:
|
|
|
|
case T_WARNING:
|
|
|
|
case T_MESSAGE: {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
const QString &msg = m_option->expandEnvVars(u1.str());
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!m_skipLevel) {
|
|
|
|
if (func_t == T_LOG) {
|
|
|
|
#ifdef PROEVALUATOR_FULL
|
|
|
|
fputs(msg.toLatin1().constData(), stderr);
|
|
|
|
#endif
|
2016-07-14 14:48:25 +00:00
|
|
|
} else if (!msg.isEmpty() || func_t != T_ERROR) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u2(function, m_tmp2);
|
2013-09-02 14:43:49 +00:00
|
|
|
m_handler->fileMessage(
|
|
|
|
(func_t == T_ERROR ? QMakeHandler::ErrorMessage :
|
|
|
|
func_t == T_WARNING ? QMakeHandler::WarningMessage :
|
|
|
|
QMakeHandler::InfoMessage)
|
|
|
|
| (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0),
|
2017-08-14 10:06:35 +00:00
|
|
|
fL1S("Project %1: %2").arg(u2.str().toUpper(), msg));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (func_t == T_ERROR && !m_cumulative) ? ReturnError : ReturnTrue;
|
|
|
|
}
|
|
|
|
case T_SYSTEM: {
|
2013-05-16 13:21:20 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
|
|
|
if (m_cumulative) // Anything else would be insanity
|
|
|
|
return ReturnFalse;
|
2017-03-02 10:29:08 +00:00
|
|
|
#if QT_CONFIG(process)
|
2012-09-05 16:29:19 +00:00
|
|
|
QProcess proc;
|
|
|
|
proc.setProcessChannelMode(QProcess::ForwardedChannels);
|
2017-08-11 12:36:23 +00:00
|
|
|
runProcess(&proc, args.at(0).toQString());
|
2012-09-05 16:29:19 +00:00
|
|
|
return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
|
|
|
|
#else
|
2016-07-14 16:10:50 +00:00
|
|
|
int ec = system((QLatin1String("cd ")
|
|
|
|
+ IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory()))
|
|
|
|
+ QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData());
|
|
|
|
# ifdef Q_OS_UNIX
|
|
|
|
if (ec != -1 && WIFSIGNALED(ec) && (WTERMSIG(ec) == SIGQUIT || WTERMSIG(ec) == SIGINT))
|
|
|
|
raise(WTERMSIG(ec));
|
|
|
|
# endif
|
|
|
|
return returnBool(ec == 0);
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2013-05-16 13:21:20 +00:00
|
|
|
#else
|
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2013-05-16 13:21:20 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
case T_ISEMPTY: {
|
|
|
|
return returnBool(values(map(args.at(0))).isEmpty());
|
|
|
|
}
|
|
|
|
case T_EXISTS: {
|
2017-08-14 09:58:21 +00:00
|
|
|
QString file = filePathEnvArg0(args);
|
2013-05-29 18:18:51 +00:00
|
|
|
// Don't use VFS here:
|
|
|
|
// - it supports neither listing nor even directories
|
|
|
|
// - it's unlikely that somebody would test for files they created themselves
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
(cherry picked from qtcreator/29a93998df8405e8799ad23934a56cd99fb36403)
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
2013-01-08 01:32:53 +00:00
|
|
|
if (IoUtils::exists(file))
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnTrue;
|
|
|
|
int slsh = file.lastIndexOf(QLatin1Char('/'));
|
|
|
|
QString fn = file.mid(slsh+1);
|
|
|
|
if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
|
|
|
|
QString dirstr = file.left(slsh+1);
|
2017-08-11 12:36:23 +00:00
|
|
|
dirstr.detach();
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
case T_MKPATH: {
|
2013-05-16 13:21:20 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
2017-08-14 09:58:21 +00:00
|
|
|
QString fn = filePathArg0(args);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!QDir::current().mkpath(fn)) {
|
|
|
|
evalError(fL1S("Cannot create directory %1.").arg(QDir::toNativeSeparators(fn)));
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2013-05-16 13:21:20 +00:00
|
|
|
#endif
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
case T_WRITE_FILE: {
|
|
|
|
QIODevice::OpenMode mode = QIODevice::Truncate;
|
2016-10-24 17:30:24 +00:00
|
|
|
QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
|
2012-09-05 16:29:19 +00:00
|
|
|
QString contents;
|
|
|
|
if (args.count() >= 2) {
|
|
|
|
const ProStringList &vals = values(args.at(1).toKey());
|
|
|
|
if (!vals.isEmpty())
|
2014-02-05 18:54:01 +00:00
|
|
|
contents = vals.join(QLatin1Char('\n')) + QLatin1Char('\n');
|
2015-12-11 11:08:47 +00:00
|
|
|
if (args.count() >= 3) {
|
2016-05-13 13:40:00 +00:00
|
|
|
const auto opts = split_value_list(args.at(2).toQStringRef());
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &opt : opts) {
|
2017-08-11 10:16:57 +00:00
|
|
|
if (opt == QLatin1String("append")) {
|
2015-12-11 11:08:47 +00:00
|
|
|
mode = QIODevice::Append;
|
2017-08-11 10:16:57 +00:00
|
|
|
} else if (opt == QLatin1String("exe")) {
|
2016-10-24 17:30:24 +00:00
|
|
|
flags |= QMakeVfs::VfsExecutable;
|
2015-12-11 11:08:47 +00:00
|
|
|
} else {
|
2018-04-27 18:39:37 +00:00
|
|
|
evalError(fL1S("write_file(): invalid flag %1.").arg(opt.toQStringView()));
|
2015-12-11 11:08:47 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2017-08-14 09:58:21 +00:00
|
|
|
QString path = filePathArg0(args);
|
2016-10-24 17:30:24 +00:00
|
|
|
return writeFile(QString(), path, mode, flags, contents);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
case T_TOUCH: {
|
2013-05-16 13:21:20 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(args.at(0), m_tmp1);
|
|
|
|
ProStringRoUser u2(args.at(1), m_tmp2);
|
|
|
|
const QString &tfn = resolvePath(u1.str());
|
|
|
|
const QString &rfn = resolvePath(u2.str());
|
2017-02-24 15:22:53 +00:00
|
|
|
QString error;
|
|
|
|
if (!IoUtils::touchFile(tfn, rfn, &error)) {
|
|
|
|
evalError(error);
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return ReturnTrue;
|
|
|
|
}
|
|
|
|
case T_CACHE: {
|
|
|
|
bool persist = true;
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
enum { TargetStash, TargetCache, TargetSuper } target = TargetCache;
|
2012-09-05 16:29:19 +00:00
|
|
|
enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
|
|
|
|
ProKey srcvar;
|
|
|
|
if (args.count() >= 2) {
|
2016-05-13 13:40:00 +00:00
|
|
|
const auto opts = split_value_list(args.at(1).toQStringRef());
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &opt : opts) {
|
2017-08-11 10:16:57 +00:00
|
|
|
if (opt == QLatin1String("transient")) {
|
2012-09-05 16:29:19 +00:00
|
|
|
persist = false;
|
2017-08-11 10:16:57 +00:00
|
|
|
} else if (opt == QLatin1String("super")) {
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
target = TargetSuper;
|
2017-08-11 10:16:57 +00:00
|
|
|
} else if (opt == QLatin1String("stash")) {
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
target = TargetStash;
|
2017-08-11 10:16:57 +00:00
|
|
|
} else if (opt == QLatin1String("set")) {
|
2012-09-05 16:29:19 +00:00
|
|
|
mode = CacheSet;
|
2017-08-11 10:16:57 +00:00
|
|
|
} else if (opt == QLatin1String("add")) {
|
2012-09-05 16:29:19 +00:00
|
|
|
mode = CacheAdd;
|
2017-08-11 10:16:57 +00:00
|
|
|
} else if (opt == QLatin1String("sub")) {
|
2012-09-05 16:29:19 +00:00
|
|
|
mode = CacheSub;
|
|
|
|
} else {
|
2018-04-27 18:39:37 +00:00
|
|
|
evalError(fL1S("cache(): invalid flag %1.").arg(opt.toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (args.count() >= 3) {
|
|
|
|
srcvar = args.at(2).toKey();
|
|
|
|
} else if (mode != CacheSet) {
|
|
|
|
evalError(fL1S("cache(): modes other than 'set' require a source variable."));
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QString varstr;
|
|
|
|
ProKey dstvar = args.at(0).toKey();
|
|
|
|
if (!dstvar.isEmpty()) {
|
|
|
|
if (srcvar.isEmpty())
|
|
|
|
srcvar = dstvar;
|
|
|
|
ProValueMap::Iterator srcvarIt;
|
|
|
|
if (!findValues(srcvar, &srcvarIt)) {
|
2017-04-07 15:10:37 +00:00
|
|
|
evalError(fL1S("Variable %1 is not defined.").arg(srcvar.toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
// The caches for the host and target may differ (e.g., when we are manipulating
|
|
|
|
// CONFIG), so we cannot compute a common new value for both.
|
|
|
|
const ProStringList &diffval = *srcvarIt;
|
|
|
|
ProStringList newval;
|
|
|
|
bool changed = false;
|
|
|
|
for (bool hostBuild = false; ; hostBuild = true) {
|
2013-07-23 15:57:44 +00:00
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
m_option->mutex.lock();
|
|
|
|
#endif
|
|
|
|
QMakeBaseEnv *baseEnv =
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
m_option->baseEnvs.value(QMakeBaseKey(m_buildRoot, m_stashfile, hostBuild));
|
2013-07-23 15:57:44 +00:00
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
// It's ok to unlock this before locking baseEnv,
|
|
|
|
// as we have no intention to initialize the env.
|
|
|
|
m_option->mutex.unlock();
|
|
|
|
#endif
|
|
|
|
do {
|
|
|
|
if (!baseEnv)
|
|
|
|
break;
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
QMutexLocker locker(&baseEnv->mutex);
|
2013-08-12 19:12:36 +00:00
|
|
|
if (baseEnv->inProgress && baseEnv->evaluator != this) {
|
2013-07-23 15:57:44 +00:00
|
|
|
// The env is still in the works, but it may be already past the cache
|
|
|
|
// loading. So we need to wait for completion and amend it as usual.
|
|
|
|
QThreadPool::globalInstance()->releaseThread();
|
|
|
|
baseEnv->cond.wait(&baseEnv->mutex);
|
|
|
|
QThreadPool::globalInstance()->reserveThread();
|
|
|
|
}
|
|
|
|
if (!baseEnv->isOk)
|
|
|
|
break;
|
|
|
|
#endif
|
2012-09-05 16:29:19 +00:00
|
|
|
QMakeEvaluator *baseEval = baseEnv->evaluator;
|
|
|
|
const ProStringList &oldval = baseEval->values(dstvar);
|
|
|
|
if (mode == CacheSet) {
|
|
|
|
newval = diffval;
|
|
|
|
} else {
|
|
|
|
newval = oldval;
|
|
|
|
if (mode == CacheAdd)
|
|
|
|
newval += diffval;
|
|
|
|
else
|
2015-01-30 18:18:54 +00:00
|
|
|
newval.removeEach(diffval);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
if (oldval != newval) {
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
if (target != TargetStash || !m_stashfile.isEmpty()) {
|
|
|
|
baseEval->valuesRef(dstvar) = newval;
|
|
|
|
if (target == TargetSuper) {
|
|
|
|
do {
|
|
|
|
if (dstvar == QLatin1String("QMAKEPATH")) {
|
|
|
|
baseEval->m_qmakepath = newval.toQStringList();
|
|
|
|
baseEval->updateMkspecPaths();
|
|
|
|
} else if (dstvar == QLatin1String("QMAKEFEATURES")) {
|
|
|
|
baseEval->m_qmakefeatures = newval.toQStringList();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
baseEval->updateFeaturePaths();
|
|
|
|
if (hostBuild == m_hostBuild)
|
|
|
|
m_featureRoots = baseEval->m_featureRoots;
|
|
|
|
} while (false);
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
changed = true;
|
|
|
|
}
|
2013-07-23 15:57:44 +00:00
|
|
|
} while (false);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (hostBuild)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// We assume that whatever got the cached value to be what it is now will do so
|
|
|
|
// the next time as well, so we just skip the persisting if nothing changed.
|
|
|
|
if (!persist || !changed)
|
|
|
|
return ReturnTrue;
|
|
|
|
varstr = dstvar.toQString();
|
|
|
|
if (mode == CacheAdd)
|
|
|
|
varstr += QLatin1String(" +=");
|
|
|
|
else if (mode == CacheSub)
|
|
|
|
varstr += QLatin1String(" -=");
|
|
|
|
else
|
|
|
|
varstr += QLatin1String(" =");
|
|
|
|
if (diffval.count() == 1) {
|
|
|
|
varstr += QLatin1Char(' ');
|
|
|
|
varstr += quoteValue(diffval.at(0));
|
|
|
|
} else if (!diffval.isEmpty()) {
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &vval : diffval) {
|
2012-09-05 16:29:19 +00:00
|
|
|
varstr += QLatin1String(" \\\n ");
|
|
|
|
varstr += quoteValue(vval);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
varstr += QLatin1Char('\n');
|
|
|
|
}
|
|
|
|
QString fn;
|
2016-10-24 17:30:24 +00:00
|
|
|
QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
if (target == TargetSuper) {
|
2012-09-05 16:29:19 +00:00
|
|
|
if (m_superfile.isEmpty()) {
|
2013-11-07 19:41:20 +00:00
|
|
|
m_superfile = QDir::cleanPath(m_outputDir + QLatin1String("/.qmake.super"));
|
2014-03-26 20:20:00 +00:00
|
|
|
printf("Info: creating super cache file %s\n", qPrintable(QDir::toNativeSeparators(m_superfile)));
|
2012-09-05 16:29:19 +00:00
|
|
|
valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
|
|
|
|
}
|
|
|
|
fn = m_superfile;
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
} else if (target == TargetCache) {
|
2012-09-05 16:29:19 +00:00
|
|
|
if (m_cachefile.isEmpty()) {
|
2013-11-07 19:41:20 +00:00
|
|
|
m_cachefile = QDir::cleanPath(m_outputDir + QLatin1String("/.qmake.cache"));
|
2014-03-26 20:20:00 +00:00
|
|
|
printf("Info: creating cache file %s\n", qPrintable(QDir::toNativeSeparators(m_cachefile)));
|
2012-09-05 16:29:19 +00:00
|
|
|
valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
|
|
|
|
// We could update m_{source,build}Root and m_featureRoots here, or even
|
|
|
|
// "re-home" our rootEnv, but this doesn't sound too useful - if somebody
|
|
|
|
// wanted qmake to find something in the build directory, he could have
|
|
|
|
// done so "from the outside".
|
|
|
|
// The sub-projects will find the new cache all by themselves.
|
|
|
|
}
|
|
|
|
fn = m_cachefile;
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
} else {
|
|
|
|
fn = m_stashfile;
|
|
|
|
if (fn.isEmpty())
|
|
|
|
fn = QDir::cleanPath(m_outputDir + QLatin1String("/.qmake.stash"));
|
2016-10-24 17:30:24 +00:00
|
|
|
if (!m_vfs->exists(fn, flags)) {
|
2014-03-26 20:20:00 +00:00
|
|
|
printf("Info: creating stash file %s\n", qPrintable(QDir::toNativeSeparators(fn)));
|
support a cache that is really just a cache
unlike .qmake.cache & co., the presence of this file has no magic
effects on where mkspecs, modules and other things are searched.
as the obvious name "cache" is of course already taken, we call it
"stash".
the file is searched up to the super cache (if present), otherwise up to
the normal cache/conf (if present), otherwise up to the root.
if it's not found, it is created next to the super cache (if present),
otherwise next to the cache/conf (if present), otherwise in the current
output directory.
note that the cache really should be created and populated by the
top-level project if there are subprojects: otherwise, if there is an
"anchor" (super/cache/conf), subprojects would race for updating the
cache and make a mess. without an "anchor", each subproject would just
create its own cache, kind of defeating its purpose. this is no
different from the existing "cache", but it's worth mentioning that
removing the "anchoring" function does not remove the "nesting order"
constraint.
Task-number: QTBUG-31340
Change-Id: I786d40cef40d14582a0dd4a9407863001bec4c98
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
2013-11-07 19:40:00 +00:00
|
|
|
valuesRef(ProKey("_QMAKE_STASH_")) << ProString(fn);
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2016-10-24 17:30:24 +00:00
|
|
|
return writeFile(fL1S("cache "), fn, QIODevice::Append, flags, varstr);
|
2013-05-16 13:21:20 +00:00
|
|
|
}
|
2016-11-24 17:41:48 +00:00
|
|
|
case T_RELOAD_PROPERTIES:
|
|
|
|
#ifdef QT_BUILD_QMAKE
|
|
|
|
m_option->reloadProperties();
|
|
|
|
#endif
|
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
default:
|
2017-04-07 15:10:37 +00:00
|
|
|
evalError(fL1S("Function '%1' is not implemented.").arg(function.toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|