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"
|
2012-09-15 13:34:17 +00:00
|
|
|
#include "qmakeevaluator_p.h"
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
#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 <qdatetime.h>
|
|
|
|
#include <qdebug.h>
|
|
|
|
#include <qdir.h>
|
|
|
|
#include <qfile.h>
|
|
|
|
#include <qfileinfo.h>
|
|
|
|
#include <qlist.h>
|
|
|
|
#include <qregexp.h>
|
|
|
|
#include <qset.h>
|
|
|
|
#include <qstack.h>
|
|
|
|
#include <qstring.h>
|
|
|
|
#include <qstringlist.h>
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
# include <qthreadpool.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_UNIX
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/utsname.h>
|
2014-07-21 17:21:02 +00:00
|
|
|
# ifdef Q_OS_BSD4
|
|
|
|
# include <sys/sysctl.h>
|
|
|
|
# endif
|
2012-09-05 16:29:19 +00:00
|
|
|
#else
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
using namespace QMakeInternal;
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
#define fL1S(s) QString::fromLatin1(s)
|
|
|
|
|
2014-07-21 17:21:02 +00:00
|
|
|
// we can't use QThread in qmake
|
|
|
|
// this function is a merger of QThread::idealThreadCount from qthread_win.cpp and qthread_unix.cpp
|
|
|
|
static int idealThreadCount()
|
|
|
|
{
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
return QThread::idealThreadCount();
|
|
|
|
#elif defined(Q_OS_WIN)
|
|
|
|
SYSTEM_INFO sysinfo;
|
|
|
|
GetSystemInfo(&sysinfo);
|
|
|
|
return sysinfo.dwNumberOfProcessors;
|
|
|
|
#else
|
|
|
|
// there are a couple more definitions in the Unix QThread::idealThreadCount, but
|
|
|
|
// we don't need them all here
|
|
|
|
int cores = 1;
|
|
|
|
# if defined(Q_OS_BSD4)
|
2015-05-28 10:45:48 +00:00
|
|
|
// FreeBSD, OpenBSD, NetBSD, BSD/OS, OS X
|
2014-07-21 17:21:02 +00:00
|
|
|
size_t len = sizeof(cores);
|
|
|
|
int mib[2];
|
|
|
|
mib[0] = CTL_HW;
|
|
|
|
mib[1] = HW_NCPU;
|
|
|
|
if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) {
|
|
|
|
perror("sysctl");
|
|
|
|
}
|
|
|
|
# elif defined(_SC_NPROCESSORS_ONLN)
|
|
|
|
// the rest: Linux, Solaris, AIX, Tru64
|
|
|
|
cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
if (cores == -1)
|
|
|
|
return 1;
|
|
|
|
# endif
|
|
|
|
return cores;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
|
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
|
|
|
QMakeBaseKey::QMakeBaseKey(const QString &_root, const QString &_stash, bool _hostBuild)
|
|
|
|
: root(_root), stash(_stash), hostBuild(_hostBuild)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
uint qHash(const QMakeBaseKey &key)
|
|
|
|
{
|
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
|
|
|
return qHash(key.root) ^ qHash(key.stash) ^ (uint)key.hostBuild;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
|
|
|
|
{
|
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
|
|
|
return one.root == two.root && one.stash == two.stash && one.hostBuild == two.hostBuild;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QMakeBaseEnv::QMakeBaseEnv()
|
2018-08-02 22:22:24 +00:00
|
|
|
: evaluator(nullptr)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
inProgress = false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
QMakeBaseEnv::~QMakeBaseEnv()
|
|
|
|
{
|
|
|
|
delete evaluator;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace QMakeInternal {
|
|
|
|
QMakeStatics statics;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::initStatics()
|
|
|
|
{
|
|
|
|
if (!statics.field_sep.isNull())
|
|
|
|
return;
|
|
|
|
|
|
|
|
statics.field_sep = QLatin1String(" ");
|
|
|
|
statics.strtrue = QLatin1String("true");
|
|
|
|
statics.strfalse = QLatin1String("false");
|
|
|
|
statics.strCONFIG = ProKey("CONFIG");
|
|
|
|
statics.strARGS = ProKey("ARGS");
|
2013-09-19 15:21:39 +00:00
|
|
|
statics.strARGC = ProKey("ARGC");
|
2012-09-05 16:29:19 +00:00
|
|
|
statics.strDot = QLatin1String(".");
|
|
|
|
statics.strDotDot = QLatin1String("..");
|
|
|
|
statics.strever = QLatin1String("ever");
|
|
|
|
statics.strforever = QLatin1String("forever");
|
|
|
|
statics.strhost_build = QLatin1String("host_build");
|
|
|
|
statics.strTEMPLATE = ProKey("TEMPLATE");
|
2013-02-15 11:24:22 +00:00
|
|
|
statics.strQMAKE_PLATFORM = ProKey("QMAKE_PLATFORM");
|
2016-12-16 12:11:49 +00:00
|
|
|
statics.strQMAKE_DIR_SEP = ProKey("QMAKE_DIR_SEP");
|
2013-10-11 13:48:44 +00:00
|
|
|
statics.strQMAKESPEC = ProKey("QMAKESPEC");
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
|
|
|
statics.strREQUIRES = ProKey("REQUIRES");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
statics.fakeValue = ProStringList(ProString("_FAKE_")); // It has to have a unique begin() value
|
|
|
|
|
|
|
|
initFunctionStatics();
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char * const oldname, * const newname;
|
|
|
|
} mapInits[] = {
|
|
|
|
{ "INTERFACES", "FORMS" },
|
|
|
|
{ "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
|
|
|
|
{ "TARGETDEPS", "POST_TARGETDEPS" },
|
|
|
|
{ "LIBPATH", "QMAKE_LIBDIR" },
|
|
|
|
{ "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
|
|
|
|
{ "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
|
|
|
|
{ "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
|
|
|
|
{ "PRECOMPH", "PRECOMPILED_HEADER" },
|
|
|
|
{ "PRECOMPCPP", "PRECOMPILED_SOURCE" },
|
|
|
|
{ "INCPATH", "INCLUDEPATH" },
|
|
|
|
{ "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
|
|
|
|
{ "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
|
|
|
|
{ "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
|
|
|
|
{ "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
|
|
|
|
{ "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
|
|
|
|
{ "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
|
|
|
|
{ "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
|
|
|
|
{ "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
|
|
|
|
{ "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" },
|
2015-05-26 15:32:47 +00:00
|
|
|
{ "IN_PWD", "PWD" },
|
|
|
|
{ "DEPLOYMENT", "INSTALLS" }
|
2012-09-05 16:29:19 +00:00
|
|
|
};
|
2016-02-01 08:49:22 +00:00
|
|
|
statics.varMap.reserve((int)(sizeof(mapInits)/sizeof(mapInits[0])));
|
2012-09-05 16:29:19 +00:00
|
|
|
for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
|
|
|
|
statics.varMap.insert(ProKey(mapInits[i].oldname), ProKey(mapInits[i].newname));
|
|
|
|
}
|
|
|
|
|
|
|
|
const ProKey &QMakeEvaluator::map(const ProKey &var)
|
|
|
|
{
|
|
|
|
QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(var);
|
|
|
|
if (it == statics.varMap.constEnd())
|
|
|
|
return var;
|
|
|
|
deprecationWarning(fL1S("Variable %1 is deprecated; use %2 instead.")
|
|
|
|
.arg(var.toQString(), it.value().toQString()));
|
|
|
|
return it.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-29 18:18:51 +00:00
|
|
|
QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
|
|
|
|
QMakeHandler *handler)
|
2012-09-05 16:29:19 +00:00
|
|
|
:
|
|
|
|
#ifdef PROEVALUATOR_DEBUG
|
|
|
|
m_debugLevel(option->debugLevel),
|
|
|
|
#endif
|
2013-05-29 18:18:51 +00:00
|
|
|
m_option(option), m_parser(parser), m_handler(handler), m_vfs(vfs)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
// So that single-threaded apps don't have to call initialize() for now.
|
|
|
|
initStatics();
|
|
|
|
|
|
|
|
// Configuration, more or less
|
2018-08-02 22:22:24 +00:00
|
|
|
m_caller = nullptr;
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef PROEVALUATOR_CUMULATIVE
|
|
|
|
m_cumulative = false;
|
|
|
|
#endif
|
|
|
|
m_hostBuild = false;
|
|
|
|
|
|
|
|
// Evaluator state
|
|
|
|
#ifdef PROEVALUATOR_CUMULATIVE
|
|
|
|
m_skipLevel = 0;
|
|
|
|
#endif
|
|
|
|
m_listCount = 0;
|
2017-08-11 12:55:37 +00:00
|
|
|
m_toggle = 0;
|
2012-09-05 16:29:19 +00:00
|
|
|
m_valuemapStack.push(ProValueMap());
|
|
|
|
m_valuemapInited = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::~QMakeEvaluator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-06-16 12:31:30 +00:00
|
|
|
void QMakeEvaluator::initFrom(const QMakeEvaluator *other)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2014-06-16 12:31:30 +00:00
|
|
|
Q_ASSERT_X(other, "QMakeEvaluator::visitProFile", "Project not prepared");
|
|
|
|
m_functionDefs = other->m_functionDefs;
|
|
|
|
m_valuemapStack = other->m_valuemapStack;
|
2012-09-05 16:29:19 +00:00
|
|
|
m_valuemapInited = true;
|
2014-06-16 12:31:30 +00:00
|
|
|
m_qmakespec = other->m_qmakespec;
|
|
|
|
m_qmakespecName = other->m_qmakespecName;
|
|
|
|
m_mkspecPaths = other->m_mkspecPaths;
|
|
|
|
m_featureRoots = other->m_featureRoots;
|
|
|
|
m_dirSep = other->m_dirSep;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//////// Evaluator tools /////////
|
|
|
|
|
|
|
|
uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr)
|
|
|
|
{
|
|
|
|
uint len = *tokPtr++;
|
|
|
|
len |= (uint)*tokPtr++ << 16;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::skipStr(const ushort *&tokPtr)
|
|
|
|
{
|
|
|
|
uint len = *tokPtr++;
|
|
|
|
tokPtr += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::skipHashStr(const ushort *&tokPtr)
|
|
|
|
{
|
|
|
|
tokPtr += 2;
|
|
|
|
uint len = *tokPtr++;
|
|
|
|
tokPtr += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: this should not build new strings for direct sections.
|
|
|
|
// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
|
2017-08-14 16:30:29 +00:00
|
|
|
ProStringList QMakeEvaluator::split_value_list(const QStringRef &vals, int source)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
QString build;
|
|
|
|
ProStringList ret;
|
|
|
|
|
|
|
|
if (!source)
|
2017-08-14 16:30:29 +00:00
|
|
|
source = currentFileId();
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
const QChar *vals_data = vals.data();
|
|
|
|
const int vals_len = vals.length();
|
2013-03-07 20:47:42 +00:00
|
|
|
ushort quote = 0;
|
|
|
|
bool hadWord = false;
|
2012-09-05 16:29:19 +00:00
|
|
|
for (int x = 0; x < vals_len; x++) {
|
2013-03-07 20:47:42 +00:00
|
|
|
ushort unicode = vals_data[x].unicode();
|
|
|
|
if (unicode == quote) {
|
|
|
|
quote = 0;
|
2013-07-03 11:54:17 +00:00
|
|
|
hadWord = true;
|
|
|
|
build += QChar(unicode);
|
2013-03-07 20:47:42 +00:00
|
|
|
continue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-03-07 20:47:42 +00:00
|
|
|
switch (unicode) {
|
|
|
|
case '"':
|
|
|
|
case '\'':
|
2015-04-28 19:25:50 +00:00
|
|
|
if (!quote)
|
|
|
|
quote = unicode;
|
2018-05-29 14:36:53 +00:00
|
|
|
// FIXME: this is inconsistent with the "there are no empty strings" dogma.
|
2013-03-07 20:47:42 +00:00
|
|
|
hadWord = true;
|
2013-07-03 11:54:17 +00:00
|
|
|
break;
|
2013-03-07 20:47:42 +00:00
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
if (!quote) {
|
|
|
|
if (hadWord) {
|
|
|
|
ret << ProString(build).setSource(source);
|
|
|
|
build.clear();
|
|
|
|
hadWord = false;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\\':
|
2013-03-12 20:56:56 +00:00
|
|
|
if (x + 1 != vals_len) {
|
|
|
|
ushort next = vals_data[++x].unicode();
|
2013-07-03 11:54:17 +00:00
|
|
|
if (next == '\'' || next == '"' || next == '\\') {
|
|
|
|
build += QChar(unicode);
|
2013-03-12 20:56:56 +00:00
|
|
|
unicode = next;
|
2013-07-03 11:54:17 +00:00
|
|
|
} else {
|
2013-03-12 20:56:56 +00:00
|
|
|
--x;
|
2013-07-03 11:54:17 +00:00
|
|
|
}
|
2013-03-12 20:56:56 +00:00
|
|
|
}
|
2016-06-27 13:43:33 +00:00
|
|
|
Q_FALLTHROUGH();
|
2013-03-07 20:47:42 +00:00
|
|
|
default:
|
|
|
|
hadWord = true;
|
|
|
|
break;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-07-03 11:54:17 +00:00
|
|
|
build += QChar(unicode);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-03-07 20:47:42 +00:00
|
|
|
if (hadWord)
|
2012-09-05 16:29:19 +00:00
|
|
|
ret << ProString(build).setSource(source);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void replaceInList(ProStringList *varlist,
|
|
|
|
const QRegExp ®exp, const QString &replace, bool global, QString &tmp)
|
|
|
|
{
|
|
|
|
for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(*varit, tmp);
|
|
|
|
QString val = u1.str();
|
2012-09-05 16:29:19 +00:00
|
|
|
QString copy = val; // Force detach and have a reference value
|
|
|
|
val.replace(regexp, replace);
|
|
|
|
if (!val.isSharedWith(copy) && val != copy) {
|
|
|
|
if (val.isEmpty()) {
|
|
|
|
varit = varlist->erase(varit);
|
|
|
|
} else {
|
|
|
|
(*varit).setValue(val);
|
|
|
|
++varit;
|
|
|
|
}
|
|
|
|
if (!global)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
++varit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////// Evaluator /////////
|
|
|
|
|
|
|
|
static ALWAYS_INLINE void addStr(
|
|
|
|
const ProString &str, ProStringList *ret, bool &pending, bool joined)
|
|
|
|
{
|
|
|
|
if (joined) {
|
|
|
|
ret->last().append(str, &pending);
|
|
|
|
} else {
|
|
|
|
if (!pending) {
|
|
|
|
pending = true;
|
|
|
|
*ret << str;
|
|
|
|
} else {
|
|
|
|
ret->last().append(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ALWAYS_INLINE void addStrList(
|
|
|
|
const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
|
|
|
|
{
|
|
|
|
if (!list.isEmpty()) {
|
|
|
|
if (joined) {
|
|
|
|
ret->last().append(list, &pending, !(tok & TokQuoted));
|
|
|
|
} else {
|
|
|
|
if (tok & TokQuoted) {
|
|
|
|
if (!pending) {
|
|
|
|
pending = true;
|
|
|
|
*ret << ProString();
|
|
|
|
}
|
|
|
|
ret->last().append(list);
|
|
|
|
} else {
|
|
|
|
if (!pending) {
|
|
|
|
// Another qmake bizzarity: if nothing is pending and the
|
|
|
|
// first element is empty, it will be eaten
|
|
|
|
if (!list.at(0).isEmpty()) {
|
|
|
|
// The common case
|
|
|
|
pending = true;
|
|
|
|
*ret += list;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret->last().append(list.at(0));
|
|
|
|
}
|
|
|
|
// This is somewhat slow, but a corner case
|
|
|
|
for (int j = 1; j < list.size(); ++j) {
|
|
|
|
pending = true;
|
|
|
|
*ret << list.at(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpression(
|
2012-09-05 16:29:19 +00:00
|
|
|
const ushort *&tokPtr, ProStringList *ret, bool joined)
|
|
|
|
{
|
|
|
|
debugMsg(2, joined ? "evaluating joined expression" : "evaluating expression");
|
2015-03-04 20:38:31 +00:00
|
|
|
ProFile *pro = m_current.pro;
|
2012-09-05 16:29:19 +00:00
|
|
|
if (joined)
|
|
|
|
*ret << ProString();
|
|
|
|
bool pending = false;
|
|
|
|
forever {
|
|
|
|
ushort tok = *tokPtr++;
|
|
|
|
if (tok & TokNewStr) {
|
|
|
|
debugMsg(2, "new string");
|
|
|
|
pending = false;
|
|
|
|
}
|
|
|
|
ushort maskedTok = tok & TokMask;
|
|
|
|
switch (maskedTok) {
|
|
|
|
case TokLine:
|
|
|
|
m_current.line = *tokPtr++;
|
|
|
|
break;
|
|
|
|
case TokLiteral: {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProString &val = pro->getStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(2, "literal %s", dbgStr(val));
|
|
|
|
addStr(val, ret, pending, joined);
|
|
|
|
break; }
|
|
|
|
case TokHashLiteral: {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProKey &val = pro->getHashStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(2, "hashed literal %s", dbgStr(val.toString()));
|
|
|
|
addStr(val, ret, pending, joined);
|
|
|
|
break; }
|
|
|
|
case TokVariable: {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProKey &var = pro->getHashStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
const ProStringList &val = values(map(var));
|
|
|
|
debugMsg(2, "variable %s => %s", dbgKey(var), dbgStrList(val));
|
|
|
|
addStrList(val, tok, ret, pending, joined);
|
|
|
|
break; }
|
|
|
|
case TokProperty: {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProKey &var = pro->getHashStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
const ProString &val = propertyValue(var);
|
|
|
|
debugMsg(2, "property %s => %s", dbgKey(var), dbgStr(val));
|
|
|
|
addStr(val, ret, pending, joined);
|
|
|
|
break; }
|
|
|
|
case TokEnvVar: {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProString &var = pro->getStr(tokPtr);
|
2013-09-06 13:14:38 +00:00
|
|
|
const ProString &val = ProString(m_option->getEnv(var.toQString()));
|
2013-02-13 11:37:04 +00:00
|
|
|
debugMsg(2, "env var %s => %s", dbgStr(var), dbgStr(val));
|
|
|
|
addStr(val, ret, pending, joined);
|
2012-09-05 16:29:19 +00:00
|
|
|
break; }
|
|
|
|
case TokFuncName: {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProKey &func = pro->getHashStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(2, "function %s", dbgKey(func));
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList val;
|
|
|
|
if (evaluateExpandFunction(func, tokPtr, &val) == ReturnError)
|
|
|
|
return ReturnError;
|
|
|
|
addStrList(val, tok, ret, pending, joined);
|
2012-09-05 16:29:19 +00:00
|
|
|
break; }
|
|
|
|
default:
|
|
|
|
debugMsg(2, "evaluated expression => %s", dbgStrList(*ret));
|
|
|
|
tokPtr--;
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::skipExpression(const ushort *&pTokPtr)
|
|
|
|
{
|
|
|
|
const ushort *tokPtr = pTokPtr;
|
|
|
|
forever {
|
|
|
|
ushort tok = *tokPtr++;
|
|
|
|
switch (tok) {
|
|
|
|
case TokLine:
|
|
|
|
m_current.line = *tokPtr++;
|
|
|
|
break;
|
|
|
|
case TokValueTerminator:
|
|
|
|
case TokFuncTerminator:
|
|
|
|
pTokPtr = tokPtr;
|
|
|
|
return;
|
|
|
|
case TokArgSeparator:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
switch (tok & TokMask) {
|
|
|
|
case TokLiteral:
|
|
|
|
case TokEnvVar:
|
|
|
|
skipStr(tokPtr);
|
|
|
|
break;
|
|
|
|
case TokHashLiteral:
|
|
|
|
case TokVariable:
|
|
|
|
case TokProperty:
|
|
|
|
skipHashStr(tokPtr);
|
|
|
|
break;
|
|
|
|
case TokFuncName:
|
|
|
|
skipHashStr(tokPtr);
|
|
|
|
pTokPtr = tokPtr;
|
|
|
|
skipExpression(pTokPtr);
|
|
|
|
tokPtr = pTokPtr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
|
|
|
|
ProFile *pro, const ushort *tokPtr)
|
|
|
|
{
|
|
|
|
m_current.pro = pro;
|
|
|
|
m_current.line = 0;
|
|
|
|
return visitProBlock(tokPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
|
|
|
|
const ushort *tokPtr)
|
|
|
|
{
|
|
|
|
traceMsg("entering block");
|
|
|
|
ProStringList curr;
|
2015-03-04 20:38:31 +00:00
|
|
|
ProFile *pro = m_current.pro;
|
2012-09-05 16:29:19 +00:00
|
|
|
bool okey = true, or_op = false, invert = false;
|
|
|
|
uint blockLen;
|
|
|
|
while (ushort tok = *tokPtr++) {
|
|
|
|
VisitReturn ret;
|
|
|
|
switch (tok) {
|
|
|
|
case TokLine:
|
|
|
|
m_current.line = *tokPtr++;
|
|
|
|
continue;
|
|
|
|
case TokAssign:
|
|
|
|
case TokAppend:
|
|
|
|
case TokAppendUnique:
|
|
|
|
case TokRemove:
|
|
|
|
case TokReplace:
|
2016-06-29 14:56:57 +00:00
|
|
|
ret = visitProVariable(tok, curr, tokPtr);
|
|
|
|
if (ret == ReturnError)
|
|
|
|
break;
|
2012-09-05 16:29:19 +00:00
|
|
|
curr.clear();
|
|
|
|
continue;
|
|
|
|
case TokBranch:
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
if (m_cumulative) {
|
|
|
|
#ifdef PROEVALUATOR_CUMULATIVE
|
|
|
|
if (!okey)
|
|
|
|
m_skipLevel++;
|
|
|
|
ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
|
|
|
|
tokPtr += blockLen;
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
if (!okey)
|
|
|
|
m_skipLevel--;
|
|
|
|
else
|
|
|
|
m_skipLevel++;
|
|
|
|
if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
|
|
|
|
ret = visitProBlock(tokPtr);
|
|
|
|
if (okey)
|
|
|
|
m_skipLevel--;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
if (okey) {
|
|
|
|
traceMsg("taking 'then' branch");
|
|
|
|
ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
|
|
|
|
traceMsg("finished 'then' branch");
|
|
|
|
}
|
|
|
|
tokPtr += blockLen;
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
if (!okey) {
|
|
|
|
traceMsg("taking 'else' branch");
|
|
|
|
ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
|
|
|
|
traceMsg("finished 'else' branch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tokPtr += blockLen;
|
|
|
|
okey = true, or_op = false; // force next evaluation
|
|
|
|
break;
|
|
|
|
case TokForLoop:
|
2013-08-09 10:17:11 +00:00
|
|
|
if (m_cumulative || okey != or_op) {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProKey &variable = pro->getHashStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
uint exprLen = getBlockLen(tokPtr);
|
|
|
|
const ushort *exprPtr = tokPtr;
|
|
|
|
tokPtr += exprLen;
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
ret = visitProLoop(variable, exprPtr, tokPtr);
|
|
|
|
} else {
|
|
|
|
skipHashStr(tokPtr);
|
|
|
|
uint exprLen = getBlockLen(tokPtr);
|
|
|
|
tokPtr += exprLen;
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
traceMsg("skipped loop");
|
|
|
|
ret = ReturnTrue;
|
|
|
|
}
|
|
|
|
tokPtr += blockLen;
|
|
|
|
okey = true, or_op = false; // force next evaluation
|
|
|
|
break;
|
2016-11-11 10:40:24 +00:00
|
|
|
case TokBypassNesting:
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
if ((m_cumulative || okey != or_op) && blockLen) {
|
|
|
|
ProValueMapStack savedValuemapStack = m_valuemapStack;
|
|
|
|
m_valuemapStack.clear();
|
|
|
|
m_valuemapStack.append(savedValuemapStack.takeFirst());
|
|
|
|
traceMsg("visiting nesting-bypassing block");
|
|
|
|
ret = visitProBlock(tokPtr);
|
|
|
|
traceMsg("visited nesting-bypassing block");
|
|
|
|
savedValuemapStack.prepend(m_valuemapStack.first());
|
|
|
|
m_valuemapStack = savedValuemapStack;
|
|
|
|
} else {
|
|
|
|
traceMsg("skipped nesting-bypassing block");
|
|
|
|
ret = ReturnTrue;
|
|
|
|
}
|
|
|
|
tokPtr += blockLen;
|
|
|
|
okey = true, or_op = false; // force next evaluation
|
|
|
|
break;
|
2012-09-05 16:29:19 +00:00
|
|
|
case TokTestDef:
|
|
|
|
case TokReplaceDef:
|
|
|
|
if (m_cumulative || okey != or_op) {
|
2015-03-04 20:38:31 +00:00
|
|
|
const ProKey &name = pro->getHashStr(tokPtr);
|
2012-09-05 16:29:19 +00:00
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
visitProFunctionDef(tok, name, tokPtr);
|
|
|
|
traceMsg("defined %s function %s",
|
|
|
|
tok == TokTestDef ? "test" : "replace", dbgKey(name));
|
|
|
|
} else {
|
|
|
|
traceMsg("skipped function definition");
|
|
|
|
skipHashStr(tokPtr);
|
|
|
|
blockLen = getBlockLen(tokPtr);
|
|
|
|
}
|
|
|
|
tokPtr += blockLen;
|
|
|
|
okey = true, or_op = false; // force next evaluation
|
|
|
|
continue;
|
|
|
|
case TokNot:
|
|
|
|
traceMsg("NOT");
|
|
|
|
invert ^= true;
|
|
|
|
continue;
|
|
|
|
case TokAnd:
|
|
|
|
traceMsg("AND");
|
|
|
|
or_op = false;
|
|
|
|
continue;
|
|
|
|
case TokOr:
|
|
|
|
traceMsg("OR");
|
|
|
|
or_op = true;
|
|
|
|
continue;
|
|
|
|
case TokCondition:
|
|
|
|
if (!m_skipLevel && okey != or_op) {
|
|
|
|
if (curr.size() != 1) {
|
|
|
|
if (!m_cumulative || !curr.isEmpty())
|
|
|
|
evalError(fL1S("Conditional must expand to exactly one word."));
|
|
|
|
okey = false;
|
|
|
|
} else {
|
2016-05-13 13:26:42 +00:00
|
|
|
okey = isActiveConfig(curr.at(0).toQStringRef(), true);
|
2012-09-05 16:29:19 +00:00
|
|
|
traceMsg("condition %s is %s", dbgStr(curr.at(0)), dbgBool(okey));
|
|
|
|
okey ^= invert;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
traceMsg("skipped condition %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>");
|
|
|
|
}
|
|
|
|
or_op = !okey; // tentatively force next evaluation
|
|
|
|
invert = false;
|
|
|
|
curr.clear();
|
|
|
|
continue;
|
|
|
|
case TokTestCall:
|
|
|
|
if (!m_skipLevel && okey != or_op) {
|
|
|
|
if (curr.size() != 1) {
|
|
|
|
if (!m_cumulative || !curr.isEmpty())
|
|
|
|
evalError(fL1S("Test name must expand to exactly one word."));
|
|
|
|
skipExpression(tokPtr);
|
|
|
|
okey = false;
|
|
|
|
} else {
|
|
|
|
traceMsg("evaluating test function %s", dbgStr(curr.at(0)));
|
|
|
|
ret = evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
|
|
|
|
switch (ret) {
|
|
|
|
case ReturnTrue: okey = true; break;
|
|
|
|
case ReturnFalse: okey = false; break;
|
|
|
|
default:
|
|
|
|
traceMsg("aborting block, function status: %s", dbgReturn(ret));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
traceMsg("test function returned %s", dbgBool(okey));
|
|
|
|
okey ^= invert;
|
|
|
|
}
|
|
|
|
} else if (m_cumulative) {
|
|
|
|
#ifdef PROEVALUATOR_CUMULATIVE
|
|
|
|
m_skipLevel++;
|
|
|
|
if (curr.size() != 1)
|
|
|
|
skipExpression(tokPtr);
|
|
|
|
else
|
|
|
|
evaluateConditionalFunction(curr.at(0).toKey(), tokPtr);
|
|
|
|
m_skipLevel--;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
skipExpression(tokPtr);
|
|
|
|
traceMsg("skipped test function %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>");
|
|
|
|
}
|
|
|
|
or_op = !okey; // tentatively force next evaluation
|
|
|
|
invert = false;
|
|
|
|
curr.clear();
|
|
|
|
continue;
|
2012-09-11 17:30:29 +00:00
|
|
|
case TokReturn:
|
|
|
|
m_returnValue = curr;
|
|
|
|
curr.clear();
|
|
|
|
ret = ReturnReturn;
|
|
|
|
goto ctrlstm;
|
|
|
|
case TokBreak:
|
|
|
|
ret = ReturnBreak;
|
|
|
|
goto ctrlstm;
|
|
|
|
case TokNext:
|
|
|
|
ret = ReturnNext;
|
|
|
|
ctrlstm:
|
|
|
|
if (!m_skipLevel && okey != or_op) {
|
|
|
|
traceMsg("flow control statement '%s', aborting block", dbgReturn(ret));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
traceMsg("skipped flow control statement '%s'", dbgReturn(ret));
|
|
|
|
okey = false, or_op = true; // force next evaluation
|
|
|
|
continue;
|
2012-09-05 16:29:19 +00:00
|
|
|
default: {
|
|
|
|
const ushort *oTokPtr = --tokPtr;
|
2016-06-29 14:56:57 +00:00
|
|
|
ret = evaluateExpression(tokPtr, &curr, false);
|
|
|
|
if (ret == ReturnError || tokPtr != oTokPtr)
|
|
|
|
break;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ret != ReturnTrue && ret != ReturnFalse) {
|
|
|
|
traceMsg("aborting block, status: %s", dbgReturn(ret));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
traceMsg("leaving block, okey=%s", dbgBool(okey));
|
|
|
|
return returnBool(okey);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QMakeEvaluator::visitProFunctionDef(
|
|
|
|
ushort tok, const ProKey &name, const ushort *tokPtr)
|
|
|
|
{
|
|
|
|
QHash<ProKey, ProFunctionDef> *hash =
|
|
|
|
(tok == TokTestDef
|
|
|
|
? &m_functionDefs.testFunctions
|
|
|
|
: &m_functionDefs.replaceFunctions);
|
|
|
|
hash->insert(name, ProFunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
|
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
|
|
|
|
const ProKey &_variable, const ushort *exprPtr, const ushort *tokPtr)
|
|
|
|
{
|
|
|
|
VisitReturn ret = ReturnTrue;
|
|
|
|
bool infinite = false;
|
|
|
|
int index = 0;
|
|
|
|
ProKey variable;
|
|
|
|
ProStringList oldVarVal;
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList it_list_out;
|
|
|
|
if (expandVariableReferences(exprPtr, 0, &it_list_out, true) == ReturnError)
|
|
|
|
return ReturnError;
|
|
|
|
ProString it_list = it_list_out.at(0);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (_variable.isEmpty()) {
|
|
|
|
if (it_list != statics.strever) {
|
|
|
|
evalError(fL1S("Invalid loop expression."));
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
|
|
|
it_list = ProString(statics.strforever);
|
|
|
|
} else {
|
|
|
|
variable = map(_variable);
|
|
|
|
oldVarVal = values(variable);
|
|
|
|
}
|
|
|
|
ProStringList list = values(it_list.toKey());
|
|
|
|
if (list.isEmpty()) {
|
|
|
|
if (it_list == statics.strforever) {
|
2013-08-09 10:17:11 +00:00
|
|
|
if (m_cumulative) {
|
|
|
|
// The termination conditions wouldn't be evaluated, so we must skip it.
|
|
|
|
traceMsg("skipping forever loop in cumulative mode");
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
infinite = true;
|
|
|
|
} else {
|
2017-08-10 18:53:15 +00:00
|
|
|
const QStringRef &itl = it_list.toQStringRef();
|
2012-09-05 16:29:19 +00:00
|
|
|
int dotdot = itl.indexOf(statics.strDotDot);
|
|
|
|
if (dotdot != -1) {
|
|
|
|
bool ok;
|
|
|
|
int start = itl.left(dotdot).toInt(&ok);
|
|
|
|
if (ok) {
|
|
|
|
int end = itl.mid(dotdot+2).toInt(&ok);
|
|
|
|
if (ok) {
|
2016-02-01 08:49:22 +00:00
|
|
|
const int absDiff = qAbs(end - start);
|
|
|
|
if (m_cumulative && absDiff > 100) {
|
2013-08-09 10:17:11 +00:00
|
|
|
// Such a loop is unlikely to contribute something useful to the
|
|
|
|
// file collection, and may cause considerable delay.
|
|
|
|
traceMsg("skipping excessive loop in cumulative mode");
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2016-02-01 08:49:22 +00:00
|
|
|
list.reserve(absDiff + 1);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (start < end) {
|
|
|
|
for (int i = start; i <= end; i++)
|
|
|
|
list << ProString(QString::number(i));
|
|
|
|
} else {
|
|
|
|
for (int i = start; i >= end; i--)
|
|
|
|
list << ProString(QString::number(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (infinite)
|
|
|
|
traceMsg("entering infinite loop for %s", dbgKey(variable));
|
|
|
|
else
|
|
|
|
traceMsg("entering loop for %s over %s", dbgKey(variable), dbgStrList(list));
|
|
|
|
|
|
|
|
forever {
|
|
|
|
if (infinite) {
|
|
|
|
if (!variable.isEmpty())
|
2015-05-11 12:01:49 +00:00
|
|
|
m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index)));
|
|
|
|
if (++index > 1000) {
|
2012-09-05 16:29:19 +00:00
|
|
|
evalError(fL1S("Ran into infinite loop (> 1000 iterations)."));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
traceMsg("loop iteration %d", index);
|
|
|
|
} else {
|
|
|
|
ProString val;
|
|
|
|
do {
|
|
|
|
if (index >= list.count())
|
|
|
|
goto do_break;
|
|
|
|
val = list.at(index++);
|
|
|
|
} while (val.isEmpty()); // stupid, but qmake is like that
|
|
|
|
traceMsg("loop iteration %s", dbgStr(val));
|
|
|
|
m_valuemapStack.top()[variable] = ProStringList(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = visitProBlock(tokPtr);
|
|
|
|
switch (ret) {
|
|
|
|
case ReturnTrue:
|
|
|
|
case ReturnFalse:
|
|
|
|
break;
|
|
|
|
case ReturnNext:
|
|
|
|
ret = ReturnTrue;
|
|
|
|
break;
|
|
|
|
case ReturnBreak:
|
|
|
|
ret = ReturnTrue;
|
|
|
|
goto do_break;
|
|
|
|
default:
|
|
|
|
goto do_break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
do_break:
|
|
|
|
|
|
|
|
traceMsg("done looping");
|
|
|
|
|
|
|
|
if (!variable.isEmpty())
|
|
|
|
m_valuemapStack.top()[variable] = oldVarVal;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::visitProVariable(
|
2012-09-05 16:29:19 +00:00
|
|
|
ushort tok, const ProStringList &curr, const ushort *&tokPtr)
|
|
|
|
{
|
|
|
|
int sizeHint = *tokPtr++;
|
|
|
|
|
|
|
|
if (curr.size() != 1) {
|
|
|
|
skipExpression(tokPtr);
|
|
|
|
if (!m_cumulative || !curr.isEmpty())
|
|
|
|
evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
const ProKey &varName = map(curr.first());
|
|
|
|
|
|
|
|
if (tok == TokReplace) { // ~=
|
|
|
|
// DEFINES ~= s/a/b/?[gqi]
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList varVal;
|
|
|
|
if (expandVariableReferences(tokPtr, sizeHint, &varVal, true) == ReturnError)
|
|
|
|
return ReturnError;
|
2017-08-10 18:53:15 +00:00
|
|
|
const QStringRef &val = varVal.at(0).toQStringRef();
|
2012-09-05 16:29:19 +00:00
|
|
|
if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
|
|
|
|
evalError(fL1S("The ~= operator can handle only the s/// function."));
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
QChar sep = val.at(1);
|
2018-05-29 14:36:53 +00:00
|
|
|
auto func = val.split(sep, QString::KeepEmptyParts);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (func.count() < 3 || func.count() > 4) {
|
|
|
|
evalError(fL1S("The s/// function expects 3 or 4 arguments."));
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool global = false, quote = false, case_sense = false;
|
|
|
|
if (func.count() == 4) {
|
|
|
|
global = func[3].indexOf(QLatin1Char('g')) != -1;
|
|
|
|
case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
|
|
|
|
quote = func[3].indexOf(QLatin1Char('q')) != -1;
|
|
|
|
}
|
2017-08-10 18:53:15 +00:00
|
|
|
QString pattern = func[1].toString();
|
|
|
|
QString replace = func[2].toString();
|
2012-09-05 16:29:19 +00:00
|
|
|
if (quote)
|
|
|
|
pattern = QRegExp::escape(pattern);
|
|
|
|
|
|
|
|
QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
|
|
|
|
|
|
|
// We could make a union of modified and unmodified values,
|
|
|
|
// but this will break just as much as it fixes, so leave it as is.
|
|
|
|
replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
|
|
|
|
debugMsg(2, "replaced %s with %s", dbgQStr(pattern), dbgQStr(replace));
|
|
|
|
} else {
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList varVal;
|
|
|
|
if (expandVariableReferences(tokPtr, sizeHint, &varVal, false) == ReturnError)
|
|
|
|
return ReturnError;
|
2012-09-05 16:29:19 +00:00
|
|
|
switch (tok) {
|
|
|
|
default: // whatever - cannot happen
|
|
|
|
case TokAssign: // =
|
2015-01-30 18:18:54 +00:00
|
|
|
varVal.removeEmpty();
|
2013-11-15 18:28:50 +00:00
|
|
|
// FIXME: add check+warning about accidental value removal.
|
|
|
|
// This may be a bit too noisy, though.
|
|
|
|
m_valuemapStack.top()[varName] = varVal;
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(2, "assigning");
|
|
|
|
break;
|
|
|
|
case TokAppendUnique: // *=
|
2015-01-30 18:18:54 +00:00
|
|
|
valuesRef(varName).insertUnique(varVal);
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(2, "appending unique");
|
|
|
|
break;
|
|
|
|
case TokAppend: // +=
|
2015-01-30 18:18:54 +00:00
|
|
|
varVal.removeEmpty();
|
2012-09-05 16:29:19 +00:00
|
|
|
valuesRef(varName) += varVal;
|
|
|
|
debugMsg(2, "appending");
|
|
|
|
break;
|
|
|
|
case TokRemove: // -=
|
|
|
|
if (!m_cumulative) {
|
2015-01-30 18:18:54 +00:00
|
|
|
valuesRef(varName).removeEach(varVal);
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
2013-11-15 18:28:50 +00:00
|
|
|
// We are stingy with our values.
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
debugMsg(2, "removing");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
traceMsg("%s := %s", dbgKey(varName), dbgStrList(values(varName)));
|
|
|
|
|
|
|
|
if (varName == statics.strTEMPLATE)
|
|
|
|
setTemplate();
|
2013-02-15 11:24:22 +00:00
|
|
|
else if (varName == statics.strQMAKE_PLATFORM)
|
2018-08-02 22:22:24 +00:00
|
|
|
m_featureRoots = nullptr;
|
2016-12-16 12:11:49 +00:00
|
|
|
else if (varName == statics.strQMAKE_DIR_SEP)
|
|
|
|
m_dirSep = first(varName);
|
2013-10-11 13:48:44 +00:00
|
|
|
else if (varName == statics.strQMAKESPEC) {
|
|
|
|
if (!values(varName).isEmpty()) {
|
2014-05-22 13:21:36 +00:00
|
|
|
QString spec = values(varName).first().toQString();
|
|
|
|
if (IoUtils::isAbsolutePath(spec)) {
|
|
|
|
m_qmakespec = spec;
|
2016-12-16 12:31:07 +00:00
|
|
|
m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
|
2018-08-02 22:22:24 +00:00
|
|
|
m_featureRoots = nullptr;
|
2014-05-22 13:21:36 +00:00
|
|
|
}
|
2013-10-11 13:48:44 +00:00
|
|
|
}
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef PROEVALUATOR_FULL
|
|
|
|
else if (varName == statics.strREQUIRES)
|
2016-06-30 14:02:29 +00:00
|
|
|
return checkRequirements(values(varName));
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2016-06-29 14:56:57 +00:00
|
|
|
|
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::setTemplate()
|
|
|
|
{
|
|
|
|
ProStringList &values = valuesRef(statics.strTEMPLATE);
|
|
|
|
if (!m_option->user_template.isEmpty()) {
|
|
|
|
// Don't allow override
|
|
|
|
values = ProStringList(ProString(m_option->user_template));
|
|
|
|
} else {
|
|
|
|
if (values.isEmpty())
|
|
|
|
values.append(ProString("app"));
|
|
|
|
else
|
|
|
|
values.erase(values.begin() + 1, values.end());
|
|
|
|
}
|
|
|
|
if (!m_option->user_template_prefix.isEmpty()) {
|
2017-08-11 10:16:57 +00:00
|
|
|
ProString val = values.first();
|
|
|
|
if (!val.startsWith(m_option->user_template_prefix))
|
|
|
|
values = ProStringList(ProString(m_option->user_template_prefix + val));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-22 15:45:09 +00:00
|
|
|
#if defined(Q_CC_MSVC)
|
|
|
|
static ProString msvcBinDirToQMakeArch(QString subdir)
|
|
|
|
{
|
|
|
|
int idx = subdir.indexOf(QLatin1Char('\\'));
|
|
|
|
if (idx == -1)
|
|
|
|
return ProString("x86");
|
|
|
|
subdir.remove(0, idx + 1);
|
|
|
|
idx = subdir.indexOf(QLatin1Char('_'));
|
|
|
|
if (idx >= 0)
|
|
|
|
subdir.remove(0, idx + 1);
|
|
|
|
subdir = subdir.toLower();
|
2016-01-19 07:55:32 +00:00
|
|
|
if (subdir == QLatin1String("amd64"))
|
2014-12-22 15:45:09 +00:00
|
|
|
return ProString("x86_64");
|
2016-11-17 09:40:44 +00:00
|
|
|
// Since 2017 the folder structure from here is HostX64|X86/x64|x86
|
|
|
|
idx = subdir.indexOf(QLatin1Char('\\'));
|
|
|
|
if (idx == -1)
|
|
|
|
return ProString("x86");
|
|
|
|
subdir.remove(0, idx + 1);
|
|
|
|
if (subdir == QLatin1String("x64"))
|
|
|
|
return ProString("x86_64");
|
2014-12-22 15:45:09 +00:00
|
|
|
return ProString(subdir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ProString defaultMsvcArchitecture()
|
|
|
|
{
|
2015-01-22 14:46:36 +00:00
|
|
|
#if defined(Q_OS_WIN64)
|
|
|
|
return ProString("x86_64");
|
|
|
|
#else
|
2014-12-22 15:45:09 +00:00
|
|
|
return ProString("x86");
|
2015-01-22 14:46:36 +00:00
|
|
|
#endif
|
2014-12-22 15:45:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ProString msvcArchitecture(const QString &vcInstallDir, const QString &pathVar)
|
|
|
|
{
|
|
|
|
if (vcInstallDir.isEmpty())
|
|
|
|
return defaultMsvcArchitecture();
|
|
|
|
QString vcBinDir = vcInstallDir;
|
|
|
|
if (vcBinDir.endsWith(QLatin1Char('\\')))
|
|
|
|
vcBinDir.chop(1);
|
2018-05-29 14:36:53 +00:00
|
|
|
const auto dirs = pathVar.split(QLatin1Char(';'), QString::SkipEmptyParts);
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const QString &dir : dirs) {
|
2014-12-22 15:45:09 +00:00
|
|
|
if (!dir.startsWith(vcBinDir, Qt::CaseInsensitive))
|
|
|
|
continue;
|
|
|
|
const ProString arch = msvcBinDirToQMakeArch(dir.mid(vcBinDir.length() + 1));
|
|
|
|
if (!arch.isEmpty())
|
|
|
|
return arch;
|
|
|
|
}
|
|
|
|
return defaultMsvcArchitecture();
|
|
|
|
}
|
|
|
|
#endif // defined(Q_CC_MSVC)
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
void QMakeEvaluator::loadDefaults()
|
|
|
|
{
|
|
|
|
ProValueMap &vars = m_valuemapStack.top();
|
|
|
|
|
|
|
|
vars[ProKey("DIR_SEPARATOR")] << ProString(m_option->dir_sep);
|
|
|
|
vars[ProKey("DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep);
|
|
|
|
vars[ProKey("_DATE_")] << ProString(QDateTime::currentDateTime().toString());
|
|
|
|
if (!m_option->qmake_abslocation.isEmpty())
|
|
|
|
vars[ProKey("QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation);
|
2013-10-23 12:02:59 +00:00
|
|
|
if (!m_option->qmake_args.isEmpty())
|
|
|
|
vars[ProKey("QMAKE_ARGS")] = ProStringList(m_option->qmake_args);
|
2015-07-13 09:21:22 +00:00
|
|
|
if (!m_option->qtconf.isEmpty())
|
|
|
|
vars[ProKey("QMAKE_QTCONF")] = ProString(m_option->qtconf);
|
2014-07-21 17:21:02 +00:00
|
|
|
vars[ProKey("QMAKE_HOST.cpu_count")] = ProString(QString::number(idealThreadCount()));
|
2012-09-05 16:29:19 +00:00
|
|
|
#if defined(Q_OS_WIN32)
|
|
|
|
vars[ProKey("QMAKE_HOST.os")] << ProString("Windows");
|
|
|
|
|
|
|
|
DWORD name_length = 1024;
|
|
|
|
wchar_t name[1024];
|
|
|
|
if (GetComputerName(name, &name_length))
|
|
|
|
vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name));
|
|
|
|
|
2016-10-13 07:49:38 +00:00
|
|
|
vars[ProKey("QMAKE_HOST.version")] << ProString(QSysInfo::kernelVersion());
|
|
|
|
vars[ProKey("QMAKE_HOST.version_string")] << ProString(QSysInfo::productVersion());
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
SYSTEM_INFO info;
|
|
|
|
GetSystemInfo(&info);
|
|
|
|
ProString archStr;
|
|
|
|
switch (info.wProcessorArchitecture) {
|
|
|
|
# ifdef PROCESSOR_ARCHITECTURE_AMD64
|
|
|
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
|
|
|
archStr = ProString("x86_64");
|
|
|
|
break;
|
|
|
|
# endif
|
|
|
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
|
|
|
archStr = ProString("x86");
|
|
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_IA64:
|
|
|
|
# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
|
|
|
|
case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
|
|
|
|
# endif
|
|
|
|
archStr = ProString("IA64");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
archStr = ProString("Unknown");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vars[ProKey("QMAKE_HOST.arch")] << archStr;
|
|
|
|
|
|
|
|
# if defined(Q_CC_MSVC) // ### bogus condition, but nobody x-builds for msvc with a different qmake
|
2016-11-17 09:40:44 +00:00
|
|
|
// Since VS 2017 we need VCToolsInstallDir instead of VCINSTALLDIR
|
|
|
|
QString vcInstallDir = m_option->getEnv(QLatin1String("VCToolsInstallDir"));
|
|
|
|
if (vcInstallDir.isEmpty())
|
|
|
|
vcInstallDir = m_option->getEnv(QLatin1String("VCINSTALLDIR"));
|
2014-12-22 15:45:09 +00:00
|
|
|
vars[ProKey("QMAKE_TARGET.arch")] = msvcArchitecture(
|
2016-11-17 09:40:44 +00:00
|
|
|
vcInstallDir,
|
2014-12-22 15:45:09 +00:00
|
|
|
m_option->getEnv(QLatin1String("PATH")));
|
2012-09-05 16:29:19 +00:00
|
|
|
# endif
|
|
|
|
#elif defined(Q_OS_UNIX)
|
|
|
|
struct utsname name;
|
2013-06-21 14:02:39 +00:00
|
|
|
if (uname(&name) != -1) {
|
2012-09-05 16:29:19 +00:00
|
|
|
vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
|
|
|
|
vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename));
|
|
|
|
vars[ProKey("QMAKE_HOST.version")] << ProString(name.release);
|
|
|
|
vars[ProKey("QMAKE_HOST.version_string")] << ProString(name.version);
|
|
|
|
vars[ProKey("QMAKE_HOST.arch")] << ProString(name.machine);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_valuemapInited = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QMakeEvaluator::prepareProject(const QString &inDir)
|
|
|
|
{
|
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 superdir;
|
|
|
|
if (m_option->do_cache) {
|
|
|
|
QString conffile;
|
|
|
|
QString cachefile = m_option->cachefile;
|
|
|
|
if (cachefile.isEmpty()) { //find it as it has not been specified
|
|
|
|
if (m_outputDir.isEmpty())
|
|
|
|
goto no_cache;
|
|
|
|
superdir = m_outputDir;
|
|
|
|
forever {
|
|
|
|
QString superfile = superdir + QLatin1String("/.qmake.super");
|
2016-10-24 17:30:24 +00:00
|
|
|
if (m_vfs->exists(superfile, flags)) {
|
2013-02-25 19:08:23 +00:00
|
|
|
m_superfile = QDir::cleanPath(superfile);
|
2012-09-05 16:29:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
QFileInfo qdfi(superdir);
|
|
|
|
if (qdfi.isRoot()) {
|
|
|
|
superdir.clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
superdir = qdfi.path();
|
|
|
|
}
|
|
|
|
QString dir = m_outputDir;
|
|
|
|
forever {
|
|
|
|
cachefile = dir + QLatin1String("/.qmake.cache");
|
2016-10-24 17:30:24 +00:00
|
|
|
if (!m_vfs->exists(cachefile, flags))
|
2012-09-05 16:29:19 +00:00
|
|
|
cachefile.clear();
|
2019-05-31 13:06:14 +00:00
|
|
|
if (!cachefile.isEmpty()) {
|
2012-09-05 16:29:19 +00:00
|
|
|
m_buildRoot = dir;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dir == superdir)
|
|
|
|
goto no_cache;
|
|
|
|
QFileInfo qdfi(dir);
|
2019-05-31 13:06:14 +00:00
|
|
|
if (qdfi.isRoot()) {
|
|
|
|
cachefile.clear();
|
|
|
|
break;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
dir = qdfi.path();
|
|
|
|
}
|
2019-05-31 13:06:14 +00:00
|
|
|
QString sdir = inDir;
|
|
|
|
forever {
|
|
|
|
conffile = sdir + QLatin1String("/.qmake.conf");
|
|
|
|
if (!m_vfs->exists(conffile, flags))
|
|
|
|
conffile.clear();
|
|
|
|
if (!conffile.isEmpty()) {
|
|
|
|
if (sdir != m_buildRoot)
|
|
|
|
m_sourceRoot = sdir;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QFileInfo qsdfi(sdir);
|
|
|
|
if (qsdfi.isRoot()) {
|
|
|
|
conffile.clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sdir = qsdfi.path();
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
|
|
|
m_buildRoot = QFileInfo(cachefile).path();
|
|
|
|
}
|
2019-05-31 13:06:14 +00:00
|
|
|
if (!conffile.isEmpty())
|
|
|
|
m_conffile = QDir::cleanPath(conffile);
|
|
|
|
if (!cachefile.isEmpty())
|
|
|
|
m_cachefile = QDir::cleanPath(cachefile);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
no_cache:
|
|
|
|
|
|
|
|
QString dir = m_outputDir;
|
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
|
|
|
forever {
|
|
|
|
QString stashfile = dir + QLatin1String("/.qmake.stash");
|
2016-10-24 17:30:24 +00:00
|
|
|
if (dir == (!superdir.isEmpty() ? superdir : m_buildRoot) || m_vfs->exists(stashfile, flags)) {
|
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_stashfile = QDir::cleanPath(stashfile);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QFileInfo qdfi(dir);
|
|
|
|
if (qdfi.isRoot())
|
|
|
|
break;
|
|
|
|
dir = qdfi.path();
|
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QMakeEvaluator::loadSpecInternal()
|
|
|
|
{
|
2012-09-19 19:56:16 +00:00
|
|
|
if (evaluateFeatureFile(QLatin1String("spec_pre.prf")) != ReturnTrue)
|
2012-09-05 16:29:19 +00:00
|
|
|
return false;
|
|
|
|
QString spec = m_qmakespec + QLatin1String("/qmake.conf");
|
2012-09-19 19:56:16 +00:00
|
|
|
if (evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
|
2012-09-05 16:29:19 +00:00
|
|
|
evalError(fL1S("Could not read qmake configuration file %1.").arg(spec));
|
|
|
|
return false;
|
|
|
|
}
|
2012-10-29 11:43:43 +00:00
|
|
|
#ifndef QT_BUILD_QMAKE
|
|
|
|
// Legacy support for Qt4 default specs
|
|
|
|
# ifdef Q_OS_UNIX
|
2012-09-18 07:32:46 +00:00
|
|
|
if (m_qmakespec.endsWith(QLatin1String("/default-host"))
|
|
|
|
|| m_qmakespec.endsWith(QLatin1String("/default"))) {
|
2019-01-17 21:00:08 +00:00
|
|
|
QString rspec = QFileInfo(m_qmakespec).symLinkTarget();
|
2012-09-18 07:32:46 +00:00
|
|
|
if (!rspec.isEmpty())
|
|
|
|
m_qmakespec = QDir::cleanPath(QDir(m_qmakespec).absoluteFilePath(rspec));
|
|
|
|
}
|
2012-10-29 11:43:43 +00:00
|
|
|
# else
|
2012-09-05 16:29:19 +00:00
|
|
|
// We can't resolve symlinks as they do on Unix, so configure.exe puts
|
|
|
|
// the source of the qmake.conf at the end of the default/qmake.conf in
|
|
|
|
// the QMAKESPEC_ORIGINAL variable.
|
|
|
|
const ProString &orig_spec = first(ProKey("QMAKESPEC_ORIGINAL"));
|
2014-05-22 13:21:36 +00:00
|
|
|
if (!orig_spec.isEmpty()) {
|
|
|
|
QString spec = orig_spec.toQString();
|
|
|
|
if (IoUtils::isAbsolutePath(spec))
|
|
|
|
m_qmakespec = spec;
|
|
|
|
}
|
2012-10-29 11:43:43 +00:00
|
|
|
# endif
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2013-10-11 13:48:44 +00:00
|
|
|
valuesRef(ProKey("QMAKESPEC")) = ProString(m_qmakespec);
|
2012-09-18 08:54:52 +00:00
|
|
|
m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
|
2013-06-03 17:22:23 +00:00
|
|
|
// This also ensures that m_featureRoots is valid.
|
2012-09-19 19:56:16 +00:00
|
|
|
if (evaluateFeatureFile(QLatin1String("spec_post.prf")) != ReturnTrue)
|
2012-09-05 16:29:19 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QMakeEvaluator::loadSpec()
|
|
|
|
{
|
|
|
|
QString qmakespec = m_option->expandEnvVars(
|
|
|
|
m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
|
|
|
|
|
|
|
|
{
|
2013-05-29 18:18:51 +00:00
|
|
|
QMakeEvaluator evaluator(m_option, m_parser, m_vfs, m_handler);
|
2013-06-03 19:59:27 +00:00
|
|
|
evaluator.m_sourceRoot = m_sourceRoot;
|
|
|
|
evaluator.m_buildRoot = m_buildRoot;
|
2013-10-31 17:01:29 +00:00
|
|
|
|
|
|
|
if (!m_superfile.isEmpty() && evaluator.evaluateFile(
|
|
|
|
m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
|
|
|
|
return false;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-10-31 17:01:29 +00:00
|
|
|
if (!m_conffile.isEmpty() && evaluator.evaluateFile(
|
|
|
|
m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
|
|
|
|
return false;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-10-31 17:01:29 +00:00
|
|
|
if (!m_cachefile.isEmpty() && evaluator.evaluateFile(
|
|
|
|
m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
|
|
|
|
return false;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
if (qmakespec.isEmpty()) {
|
|
|
|
if (!m_hostBuild)
|
|
|
|
qmakespec = evaluator.first(ProKey("XQMAKESPEC")).toQString();
|
|
|
|
if (qmakespec.isEmpty())
|
|
|
|
qmakespec = evaluator.first(ProKey("QMAKESPEC")).toQString();
|
|
|
|
}
|
|
|
|
m_qmakepath = evaluator.values(ProKey("QMAKEPATH")).toQStringList();
|
|
|
|
m_qmakefeatures = evaluator.values(ProKey("QMAKEFEATURES")).toQStringList();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateMkspecPaths();
|
|
|
|
if (qmakespec.isEmpty())
|
2012-10-29 11:43:43 +00:00
|
|
|
qmakespec = propertyValue(ProKey(m_hostBuild ? "QMAKE_SPEC" : "QMAKE_XSPEC")).toQString();
|
|
|
|
#ifndef QT_BUILD_QMAKE
|
|
|
|
// Legacy support for Qt4 qmake in Qt Creator, etc.
|
|
|
|
if (qmakespec.isEmpty())
|
2012-11-02 18:46:19 +00:00
|
|
|
qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default");
|
2012-10-29 11:43:43 +00:00
|
|
|
#endif
|
2012-09-05 16:29:19 +00:00
|
|
|
if (IoUtils::isRelativePath(qmakespec)) {
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const QString &root : qAsConst(m_mkspecPaths)) {
|
2012-09-05 16:29:19 +00:00
|
|
|
QString mkspec = root + QLatin1Char('/') + qmakespec;
|
|
|
|
if (IoUtils::exists(mkspec)) {
|
|
|
|
qmakespec = mkspec;
|
|
|
|
goto cool;
|
|
|
|
}
|
|
|
|
}
|
2016-10-20 13:02:34 +00:00
|
|
|
evalError(fL1S("Could not find qmake spec '%1'.").arg(qmakespec));
|
2012-09-05 16:29:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
cool:
|
|
|
|
m_qmakespec = QDir::cleanPath(qmakespec);
|
|
|
|
|
2013-10-31 17:01:29 +00:00
|
|
|
if (!m_superfile.isEmpty()) {
|
|
|
|
valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
|
|
|
|
if (evaluateFile(
|
|
|
|
m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue)
|
|
|
|
return false;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
if (!loadSpecInternal())
|
|
|
|
return false;
|
2013-10-31 17:01:29 +00:00
|
|
|
if (!m_conffile.isEmpty()) {
|
|
|
|
valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
|
|
|
|
if (evaluateFile(
|
|
|
|
m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
|
|
|
|
return false;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-10-31 17:01:29 +00:00
|
|
|
if (!m_cachefile.isEmpty()) {
|
|
|
|
valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile);
|
|
|
|
if (evaluateFile(
|
|
|
|
m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
|
|
|
|
return false;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2016-10-24 17:30:24 +00:00
|
|
|
QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact);
|
|
|
|
if (!m_stashfile.isEmpty() && m_vfs->exists(m_stashfile, flags)) {
|
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(m_stashfile);
|
|
|
|
if (evaluateFile(
|
|
|
|
m_stashfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue)
|
|
|
|
return false;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::setupProject()
|
|
|
|
{
|
|
|
|
setTemplate();
|
|
|
|
ProValueMap &vars = m_valuemapStack.top();
|
2017-08-14 16:30:29 +00:00
|
|
|
int proFile = currentFileId();
|
2012-10-09 08:17:43 +00:00
|
|
|
vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName()).setSource(proFile);
|
|
|
|
vars[ProKey("_PRO_FILE_")] << ProString(currentFileName()).setSource(proFile);
|
|
|
|
vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory()).setSource(proFile);
|
|
|
|
vars[ProKey("OUT_PWD")] << ProString(m_outputDir).setSource(proFile);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where)
|
|
|
|
{
|
|
|
|
if (!cmds.isEmpty()) {
|
2016-10-24 17:30:24 +00:00
|
|
|
ProFile *pro = m_parser->parsedProBlock(QStringRef(&cmds), 0, where, -1);
|
2015-02-02 17:52:24 +00:00
|
|
|
if (pro->isOk()) {
|
|
|
|
m_locationStack.push(m_current);
|
|
|
|
visitProBlock(pro, pro->tokPtr());
|
|
|
|
m_current = m_locationStack.pop();
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2015-02-02 17:52:24 +00:00
|
|
|
pro->deref();
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-08 21:44:24 +00:00
|
|
|
void QMakeEvaluator::applyExtraConfigs()
|
|
|
|
{
|
|
|
|
if (m_extraConfigs.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
evaluateCommand(fL1S("CONFIG += ") + m_extraConfigs.join(QLatin1Char(' ')), fL1S("(extra configs)"));
|
|
|
|
}
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConfigFeatures()
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
QSet<QString> processed;
|
|
|
|
forever {
|
|
|
|
bool finished = true;
|
|
|
|
ProStringList configs = values(statics.strCONFIG);
|
|
|
|
for (int i = configs.size() - 1; i >= 0; --i) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(configs.at(i), m_tmp1);
|
|
|
|
QString config = u1.str().toLower();
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!processed.contains(config)) {
|
|
|
|
config.detach();
|
|
|
|
processed.insert(config);
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn vr = evaluateFeatureFile(config, true);
|
2013-09-02 14:52:47 +00:00
|
|
|
if (vr == ReturnError && !m_cumulative)
|
2012-09-19 19:56:16 +00:00
|
|
|
return vr;
|
|
|
|
if (vr == ReturnTrue) {
|
2012-09-05 16:29:19 +00:00
|
|
|
finished = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (finished)
|
|
|
|
break;
|
|
|
|
}
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
|
|
|
|
ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags)
|
|
|
|
{
|
|
|
|
if (!m_cumulative && !pro->isOk())
|
|
|
|
return ReturnFalse;
|
|
|
|
|
|
|
|
if (flags & LoadPreFiles) {
|
|
|
|
if (!prepareProject(pro->directoryName()))
|
|
|
|
return ReturnFalse;
|
|
|
|
|
|
|
|
m_hostBuild = pro->isHostBuild();
|
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
m_option->mutex.lock();
|
|
|
|
#endif
|
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
|
|
|
QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_stashfile, m_hostBuild)];
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!*baseEnvPtr)
|
|
|
|
*baseEnvPtr = new QMakeBaseEnv;
|
|
|
|
QMakeBaseEnv *baseEnv = *baseEnvPtr;
|
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
2013-07-23 15:57:44 +00:00
|
|
|
QMutexLocker locker(&baseEnv->mutex);
|
|
|
|
m_option->mutex.unlock();
|
|
|
|
if (baseEnv->inProgress) {
|
|
|
|
QThreadPool::globalInstance()->releaseThread();
|
|
|
|
baseEnv->cond.wait(&baseEnv->mutex);
|
|
|
|
QThreadPool::globalInstance()->reserveThread();
|
|
|
|
if (!baseEnv->isOk)
|
|
|
|
return ReturnFalse;
|
|
|
|
} else
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2013-07-23 15:57:44 +00:00
|
|
|
if (!baseEnv->evaluator) {
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
2013-07-23 15:57:44 +00:00
|
|
|
baseEnv->inProgress = true;
|
|
|
|
locker.unlock();
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
|
|
|
|
2013-07-23 15:57:44 +00:00
|
|
|
QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_vfs, m_handler);
|
|
|
|
baseEnv->evaluator = baseEval;
|
|
|
|
baseEval->m_superfile = m_superfile;
|
|
|
|
baseEval->m_conffile = m_conffile;
|
|
|
|
baseEval->m_cachefile = 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
|
|
|
baseEval->m_stashfile = m_stashfile;
|
2013-07-23 15:57:44 +00:00
|
|
|
baseEval->m_sourceRoot = m_sourceRoot;
|
|
|
|
baseEval->m_buildRoot = m_buildRoot;
|
|
|
|
baseEval->m_hostBuild = m_hostBuild;
|
|
|
|
bool ok = baseEval->loadSpec();
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
2013-07-23 15:57:44 +00:00
|
|
|
locker.relock();
|
|
|
|
baseEnv->isOk = ok;
|
|
|
|
baseEnv->inProgress = false;
|
|
|
|
baseEnv->cond.wakeAll();
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
|
|
|
|
2013-07-23 15:57:44 +00:00
|
|
|
if (!ok)
|
2013-07-23 15:27:34 +00:00
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2013-07-23 15:57:44 +00:00
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
else if (!baseEnv->isOk)
|
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
|
|
|
|
2014-06-16 12:31:30 +00:00
|
|
|
initFrom(baseEnv->evaluator);
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
|
|
|
if (!m_valuemapInited)
|
|
|
|
loadDefaults();
|
|
|
|
}
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn vr;
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
m_handler->aboutToEval(currentProFile(), pro, type);
|
|
|
|
m_profileStack.push(pro);
|
|
|
|
valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
|
|
|
|
if (flags & LoadPreFiles) {
|
|
|
|
setupProject();
|
|
|
|
|
2017-02-03 17:12:52 +00:00
|
|
|
if (!m_option->extra_cmds[QMakeEvalEarly].isEmpty())
|
|
|
|
evaluateCommand(m_option->extra_cmds[QMakeEvalEarly], fL1S("(command line -early)"));
|
|
|
|
|
2013-10-09 14:26:44 +00:00
|
|
|
for (ProValueMap::ConstIterator it = m_extraVars.constBegin();
|
|
|
|
it != m_extraVars.constEnd(); ++it)
|
|
|
|
m_valuemapStack.first().insert(it.key(), it.value());
|
|
|
|
|
2013-10-08 21:44:24 +00:00
|
|
|
// In case default_pre needs to make decisions based on the current
|
|
|
|
// build pass configuration.
|
|
|
|
applyExtraConfigs();
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
if ((vr = evaluateFeatureFile(QLatin1String("default_pre.prf"))) == ReturnError)
|
|
|
|
goto failed;
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2017-02-03 17:12:52 +00:00
|
|
|
if (!m_option->extra_cmds[QMakeEvalBefore].isEmpty()) {
|
|
|
|
evaluateCommand(m_option->extra_cmds[QMakeEvalBefore], fL1S("(command line)"));
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2013-10-08 21:44:24 +00:00
|
|
|
// Again, after user configs, to override them
|
|
|
|
applyExtraConfigs();
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
debugMsg(1, "visiting file %s", qPrintable(pro->fileName()));
|
2012-09-19 19:56:16 +00:00
|
|
|
if ((vr = visitProBlock(pro, pro->tokPtr())) == ReturnError)
|
|
|
|
goto failed;
|
2012-09-05 16:29:19 +00:00
|
|
|
debugMsg(1, "done visiting file %s", qPrintable(pro->fileName()));
|
|
|
|
|
|
|
|
if (flags & LoadPostFiles) {
|
2017-02-03 17:12:52 +00:00
|
|
|
evaluateCommand(m_option->extra_cmds[QMakeEvalAfter], fL1S("(command line -after)"));
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
// Again, to ensure the project does not mess with us.
|
|
|
|
// Specifically, do not allow a project to override debug/release within a
|
|
|
|
// debug_and_release build pass - it's too late for that at this point anyway.
|
2013-10-08 21:44:24 +00:00
|
|
|
applyExtraConfigs();
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
if ((vr = evaluateFeatureFile(QLatin1String("default_post.prf"))) == ReturnError)
|
|
|
|
goto failed;
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2017-02-03 17:12:52 +00:00
|
|
|
if (!m_option->extra_cmds[QMakeEvalLate].isEmpty())
|
|
|
|
evaluateCommand(m_option->extra_cmds[QMakeEvalLate], fL1S("(command line -late)"));
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
if ((vr = evaluateConfigFeatures()) == ReturnError)
|
|
|
|
goto failed;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2012-09-19 19:56:16 +00:00
|
|
|
vr = ReturnTrue;
|
|
|
|
failed:
|
2012-09-05 16:29:19 +00:00
|
|
|
m_profileStack.pop();
|
|
|
|
valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory()));
|
|
|
|
m_handler->doneWithEval(currentProFile());
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
return vr;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QMakeEvaluator::updateMkspecPaths()
|
|
|
|
{
|
|
|
|
QStringList ret;
|
|
|
|
const QString concat = QLatin1String("/mkspecs");
|
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
const auto paths = m_option->getPathListEnv(QLatin1String("QMAKEPATH"));
|
|
|
|
for (const QString &it : paths)
|
2012-09-05 16:29:19 +00:00
|
|
|
ret << it + concat;
|
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const QString &it : qAsConst(m_qmakepath))
|
2012-09-05 16:29:19 +00:00
|
|
|
ret << it + concat;
|
|
|
|
|
|
|
|
if (!m_buildRoot.isEmpty())
|
|
|
|
ret << m_buildRoot + concat;
|
|
|
|
if (!m_sourceRoot.isEmpty())
|
|
|
|
ret << m_sourceRoot + concat;
|
|
|
|
|
|
|
|
ret << m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + concat;
|
2013-05-08 14:29:11 +00:00
|
|
|
ret << m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + concat;
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
ret.removeDuplicates();
|
|
|
|
m_mkspecPaths = ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::updateFeaturePaths()
|
|
|
|
{
|
|
|
|
QString mkspecs_concat = QLatin1String("/mkspecs");
|
|
|
|
QString features_concat = QLatin1String("/features/");
|
|
|
|
|
|
|
|
QStringList feature_roots;
|
|
|
|
|
2016-01-26 18:33:30 +00:00
|
|
|
feature_roots += m_option->getPathListEnv(QLatin1String("QMAKEFEATURES"));
|
2012-09-05 16:29:19 +00:00
|
|
|
feature_roots += m_qmakefeatures;
|
2013-09-05 16:30:05 +00:00
|
|
|
feature_roots += m_option->splitPathList(
|
2017-08-11 13:52:47 +00:00
|
|
|
m_option->propertyValue(ProKey("QMAKEFEATURES")).toQString());
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
QStringList feature_bases;
|
2013-06-03 19:56:51 +00:00
|
|
|
if (!m_buildRoot.isEmpty()) {
|
|
|
|
feature_bases << m_buildRoot + mkspecs_concat;
|
2012-09-05 16:29:19 +00:00
|
|
|
feature_bases << m_buildRoot;
|
2013-06-03 19:56:51 +00:00
|
|
|
}
|
|
|
|
if (!m_sourceRoot.isEmpty()) {
|
|
|
|
feature_bases << m_sourceRoot + mkspecs_concat;
|
2012-09-05 16:29:19 +00:00
|
|
|
feature_bases << m_sourceRoot;
|
2013-06-03 19:56:51 +00:00
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
const auto items = m_option->getPathListEnv(QLatin1String("QMAKEPATH"));
|
|
|
|
for (const QString &item : items)
|
2012-09-05 16:29:19 +00:00
|
|
|
feature_bases << (item + mkspecs_concat);
|
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const QString &item : qAsConst(m_qmakepath))
|
2012-09-05 16:29:19 +00:00
|
|
|
feature_bases << (item + mkspecs_concat);
|
|
|
|
|
2012-09-18 08:54:52 +00:00
|
|
|
if (!m_qmakespec.isEmpty()) {
|
2012-09-05 16:29:19 +00:00
|
|
|
// The spec is already platform-dependent, so no subdirs here.
|
2012-09-18 08:54:52 +00:00
|
|
|
feature_roots << (m_qmakespec + features_concat);
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
// Also check directly under the root directory of the mkspecs collection
|
2012-09-18 08:54:52 +00:00
|
|
|
QDir specdir(m_qmakespec);
|
2012-09-05 16:29:19 +00:00
|
|
|
while (!specdir.isRoot() && specdir.cdUp()) {
|
|
|
|
const QString specpath = specdir.path();
|
|
|
|
if (specpath.endsWith(mkspecs_concat)) {
|
|
|
|
if (IoUtils::exists(specpath + features_concat))
|
|
|
|
feature_bases << specpath;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-05 13:44:46 +00:00
|
|
|
feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + mkspecs_concat);
|
2013-05-08 14:29:11 +00:00
|
|
|
feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + mkspecs_concat);
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const QString &fb : qAsConst(feature_bases)) {
|
2016-01-26 13:38:54 +00:00
|
|
|
const auto sfxs = values(ProKey("QMAKE_PLATFORM"));
|
|
|
|
for (const ProString &sfx : sfxs)
|
2012-09-05 16:29:19 +00:00
|
|
|
feature_roots << (fb + features_concat + sfx + QLatin1Char('/'));
|
|
|
|
feature_roots << (fb + features_concat);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < feature_roots.count(); ++i)
|
|
|
|
if (!feature_roots.at(i).endsWith((ushort)'/'))
|
|
|
|
feature_roots[i].append((ushort)'/');
|
|
|
|
|
|
|
|
feature_roots.removeDuplicates();
|
|
|
|
|
|
|
|
QStringList ret;
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const QString &root : qAsConst(feature_roots))
|
2012-09-05 16:29:19 +00:00
|
|
|
if (IoUtils::exists(root))
|
|
|
|
ret << root;
|
2013-06-03 17:08:10 +00:00
|
|
|
m_featureRoots = new QMakeFeatureRoots(ret);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ProString QMakeEvaluator::propertyValue(const ProKey &name) const
|
|
|
|
{
|
|
|
|
if (name == QLatin1String("QMAKE_MKSPECS"))
|
|
|
|
return ProString(m_mkspecPaths.join(m_option->dirlist_sep));
|
|
|
|
ProString ret = m_option->propertyValue(name);
|
|
|
|
// if (ret.isNull())
|
2017-04-07 15:10:37 +00:00
|
|
|
// evalError(fL1S("Querying unknown property %1").arg(name.toQStringView()));
|
2012-09-05 16:29:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProFile *QMakeEvaluator::currentProFile() const
|
|
|
|
{
|
|
|
|
if (m_profileStack.count() > 0)
|
|
|
|
return m_profileStack.top();
|
2018-08-02 22:22:24 +00:00
|
|
|
return nullptr;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
2017-08-14 16:30:29 +00:00
|
|
|
int QMakeEvaluator::currentFileId() const
|
|
|
|
{
|
|
|
|
ProFile *pro = currentProFile();
|
|
|
|
if (pro)
|
|
|
|
return pro->id();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
QString QMakeEvaluator::currentFileName() const
|
|
|
|
{
|
|
|
|
ProFile *pro = currentProFile();
|
|
|
|
if (pro)
|
|
|
|
return pro->fileName();
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QMakeEvaluator::currentDirectory() const
|
|
|
|
{
|
|
|
|
ProFile *pro = currentProFile();
|
|
|
|
if (pro)
|
|
|
|
return pro->directoryName();
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2016-05-13 13:26:42 +00:00
|
|
|
bool QMakeEvaluator::isActiveConfig(const QStringRef &config, bool regex)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
// magic types for easy flipping
|
|
|
|
if (config == statics.strtrue)
|
|
|
|
return true;
|
|
|
|
if (config == statics.strfalse)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (config == statics.strhost_build)
|
|
|
|
return m_hostBuild;
|
|
|
|
|
|
|
|
if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
|
2017-08-10 18:55:39 +00:00
|
|
|
QRegExp re(config.toString(), Qt::CaseSensitive, QRegExp::Wildcard);
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
// mkspecs
|
|
|
|
if (re.exactMatch(m_qmakespecName))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// CONFIG variable
|
2016-01-26 13:38:54 +00:00
|
|
|
const auto configValues = values(statics.strCONFIG);
|
|
|
|
for (const ProString &configValue : configValues) {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(configValue, m_tmp[m_toggle ^= 1]);
|
|
|
|
if (re.exactMatch(u1.str()))
|
2012-09-05 16:29:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// mkspecs
|
|
|
|
if (m_qmakespecName == config)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// CONFIG variable
|
2016-05-13 13:26:42 +00:00
|
|
|
if (values(statics.strCONFIG).contains(config))
|
2012-09-05 16:29:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::expandVariableReferences(
|
|
|
|
const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2016-06-29 14:56:57 +00:00
|
|
|
ret->reserve(sizeHint);
|
2012-09-05 16:29:19 +00:00
|
|
|
forever {
|
2016-06-29 14:56:57 +00:00
|
|
|
if (evaluateExpression(tokPtr, ret, joined) == ReturnError)
|
|
|
|
return ReturnError;
|
2012-09-05 16:29:19 +00:00
|
|
|
switch (*tokPtr) {
|
|
|
|
case TokValueTerminator:
|
|
|
|
case TokFuncTerminator:
|
|
|
|
tokPtr++;
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
case TokArgSeparator:
|
|
|
|
if (joined) {
|
|
|
|
tokPtr++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-06-27 13:43:33 +00:00
|
|
|
Q_FALLTHROUGH();
|
2012-09-05 16:29:19 +00:00
|
|
|
default:
|
|
|
|
Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::prepareFunctionArgs(
|
|
|
|
const ushort *&tokPtr, QList<ProStringList> *ret)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
if (*tokPtr != TokFuncTerminator) {
|
|
|
|
for (;; tokPtr++) {
|
|
|
|
ProStringList arg;
|
2016-06-29 14:56:57 +00:00
|
|
|
if (evaluateExpression(tokPtr, &arg, false) == ReturnError)
|
|
|
|
return ReturnError;
|
|
|
|
*ret << arg;
|
2012-09-05 16:29:19 +00:00
|
|
|
if (*tokPtr == TokFuncTerminator)
|
|
|
|
break;
|
|
|
|
Q_ASSERT(*tokPtr == TokArgSeparator);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tokPtr++;
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFunction(
|
|
|
|
const ProFunctionDef &func, const QList<ProStringList> &argumentsList, ProStringList *ret)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn vr;
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
if (m_valuemapStack.count() >= 100) {
|
|
|
|
evalError(fL1S("Ran into infinite recursion (depth > 100)."));
|
2012-09-19 19:56:16 +00:00
|
|
|
vr = ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
} else {
|
|
|
|
m_valuemapStack.push(ProValueMap());
|
|
|
|
m_locationStack.push(m_current);
|
|
|
|
|
|
|
|
ProStringList args;
|
|
|
|
for (int i = 0; i < argumentsList.count(); ++i) {
|
|
|
|
args += argumentsList[i];
|
|
|
|
m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i];
|
|
|
|
}
|
|
|
|
m_valuemapStack.top()[statics.strARGS] = args;
|
2013-09-19 15:21:39 +00:00
|
|
|
m_valuemapStack.top()[statics.strARGC] = ProStringList(ProString(QString::number(argumentsList.count())));
|
2012-09-19 19:56:16 +00:00
|
|
|
vr = visitProBlock(func.pro(), func.tokPtr());
|
|
|
|
if (vr == ReturnReturn)
|
|
|
|
vr = ReturnTrue;
|
2016-06-29 14:56:57 +00:00
|
|
|
if (vr == ReturnTrue)
|
|
|
|
*ret = m_returnValue;
|
2012-09-05 16:29:19 +00:00
|
|
|
m_returnValue.clear();
|
|
|
|
|
|
|
|
m_current = m_locationStack.pop();
|
|
|
|
m_valuemapStack.pop();
|
|
|
|
}
|
2016-06-29 14:56:57 +00:00
|
|
|
return vr;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
|
|
|
|
const ProFunctionDef &func, const QList<ProStringList> &argumentsList,
|
|
|
|
const ProString &function)
|
|
|
|
{
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList ret;
|
|
|
|
VisitReturn vr = evaluateFunction(func, argumentsList, &ret);
|
2012-09-19 19:56:16 +00:00
|
|
|
if (vr == ReturnTrue) {
|
2012-09-05 16:29:19 +00:00
|
|
|
if (ret.isEmpty())
|
|
|
|
return ReturnTrue;
|
|
|
|
if (ret.at(0) != statics.strfalse) {
|
|
|
|
if (ret.at(0) == statics.strtrue)
|
|
|
|
return ReturnTrue;
|
2012-09-19 19:56:16 +00:00
|
|
|
bool ok;
|
2017-08-11 10:16:57 +00:00
|
|
|
int val = ret.at(0).toInt(&ok);
|
2012-09-05 16:29:19 +00:00
|
|
|
if (ok) {
|
|
|
|
if (val)
|
|
|
|
return ReturnTrue;
|
|
|
|
} else {
|
2017-08-14 10:06:35 +00:00
|
|
|
ProStringRoUser u1(function, m_tmp1);
|
2012-09-05 16:29:19 +00:00
|
|
|
evalError(fL1S("Unexpected return value from test '%1': %2.")
|
2017-08-14 10:06:35 +00:00
|
|
|
.arg(u1.str(), ret.join(QLatin1String(" :: "))));
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2012-09-19 19:56:16 +00:00
|
|
|
return vr;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
|
|
|
|
const ProKey &func, const ushort *&tokPtr)
|
|
|
|
{
|
2017-08-11 17:22:58 +00:00
|
|
|
auto adef = statics.functions.constFind(func);
|
|
|
|
if (adef != statics.functions.constEnd()) {
|
2012-09-03 18:57:59 +00:00
|
|
|
//why don't the builtin functions just use args_list? --Sam
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList args;
|
|
|
|
if (expandVariableReferences(tokPtr, 5, &args, true) == ReturnError)
|
|
|
|
return ReturnError;
|
2017-08-11 17:22:58 +00:00
|
|
|
return evaluateBuiltinConditional(*adef, func, args);
|
2012-09-03 18:57:59 +00:00
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
QHash<ProKey, ProFunctionDef>::ConstIterator it =
|
|
|
|
m_functionDefs.testFunctions.constFind(func);
|
|
|
|
if (it != m_functionDefs.testFunctions.constEnd()) {
|
2016-06-29 14:56:57 +00:00
|
|
|
QList<ProStringList> args;
|
|
|
|
if (prepareFunctionArgs(tokPtr, &args) == ReturnError)
|
|
|
|
return ReturnError;
|
2012-09-05 16:29:19 +00:00
|
|
|
traceMsg("calling %s(%s)", dbgKey(func), dbgStrListList(args));
|
|
|
|
return evaluateBoolFunction(*it, args, func);
|
|
|
|
}
|
|
|
|
|
2012-09-03 18:57:59 +00:00
|
|
|
skipExpression(tokPtr);
|
2017-04-07 15:10:37 +00:00
|
|
|
evalError(fL1S("'%1' is not a recognized test function.").arg(func.toQStringView()));
|
2012-09-03 18:57:59 +00:00
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
2016-06-29 14:56:57 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction(
|
|
|
|
const ProKey &func, const ushort *&tokPtr, ProStringList *ret)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2017-08-11 17:22:58 +00:00
|
|
|
auto adef = statics.expands.constFind(func);
|
|
|
|
if (adef != statics.expands.constEnd()) {
|
2012-09-03 18:57:59 +00:00
|
|
|
//why don't the builtin functions just use args_list? --Sam
|
2016-06-29 14:56:57 +00:00
|
|
|
ProStringList args;
|
|
|
|
if (expandVariableReferences(tokPtr, 5, &args, true) == ReturnError)
|
|
|
|
return ReturnError;
|
2017-08-11 17:22:58 +00:00
|
|
|
return evaluateBuiltinExpand(*adef, func, args, *ret);
|
2012-09-03 18:57:59 +00:00
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
QHash<ProKey, ProFunctionDef>::ConstIterator it =
|
|
|
|
m_functionDefs.replaceFunctions.constFind(func);
|
|
|
|
if (it != m_functionDefs.replaceFunctions.constEnd()) {
|
2016-06-29 14:56:57 +00:00
|
|
|
QList<ProStringList> args;
|
|
|
|
if (prepareFunctionArgs(tokPtr, &args) == ReturnError)
|
|
|
|
return ReturnError;
|
2012-09-05 16:29:19 +00:00
|
|
|
traceMsg("calling $$%s(%s)", dbgKey(func), dbgStrListList(args));
|
2016-06-29 14:56:57 +00:00
|
|
|
return evaluateFunction(*it, args, ret);
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
2012-09-03 18:57:59 +00:00
|
|
|
skipExpression(tokPtr);
|
2017-04-07 15:10:37 +00:00
|
|
|
evalError(fL1S("'%1' is not a recognized replace function.").arg(func.toQStringView()));
|
2016-06-29 14:56:57 +00:00
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
2016-06-29 13:40:17 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditional(
|
2016-07-19 17:51:41 +00:00
|
|
|
const QStringRef &cond, const QString &where, int line)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2016-06-29 13:40:17 +00:00
|
|
|
VisitReturn ret = ReturnFalse;
|
2016-10-24 17:30:24 +00:00
|
|
|
ProFile *pro = m_parser->parsedProBlock(cond, 0, where, line, QMakeParser::TestGrammar);
|
2015-02-02 17:52:24 +00:00
|
|
|
if (pro->isOk()) {
|
|
|
|
m_locationStack.push(m_current);
|
2016-06-29 13:40:17 +00:00
|
|
|
ret = visitProBlock(pro, pro->tokPtr());
|
2015-02-02 17:52:24 +00:00
|
|
|
m_current = m_locationStack.pop();
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
2015-02-02 17:52:24 +00:00
|
|
|
pro->deref();
|
2012-09-05 16:29:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_FULL
|
2016-06-30 14:02:29 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::checkRequirements(const ProStringList &deps)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS"));
|
2016-07-15 18:47:57 +00:00
|
|
|
for (const ProString &dep : deps) {
|
2016-07-19 17:51:41 +00:00
|
|
|
VisitReturn vr = evaluateConditional(dep.toQStringRef(), m_current.pro->fileName(), m_current.line);
|
2016-06-30 14:02:29 +00:00
|
|
|
if (vr == ReturnError)
|
|
|
|
return ReturnError;
|
|
|
|
if (vr != ReturnTrue)
|
2012-09-05 16:29:19 +00:00
|
|
|
failed << dep;
|
2016-06-30 14:02:29 +00:00
|
|
|
}
|
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-06-30 15:26:01 +00:00
|
|
|
static bool isFunctParam(const ProKey &variableName)
|
|
|
|
{
|
|
|
|
const int len = variableName.size();
|
|
|
|
const QChar *data = variableName.constData();
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
ushort c = data[i].unicode();
|
|
|
|
if (c < '0' || c > '9')
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-05 16:29:19 +00:00
|
|
|
ProValueMap *QMakeEvaluator::findValues(const ProKey &variableName, ProValueMap::Iterator *rit)
|
|
|
|
{
|
|
|
|
ProValueMapStack::Iterator vmi = m_valuemapStack.end();
|
2016-06-30 15:26:01 +00:00
|
|
|
for (bool first = true; ; first = false) {
|
2012-09-05 16:29:19 +00:00
|
|
|
--vmi;
|
|
|
|
ProValueMap::Iterator it = (*vmi).find(variableName);
|
|
|
|
if (it != (*vmi).end()) {
|
|
|
|
if (it->constBegin() == statics.fakeValue.constBegin())
|
2016-06-30 15:26:01 +00:00
|
|
|
break;
|
2012-09-05 16:29:19 +00:00
|
|
|
*rit = it;
|
|
|
|
return &(*vmi);
|
|
|
|
}
|
2016-06-30 15:26:01 +00:00
|
|
|
if (vmi == m_valuemapStack.begin())
|
|
|
|
break;
|
|
|
|
if (first && isFunctParam(variableName))
|
|
|
|
break;
|
|
|
|
}
|
2018-08-02 22:22:24 +00:00
|
|
|
return nullptr;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ProStringList &QMakeEvaluator::valuesRef(const ProKey &variableName)
|
|
|
|
{
|
|
|
|
ProValueMap::Iterator it = m_valuemapStack.top().find(variableName);
|
|
|
|
if (it != m_valuemapStack.top().end()) {
|
|
|
|
if (it->constBegin() == statics.fakeValue.constBegin())
|
|
|
|
it->clear();
|
|
|
|
return *it;
|
|
|
|
}
|
2016-06-30 15:26:01 +00:00
|
|
|
if (!isFunctParam(variableName)) {
|
|
|
|
ProValueMapStack::Iterator vmi = m_valuemapStack.end();
|
|
|
|
if (--vmi != m_valuemapStack.begin()) {
|
|
|
|
do {
|
|
|
|
--vmi;
|
|
|
|
ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
|
|
|
|
if (it != (*vmi).constEnd()) {
|
|
|
|
ProStringList &ret = m_valuemapStack.top()[variableName];
|
|
|
|
if (it->constBegin() != statics.fakeValue.constBegin())
|
|
|
|
ret = *it;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} while (vmi != m_valuemapStack.begin());
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
return m_valuemapStack.top()[variableName];
|
|
|
|
}
|
|
|
|
|
|
|
|
ProStringList QMakeEvaluator::values(const ProKey &variableName) const
|
|
|
|
{
|
|
|
|
ProValueMapStack::ConstIterator vmi = m_valuemapStack.constEnd();
|
2016-06-30 15:26:01 +00:00
|
|
|
for (bool first = true; ; first = false) {
|
2012-09-05 16:29:19 +00:00
|
|
|
--vmi;
|
|
|
|
ProValueMap::ConstIterator it = (*vmi).constFind(variableName);
|
|
|
|
if (it != (*vmi).constEnd()) {
|
|
|
|
if (it->constBegin() == statics.fakeValue.constBegin())
|
|
|
|
break;
|
|
|
|
return *it;
|
|
|
|
}
|
2016-06-30 15:26:01 +00:00
|
|
|
if (vmi == m_valuemapStack.constBegin())
|
|
|
|
break;
|
|
|
|
if (first && isFunctParam(variableName))
|
|
|
|
break;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
return ProStringList();
|
|
|
|
}
|
|
|
|
|
|
|
|
ProString QMakeEvaluator::first(const ProKey &variableName) const
|
|
|
|
{
|
|
|
|
const ProStringList &vals = values(variableName);
|
|
|
|
if (!vals.isEmpty())
|
|
|
|
return vals.first();
|
|
|
|
return ProString();
|
|
|
|
}
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
|
2012-09-05 16:29:19 +00:00
|
|
|
const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
|
|
|
|
{
|
2013-07-24 17:49:38 +00:00
|
|
|
QMakeParser::ParseFlags pflags = QMakeParser::ParseUseCache;
|
|
|
|
if (!(flags & LoadSilent))
|
|
|
|
pflags |= QMakeParser::ParseReportMissing;
|
|
|
|
if (ProFile *pro = m_parser->parsedProFile(fileName, pflags)) {
|
2012-09-05 16:29:19 +00:00
|
|
|
m_locationStack.push(m_current);
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn ok = visitProFile(pro, type, flags);
|
2012-09-05 16:29:19 +00:00
|
|
|
m_current = m_locationStack.pop();
|
|
|
|
pro->deref();
|
2013-06-14 07:50:46 +00:00
|
|
|
if (ok == ReturnTrue && !(flags & LoadHidden)) {
|
2012-09-05 16:29:19 +00:00
|
|
|
ProStringList &iif = m_valuemapStack.first()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
|
|
|
|
ProString ifn(fileName);
|
|
|
|
if (!iif.contains(ifn))
|
|
|
|
iif << ifn;
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
} else {
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileChecked(
|
2012-09-05 16:29:19 +00:00
|
|
|
const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
|
|
|
|
{
|
|
|
|
if (fileName.isEmpty())
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnFalse;
|
2016-01-26 13:38:54 +00:00
|
|
|
const QMakeEvaluator *ref = this;
|
2012-09-05 16:29:19 +00:00
|
|
|
do {
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProFile *pf : ref->m_profileStack)
|
2012-09-05 16:29:19 +00:00
|
|
|
if (pf->fileName() == fileName) {
|
|
|
|
evalError(fL1S("Circular inclusion of %1.").arg(fileName));
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnFalse;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
} while ((ref = ref->m_caller));
|
|
|
|
return evaluateFile(fileName, type, flags);
|
|
|
|
}
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
|
|
|
|
const QString &fileName, bool silent)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
|
|
|
QString fn = fileName;
|
|
|
|
if (!fn.endsWith(QLatin1String(".prf")))
|
|
|
|
fn += QLatin1String(".prf");
|
|
|
|
|
2013-06-03 17:08:10 +00:00
|
|
|
if (!m_featureRoots)
|
2012-09-05 16:29:19 +00:00
|
|
|
updateFeaturePaths();
|
2013-06-03 17:08:10 +00:00
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
m_featureRoots->mutex.lock();
|
|
|
|
#endif
|
2012-09-05 16:29:19 +00:00
|
|
|
QString currFn = currentFileName();
|
2013-06-03 17:08:10 +00:00
|
|
|
if (IoUtils::fileName(currFn) != IoUtils::fileName(fn))
|
|
|
|
currFn.clear();
|
|
|
|
// Null values cannot regularly exist in the hash, so they indicate that the value still
|
|
|
|
// needs to be determined. Failed lookups are represented via non-null empty strings.
|
|
|
|
QString *fnp = &m_featureRoots->cache[qMakePair(fn, currFn)];
|
|
|
|
if (fnp->isNull()) {
|
2018-03-01 15:43:46 +00:00
|
|
|
#ifdef QMAKE_OVERRIDE_PRFS
|
|
|
|
{
|
|
|
|
QString ovrfn(QLatin1String(":/qmake/override_features/") + fn);
|
|
|
|
if (QFileInfo::exists(ovrfn)) {
|
|
|
|
fn = ovrfn;
|
2013-06-03 17:08:10 +00:00
|
|
|
goto cool;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-01 15:43:46 +00:00
|
|
|
#endif
|
|
|
|
{
|
|
|
|
int start_root = 0;
|
|
|
|
const QStringList &paths = m_featureRoots->paths;
|
|
|
|
if (!currFn.isEmpty()) {
|
|
|
|
QStringRef currPath = IoUtils::pathName(currFn);
|
|
|
|
for (int root = 0; root < paths.size(); ++root)
|
|
|
|
if (currPath == paths.at(root)) {
|
|
|
|
start_root = root + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int root = start_root; root < paths.size(); ++root) {
|
|
|
|
QString fname = paths.at(root) + fn;
|
|
|
|
if (IoUtils::exists(fname)) {
|
|
|
|
fn = fname;
|
|
|
|
goto cool;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
#ifdef QMAKE_BUILTIN_PRFS
|
2013-06-03 17:08:10 +00:00
|
|
|
fn.prepend(QLatin1String(":/qmake/features/"));
|
2014-10-24 08:28:28 +00:00
|
|
|
if (QFileInfo::exists(fn))
|
2013-06-03 17:08:10 +00:00
|
|
|
goto cool;
|
2012-09-05 16:29:19 +00:00
|
|
|
#endif
|
2013-06-03 17:08:10 +00:00
|
|
|
fn = QLatin1String(""); // Indicate failed lookup. See comment above.
|
2012-09-05 16:29:19 +00:00
|
|
|
|
2013-06-03 17:08:10 +00:00
|
|
|
cool:
|
|
|
|
*fnp = fn;
|
|
|
|
} else {
|
|
|
|
fn = *fnp;
|
|
|
|
}
|
|
|
|
#ifdef PROEVALUATOR_THREAD_SAFE
|
|
|
|
m_featureRoots->mutex.unlock();
|
|
|
|
#endif
|
|
|
|
if (fn.isEmpty()) {
|
|
|
|
if (!silent)
|
|
|
|
evalError(fL1S("Cannot find feature %1").arg(fileName));
|
|
|
|
return ReturnFalse;
|
|
|
|
}
|
2012-09-05 16:29:19 +00:00
|
|
|
ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
|
|
|
|
ProString afn(fn);
|
|
|
|
if (already.contains(afn)) {
|
|
|
|
if (!silent)
|
|
|
|
languageWarning(fL1S("Feature %1 already included").arg(fileName));
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
already.append(afn);
|
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_CUMULATIVE
|
|
|
|
bool cumulative = m_cumulative;
|
|
|
|
m_cumulative = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// The path is fully normalized already.
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly);
|
2012-09-05 16:29:19 +00:00
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_CUMULATIVE
|
|
|
|
m_cumulative = cumulative;
|
|
|
|
#endif
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2012-09-19 19:56:16 +00:00
|
|
|
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto(
|
|
|
|
const QString &fileName, ProValueMap *values, LoadFlags flags)
|
2012-09-05 16:29:19 +00:00
|
|
|
{
|
2013-05-29 18:18:51 +00:00
|
|
|
QMakeEvaluator visitor(m_option, m_parser, m_vfs, m_handler);
|
2012-09-05 16:29:19 +00:00
|
|
|
visitor.m_caller = this;
|
|
|
|
visitor.m_outputDir = m_outputDir;
|
|
|
|
visitor.m_featureRoots = m_featureRoots;
|
2012-09-19 19:56:16 +00:00
|
|
|
VisitReturn ret = visitor.evaluateFileChecked(fileName, QMakeHandler::EvalAuxFile, flags);
|
|
|
|
if (ret != ReturnTrue)
|
|
|
|
return ret;
|
2012-09-05 16:29:19 +00:00
|
|
|
*values = visitor.m_valuemapStack.top();
|
|
|
|
ProKey qiif("QMAKE_INTERNAL_INCLUDED_FILES");
|
|
|
|
ProStringList &iif = m_valuemapStack.first()[qiif];
|
2016-01-26 13:38:54 +00:00
|
|
|
const auto ifns = values->value(qiif);
|
|
|
|
for (const ProString &ifn : ifns)
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!iif.contains(ifn))
|
|
|
|
iif << ifn;
|
2012-09-19 19:56:16 +00:00
|
|
|
return ReturnTrue;
|
2012-09-05 16:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::message(int type, const QString &msg) const
|
|
|
|
{
|
|
|
|
if (!m_skipLevel)
|
2015-12-17 15:27:23 +00:00
|
|
|
m_handler->message(type | (m_cumulative ? QMakeHandler::CumulativeEvalMessage : 0), msg,
|
2012-09-05 16:29:19 +00:00
|
|
|
m_current.line ? m_current.pro->fileName() : QString(),
|
|
|
|
m_current.line != 0xffff ? m_current.line : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PROEVALUATOR_DEBUG
|
|
|
|
void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (level <= m_debugLevel) {
|
|
|
|
fprintf(stderr, "DEBUG %d: ", level);
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (!m_current.pro)
|
|
|
|
fprintf(stderr, "DEBUG 1: ");
|
|
|
|
else if (m_current.line <= 0)
|
|
|
|
fprintf(stderr, "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName()));
|
|
|
|
else
|
|
|
|
fprintf(stderr, "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line);
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote)
|
|
|
|
{
|
|
|
|
QString ret;
|
|
|
|
ret.reserve(val.size() + 2);
|
|
|
|
const QChar *chars = val.constData();
|
|
|
|
bool quote = forceQuote || val.isEmpty();
|
|
|
|
for (int i = 0, l = val.size(); i < l; i++) {
|
|
|
|
QChar c = chars[i];
|
|
|
|
ushort uc = c.unicode();
|
|
|
|
if (uc < 32) {
|
|
|
|
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 {
|
|
|
|
switch (uc) {
|
|
|
|
case '\\':
|
|
|
|
ret += QLatin1String("\\\\");
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
ret += QLatin1String("\\\"");
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
ret += QLatin1String("\\'");
|
|
|
|
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 (quote) {
|
|
|
|
ret.prepend(QLatin1Char('"'));
|
|
|
|
ret.append(QLatin1Char('"'));
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas)
|
|
|
|
{
|
|
|
|
QString ret;
|
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProString &str : vals) {
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!ret.isEmpty()) {
|
|
|
|
if (commas)
|
|
|
|
ret += QLatin1Char(',');
|
|
|
|
ret += QLatin1Char(' ');
|
|
|
|
}
|
|
|
|
ret += formatValue(str);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists)
|
|
|
|
{
|
|
|
|
QString ret;
|
|
|
|
|
2016-01-26 13:38:54 +00:00
|
|
|
for (const ProStringList &list : lists) {
|
2012-09-05 16:29:19 +00:00
|
|
|
if (!ret.isEmpty())
|
|
|
|
ret += QLatin1String(", ");
|
|
|
|
ret += formatValueList(list);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|